Android CoordinatorLayout之自定义Behavior
協調布局CoordinatorLayout
- CoordinatorLayout簡介
- 自定義Behavior有兩種方式
- 1、一個View監聽另一個View的狀態變化
- 2、View監聽CoordinatorLayout里的滑動狀態
- CoordinatorLayout交互原理
- 1.子View如何拿到Behavior?
- 2.Behavior是怎么接到事件的?
- 自己實現一個協調布局
CoordinatorLayout簡介
- CoordinatorLayout中文翻譯為協調布局,它可以協調子布局之間交互,當觸摸改變孩子View的時候會影響布局從而產生聯動效果,產生的根源在于Behavior類
- CoordinatorLayout自己并不控制View,所有的控制權都在Behavior這個類
- 系統里Behavior有FloatingActionButton.Behavior和AppBarLayout.Behavior等等;我們平時用戶那些聯動都是靠系統定義的Behavior來實現
- 系統里面Behavoir有些實現了復雜的控制功能。但是畢竟有限,我們可以通過自定義的方式來實現自己的Behavior
自定義Behavior有兩種方式
1、一個View監聽另一個View的狀態變化
看運行效果如下圖所示:
實現方式如下:
1.繼承CoordinatorLayout.Behavior類
/*** 第一種自定義Behavior方式*/ public class DependencyBehavior extends CoordinatorLayout.Behavior<Button>2.重寫Behavior兩個參數的構造方法
/*** 這個構造方法必須重載,因為在CoordinatorLayout里利用反射去獲取* Behavior的時候就是拿的這個構造*/public DependencyBehavior(Context context, AttributeSet attrs) {super(context, attrs);width = context.getResources().getDisplayMetrics().widthPixels;}3.重寫layoutDependsOn和onDependentViewChanged方法
/*** 確定依賴關系** @param parent* @param child 要執行動作的View* @param dependency child要依賴的View,,,也就是Child要監聽的View* @return 根據邏輯確定依賴關系*/@Overridepublic boolean layoutDependsOn(CoordinatorLayout parent, Button child, View dependency) {Log.e("Behavior", "----child: " + child.toString());Log.e("Behavior", "----dependency: " + dependency.toString());return dependency instanceof DragTextView;}/*** 狀態(大小、位置、顯示與否等)發生變化時該方法執行* 在這里我們定義child要執行的具體動作** @param parent* @param child* @param dependency* @return*/@Overridepublic boolean onDependentViewChanged(CoordinatorLayout parent, Button child, View dependency) {int top = dependency.getTop();int left = dependency.getLeft();int x = width - left - child.getWidth();int y = top;setPosition(child, x, y);return true;}4.在xml文件中引用app:layout_behavior
<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:layout_width="88dp"android:layout_height="88dp"android:background="@color/colorPrimary"android:gravity="center"android:text="child"android:textAllCaps="false"android:textColor="@android:color/white"app:layout_behavior="com.coordinator.behavior.first.DependencyBehavior" /><com.coordinator.behavior.first.DragTextViewandroid:layout_width="88dp"android:layout_height="88dp"android:background="@color/colorAccent"android:gravity="center"android:text="dependency"android:textAllCaps="false"android:textColor="@android:color/white" /></androidx.coordinatorlayout.widget.CoordinatorLayout>2、View監聽CoordinatorLayout里的滑動狀態
這個要求CoordinatorLayout里面要有滑動的控件,最少要實現了NestedScrollingChild接口,比如RecyclerView,NestScrollView等滑動控件;
看運行效果:
1.繼承CoordinatorLayout.Behavior類或者系統的Behavior類
2.重寫Behavior兩個參數的構造方法
/*** 這個構造方法必須重載,因為在CoordinatorLayout里利用反射去獲取* Behavior的時候就是拿的這個構造*/public ScrollBehavior(Context context, AttributeSet attrs) {super(context, attrs);}3.至少重寫onStartNestedScroll和onNestedScroll方法,當然還有其他也可以重寫
/*** 當子View調用NestedScrollingChild的方法startNestedScroll時,會調用該方法。* 一定要按照自己的需求返回true,該方法決定了當前控件是否能接收到其內部View(并非是直接子View)滑動時的參數** @param coordinatorLayout* @param child 這個是要依賴其他滾動View的另一個View,這里是FloatingActionButton* @param directTargetChild 直接觸發嵌套滾動的view的對象* @param target 觸發嵌套滾動的view (在這里如果不涉及多層嵌套的話,target和directTargetChild是相同的)* @param nestedScrollAxes 嵌套滑動的方向標志* @return 根據返回值確定我們關心那個方向的滑動(x軸 / y軸),這里我們關心的是y軸方向*/@Overridepublic boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,FloatingActionButton child,View directTargetChild,View target,int nestedScrollAxes) {Log.e("123", "directTargetChild: " + directTargetChild);Log.e("123", "target: " + target);//在這里directTargetChild和target都是NestedScrollViewreturn (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;}/*** 當子View調用dispatchNestedPreScroll時會調用該方法* 該方法會傳入內部View移動的dx,dy,如果你需要消耗一定的dx,dy,就會通過最后一個參數consumed* 進行指定。例如我要消耗一半的dy,就可以寫consumed[1]=dy/2* dx表示本次滾動x方向產生的總距離** @param coordinatorLayout* @param child 此處是FloatingActionButton* @param target 同上* @param dxConsumed target已經消費的x方向的距離* @param dyConsumed target已經消費的x方向的距離* @param dxUnconsumed x方向剩下的滑動距離* @param dyUnconsumed y方向剩下的滑動距離*/@Overridepublic void onNestedScroll(CoordinatorLayout coordinatorLayout,FloatingActionButton child,View target,int dxConsumed,int dyConsumed,int dxUnconsumed,int dyUnconsumed) {//Log.e("234", "===============dxConsumed: " + dxConsumed);Log.e("234", "===============dyConsumed: " + dyConsumed);//Log.e("234","===============dxUnconsumed: "+dxUnconsumed);Log.e("234", "===============dyUnconsumed: " + dyUnconsumed);if (((dyConsumed > 0 && dyUnconsumed == 0)|| (dyConsumed == 0 && dyUnconsumed > 0))) {//上滑隱藏child.animate().scaleY(0).scaleX(0).setDuration(200).start();} else if (((dyConsumed < 0 && dyUnconsumed == 0)|| (dyConsumed == 0 && dyUnconsumed < 0))) {//下滑顯示child.animate().scaleY(1).scaleX(1).setDuration(200).start();}}4.在xml文件中引用app:layout_behavior
<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><androidx.core.widget.NestedScrollViewandroid:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="@string/text" /></androidx.core.widget.NestedScrollView><com.google.android.material.floatingactionbutton.FloatingActionButtonandroid:id="@+id/fab"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="end|bottom"android:layout_margin="16dp"android:src="@android:drawable/ic_dialog_email"app:backgroundTint="@color/colorAccent"app:elevation="8dp"app:fabSize="mini"app:layout_behavior="com.coordinator.behavior.second.ScrollBehavior"app:pressedTranslationZ="16dp"app:rippleColor="@color/colorPrimary" /></androidx.coordinatorlayout.widget.CoordinatorLayout>CoordinatorLayout交互原理
1.子View如何拿到Behavior?
比如下面的xml文件中是怎么拿到Behavior的呢?
CoordinatorLayout有一個過程是要添加孩子View,我們就先從哪里入手吧,
這里的LayoutParam肯定是CoordinatorLayout.LayoutParam生成的吧;
這里明顯Behavior就是在LayoutParams里面解析得來的,就是解析的app:layout_behavior屬性;
2.Behavior是怎么接到事件的?
上面只是代碼一種特殊的情況,其實在事件傳遞中,你會發現,所有出現Behavior的地方Behavior都會去接管處理!這里不想云里霧里一點點分析,那樣容易只見樹木不見森林。你就從
這些事件往代碼里面分析,就能夠看到代碼段;
Behavior b = lp.getBehavior()你懂得,就是把所有的事件處理都轉給它;
自己實現一個協調布局
運行效果如下圖:
1.實現自己的協調布局BehaviorCoordinatorLayout
2.實現自己的LayoutParams
@Overridepublic LayoutParams generateLayoutParams(AttributeSet attrs) {return new LP(getContext(), attrs);}public static class LP extends RelativeLayout.LayoutParams {BeHavior mBehavior;public LP(Context c, AttributeSet attrs) {super(c, attrs);TypedArray ta = c.obtainStyledAttributes(attrs, R.styleable.BehaviorCoordinatorLayout_Layout);if (ta.hasValue(R.styleable.BehaviorCoordinatorLayout_Layout_custom_layout_behavior)) {String clStr = ta.getString(R.styleable.BehaviorCoordinatorLayout_Layout_custom_layout_behavior);Log.e("tag", clStr);mBehavior = parseBehavior(c, attrs, clStr);Log.e("tag", (mBehavior == null) + " ");}ta.recycle();}public BeHavior parseBehavior(Context context, AttributeSet attrs, String name) {try {Class<?> clazz = Class.forName(name, false, context.getClassLoader());Constructor c = clazz.getConstructor(Context.class, AttributeSet.class);c.setAccessible(true);return (BeHavior) c.newInstance(context, attrs);} catch (Exception e) {e.printStackTrace();return null;}}}3.實現自己的Behavior類
public class BeHavior<V extends View> {public BeHavior(Context context, AttributeSet attrs) {}public boolean onLayoutChild(BehaviorCoordinatorLayout parent, V child,int layoutDirection) {return false;}public void onNestedScroll(BehaviorCoordinatorLayout coordinatorLayout, V child,View target, int dxConsumed, int dyConsumed, int dxUnconsumed,int dyUnconsumed) {}}4.實現自己的Behavior子類
ublic class ImageViewBehavior extends BeHavior<ImageView> {public ImageViewBehavior(Context context, AttributeSet attrs) {super(context, attrs);}int maxHeight = 400;int originHeight = 0;@Overridepublic boolean onLayoutChild(BehaviorCoordinatorLayout parent, ImageView child, int layoutDirection) {if (originHeight == 0) {originHeight = child.getHeight();}return super.onLayoutChild(parent, child, layoutDirection);}@Overridepublic void onNestedScroll(BehaviorCoordinatorLayout coordinatorLayout, ImageView child,View scrollview, int dxConsumed, int dyConsumed,int dxUnconsumed, int dyUnconsumed) {if (scrollview.getScrollY() > 0) {BehaviorCoordinatorLayout.LP lp = (BehaviorCoordinatorLayout.LP) child.getLayoutParams();lp.height = lp.height - Math.abs(dyConsumed);if (lp.height <= originHeight) {lp.height = originHeight;}child.setLayoutParams(lp);} else if (scrollview.getScrollY() == 0) {BehaviorCoordinatorLayout.LP lp = (BehaviorCoordinatorLayout.LP) child.getLayoutParams();lp.height = lp.height + Math.abs(dyUnconsumed);if (lp.height >= maxHeight) {lp.height = maxHeight;}child.setLayoutParams(lp);}} } public class ToolbarBehavior extends BeHavior<Toolbar> {public ToolbarBehavior(Context context, AttributeSet attrs) {super(context, attrs);}int maxHeight = 400;@Overridepublic void onNestedScroll(BehaviorCoordinatorLayout coordinatorLayout, Toolbar child, View scrollView,int dxConsumed, int dyConsumed,int dxUnconsumed, int dyUnconsumed) {if (scrollView.getScrollY() <= maxHeight) {child.setAlpha(scrollView.getScrollY() * 1.0f / maxHeight);} else if (scrollView.getScrollY() == 0) {child.setAlpha(0);}}}5.xml布局中引用
<?xml version="1.0" encoding="utf-8"?> <com.coordinator.behavior.custom.BehaviorCoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:id="@+id/img"android:layout_width="match_parent"android:layout_height="50dp"android:adjustViewBounds="true"android:scaleType="fitXY"android:contentDescription="@string/app_name"android:src="@mipmap/london"app:custom_layout_behavior="com.coordinator.behavior.custom.ImageViewBehavior" /><androidx.core.widget.NestedScrollViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@id/img"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="@string/text" /></androidx.core.widget.NestedScrollView><androidx.appcompat.widget.Toolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="50dp"android:background="@color/colorPrimary"app:custom_layout_behavior="com.coordinator.behavior.custom.ToolbarBehavior" /> </com.coordinator.behavior.custom.BehaviorCoordinatorLayout>總結
以上是生活随笔為你收集整理的Android CoordinatorLayout之自定义Behavior的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么样的知识付费系统功能,更有利于平台与
- 下一篇: GCP/临床试验基础知识集锦