ListView与ScrollView滑动冲突处理,是一个很经典的案例,网络上有各种各样的解决方案,比如使用LinearLayout取代ListView、重写ListView的onMeasure方法都能很好的解决这个问题。
但是本次采用的是通过重写ListView的dispatchTouchEvent来处理滑动冲突,并以此加深对Android事件分发机制的理解。
在此之前先了解下面这三个方法:
1. dispatchTouchEvent(MotionEvent event)
如果事件传递给View那么此方法一定能够被调用,其返回结果表示是否消费当前的事件。
2. onIntercepteTouchEvent(MotionEvent event)
表示是否拦截当前事件,如果当前View拦截了某个事件,那么在同一事件序列当中,此方法不会再次被调用,其返回结果表示是否拦截
3. onTouchEvent(MotionEvent event)
在dispatchTouchEvent(MotionEvent event)方法中调用,用于处理点击事件,返回结果表示是否消费当前事件,如果不消费则在同一事件序列中,当前View无法再次接收到事件。
熟悉了这三个方法再说说最关键的内容了,就是如何重写dispatchTouchEvent(MotionEvent event),下面上代码:
View:
public class InsideInterceptListView extends ListView {
private float lasty;
public InsideInterceptListView(Context context) {
super(context);
}
public InsideInterceptListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public InsideInterceptListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
float y = ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
//此处必须让父View不拦截事件,否则后面的事件无法获取
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
//获取ListView当前的第一个可见item
int frist = getFirstVisiblePosition();
//获取ListView当前的最后一个可见item
int last = getLastVisiblePosition();
//获取item总数
int child = getCount();
Log.i("child", "child: " + child);
if (y > lasty && frist == 0) {
//ListView的第一个可见View的position == 0 并且向下滑动时,请求父View拦截事件
getParent().requestDisallowInterceptTouchEvent(false);
} else if (y < lasty && last == child - 1) {
//ListView的最后一个个可见View的position是最后一个item, 并且向上滑动时,请求父View拦截事件
getParent().requestDisallowInterceptTouchEvent(false);
} else {
//其它情况,事件由本View消费
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_UP:
getParent().requestDisallowInterceptTouchEvent(true);
break;
default:
break;
}
lasty = y;
return super.dispatchTouchEvent(ev);
}
}
View中主要是通过重写dispatchTouchEvent方法,在方法内根据当前滑动的方向和当前view的item数量去请求父类是否拦截事件。
public class InsideInterceptActivity extends Activity {
private InsideInterceptListView lvTest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inside_intercept);
initView();
}
/**
* 初始化控件
*/
@SuppressLint("CommitTransaction")
private void initView() {
lvTest = (InsideInterceptListView) findViewById(R.id.test_lv);
List<String> data = new ArrayList<String>();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);
for (int i = 0; i < 50; i++) {
data.add("ITEM:" + i);
}
lvTest.setAdapter(adapter);
}
}
Activity里面没技术含量,基本的实现而已
XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<ScrollView
android:id="@+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/titil_tv"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<com.xuzhenhao.demo.views.InsideInterceptListView
android:id="@+id/test_lv"
android:layout_width="match_parent"
android:layout_height="400dp"
android:scrollbars="none"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="标题:内部拦截"
android:textSize="16sp"
android:textColor="#6C080A"/>
</LinearLayout>
</ScrollView>
</RelativeLayout>
xml里面只是简单的布局。
下面看一看最终的效果!!
View的事件分发机制学习笔记
Android View 事件分发机制 源码解析 (上)