Android CoordinatorLayout之自定义Behavior

1.CoordinatorLayout是什么?CoordinatorLayout继承自ViewGroup,并实现了NestedScrollingParent2接口,类似一个功能强大的FrameLayout;CoordinatorLayout的产生主要有两个主要的目的:1)在布局文件中作为最顶层的布局容器控件;2)在CoordinatorLayout中直接为子控件提供特定的交互功能;相当于是给CoordinatorLayout里直接子控件建立依赖关系,使原本相互独立的控件,产生依赖关系,可以实
摘要由CSDN通过智能技术生成

1.CoordinatorLayout是什么?

CoordinatorLayout继承自ViewGroup,并实现了NestedScrollingParent2接口,类似一个功能强大的FrameLayout;

CoordinatorLayout的产生主要有两个主要的目的:

1)在布局文件中作为最顶层的布局容器控件;

2)在CoordinatorLayout中直接为子控件提供特定的交互功能;相当于是给CoordinatorLayout里直接子控件建立依赖关系,使原本相互独立的控件,产生依赖关系,可以实现一个目标控件的改变时,另一个随着改变;

2.Behavior介绍

Behavior实际就是将一些布局的过程以及**Nested Scrolling**的过程暴露出来,利用代理和组合的模式,可以让开发者为CoordinatorLayout添加各种效果插件;

2.1Behavior是什么?

用于CoordinatorLayout子视图的交互行为插件。行为实现一个或多个用户可以在子视图上进行的交互。这些交互可能包括拖动、滑动、甩动或任何其他手势

2.2Behavior定义

CoordinatorLayout内部类 
public static abstract class Behavior<V extends View> {

        public Behavior() {
        }

        public Behavior(Context context, AttributeSet attrs) {
        }
        
        ...
}

其中有个一个泛型V,它的作用是指定要使用这个Behavior的View的类型,可以是Button、TextView等等。如果希望所有的View都可以使用则指定泛型为View即可;

2.3如何为视图指定Behavior

通过代码我们发现在CoordinatorLayout内部类定义了LayoutParams布局参数类,LayoutParams定义了Behavior行为成员变量,即加入需要为子视图添加Behavior必须是CoordinatorLayout直接子视图,否则无法设置Behavior,正确使用方式例如:

<androidx.coordinatorlayout.widget.CoordinatorLayout>

<TextView app:layout_behavior="...Behavior"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

CoordinatorLayout子视图布局参数类LayoutParams如下:

CoordinatorLayout内部类
//定义CoordinatorLayout子视图的布局参数
public static class LayoutParams extends MarginLayoutParams {
        //定义Behavior
        Behavior mBehavior;
         //读取配置文件的layout_behavior参数Behavior
         LayoutParams(@NonNull Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);

            final TypedArray a = context.obtainStyledAttributes(attrs,
                    R.styleable.CoordinatorLayout_Layout);
            //判断是否有Behavior属性
            mBehaviorResolved = a.hasValue(
                    R.styleable.CoordinatorLayout_Layout_layout_behavior);
            //解析Behavior
            if (mBehaviorResolved) {
                mBehavior = parseBehavior(context, attrs, a.getString(
                        R.styleable.CoordinatorLayout_Layout_layout_behavior));
            }
            a.recycle();
            //通知已经为子视图设置Behavior行为
            if (mBehavior != null) {
                // If we have a Behavior, dispatch that it has been attached
                mBehavior.onAttachedToLayoutParams(this);
            }
        }
        
        //通过代码设置Behavior
        public void setBehavior(@Nullable Behavior behavior) {
            if (mBehavior != behavior) {
                if (mBehavior != null) {
                    // 首先通知CoordinatorLayout子视图移除旧的Behavior
                    mBehavior.onDetachedFromLayoutParams();
                }
                //CoordinatorLayout子视图设置新的Behavior
                mBehavior = behavior;
                mBehaviorTag = null;
                mBehaviorResolved = true;
                //
                if (behavior != null) {
                    // 通知CoordinatorLayout子视图绑定了新的Behavior
                    behavior.onAttachedToLayoutParams(this);
                }
            }
        }
}

LayoutParams类里面定义了Behavior成员变量mBehavior

2.3.1通过配置文件指定

<TextView
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               app:layout_behavior="@string/appbar_scrolling_view_behavior"></TextView>

通过app:layout_behavior指定实现Behavior类的行为,在读取子视图xml配置参数时读取实现Behavior类行为类全路径(即LayoutParams构造函数内读取配置的Behavior: mBehavior = parseBehavior(context, attrs, a.getString(
                        R.styleable.CoordinatorLayout_Layout_layout_behavior)););

注意:appbar_scrolling_view_behavior对应的是Behavior的类全路径

<string name="appbar_scrolling_view_behavior" translatable="false">com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior</string>

2.3.2通过CoordinatorLayout内部类LayoutParams类setBehavior()方法设置

setBehavior()可以设置新的Behavior或者null移除Behavior;

示例代码:

CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams)findViewById(R.id.tv_behavior).getLayoutParams();
        layoutParams.setBehavior(CoordinatorLayout.Behavior);

2.4自定义Behavior关键方法介绍

自定义Behavior关键方法可以简单理解为两部分控制视图布局视图行为监听控制(**Nested Scrolling**的过程)

2.4.1Behavior控制视图布局

layoutDependsOn确认Behavior关联的视图依赖的视图:

 public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull V child,
                @NonNull View dependency) {
            return false;
        }

负责查询使用该BehaviorView要监听哪个类型的View状态变化,如果是则返回true,那么之后其他操作就会围绕这个依赖视图而进行了;parent代表CoordinatorLayoutchild代表使用该Behavior关联Viewdependency代表要监听的View

例如:系统类com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
      // We depend on any AppBarLayouts
      return dependency instanceof AppBarLayout;
    }

设置ScrollingViewBehaviorView依赖项是AppBarLayout布局则返回true,那么之后其他操作就会围绕这个依赖视图dependency进行;

onLayoutChild帮助Behavior控制的CoordinatorLayout子视图布局:

public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull V child,
                int layoutDirection) {
            return false;
        }

当CoordinatorLayout准备布局的时候调用onLayoutChild方法,负责对被Behavior控制的视图进行布局,就是将ViewGrouponLayout针对该视图的部分抽出来给Behavior处理;返回true表示Behavior代理CoordinatorLayout处理孩子视图的布局,false请求默认布局Behavior(CoordinatorLayout的onLayout方法继续处理);

例如:系统类com.google.android.material.appbar.ViewOffsetBehavior

@Override
  public boolean onLayoutChild(
      @NonNull CoordinatorLayout parent, @NonNull V child, int layoutDirection) {
    // First let lay the child out
    layoutChild(parent, child, layoutDirection);

    if (viewOffsetHelper == null) {
      viewOffsetHelper = new ViewOffsetHelper(child);
    }
    viewOffsetHelper.onViewLayout();
    viewOffsetHelper.applyOffsets();

    if (tempTopBottomOffset != 0) {
      viewOffsetHelper.setTopAndBottomOffset(tempTopBottomOffset);
      tempTopBottomOffset = 0;
    }
    if (tempLeftRightOffset != 0) {
      viewOffsetHelper.setLeftAndRightOffset(tempLeftRightOffset);
      tempLeftRightOffset = 0;
    }

    return true;
  }

返回true表示Behavior代理CoordinatorLayout完成Behavior关联的孩子视图的布局,false请求默认布局Behavior(CoordinatorLayout的onLayout方法继续处理); 

onDependentViewChanged确认关联Behavior视图依赖的CoordinatorLayout下子视图的位置和尺寸是否发生变化

public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull V child,
                @NonNull View dependency) {
            return false;
        }

根据依赖视图进行调整的方法,当依赖视图发生尺寸大小和位置变化时,这个方法就会调用;返回true表示Behavior更新了CoordinatorLayout关联Behavior子视图的位置或尺寸,false未改变CoordinatorLayout关联Behavior子视图的位置或尺寸;

2.4.2Behavior视图行为监听控制(几个关键方法回调实现)

Behavior实际上是NestedScrollingParent2(NSP)的一个代理

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现这样的效果,需要自定义一个 Behavior 类。下面是一个简单的示例代码: ``` public class MyBehavior extends CoordinatorLayout.Behavior<View> { private int mTotalScrollRange; public MyBehavior() { super(); } public MyBehavior(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { return dependency instanceof AppBarLayout; } @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { if (dependency instanceof AppBarLayout) { mTotalScrollRange = ((AppBarLayout) dependency).getTotalScrollRange(); float ratio = -dependency.getY() / mTotalScrollRange; child.setTranslationY(ratio * child.getHeight()); return true; } return false; } } ``` 在这个 Behavior 类中,我们首先判断依赖的 view 是否是 AppBarLayout,如果是就设置一个 mTotalScrollRange 变量,该变量保存了 AppBarLayout 的总滑动范围。在 onDependentViewChanged 方法中,我们计算出当前的滑动比例 ratio,并根据这个比例来设置子 view 的 Y 轴偏移量,以实现子 view 的折叠和展开。 接下来,在布局文件中将该 Behavior 应用到 RecyclerView 对应的子 view 上即可: ``` <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior=".MyBehavior" /> ``` 注意,这个 Behavior 只是一个简单的示例,实际应用中可能需要根据具体需求进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值