博客学习地址
针对 CoordinatorLayout 及 Behavior 的一次细节较真
Android CoordinatorLayout之自定义Behavior
Android触摸滑动全解(二)——ViewGroup中触摸事件详解
问题描述
最近想了解一下Behavior,感觉这个东西很有用呀,可以解决嵌套滑动导致的问题,然后看了许多人的demo,期间遇到一些问题,在此记录一下。
我是直接看了Android CoordinatorLayout之自定义Behavior里面最后一个demo来学习的。但是他是通过实现NestedScrollingChild来操作的。而我想通过NestedScrollingChild3来达到效果
问题
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改变,也不会被调用
网友评论