知识点:
一个典型的触摸事件会经历,DOWN->MOVE->UP。通过MotionEvent我们可以得到相应触摸事件发生的坐标x、y。为此,系统提供了两组方法获取事件坐标。getX/getY和getRawX/getRawY。
这两组方法是有区别的,getX/getY返回的是相对于当前View左上角的x和y坐标,而getRawX/getRawY返回的是相对于手机屏幕左上角的x和y坐标。
本例是Scrollview嵌套ListView的demo。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".MainActivity2">
<ScrollView
android:id="@+id/demo_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="Title"
android:textSize="50dp" />
<ListView
android:id="@+id/demo_lv"
android:layout_width="match_parent"
android:layout_height="600dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="Bottom"
android:textSize="50dp" />
</LinearLayout>
</ScrollView>
</LinearLayout>
在没有解决冲突前,如果滑动中间的ListView部分,会出现ListView中的列表内容不会滑动,而是整个ScrollView滑动的现象,或者一会儿ListView滑动,一会儿ScrollView滑动。
1.自定义Scrollview外部拦截法解决冲突
这种方式需要外层的控件在重写的onInterceptTouchEvent时进行拦截判断。
class CustomScrollView extends ScrollView {
ListView listView;
private float mLastY;
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
super.onInterceptTouchEvent(ev);
boolean intercept = false;
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
intercept = false;
break;
case MotionEvent.ACTION_MOVE:
listView = (ListView) ((ViewGroup)getChildAt(0)).getChildAt(1);
//ListView滑动到顶部,且继续下滑,让scrollView拦截事件
if (listView.getFirstVisiblePosition() == 0 && (ev.getY() - mLastY) > 0) {
//scrollView拦截事件
intercept = true;
}
//listView滑动到底部,如果继续上滑,就让scrollView拦截事件
else if (listView.getLastVisiblePosition() ==listView.getCount() - 1 && (ev.getY() - mLastY) < 0) {
//scrollView拦截事件
intercept = true;
} else {
//不允许scrollView拦截事件
intercept = false;
}
break;
case MotionEvent.ACTION_UP:
intercept = false;
break;
}
mLastY = ev.getY();
return intercept;
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".MainActivity2">
<com.example.dispatcheventdemo.demo1.CustomScrollView
android:id="@+id/demo_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="Title"
android:textSize="50dp" />
<ListView
android:id="@+id/demo_lv"
android:layout_width="match_parent"
android:layout_height="600dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="Bottom"
android:textSize="50dp" />
</LinearLayout>
</com.example.dispatcheventdemo.demo1.CustomScrollView>
</LinearLayout>
public class MainActivity2 extends AppCompatActivity {
private String[] data = {"Apple", "Banana", "Orange", "Watermelon",
"Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango",
"Apple", "Banana", "Orange", "Watermelon",
"Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango",
"Apple", "Banana", "Orange", "Watermelon",
"Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango",
"Apple", "Banana", "Orange", "Watermelon",
"Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
showList();
}
private void showList() {
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity2.this, android.R.layout.simple_list_item_1, data);
ListView listView = findViewById(R.id.demo_lv);
listView.setAdapter(adapter);
}
}
2.自定义ListView内部拦截法解决冲突
这里需要在内层控件中重写的dispatchTouchEvent方法处判断外层控件的拦截逻辑。
public class CustomListView extends ListView {
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
//为listview/Y,设置初始值,默认为0.0(ListView条目一位置)
private float mLastY;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
//不允许上层的ScrollView拦截事件.
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
//满足listView滑动到顶部,如果继续下滑,那就允许scrollView拦截事件
if (getFirstVisiblePosition() == 0 && (ev.getY() - mLastY) > 0) {
//允许ScrollView拦截事件
getParent().requestDisallowInterceptTouchEvent(false);
}
//满足listView滑动到底部,如果继续上滑,允许scrollView拦截事件
else if (getLastVisiblePosition() == getCount() - 1 && (ev.getY() - mLastY) < 0) {
//允许ScrollView拦截事件
getParent().requestDisallowInterceptTouchEvent(false);
} else {
//其它情形时不允ScrollView拦截事件
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_UP:
break;
}
mLastY = ev.getY();
return super.dispatchTouchEvent(ev);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".MainActivity2">
<ScrollView
android:id="@+id/demo_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="Title"
android:textSize="50dp" />
<com.example.dispatcheventdemo.demo1.CustomListView
android:id="@+id/demo_lv"
android:layout_width="match_parent"
android:layout_height="600dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="Bottom"
android:textSize="50dp" />
</LinearLayout>
</ScrollView>
</LinearLayout>