美文网首页高级UI仓库Android应用开发那些事
Android之嵌套联动<四>:自定义Behavio

Android之嵌套联动<四>:自定义Behavio

作者: NoBugException | 来源:发表于2019-08-13 18:35 被阅读11次

    在前两篇文章中,都使用了默认的Behavior,那么默认的Behavior是什么呢?

    首先看一下布局代码:

    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        tools:context=".MainActivity"
        android:background="#cccccc">
    
        <android.support.design.widget.AppBarLayout
            android:id="@+id/app_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="visible"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
    
            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolbar_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:contentScrim="?attr/colorPrimaryDark"
                app:title="CollapsingToolbarLayout演示"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                app:collapsedTitleGravity="center"
                app:expandedTitleGravity="bottom"
                app:scrimAnimationDuration="500"
                app:toolbarId="@+id/toolbar">
    
                <ImageView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:scaleType="centerCrop"
                    android:src="@mipmap/che4"
                    android:visibility="visible" />
    
                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    android:background="@android:color/transparent"
                    app:title="我是Toolbar"
                    app:navigationIcon="@mipmap/back"
                    app:layout_collapseMode="parallax"
                    app:layout_collapseParallaxMultiplier="0.7"  />
    
            </android.support.design.widget.CollapsingToolbarLayout>
        </android.support.design.widget.AppBarLayout>
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="visible"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />
    
    </android.support.design.widget.CoordinatorLayout>
    

    在RecyclerView标签中有一个配置

    app:layout_behavior="@string/appbar_scrolling_view_behavior" 
    

    该string的值为:

    <string name="appbar_scrolling_view_behavior" translatable="false">android.support.design.widget.AppBarLayout$ScrollingViewBehavior</string>
    

    可以发现,默认的Behavior其实就是AppBarLayout控件的内部类ScrollingViewBehaviorScrollingViewBehavior也有父类,它的顶级父类是CoordinatorLayout.Behavior,本章将详细说明抽象类CoordinatorLayout.Behavior,其源码如下:

    public abstract static class Behavior<V extends View> {
        public Behavior() {
        }
    
        public Behavior(Context context, AttributeSet attrs) {
        }
    
        public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams params) {
        }
    
        public void onDetachedFromLayoutParams() {
        }
    
        public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull MotionEvent ev) {
            return false;
        }
    
        public boolean onTouchEvent(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull MotionEvent ev) {
            return false;
        }
    
        @ColorInt
        public int getScrimColor(@NonNull CoordinatorLayout parent, @NonNull V child) {
            return -16777216;
        }
    
        @FloatRange(
            from = 0.0D,
            to = 1.0D
        )
        public float getScrimOpacity(@NonNull CoordinatorLayout parent, @NonNull V child) {
            return 0.0F;
        }
    
        public boolean blocksInteractionBelow(@NonNull CoordinatorLayout parent, @NonNull V child) {
            return this.getScrimOpacity(parent, child) > 0.0F;
        }
    
        public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull View dependency) {
            return false;
        }
    
        public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull View dependency) {
            return false;
        }
    
        public void onDependentViewRemoved(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull View dependency) {
        }
    
        public boolean onMeasureChild(@NonNull CoordinatorLayout parent, @NonNull V child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
            return false;
        }
    
        public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull V child, int layoutDirection) {
            return false;
        }
    
        public static void setTag(@NonNull View child, @Nullable Object tag) {
            CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)child.getLayoutParams();
            lp.mBehaviorTag = tag;
        }
    
        @Nullable
        public static Object getTag(@NonNull View child) {
            CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)child.getLayoutParams();
            return lp.mBehaviorTag;
        }
    
        /** @deprecated */
        @Deprecated
        public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes) {
            return false;
        }
    
        public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
            return type == 0 ? this.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes) : false;
        }
    
        /** @deprecated */
        @Deprecated
        public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes) {
        }
    
        public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
            if (type == 0) {
                this.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, axes);
            }
    
        }
    
        /** @deprecated */
        @Deprecated
        public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target) {
        }
    
        public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int type) {
            if (type == 0) {
                this.onStopNestedScroll(coordinatorLayout, child, target);
            }
    
        }
    
        /** @deprecated */
        @Deprecated
        public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        }
    
        public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
            if (type == 0) {
                this.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
            }
    
        }
    
        /** @deprecated */
        @Deprecated
        public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dx, int dy, @NonNull int[] consumed) {
        }
    
        public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
            if (type == 0) {
                this.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
            }
    
        }
    
        public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, float velocityX, float velocityY, boolean consumed) {
            return false;
        }
    
        public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, float velocityX, float velocityY) {
            return false;
        }
    
        @NonNull
        public WindowInsetsCompat onApplyWindowInsets(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull WindowInsetsCompat insets) {
            return insets;
        }
    
        public boolean onRequestChildRectangleOnScreen(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull Rect rectangle, boolean immediate) {
            return false;
        }
    
        public void onRestoreInstanceState(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull Parcelable state) {
        }
    
        @Nullable
        public Parcelable onSaveInstanceState(@NonNull CoordinatorLayout parent, @NonNull V child) {
            return BaseSavedState.EMPTY_STATE;
        }
    
        public boolean getInsetDodgeRect(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull Rect rect) {
            return false;
        }
    }
    

    由于自定义Behavior,总是直接或间接集成CoordinatorLayout的内部类Behavior,所以CoordinatorLayout+Behavior就可以实现嵌套联动

    (1)Behavior的基本构造方法

    当自定义Behavior时,必须写全两个基本构造方法,否则Behavior的初始化可能会失败,代码如下:

    public class MyCustomBehavior extends CoordinatorLayout.Behavior {
    
        public MyCustomBehavior() {
        }
    
        public MyCustomBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    }
    
    (2)整理下布局
    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        tools:context=".MainActivity"
        android:background="#cccccc">
    
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            android:background="@mipmap/che4"
            android:visibility="visible"
            app:layout_behavior="@string/mycustombehavior"/>
    
        <Button
            android:id="@+id/textview"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="请让我上下移动"
            android:layout_marginTop="400dp"
            android:textSize="20sp"
            android:background="#00ffff"
            android:gravity="center" />
    
    </android.support.design.widget.CoordinatorLayout>
    

    其中,mycustombehavior已在string资源文件中配置好值了

    <resources>
    
        <string name="mycustombehavior">com.xxx.ooo.recycleview.MyCustomBehavior</string>
    
    </resources>
    

    当然,也可以直接应用对应的类

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            android:background="@mipmap/che4"
            android:visibility="visible"
            app:layout_behavior=".MyCustomBehavior"/>
    
    (3)layoutDependsOn和onDependentViewChanged实现简单联动
    • layoutDependsOn:确定使用Behavior的View要依赖的View的类型
    • onDependentViewChanged:当被依赖的View状态改变时回调

    实现简单联动需要满足几个前提:

    [第一] Behavior必须在CoordinatorLayout下才能生效
    [第二] 必须确定Behavior的View要依赖的View的类型(layoutDependsOn)
    [第三] 被依赖的View状态必须不停的改变才能使联动生效。

    本例中,Button是依赖View,ImageView配置layout_behavior与Button形成依赖关系,通过触摸移动来保证Button的状态不停的改变

        findViewById(R.id.textview).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()){
    
                    case MotionEvent.ACTION_MOVE:
                        //v.setX(event.getRawX()-v.getWidth()/2);
                        v.setY(event.getRawY()-v.getHeight()/2);
                        break;
                }
                 return false;
            }
        });
    

    MyCustomBehavior代码如下:

    public class MyCustomBehavior extends CoordinatorLayout.Behavior {
    
        private float buttonY;
    
        public MyCustomBehavior() {
        }
    
        public MyCustomBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
            return dependency instanceof Button;
        }
    
        @Override
        public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
            if (buttonY == 0) {
                //按钮的初始高度
                buttonY = dependency.getY();
            }
            child.setTranslationY(dependency.getY() - buttonY);
            return true;
        }
    }
    

    效果如下:

    134.gif

    以上效果不是很友好,接下来的例子中,将结合AppBarLayout+CollapsingToolbarLayout再演示一遍效果

    135.gif

    代码如下:

    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        tools:context=".MainActivity"
        android:background="#cccccc">
    
        <android.support.design.widget.AppBarLayout
            android:id="@+id/app_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="visible"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
    
            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolbar_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:contentScrim="#00ffffff"
                app:title="CollapsingToolbarLayout演示"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                app:collapsedTitleGravity="center"
                app:expandedTitleGravity="bottom">
    
                <ImageView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:scaleType="centerCrop"
                    android:src="@mipmap/che4"
                    android:visibility="visible" />
    
                <!--<android.support.v7.widget.Toolbar-->
                    <!--android:id="@+id/toolbar"-->
                    <!--android:layout_width="match_parent"-->
                    <!--android:layout_height="?attr/actionBarSize"-->
                    <!--android:background="@android:color/transparent"-->
                    <!--app:title="我是Toolbar"-->
                    <!--app:navigationIcon="@mipmap/back"-->
                    <!--app:layout_collapseMode="parallax"-->
                    <!--app:layout_collapseParallaxMultiplier="0.7"  />-->
    
            </android.support.design.widget.CollapsingToolbarLayout>
        </android.support.design.widget.AppBarLayout>
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="visible"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="#E66C6C"
            android:text="我是一个文本"
            android:gravity="center"
            android:textColor="#ffffff"
            android:textSize="20sp"
            app:layout_behavior=".MyCustomBehavior"/>
    
    </android.support.design.widget.CoordinatorLayout>
    
    public class MyCustomBehavior extends CoordinatorLayout.Behavior {
    
        private float deltaY;
    
        public MyCustomBehavior() {
        }
    
        public MyCustomBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
            return dependency instanceof RecyclerView;
        }
    
        @Override
        public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
            if (deltaY == 0) {
                deltaY = dependency.getY() - child.getHeight();
            }
            float dy = dependency.getY() - child.getHeight();
            dy = dy < 0 ? 0 : dy;
            float alpha = 1 - (dy / deltaY);
            child.setAlpha(alpha);
            return true;
        }
    }
    

    [本章完...]

    相关文章

      网友评论

        本文标题:Android之嵌套联动<四>:自定义Behavio

        本文链接:https://www.haomeiwen.com/subject/ksvmjctx.html