您好,欢迎来到二三娱乐。
搜索
您的当前位置:首页自定义Behavior的简单打造

自定义Behavior的简单打造

来源:二三娱乐

一、Behavior是什么鬼?

Behavior是CoordinatorLayout的一个静态抽象内部类,拥有两个构造函数,一个泛型。

public static abstract class Behavior<V extends View> {
          public Behavior() {}
          public Behavior(Context context, AttributeSet attrs) {}
          ......
          ......
}

这个泛型的参数View是指定使用这个Behaior的View,其他的view是不能使用,比如AppBarLayout里面的Behavior就传入了AppBarLayout。


000.PNG

二、Behavior 能干嘛了?

三、怎么干?

想干事总要有几套功法,所以Behavior给我们提供了几招制敌之法,下面我们来一一的看下:

  • layoutDependsOn();确定你是否依赖那些view,所依赖的view改变时,使用当前Behavior的view作出相应的变化。
  • onDependentViewChanged();当依赖的view发生变化时,使用当前Behavior的view作出相应的变化。
  • onStartNestedScroll();依赖的view开始发生滑动变化(所以来的view必须实现NestedScrollingChild,否则将不支持该类滑动方法。系统只有NestedScrollView和RecyclerView实现了)
  • onNestedScroll();依赖的view发生滑动变化
  • onNestedFling();当依赖view快速滑动式调用

四、开搞

首先我们实现一下这样的效果(模仿):

04.gif
  • 第一步,新建一个类叫做MyBehavior继承致CoordinatorLayout.Behavior,这里不传入泛型参数,所以得view都可以用。然后写需要依赖view实现什么效果代码如下:
public class MyBehavior extends CoordinatorLayout.Behavior {

    private static final String STRING = "TOP";

    Context mContext;

    public MyBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
    }

    //当依赖的view发生改变时
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        //始终处于topvView的下方
        child.setY(dependency.getY()+dependency.getHeight());
        return true;
    }

    //确定是否依赖该view,ture为依赖
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return STRING.equals(dependency.getTag());
    }
}
  • 第二步,新建一个布局,给需要显示在上面的view添加一个TAG值为代码中STRING 的值,然后给需要的view添加layout_behavior属性值为MyBehavIor的包路径,全部代码如下:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 
    
 
android:layout_width="match_parent"
android:layout_height="match_parent"    android:fitsSystemWindows="true">   
 <View
        android:id="@+id/top" 
       android:layout_width="match_parent" 
       android:layout_height="100dp"
        android:background="#aec" 
       android:tag="TOP" />
    <View 
       android:id="@+id/bottom"
       android:layout_width="match_parent" 
       android:layout_height="100dp"
        android:background="#fce"
        app:layout_behavior=".MyBehavior" 
       />
</android.support.design.widget.CoordinatorLayout>

Activity代码实现如下:

public class BehaviorAct extends AppCompatActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.behavior_layout);
        /**
         * 在上面的view响应事件进行滑动
         */
        findViewById(R.id.top).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if(event.getAction() == MotionEvent.ACTION_MOVE){
                    v.setY(event.getRawY());
                }
                return true;
            }
        });
    }
}

然后我们在实现这样的一个效果

05.gif

代码编写步骤和上面一样,只是在新建的Behaivor中使用的时上文上述方法中的后三种,代码如下所示:

public class ScrollBehavior extends CoordinatorLayout.Behavior {
    int offsetTotal = 0;

    private int childHeight;//滑动子项的高度,也是上滑控制的极限
    private boolean isScrolling = true;
    private int beforValue = 0;

    private int count;


    public ScrollBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    //继承的
    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
        /**
         * 这里要返回true,才会接收到后续的滑动事件
         * (理解:也就是说我们在这里可以做各种判断来确定,要操作的view是否要对下一步滑动作出反应)
         */
        childHeight = child.getHeight();

        return true;
    }

    //对滑动事件作出处理
    @Override
    public void onNestedScroll(CoordinatorLayout coordinatorLayout, final View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        Log.e("sun","数据******"+dyConsumed);
        set(child, dyConsumed);
    }

    private void set(View child, int dyConsumed) {
        int old = offsetTotal;//上一次的位置
        int top = old - dyConsumed;//这一次应该到达的位置
        top = Math.max(top,-childHeight);//上滑时和子view的高度比较(这样在如果在上偏移(top)超过view高度时,能防止偏移过度)
        top = Math.min(top,0);//下滑时和0比较(这样如果向下偏移过度)
        offsetTotal = top;//将这次的位置记录下来
        if(old == top){
            return;
        }
        int data = top - old;
        child.offsetTopAndBottom(data);
    }

    //进行快速滑动调用的方法
    @Override
    public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY, boolean consumed) {
        return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
    }
}

布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 
    
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textSize="40sp"
            android:text="A\n B\nC\n D\nE\n F\nG\n H\nI\n J\nK\n M\nN\n O\nP\n Q\nR\n S\nA\n B\nC\n D\nE\n F\nG\n H\nI\n J\nK\n M\nN\n O\nP\n Q\nR\n S\n"/>
    </android.support.v4.widget.NestedScrollView>
    <View
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="#afa"
        app:layout_behavior=".ScrollBehavior"/>
</android.support.design.widget.CoordinatorLayout>

思考:第一种与第二种的实现机制是怎样的呢?

首先第一种,CoordinatorLayout初始化时会遍历他的子view,如果有时使用Behavior的就会通过反射建立相应Behavior,我粗略的翻看了源码(实乃菜鸟,太高深的也不懂...),其下有这样的方法:

00000000.PNG
而当我们所依赖的view发生变化时,CoordinatorLayout在其内部实现了ViewTreeObserver.OnPreDrawListener的接口,该接口是在即将进行绘图之前调用( ),也就是用来监听view发生位置变化,因为我们对依赖view的onTouch事件进行了重写并改变了控件位置系统会对视图进行重新绘制,然后就会调用*** onChildViewsChanged() 方法。
003.PNG
onChildViewsChanged() *** 方法中首先遍历所有的子view,检查每一个子view所依赖的view是否是其它的子view,如果是调用*** offsetChildToAnchor(child, layoutDirection); *** 而在该方法中会获取子view的behavior实例,最终调用Behavior的*** onDependentViewChanged() 方法。而后还有一层循环,通过 onChildViewsChanged() 方法传进来的type,进行判断操作,最后也是调用Bhavior的 onDependentViewChanged() ***方法,从而实现对内部视图的控制。 004.PNG 005.PNG 006.PNG

Copyright © 2019- yule263.com 版权所有 湘ICP备2023023988号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务