美文网首页android开发Android开发感悟Android知识库
UI之自定义Behavior实现AppBarLayout越界弹性

UI之自定义Behavior实现AppBarLayout越界弹性

作者: zly394 | 来源:发表于2017-03-09 15:04 被阅读3065次

    先上效果

    一、继承AppBarLayout.Behavior

    AppBarLayout有一个默认的Behavior,即AppBarLayout.BehaviorAppBarLayout.Behavior已注解的方式设置给AppBarLayout。

    @CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
    public class AppBarLayout extends LinearLayout {
        ...
    }
    

    1.继承AppBarLayout.Behavior自定义Behavior

    我们可以继承AppBarLayout.Behavior并重新设置给AppBarLayout来修改AppBarLayout的默认滚动行为,实现AppBarLayout的弹性越界效果就可以通过这种方式实现。

    继承AppBarLayout.Behavior需要重写构造方法

    public class AppBarLayoutOverScrollViewBehavior extends AppBarLayout.Behavior {
    
        public AppBarLayoutOverScrollViewBehavior() {
        }
    
        public AppBarLayoutOverScrollViewBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
    }
    

    2.将自定义的Behavior设置给AppBarLayout

    可以通过两种方式将自定义的Behavior设置给AppBarLayout

    1. 在布局文件中设置

      <android.support.design.widget.AppBarLayout
          ...
          app:layout_behavior="packageName.AppBarLayoutOverScrollViewBehavior">
      </android.support.design.widget.AppBarLayout>
      
    2. 在代码中设置

      AppBarLayout appBar = (AppBarLayout) findViewById(R.id.appbar);
      CoordinatorLayout.LayoutParams params = new CoordinatorLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
      params.setBehavior(new AppBarLayoutOverScrollViewBehavior());
      appBar.setLayoutParams(params);
      

    设置完成后,自定义的Behavior就会生效,但是因为没有重写任何方法,所以AppBarLayout的滚动行为不会发生变化。

    二、Behavior中的回调方法分析

    将自定义的Behavior设置给AppBarLayout后,可以在自定义的Behavior中重写滚动相关回调方法

    public class AppBarLayoutOverScrollViewBehavior extends AppBarLayout.Behavior {
    
        ...
    
        /**
         * AppBarLayout布局时调用
         *
         * @param parent 父布局CoordinatorLayout
         * @param abl 使用此Behavior的AppBarLayout
         * @param layoutDirection 布局方向
         * @return 返回true表示子View重新布局,返回false表示请求默认布局
         */
        @Override
        public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl, int layoutDirection) {
            return super.onLayoutChild(parent, abl, layoutDirection);
        }
    
        /**
         * 当CoordinatorLayout的子View尝试发起嵌套滚动时调用
         *
         * @param parent 父布局CoordinatorLayout
         * @param child 使用此Behavior的AppBarLayout
         * @param directTargetChild CoordinatorLayout的子View,或者是包含嵌套滚动操作的目标View
         * @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
         * @param nestedScrollAxes 嵌套滚动的方向
         * @return 返回true表示接受滚动
         */
        @Override
        public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
            return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
        }
    
        /**
         * 当嵌套滚动已由CoordinatorLayout接受时调用
         *
         * @param coordinatorLayout 父布局CoordinatorLayout
         * @param child 使用此Behavior的AppBarLayout
         * @param directTargetChild CoordinatorLayout的子View,或者是包含嵌套滚动操作的目标View
         * @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
         * @param nestedScrollAxes 嵌套滚动的方向
         */
        @Override
        public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
            super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
        }
    
        /**
         * 当准备开始嵌套滚动时调用
         *
         * @param coordinatorLayout 父布局CoordinatorLayout
         * @param child 使用此Behavior的AppBarLayout
         * @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
         * @param dx 用户在水平方向上滑动的像素数
         * @param dy 用户在垂直方向上滑动的像素数
         * @param consumed 输出参数,consumed[0]为水平方向应该消耗的距离,consumed[1]为垂直方向应该消耗的距离
         */
        @Override
        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) {
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
        }
    
        /**
         * 嵌套滚动时调用
         *
         * @param coordinatorLayout 父布局CoordinatorLayout
         * @param child 使用此Behavior的AppBarLayout
         * @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
         * @param dxConsumed 由目标View滚动操作消耗的水平像素数
         * @param dyConsumed 由目标View滚动操作消耗的垂直像素数
         * @param dxUnconsumed 由用户请求但是目标View滚动操作未消耗的水平像素数
         * @param dyUnconsumed 由用户请求但是目标View滚动操作未消耗的垂直像素数
         */
        @Override
        public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
            super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
        }
    
        /**
         * 当嵌套滚动的子View准备快速滚动时调用
         *
         * @param coordinatorLayout 父布局CoordinatorLayout
         * @param child 使用此Behavior的AppBarLayout
         * @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
         * @param velocityX 水平方向的速度
         * @param velocityY 垂直方向的速度
         * @return 如果Behavior消耗了快速滚动返回true
         */
        @Override
        public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY) {
            return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
        }
    
        /**
         * 当嵌套滚动的子View快速滚动时调用
         *
         * @param coordinatorLayout 父布局CoordinatorLayout
         * @param child 使用此Behavior的AppBarLayout
         * @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
         * @param velocityX 水平方向的速度
         * @param velocityY 垂直方向的速度
         * @param consumed 如果嵌套的子View消耗了快速滚动则为true
         * @return 如果Behavior消耗了快速滚动返回true
         */
        @Override
        public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {
            return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
        }
    
        /**
         * 当定制滚动时调用
         *
         * @param coordinatorLayout 父布局CoordinatorLayout
         * @param abl 使用此Behavior的AppBarLayout
         * @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
         */
        @Override
        public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target) {
            super.onStopNestedScroll(coordinatorLayout, abl, target);
        }
    }
    

    可以通过打印log来观察AppBarLayout在滚动时Behavior中回调方法的调用情况。

    通过观察可以发现:

    1. 上滑时
      • 当AppBarLayout由展开到收起时,会依次调用onStartNestedScroll()->onNestedScrollAccepted()->onNestedPreScroll()->onStopNestedScroll()
      • 当AppBarLayout收起后继续向上滑动时,会依次调用onStartNestedScroll()->onNestedScrollAccepted()->onNestedPreScroll()->onNestedScroll()->onStopNestedScroll()
    2. 下滑时
      • 当AppBarLayout全部展开时(即未到顶部时),会依次调用onStartNestedScroll()->onNestedScrollAccepted()->onNestedPreScroll()->onNestedScroll()->onStopNestedScroll()
      • 当AppBarLayout全部展开时(即到顶部时),继续向下滑动屏幕,会依次调用onStartNestedScroll()->onNestedScrollAccepted()->onNestedPreScroll()->onNestedScroll()->onStopNestedScroll()
    3. 当有快速滑动时会在onStopNestedScroll()前依次调用onNestedPreFling()->onNestedFling()

    所以要修改AppBarLayout的越界行为可以重写onNestedPreScroll()或onNestedScroll(),因为AppBarLayout收起时不会调用onNestedScroll(),所以只能选择重写onNestedPreScroll(),具体原因下面会有说明。

    三、重写Behavior的相关方法

    1.获取越界时需要改变尺寸的View

    布局时会调用onLayoutChild(),所以在该方法中可获取需要改变尺寸的View,可以使用View的findViewWithTag方法获取指定的View,并初始化属性。

    public class AppBarLayoutOverScrollViewBehavior extends AppBarLayout.Behavior {
        private static final String TAG = "overScroll";
        private View mTargetView;       // 目标View
        private int mParentHeight;      // AppBarLayout的初始高度
        private int mTargetViewHeight;  // 目标View的高度
    
        @Override
        public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl, int layoutDirection) {
            boolean handled = super.onLayoutChild(parent, abl, layoutDirection);
            // 需要在调用过super.onLayoutChild()方法之后获取
            if (mTargetView == null) {
                mTargetView = parent.findViewWithTag(TAG);
                if (mTargetView != null) {
                    initial(abl);
                }
            }
            return handled;
        }
        
        private void initial(AppBarLayout abl) {
            // 必须设置ClipChildren为false,这样目标View在放大时才能超出布局的范围
            abl.setClipChildren(false);
            mParentHeight = abl.getHeight();
            mTargetViewHeight = mTargetView.getHeight();
        }
    
        ...
    
    }
    

    需要在布局文件或代码中给目标View指定tag,如下:

    <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">
    
        <android.support.design.widget.AppBarLayout
            android:id="@+id/appbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fitsSystemWindows="true"
            android:theme="@style/AppTheme.AppBarOverlay"
            android:transitionName="picture"
            app:layout_behavior="com.zly.exifviewer.widget.behavior.AppBarLayoutOverScrollViewBehavior"
            tools:targetApi="lollipop">
    
            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/collapsingToolbarLayout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:contentScrim="@color/colorPrimary"
                app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
                app:statusBarScrim="@color/colorPrimaryDark">
    
                <ImageView
                    android:id="@+id/siv_picture"
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    android:fitsSystemWindows="true"
                    android:foreground="@drawable/shape_fg_picture"
                    android:scaleType="centerCrop"
                    android:tag="overScroll"
                    app:layout_collapseMode="parallax"
                    tools:src="@android:drawable/sym_def_app_icon" />
    
                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    app:contentInsetEnd="64dp"
                    app:layout_collapseMode="pin"
                    app:popupTheme="@style/AppTheme.PopupOverlay" />
            </android.support.design.widget.CollapsingToolbarLayout>
        </android.support.design.widget.AppBarLayout>
    
        <android.support.v4.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
            ...
    
        </android.support.v4.widget.NestedScrollView>
    
    </android.support.design.widget.CoordinatorLayout>
    

    2.下滑处理

    重写onNestedPreScroll()修改AppBarLayou滑动的顶部后的行为

    private static final float TARGET_HEIGHT = 500; // 最大滑动距离
    private float mTotalDy;     // 总滑动的像素数
    private float mLastScale;   // 最终放大比例
    private int mLastBottom;    // AppBarLayout的最终Bottom值
    
    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) {
        // 1.mTargetView不为null
        // 2.是向下滑动,dy<0表示向下滑动
        // 3.AppBarLayout已经完全展开,child.getBottom() >= mParentHeight
        if (mTargetView != null && dy < 0 && child.getBottom() >= mParentHeight) {
            // 累加垂直方向上滑动的像素数
            mTotalDy += -dy;
            // 不能大于最大滑动距离
            mTotalDy = Math.min(mTotalDy, TARGET_HEIGHT);
            // 计算目标View缩放比例,不能小于1
            mLastScale = Math.max(1f, 1f + mTotalDy / TARGET_HEIGHT);
            // 缩放目标View
            ViewCompat.setScaleX(mTargetView, mLastScale);
            ViewCompat.setScaleY(mTargetView, mLastScale);
            // 计算目标View放大后增加的高度
            mLastBottom = mParentHeight + (int) (mTargetViewHeight / 2 * (mLastScale - 1));
            // 修改AppBarLayout的高度
            child.setBottom(mLastBottom);
        } else {
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
        }
    }
    

    此时可以实现下滑越界时目标View放大,AppBarLayout变高的效果。

    3.上滑处理

    下滑时目标View放大,AppBarLayout变高,如果此时用户不松开手指,直接上滑,需要目标View缩小,并且AppBarLayout变高。

    默认情况下AppBarLayout的滑动是通过修改top和bottom实现的,所以上滑时,AppBarLayout为整体向上移动,高度不会发生改变,并且AppBarLayout下面的ScrollView也会向上滚动;而我们需要的是在AppBarLayout的高度大于原始高度时,减小AppBarLayout的高度,top不发生改变,并且AppBarLayout下面的ScrollView不会向上滚动

    AppBarLayout上滑时不会调用onNestedScroll(),所以只能在onNestedPreScroll()方法中修改,这也是为什么选择onNestedPreScroll()方法的原因

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) {
        if (mTargetView != null && dy < 0 && child.getBottom() >= mParentHeight) {
            ...
        } else 
        // 1.mTargetView不为null
        // 2.是向上滑动,dy>0表示向下滑动
        // 3.AppBarLayout尚未恢复到原始高度child.getBottom() > mParentHeight
        if (mTargetView != null && dy > 0 && child.getBottom() > mParentHeight) {
            // 累减垂直方向上滑动的像素数
            mTotalDy -= dy;
            // 计算目标View缩放比例,不能小于1
            mLastScale = Math.max(1f, 1f + mTotalDy / TARGET_HEIGHT);
            // 缩放目标View
            ViewCompat.setScaleX(mTargetView, mLastScale);
            ViewCompat.setScaleY(mTargetView, mLastScale);
            // 计算目标View缩小后减少的高度
            mLastBottom = mParentHeight + (int) (mTargetViewHeight / 2 * (mLastScale - 1));
            // 修改AppBarLayout的高度
            child.setBottom(mLastBottom);
            // 保持target不滑动
            target.setScrollY(0);
        } else {
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
        }
    }
    

    与上滑的逻辑基本一直,所以可写为一个方法

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) {
        if (mTargetView != null && ((dy < 0 && child.getBottom() >= mParentHeight) || (dy > 0 && child.getBottom() > mParentHeight))) {
            scale(child, target, dy);
        } else {
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
        }
    }
    
    private void scale(AppBarLayout abl, View target, int dy) {
        mTotalDy += -dy;
        mTotalDy = Math.min(mTotalDy, TARGET_HEIGHT);
        mLastScale = Math.max(1f, 1f + mTotalDy / TARGET_HEIGHT);
        ViewCompat.setScaleX(mTargetView, mLastScale);
        ViewCompat.setScaleY(mTargetView, mLastScale);
        mLastBottom = mParentHeight + (int) (mTargetViewHeight / 2 * (mLastScale - 1));
        abl.setBottom(mLastBottom);
        target.setScrollY(0);
    }
    

    4.还原

    当AppBarLayout处于越界时,如果用户松开手指,此时应该让目标View和AppBarLayout都还原到原始状态,重写onStopNestedScroll()方法

    @Override
    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target) {
        recovery(abl);
        super.onStopNestedScroll(coordinatorLayout, abl, target);
    }
    
    private void recovery(final AppBarLayout abl) {
        if (mTotalDy > 0) {
            mTotalDy = 0;
            // 使用属性动画还原
            ValueAnimator anim = ValueAnimator.ofFloat(mLastScale, 1f).setDuration(200);
            anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (float) animation.getAnimatedValue();
                    ViewCompat.setScaleX(mTargetView, value);
                    ViewCompat.setScaleY(mTargetView, value);
                    abl.setBottom((int) (mLastBottom - (mLastBottom - mParentHeight) * animation.getAnimatedFraction()));
                }
            });
            anim.start();
        }
    }
    

    5.优化

    由于用户在滑动时有可能触发快速滑动,会导致在AppBarLayout收起后触发还原动画,重新修改AppBarLayout的Bottom,从而显示错误,所以当发生快速滑动时需要禁止还原动画,直接还原到初始状态

    private boolean isAnimate;  //是否有动画
    
    @Override
    public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
        // 开始滑动时,启用动画
        isAnimate = true;
        return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
    }
    
    @Override
    public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY) {
        // 如果触发了快速滚动且垂直方向上速度大于100,则禁用动画
        if (velocityY > 100) {
            isAnimate = false;
        }
        return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
    }
    
    private void recovery(final AppBarLayout abl) {
        if (mTotalDy > 0) {
            mTotalDy = 0;
            if (isAnimate) {
                ValueAnimator anim = ValueAnimator.ofFloat(mLastScale, 1f).setDuration(200);
                anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float value = (float) animation.getAnimatedValue();
                        ViewCompat.setScaleX(mTargetView, value);
                        ViewCompat.setScaleY(mTargetView, value);
                        abl.setBottom((int) (mLastBottom - (mLastBottom - mParentHeight) * animation.getAnimatedFraction()));
                    }
                });
                anim.start();
            } else {
                ViewCompat.setScaleX(mTargetView, 1f);
                ViewCompat.setScaleY(mTargetView, 1f);
                abl.setBottom(mParentHeight);
            }
        }
    }
    
    

    可以从这里获取代码

    相关文章

      网友评论

      • gerg_peng:写的挺好的。
      • 50191e06a3ba:@zly394 AppBarLayout里再添加一个TabLayout的话,滑动的时候tablay不能滑动,请问这是什么原因啊?
        <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        app:layout_behavior="cn.cloudworkshop.miaoding.view.AppBarLayoutOverScrollViewBehavior">

        <android.support.design.widget.CollapsingToolbarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
        </android.support.design.widget.CollapsingToolbarLayout>
        <com.flyco.tablayout.SlidingTabLayout
        android:id="@+id/tab_designer_works"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="@android:color/white"
        app:tl_indicator_color="#414141"
        app:tl_indicator_height="2.5dp"
        app:tl_indicator_margin_bottom="5dp"
        app:tl_indicator_width_equal_title="true"
        app:tl_tab_space_equal="true"
        app:tl_textBold="SELECT"
        app:tl_textSelectColor="#4F4F4F"
        app:tl_textUnselectColor="#7A7A7A"
        app:tl_textsize="12sp"
        app:layout_collapseMode="parallax">
        </com.flyco.tablayout.SlidingTabLayout>

        </android.support.design.widget.AppBarLayout>
        zly394:@skykite 这里拉伸图片时的实现是通过修改AppBarLayout的高度实现的,所以TabLayout是不会滑动的,需要自己实现,可以在修改AppBarLayout的同时修改TabLayout的位置。
        50191e06a3ba:@zly394 我刚刚试了,上滑没问题,下滑过程中没问题,就是下拉滑动到顶部拉伸图片时,tablayout不会跟随viewpager下移
        zly394:@skykite 应该是你没有给TabLayout添加滑动标志吧,app:layout_scrollFlags="scroll"
      • 昊_f343:有没有Windows版的源码,我怎么用的这两个类有问题?
        zly394: @昊_f343 这不分版本呀,都可以用。
      • 董存瑞晓笼包:写的很好,但是没发现拉下来放大后缩回去这个比例不对么。还是target.setscrolly(0)失效了,向上划了一段时间,会导致图片缩小的时候出问题。
        zly394:@董存瑞晓笼包 具体是什么问题?
      • 信义天地:您好,为什么获取代码的链接打不开?:sweat:
        zly394: @信义天地 需要翻墙访问
        信义天地:@zly394 梯子?梯子是啥?
        zly394: @信义天地 需要梯子…
      • 雕刻的时光岁月:以解决,在这里贴出解决方法
        首先先实现GlideModuel接口

        public class MyGlideModule implements GlideModule {
        @Override
        public void applyOptions(Context context, GlideBuilder builder) {
        ViewTarget.setTagId(R.id.glide_tag_id);
        }

        @Override
        public void registerComponents(Context context, Glide glide) {

        }
        }

        然后在xml下添加
        <item name="glide_tag_id" type="id"/>
        最后在清单文件中添加

        <meta-data
        android:name="包名.MyGlideMoudle"
        android:value="GlideModule" />
        雕刻的时光岁月:@昊_f343 成功了
        昊_f343:这个你运行成功了吗?
      • 雕刻的时光岁月:java.lang.IllegalArgumentException: You must not call setTag() on a view Glide is targeting
      • 雕刻的时光岁月:楼主,再给imageview设置tag,然后使用glide加载图片冲突
        zly394:@雕刻的时光岁月 网上有glide设置tag异常的处理方式,你可以找一下

      本文标题:UI之自定义Behavior实现AppBarLayout越界弹性

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