一.滑动冲突场景
二. 场景二:模拟场景 ScrollView和自定义ListView
- 纵向同向的滑动冲突事件场景
- RecycleView内部有处理冲突,所以重现不了冲突现象,所以用自定义ListView实现。
- 解决方案:通过内部拦截法处理冲突
- 未完善 这自定义ListView处理滑动到顶部和底部的逻辑还不够完善,未能够准确判断到最底/最顶的Item View滑到底部或者顶部。
三. 重点代码
自定义ListView:
重写dispatchTouchEvent方法,在down事件时申请父容器ScrollView不拦截事件,使后续事件能传递到子View。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
//这里申请父容器ScrollView不拦截down事件,ScrollView默认自己处理了down事件,会导致子View获取不了
//down事件,那么之后后续事件都将传递不到子View.
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return super.dispatchTouchEvent(ev);
}
自定义ListView:
重写onTouchEvent方法,在滑动业务逻辑判断是否需要处理事件,否则将申请父容器ScrollView拦截事件,不消费事件。
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean result = super.onTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
//当 y轴往下偏移 偏移量>0 ;y轴往上偏移 偏移量<0
isDownOrUpMove = (ev.getY() - mLastY) > 0;
//当可见列表第一个item 下标为0,证明滑到顶部位置
if (getFirstVisiblePosition() == 0) {
if (isDownOrUpMove) {
//当向下滑,ScrollView处理事件,整体内容向下滑动
//所以,申请ScrollView不拦截事件为false,即ScrollView将拦截Move事件
setRequestDisallowIntercept(false);
result = false;
} else {
//当向上滑,ListView处理事件,ListView内容往上滑动
result = true;
}
} else if (getLastVisiblePosition() == getCount() - 1) {
//同理,证明滑到底部部位置
if (isDownOrUpMove) {
//当向下滑,ListView处理事件,ListView内容往下滑动
result = true;
} else {
//当向上滑,ScrollView处理事件,整体内容向上滑动
setRequestDisallowIntercept(false);
result = false;
}
}
break;
}
mLastY = ev.getY();
return result;
}
- 处理冲突后,正常场景
- dispatchTouchEvent不向父容器 申请不拦截事件的现象:
if (parent != null) {
// parent.requestDisallowInterceptTouchEvent(true);
}
四. 具体代码
- 测试Activity
public class VerticalAndVerticalScrollActivity extends AppCompatActivity {
@Bind(R.id.lv)
VerticalInterceptListView mLv;
@Bind(R.id.tv_top)
TextView mTvTop;
@Bind(R.id.tv_bottom)
TextView mTvBottom;
private String TAG = "VerticalAndVerticalScrollActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_vertical_and_vertical_scroll);
ButterKnife.bind(this, this);
initData();
}
private void initData() {
//初始化数据
StringBuffer sbTop = new StringBuffer();
StringBuffer sbBottom = new StringBuffer();
CharSequence textTop = mTvTop.getText();
CharSequence textBottom = mTvBottom.getText();
for (int i = 0; i < 12; i++) {
sbTop.append(textTop + "\n");
}
for (int i = 0; i < 20; i++) {
sbBottom.append(textBottom + "\n");
}
mTvTop.setText(sbTop);
mTvBottom.setText(sbBottom);
final ArrayList<String> strings = new ArrayList<>();
for (int i = 1; i < 21; i++) {
strings.add("第" + i + "个");
}
//初始化ListView
mLv.setParent((ViewGroup) findViewById(R.id.sv));
mLv.setAdapter(new BaseAdapter() {
@Override
public int getCount() {
if (strings != null) {
return strings.size();
}
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
convertView = LayoutInflater.from(VerticalAndVerticalScrollActivity.this).inflate(R.layout.item_sticky_head, parent, false);
viewHolder = new ViewHolder();
viewHolder.setTextView((TextView) convertView.findViewById(R.id.tv_sticky_head));
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.getTextView().setText(strings.get(position));
return convertView;
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
ButterKnife.unbind(this);
}
/**
* ListView ViewHolder
*/
class ViewHolder {
private TextView mTextView;
public TextView getTextView() {
return mTextView;
}
public void setTextView(TextView textView) {
mTextView = textView;
}
}
}
布局
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sv"
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:textSize="16sp"
android:id="@+id/tv_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ListView上面"/>
<com.example.robertluozizhao.framecollectdemo.view.custom.VerticalInterceptListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="300dp"/>
<TextView
android:textSize="16sp"
android:id="@+id/tv_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ListView下面"
/>
</LinearLayout>
</ScrollView>
- 自定义ListView
public class VerticalInterceptListView extends ListView {
private ViewGroup parent;//设置为ScrollView
private float mLastY;
private boolean isDownOrUpMove = true; //标识向下滑动 还是 向上滑动
public VerticalInterceptListView(Context context) {
this(context, null);
}
public VerticalInterceptListView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean result = super.onTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
//当 y轴往下偏移 偏移量>0 ;y轴往上偏移 偏移量<0
isDownOrUpMove = (ev.getY() - mLastY) > 0;
//当可见列表第一个item 下标为0,证明滑到顶部位置
if (getFirstVisiblePosition() == 0) {
if (isDownOrUpMove) {
//当向下滑,ScrollView处理事件,整体内容向下滑动
//所以,申请ScrollView不拦截事件为false,即ScrollView将拦截Move事件
setRequestDisallowIntercept(false);
result = false;
} else {
//当向上滑,ListView处理事件,ListView内容往上滑动
result = true;
}
} else if (getLastVisiblePosition() == getCount() - 1) {
//同理,证明滑到底部部位置
if (isDownOrUpMove) {
//当向下滑,ListView处理事件,ListView内容往下滑动
result = true;
} else {
//当向上滑,ScrollView处理事件,整体内容向上滑动
setRequestDisallowIntercept(false);
result = false;
}
}
break;
}
mLastY = ev.getY();
return result;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
//这里申请父容器ScrollView不拦截down事件,ScrollView默认自己处理了down事件,会导致子View获取不了
//down事件,那么之后后续事件都将传递不到子View.
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return super.dispatchTouchEvent(ev);
}
public void setParent(ViewGroup parent) {
this.parent = parent;
}
public void setRequestDisallowIntercept(boolean disallowIntercept) {
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(disallowIntercept);
requestLayout();//刷新重绘,让View重走测量,布局方法
}
}
public boolean getDownOrUpMoveState() {
return isDownOrUpMove;
}
}
笔记
1.Recycle有处理过滑动冲突。
2.内部拦截法:dispatchTouchEvent方法对于down事件的处理,必须要先申请
父容器ScrollView不拦截down事件。