滑动冲突
2016-06-28 23:35
183 查看
在读完了上一节
之后,可能有点意犹未尽的感觉,这一篇将说明requestDisallowInterceptTouchEvent在解决滑动冲突时的巨大作用。 在有多个滚动控件的时候常常提到滑动冲突,那什么是滑动冲突,又如何解决呢?为什么原生的控件一般都不存在滑动冲突的问题?这就是本文要说的故事
的requestDisallowInterceptTouchEvent,简直是量身定做的啊。
![](https://img-blog.csdn.net/20160628233520584?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
代码非常简单,工程是ScrollConflict
很简单,在onTouchEvent处理move事件的时候,调用一遍getParent.requestDisallowInterceptTouchEvent(true),告诉父亲,这是我爱吃的,你以后不准抢,这样listview就不会来拦截这个事件了,修改部分如下,
![](https://img-blog.csdn.net/20160628234053623?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
经常看到有人说viewpager放在listview里会产生滑动冲突,还有人给出了若干解决方案,我自己写了个试了一下,发现没有冲突啊?到底是他们错了,还是我错了呢?
原来低版本的时候的确会有冲突,高版本修复了这个问题
这是4.4_r1的Viewpager的onTouchEvent的处理move的代码,在supportv4包内,
而上一个版本4.3.1_r1,
可以明显看到,高版本调用了 requestParentDisallowInterceptTouchEvent(true);,而requestParentDisallowInterceptTouchEvent代码如下,就是掉parent的requestDisallowInterceptTouchEvent,不许父亲吃我的事件
所以我们使用高版本的v4包或者v7包,就不用考虑滑动冲突的问题,ohlala。
我再看了下ScrollView
再看下HorizontalScrollView
看来原生控件都是类似的,在onTouchEvent的move里判断是否是我的滑动事件,如果是,那就拦截,条件一般就是Math.abs(deltaY) > mTouchSlop
所以大部分原生控件其实都已经解决了滑动冲突的问题,但是我们自己写的控件,或者说github上的一些控件经常有滑动冲突的问题需要解决,如何解决呢?完全可以参考原生控件的方法。
android事件分发(三)重要的函数requestDisallowInterceptTouchEvent
之后,可能有点意犹未尽的感觉,这一篇将说明requestDisallowInterceptTouchEvent在解决滑动冲突时的巨大作用。 在有多个滚动控件的时候常常提到滑动冲突,那什么是滑动冲突,又如何解决呢?为什么原生的控件一般都不存在滑动冲突的问题?这就是本文要说的故事滑动冲突概念
什么是滑动冲突,比如我有一个listview,还有一个viewpager,listview可以纵向滑动,viewpager可以横向滑动,我在viewpager上横向滑了一大段,在这个滑动过程中,很可能我们的手不仅仅横向滑了,纵向也滑了,谁也无法保证自己滑的是一条直线,很可能是横向滑了100dp,纵向抖动了3dp,此时理想的情况是只有viewpager滑动,listview不要瞎JB乱动,但是现实往往是残酷的,viewpager动了,listview也动了,这就是滑动冲突。我们想要的效果是如果viewpager开始滑了,那么listview就不准滑,除非手指抬起再按下,也就是说在一个cycle内,listview不准滑,这让我们想起了上节的requestDisallowInterceptTouchEvent,简直是量身定做的啊。
滑动冲突举例
下面用一个例子来说明如何解决滑动冲突,我们有个listview,listview内的item是可滑动的,左划会出现删除按钮,效果如下,存在滑动冲突package com.fish.scrollconflict; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.TextView; import java.util.List; public class MyBaseAdapter extends BaseAdapter { private Context mContext; private List<String> mData; private LayoutInflater mInflater; public static final class ViewHolder { public TextView textView; public Button btn; } public MyBaseAdapter(Context context, List<String> data) { mContext = context; mData = data; this.mInflater = LayoutInflater.from(context); } @Override public int getCount() { return mData.size(); } @Override public Object getItem(int arg0) { return mData.get(arg0); } @Override public long getItemId(int arg0) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { // LogUtil.fish("getview "+position); ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); convertView = mInflater.inflate(R.layout.item, parent, false); holder.textView = (TextView) convertView.findViewById(R.id.tv); holder.btn = (Button) convertView.findViewById(R.id.btn); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.textView.setText(mData.get(position)); // holder.textView.setText((String) mData.get(position).get("textView")); //给每一个列表后面的按钮添加响应事件 holder.btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // showInfo(); } }); return convertView; } }
package com.fish.scrollconflict; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.Button; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; /** * Created by fish on 16/6/28. */ public class ScrollLayout extends LinearLayout { public ScrollLayout(Context context) { super(context); } public ScrollLayout(Context context, AttributeSet attrs) { super(context, attrs); } public ScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } TextView tv; Button btn; float mLastX; float mLastY; boolean isScrolling; @Override protected void onFinishInflate() { super.onFinishInflate(); tv = (TextView) findViewById(R.id.tv); btn = (Button) findViewById(R.id.btn); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLastX = event.getX(); mLastY = event.getY(); isScrolling = false; return true; case MotionEvent.ACTION_MOVE: //左划,应该为负的 float xDiff = event.getX() - mLastX; mLastX = event.getX(); LinearLayout.LayoutParams params = (LayoutParams) tv.getLayoutParams(); params.leftMargin += xDiff; if (params.leftMargin > 0) { params.leftMargin = 0; } tv.setLayoutParams(params); return true; } return super.onTouchEvent(event); } }
<?xml version="1.0" encoding="utf-8"?> <com.fish.scrollconflict.ScrollLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="350dp" android:layout_height="100dp" android:gravity="center_vertical" android:background="#44ff0000"> <TextView android:textSize="20sp" android:id="@+id/tv" android:layout_width="350dp" android:layout_height="wrap_content" android:text="Hello 我是无辜的!" /> <Button android:textSize="20sp" android:id="@+id/btn" android:layout_width="100dp" android:layout_height="wrap_content" android:text="删除" /> </com.fish.scrollconflict.ScrollLayout>很明显上图中存在滑动冲突,那么怎么解决呢?
很简单,在onTouchEvent处理move事件的时候,调用一遍getParent.requestDisallowInterceptTouchEvent(true),告诉父亲,这是我爱吃的,你以后不准抢,这样listview就不会来拦截这个事件了,修改部分如下,
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLastX = event.getX(); mLastY = event.getY(); isScrolling = false; return true; case MotionEvent.ACTION_MOVE: //左划,应该为负的 float xDiff = event.getX() - mLastX; float yDiff = event.getY() - mLastY; if (Math.abs(xDiff) > Math.abs(yDiff)) { isScrolling = true; getParent().requestDisallowInterceptTouchEvent(true); } mLastX = event.getX(); LinearLayout.LayoutParams params = (LayoutParams) tv.getLayoutParams(); params.leftMargin += xDiff; if (params.leftMargin > 0) { params.leftMargin = 0; } tv.setLayoutParams(params); return true; } return super.onTouchEvent(event); }非常简单就解决了滑动冲突,看下图,可以发现,不会有滑动冲突了,listview不会乱滚了
viewpager在listview内会不会滑动冲突
经常看到有人说viewpager放在listview里会产生滑动冲突,还有人给出了若干解决方案,我自己写了个试了一下,发现没有冲突啊?到底是他们错了,还是我错了呢?原来低版本的时候的确会有冲突,高版本修复了这个问题
这是4.4_r1的Viewpager的onTouchEvent的处理move的代码,在supportv4包内,
case MotionEvent.ACTION_MOVE: if (!mIsBeingDragged) { final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, pointerIndex); final float xDiff = Math.abs(x - mLastMotionX); final float y = MotionEventCompat.getY(ev, pointerIndex); final float yDiff = Math.abs(y - mLastMotionY); if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff); if (xDiff > mTouchSlop && xDiff > yDiff) { if (DEBUG) Log.v(TAG, "Starting drag!"); mIsBeingDragged = true; requestParentDisallowInterceptTouchEvent(true); mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop : mInitialMotionX - mTouchSlop; mLastMotionY = y; setScrollState(SCROLL_STATE_DRAGGING); setScrollingCacheEnabled(true); // Disallow Parent Intercept, just in case ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); } } } // Not else! Note that mIsBeingDragged can be set above. if (mIsBeingDragged) { // Scroll to follow the motion event final int activePointerIndex = MotionEventCompat.findPointerIndex( ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, activePointerIndex); needsInvalidate |= performDrag(x); } break;
而上一个版本4.3.1_r1,
case MotionEvent.ACTION_MOVE: if (!mIsBeingDragged) { final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, pointerIndex); final float xDiff = Math.abs(x - mLastMotionX); final float y = MotionEventCompat.getY(ev, pointerIndex); final float yDiff = Math.abs(y - mLastMotionY); if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff); if (xDiff > mTouchSlop && xDiff > yDiff) { if (DEBUG) Log.v(TAG, "Starting drag!"); mIsBeingDragged = true; mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop : mInitialMotionX - mTouchSlop; mLastMotionY = y; setScrollState(SCROLL_STATE_DRAGGING); setScrollingCacheEnabled(true); } } // Not else! Note that mIsBeingDragged can be set above. if (mIsBeingDragged) { // Scroll to follow the motion event final int activePointerIndex = MotionEventCompat.findPointerIndex( ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, activePointerIndex); needsInvalidate |= performDrag(x); } break;
可以明显看到,高版本调用了 requestParentDisallowInterceptTouchEvent(true);,而requestParentDisallowInterceptTouchEvent代码如下,就是掉parent的requestDisallowInterceptTouchEvent,不许父亲吃我的事件
private void requestParentDisallowInterceptTouchEvent(boolean disallowIntercept) { final ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(disallowIntercept); } }
所以我们使用高版本的v4包或者v7包,就不用考虑滑动冲突的问题,ohlala。
原生控件如何解决滑动冲突
我再看了下ScrollViewif (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) { final ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); } mIsBeingDragged = true; if (deltaY > 0) { deltaY -= mTouchSlop; } else { deltaY += mTouchSlop; } }
再看下HorizontalScrollView
if (!mIsBeingDragged && Math.abs(deltaX) > mTouchSlop) { final ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); } mIsBeingDragged = true; if (deltaX > 0) { deltaX -= mTouchSlop; } else { deltaX += mTouchSlop; } }
所以大部分原生控件其实都已经解决了滑动冲突的问题,但是我们自己写的控件,或者说github上的一些控件经常有滑动冲突的问题需要解决,如何解决呢?完全可以参考原生控件的方法。
总结
原生控件基本上都解决了滑动冲突问题,但是我们自己写的控件或者github上的控件很多都没解决,需要我们自己解决,解决方法可就是在onTouchEvent里面处理相关文章推荐
- 搜索 继续畅通工程 problem e
- GRASP----(职责分配原则)
- LinearLayout边框,圆角
- 用七宗罪的角度理解用户需求
- nfs网络文件系统
- UVA10305 欢迎LFX学弟
- 10008---linux 添加用户、权限
- JAVA设计模式--工厂方法模式
- Udig开发环境搭建/入门教程
- (OK) android update sdk - virtualbox nat port forwarding adb "device offline"
- json往前台送数据中文乱码
- R语言-简单多元回归
- KUKA youBot在ROS下的驱动问题
- 测试小卒子--linux--查找命令
- 嵌入式lab4——Bootloader
- 启动eclipse时,出现以下错误:发现了以元素 'd:skin' 开头的无效内容,此处不应含有子元素。
- Android学习笔记2-HttpUtil工具类
- MySQL实践-数据分组和过滤
- 关于Laravel简易路由操作的体会
- python requests模拟登陆github