滑动冲突解决方案
在我们的布局中有可能使用Scrollview嵌套Listview的情况,这时候如果不加任何的处理,就会发生滑动冲突问题,即Listview没有完全显示,也不能滑动,触摸事件被Scrollview拦截而使只有Scrollview在滑动。根据Android开发ViewGroup及View事件分发总结,我们可以用以下的方案来解决:
根据对捕获到的事件,我们可以对事件作出判断(处理或不处理),我们可以采用设置监听的方式setOnTouchListener(),也可以继承View而复写OnTouchEvent()的方式。但在ViewGroup中要判断是否拦截事件的时候则只能复写disPatchTouchEvent()方法。在本例中采用继承Scrollview和设置Listview监听器的方式。
先看Activity中的代码:
package com.zyxr.www.huadongchongtu;
import android.content.Intent;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.ScrollView;
import com.zyxr.www.R;
public class HuaDongActivity extends AppCompatActivity {
private ListView mListView;
private ScrollView mScroView;
boolean isTopOrBottom = false;//判断是否滑到了顶部或底部
private boolean hasChange = false;//判断listview滑动状态是否改变的标记
private int mListViewHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hua_dong);
mListView = (ListView) findViewById(R.id.huadong_list);
mScroView = (ScrollView) findViewById(R.id.huadong_scro);
mListView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if(motionEvent.getAction()!=MotionEvent.ACTION_UP){
mScroView.requestDisallowInterceptTouchEvent(true);//不允许ScrollView拦截事件
}else {
mScroView.requestDisallowInterceptTouchEvent(false);//允许ScrollView拦截事件
}
return false;
}
});
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int i) {
hasChange = true;
}
@Override
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if(hasChange){
if ((visibleItemCount > 0) && (firstVisibleItem == 0)) {
if (absListView.getChildAt(0).getTop() >= 0) { //判断listview是否滑动到了顶部
isTopOrBottom = true;
Intent intent = new Intent("com.zyxr.www");
sendBroadcast(intent);//以广播的形式通知scrollview需要拦截事件
}
}
else if ((firstVisibleItem + visibleItemCount) == totalItemCount){
View lastVisibleItemView = mListView.getChildAt(mListView.getChildCount() - 1);
if(lastVisibleItemView != null && lastVisibleItemView.getBottom() == mListViewHeight){ //判断listview是否滑动到了底部
isTopOrBottom = true;
Intent intent = new Intent("com.zyxr.www");
sendBroadcast(intent);//以广播的形式通知scrollview需要拦截事件
}
}
}
}
});
mListView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mListViewHeight = mListView.getHeight();//获取listview的高度
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
mListView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
mListView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
});
}
}
布局中加几张大的图片以使Scrollview和LIstview都能滑动,以下是Activity加载的布局:
<?xml version="1.0" encoding="utf-8"?>
<com.zyxr.www.huadongchongtu.MScrollView
android:id="@+id/huadong_scro"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.zyxr.www.huadongchongtu.HuaDongActivity">
<ImageView
android:layout_width="wrap_content"
android:src="@drawable/p1"
android:scaleType="fitXY"
android:layout_height="200dp"/>
<ImageView
android:layout_width="wrap_content"
android:scaleType="fitXY"
android:layout_height="200dp"
android:src="@drawable/p2"/>
<ListView
android:id="@+id/huadong_list"
android:layout_width="match_parent"
android:layout_height="200dp"
android:entries="@array/ceshi">
</ListView>
<ImageView
android:layout_width="wrap_content"
android:scaleType="fitXY"
android:layout_height="200dp"
android:src="@drawable/p3"/>
</LinearLayout>
</com.zyxr.www.huadongchongtu.MScrollView>
在资源文件Strings中加入:
<string-array name="ceshi">
<item>南山区</item>
<item>福田区</item>
<item>光明新区</item>
<item>龙华新区</item>
<item>罗湖区</item>
<item>宝安区</item>
</string-array>
下面是继承Scrollview并复写其中的方法:
package com.zyxr.www.huadongchongtu;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ScrollView;
/**
* Created by Administrator on 2016/7/28.
*/
public class MScrollView extends ScrollView{
Context context ;
private boolean isTopOrBottom = false;
public MScrollView(Context context) {
super(context);
this.context = context;
init();
}
private void init() {
BroadcastReceiver receiver = new MBroadCast();
IntentFilter filter = new IntentFilter("com.zyxr.www");
context.registerReceiver(receiver,filter);//注册广播
}
public MScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
public MScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
init();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(isTopOrBottom){
return true;//将后续事件拦截了,后续事件该方法也不会执行
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_UP){
isTopOrBottom = false; //释放手时保证下次触摸scrollview不会拦截事件
}
return super.onTouchEvent(ev);
}
public class MBroadCast extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
isTopOrBottom = true;//到达了listview顶部
requestDisallowInterceptTouchEvent(false);//告诉自己可以拦截事件,即会执行onInterceptTouchEvent方法
}
}
}
代码很简单,其中也有不足的地方,其实只要把原理及结论都弄清楚了,一切问题都迎刃而解,解决的方案也会多种多样,嗯,就这样!