滑动冲突现象及解决方案(笔记三)

一.滑动冲突场景

在这里插入图片描述

二. 场景二:模拟场景 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事件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值