参考链接:
1:https://blog.csdn.net/yancychas/article/details/78306720
1:滑动冲突类型
1:外部的滑动方向和内部滑动的方向不一致
根据当前滑动方向,水平还是垂直来判断这个事件到底该交给谁来处理
如:外部左右和内部上下
2:外部滑动方向和内部滑动的方向一致
通过业务逻辑来进行判断
如:
1:内部只让内部滑动,外部让外部滑动
2:在固定位置有滑动,将导致外部滑动,如果在固定位置无滑动,将启动内部滑动
3:以上两种问题的组合
分层处理,每次分为两层进行滑动冲突处理
2:冲突的解决机制
1:制定合适的滑动策略
如以下策略:
1:dx和dy的大小比较
2:夹角的大小
2:按照滑动策略分发事件
1:外部拦截法
2:内部拦截法
3:常用的滑动冲突示例
1:微博竖向滑动场景
微博的这个是同方向,竖向滑动冲突的场景,可以看到发现布局整体是可以滚动的,而且下方的微博列表也是可以滚动的。根据业务逻辑,当热门,榜单...这一行标签栏滑动到顶部的时候微博列表才可以滚动。否则就是发现布局的整体滚动。这个场景是不是在很多app里面都能够见到呢!
2:天猫横向滑动场景
天猫的这个是同方向,横向滑动冲突的场景,内外两层都是可以横向滚动的。它的处理逻辑也很明显,根据用户滑动的位置来判断到底是那个View需要响应滑动
4:代码实例
1:内部拦截法使用实例
说明:使用了ScrollView,嵌套了listview,在listview的上方和下方都有view,这种场景比较常见,比如京东的活动列表页面中滑动,视图如下:
模拟上面的滑动情况,代码如下:
1:Layout.xml
布局文件: <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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" tools:context=".slidingconflict.SlidingConflictActivity"> <ScrollView android:id="@+id/scrollview" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="690dp" android:text="我是一个文字" android:textSize="50dp" android:gravity="center" android:textColor="@android:color/white" android:background="@color/colorPrimary"/> <TextView android:layout_width="match_parent" android:layout_height="320dp" android:text="这是第二个文字" android:textSize="50dp" android:gravity="center" android:textColor="@android:color/white" android:background="@color/colorAccent"/> <com.qm.miaobao.textnewfun.slidingconflict.SlidingListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="400dp"/> <TextView android:layout_width="match_parent" android:layout_height="320dp" android:text="这是第三个文字" android:textSize="50dp" android:gravity="center" android:textColor="@android:color/white" android:background="@color/colorAccent"/> </LinearLayout> </ScrollView> </androidx.constraintlayout.widget.ConstraintLayout>
2:重新ListView,处理滑动冲突的主要代码在dispatchTouchEvent事件中
public class SlidingListView extends ListView { private float mLastY = 0; public SlidingListView(Context context) { super(context); } public SlidingListView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_MOVE: //2:检查是否滑动到listview的顶部,然后继续上滑 //3:检查是否滑动到listView的底部,然后继续下滑 if((getFirstVisiblePosition() == 0 && (ev.getY() - mLastY) > 0) ||(getLastVisiblePosition() == getCount() - 1 && (ev.getY() - mLastY) < 0)){ //4:允许上层viewGroup拦截事件 getParent().requestDisallowInterceptTouchEvent(false); }else { //5:不允许上层viewgroup拦截事件 getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_DOWN: //1:不允许上层viewGroup拦截事件 getParent().requestDisallowInterceptTouchEvent(true); break; } mLastY = ev.getY(); return super.dispatchTouchEvent(ev); } }
3:调用的测试代码
public class SlidingConflictActivity extends AppCompatActivity { private ListView listView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sliding_conflict); listView = findViewById(R.id.listview); initListView(); } private void initListView() { List<HashMap<String,Object>> arrayList = new ArrayList<>(); for(int i = 0;i<12;i++){ HashMap<String,Object> hashMap = new HashMap<>(); hashMap.put("图片",R.drawable.ic_launcher_background); hashMap.put("文字", "数据"+i); arrayList.add(hashMap); } SimpleAdapter simpleAdapter = new SimpleAdapter(this,arrayList, R.layout.layout,new String[]{"图片","文字"},new int[]{R.id.iv_img,R.id.tv_name}); listView.setAdapter(simpleAdapter); // Utility.setListViewHeightBasedOnChildren(listView); } }
2:外部拦截法使用实例
public class ListScrollView extends ScrollView { private ListView listView; public ListScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public ListScrollView(Context context) { super(context); } /** * 覆写onInterceptTouchEvent方法,点击操作发生在ListView的区域的时候, * 返回false让ScrollView的onTouchEvent接收不到MotionEvent,而是把Event传到下一级的控件中 */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub if (listView != null && checkArea(listView, ev)) { return false; } return super.onInterceptTouchEvent(ev); } /** * 测试view是否在点击范围内 * @param locate * @param v * @return */ private boolean checkArea(View v, MotionEvent event){ float x = event.getRawX(); float y = event.getRawY(); int[] locate = new int[2]; v.getLocationOnScreen(locate); int l = locate[0]; int r = l + v.getWidth(); int t = locate[1]; int b = t + v.getHeight(); if (l < x && x < r && t < y && y < b) { return true; } return false; } public ListView getListView() { return listView; } public void setListView(ListView listView) { this.listView = listView; } }
该处代码只是表明外部拦截的参考示例,并不建议使用到工程中,因为在scrollview已经做了事件滑动冲突的事情,如果再添加上新的滑动冲突事件,可能造成很多滑动问题
3:适配过滑动冲突的滑动ViewGroup
ScrollView,RecyclerView,HorizontalScrollView,在使用这些ViewGroup时,需要注意考虑同方向的滑动冲突问题