高级UI仓库Android应用开发那些事

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

2019-08-13  本文已影响11人  NoBugException

在前两篇文章中,都使用了默认的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实现简单联动

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

[第一] 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;
    }
}

[本章完...]

上一篇下一篇

猜你喜欢

热点阅读