Android-CoordinatorLayout.……

android 使用 Behavior 遇到的问题

2020-04-21  本文已影响0人  一个冬季
博客学习地址

针对 CoordinatorLayout 及 Behavior 的一次细节较真
Android CoordinatorLayout之自定义Behavior
Android触摸滑动全解(二)——ViewGroup中触摸事件详解

问题描述

最近想了解一下Behavior,感觉这个东西很有用呀,可以解决嵌套滑动导致的问题,然后看了许多人的demo,期间遇到一些问题,在此记录一下。
我是直接看了Android CoordinatorLayout之自定义Behavior里面最后一个demo来学习的。但是他是通过实现NestedScrollingChild来操作的。而我想通过NestedScrollingChild3来达到效果

demo地址

问题

1、继承LinearLayout后,发现不执行MotionEvent.ACTION_MOVE 事件
2、开始onNestedPreScroll还可以调用,后面就怎么都调用不了
3、layoutDependsOn 不调用

解决

1、继承LinearLayout后,发现不执行MotionEvent.ACTION_MOVE 事件

首先我们要知道,如果我们子view没有去消耗事件(也就是没有在onTouchEvent 返回true),后续一系列的ACTION_MOVE 、ACTION_UP 等都不会被调用

2、开始onNestedPreScroll还可以调用,后面就怎么都调用不了

标题只是写了一个大概问题,描述的不是很清楚,我这里详细说一下
xml格式如下

<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/coordinatorlayout"
    xmlns:app="http://schemas.android.com/apk/res-auto">
//最开始手指滚动FrameLayout 会调用 onNestedPreScroll ,然后再滚动recyclerview 完后,再次滚动 FrameLayout 就不会调用 onNestedPreScroll
    <FrameLayout
        app:layout_behavior="@string/behavior_sample_framelayout_behavior"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <com.example.myapplicationbehavior.thired.ThiredLinLayout
            android:id="@+id/ll_thired"
            android:layout_width="match_parent"
            android:layout_height="400dp">
            <ImageView
                android:src="@drawable/icon_b"
                android:scaleType="fitXY"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>

        </com.example.myapplicationbehavior.thired.ThiredLinLayout>
    </FrameLayout>
    <androidx.recyclerview.widget.RecyclerView
          ..../>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

在NestedScrollingChildHelper源码里面,hasNestedScrollingParent(type)一直为true,就导致无法调用onStartNestedScroll,所以onNestedPreScroll也会跟着没办法调用

 public boolean startNestedScroll(@ScrollAxis int axes, @NestedScrollType int type) {
        if (hasNestedScrollingParent(type)) {  这一行会返回true,导致不会进入下面的逻辑了。这里的意思:是否有嵌套滑动的parent
           
            return true;
        }
        if (isNestedScrollingEnabled()) {
            ViewParent p = mView.getParent();
            View child = mView;
            while (p != null) {
                if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes, type)) {
                    setNestedScrollingParentForType(type, p);
                    ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes, type);
                    return true;
                }
                if (p instanceof View) {
                    child = (View) p;
                }
                p = p.getParent();
            }
        }
        return false;
    }
  
    public boolean hasNestedScrollingParent(@NestedScrollType int type) {
        return getNestedScrollingParentForType(type) != null;
    }

   private ViewParent getNestedScrollingParentForType(@NestedScrollType int type) {
        switch (type) {
            case TYPE_TOUCH:
                return mNestedScrollingParentTouch;   究其原因是这个不会为空导致
            case TYPE_NON_TOUCH:
                return mNestedScrollingParentNonTouch;
        }
        return null;
    }
   
    这个方法是将mNestedScrollingParentTouch 置位null,前提是你要调用stopNestedScroll(type)
    private void setNestedScrollingParentForType(@NestedScrollType int type, ViewParent p) {
        switch (type) {
            case TYPE_TOUCH:
                mNestedScrollingParentTouch = p;   这里会被赋予null,前提是你要调用stopNestedScroll(type)
                break;
            case TYPE_NON_TOUCH:
                mNestedScrollingParentNonTouch = p;
                break;
        }
    }

 看这里,这里会去调用setNestedScrollingParentForType,将mNestedScrollingParentTouch 设置为空
  public void stopNestedScroll(@NestedScrollType int type) {
        ViewParent parent = getNestedScrollingParentForType(type);
        if (parent != null) {
            ViewParentCompat.onStopNestedScroll(parent, mView, type);
            setNestedScrollingParentForType(type, null);
        }
    }

所以这里问题就简单了,我们只需要这样处理

public class ThiredLinLayout extends LinearLayout implements NestedScrollingChild3 {
  ....省略一些代码
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:{
                stopNestedScroll(ViewCompat.TYPE_TOUCH);    直接加个这个进来
                lastY = (int) event.getY();
                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL,ViewCompat.TYPE_TOUCH);
            }
            break;
            case MotionEvent.ACTION_MOVE:{
                int dy = lastY - (int) (event.getY());
                dispatchNestedPreScroll(0, dy, consumed, offset,ViewCompat.TYPE_TOUCH);
                lastY = (int) event.getY();
            }
            break;

            case MotionEvent.ACTION_CANCEL:
            {
                stopNestedScroll(ViewCompat.TYPE_TOUCH);   直接加个这个进来
            }
            break;
        }
        return true;
    }
....省略一些代码
    @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        getmScrollingChildHelper().setNestedScrollingEnabled(true);
    }

    @Override
    public void stopNestedScroll(int type) {
        getmScrollingChildHelper().stopNestedScroll(type);
    }

    public NestedScrollingChildHelper getmScrollingChildHelper() {
        if (mScrollingChildHelper == null){
            mScrollingChildHelper = new NestedScrollingChildHelper(this);
        }
        return mScrollingChildHelper;
    }
}

3、layoutDependsOn 不调用

xml格式如下

<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/coordinatorlayout"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <FrameLayout
        app:layout_behavior="@string/behavior_sample_framelayout_behavior"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <com.example.myapplicationbehavior.thired.ThiredLinLayout
            android:id="@+id/ll_thired"
            android:layout_width="match_parent"
            android:layout_height="400dp">
            <ImageView
                android:src="@drawable/icon_b"
                android:scaleType="fitXY"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>

        </com.example.myapplicationbehavior.thired.ThiredLinLayout>
    </FrameLayout>
    <androidx.recyclerview.widget.RecyclerView
          ..../>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

layoutDependsOn 的意思是,依赖谁

    @Override
    public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull RecyclerView child, @NonNull View dependency) {
        return dependency instanceof FrameLayout;
    }

类似我这样,只有FrameLayout改变的时候,我才会被调用,如果我里面的ThiredLinLayout改变,也不会被调用

上一篇 下一篇

猜你喜欢

热点阅读