View列表嵌套滑动冲突

文章介绍了在抖音直播间中,如何实现上下滑动切换直播间、左右滑动清屏以及进入博主主页等功能。技术上涉及到了RecyclerView的自定义,手势监听,以及通过onInterceptTouchEvent和onTouchEvent处理滑动事件。同时,文章提到了滑动消息列表的处理,涉及到事件传递和消费的控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        如今人人玩抖音、小视频的社会,功能特效也越来越多,越来越具有特色性,玩法多重多样,刷着视频突然对一个主播感兴趣,点击进入一个直播间,哇,不愧是大主播,礼物特效、消息一条一条喷薄而出,作为一名技术开发者,就喜欢多多钻钻,善于发挥脑细胞能量;

        场景分析:进入一个直播间,上下滑动可以切换直播间,左右滑动可以清屏直播,向右滑动可以进入博主主页进行查看,滑动消息区域可以查看以前的消息等;看起来一个简单的操作,分析过后发现用到的技术还不少;

        技术分析:首先想到的是既然可以上下滑动,Recyclerview主动举起了手,左右滑动、上下滑动那就是监听手势相关,dispatchTouchEvent()–>onInterceptTouchEvent()–>onTouchEvent(),滑动消息列表通知父View消息View自己要消费事件,通过ACTION_DOWN、ACTION_MOVE,监听点击区域和滑动区域来控制事件有谁消费,事件传递不再累赘可自行查阅,主要是在onInterceptTouchEvent里面通过点击区域来控制谁消费;

分析完成就开始干吧,拿笔来。

这里自定义一个FramLayout嵌套一个RecyclerView(直播间)+Fragment(适配器Item)+Recyclerview(消息列表)

/**
 * 视频播放上下滑动切换控件
 */
public class LiveRecyclerView extends FrameLayout {
    private static final String TAG = LiveRecyclerView.class.getSimpleName();
    private static final int COLOR_BG = Color.BLACK; // 背景色
    private static final float DRAG_RATE = 2.5f; // 下拉上拉的粘性(数值越大越难下拉)
    private static final int TEXT_COLOR = 0xff999999; // 提示文字颜色
    private static final float TEXT_SIZE = 12; // 提示文字大小
    private static final float TEXT_MARGIN = 150; // 提示文字和 RecyclerView 的间距

    private RecyclerView recyclerView;
    private TextView tvTip;
    private PagerLayoutManager layoutManager;

    private float mDownX = -1;
    private float mDownY = -1;
    private float mLastY = -1;
    private boolean isPulling;

    private RoomScrollControlListener mRoomScrollListener = null;

    public void setRoomScrollListener(RoomScrollControlListener roomScrollListener) {
        mRoomScrollListener = roomScrollListener;
    }


    public LiveRecyclerView(Context context) {
        super(context);
        init();
    }

    public LiveRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public LiveRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        setBackgroundColor(COLOR_BG);
        // 提示TextView
        tvTip = new TextView(getContext());
        tvTip.setTextSize(TEXT_SIZE);
        tvTip.setTextColor(TEXT_COLOR);
        tvTip.setGravity(Gravity.CENTER_HORIZONTAL);
        addView(tvTip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        // RecyclerView
        recyclerView = new NestRecyclerView(getContext());
        addView(recyclerView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        layoutManager = new PagerLayoutManager(getContext());
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.getParent().requestDisallowInterceptTouchEvent(true);
    }


    public RecyclerView getRecyclerView() {
        return recyclerView;
    }

    public void setAdapter(LivePlayAdapter adapter) {
        recyclerView.setAdapter(adapter);
        layoutManager.setOnPageChangeListener(adapter);
    }

    public void scrollToPosition(int position) {
        layoutManager.scrollToPositionWithOffset(position, 0);
    }

    //是否禁止滑动
    public void setCanSwipe(boolean canSwipe) {
        this.canSwipe = canSwipe;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mDownX = ev.getRawX();
                mDownY = ev.getRawY();
                mLastY = ev.getRawY();
                Log.e(TAG, "onInterceptTouchEvent  ACTION_DOWN   X坐标:" + mDownX + "   Y坐标:" + mDownY);
                break;
            case MotionEvent.ACTION_MOVE:
//                Log.e(TAG, "onInterceptTouchEvent  ACTION_MOVE");
                float deltaX = ev.getRawX() - mDownX;
                float deltaY = ev.getRawY() - mDownY;
                if (Math.abs(deltaY) > Math.abs(deltaX)) {
                    if (mDownX > 63 && mDownX < 450 && mDownY > 750 && mDownY < 1221) {
                        Log.e(TAG, "上下滑动大于左右且点击在消息列表区域");
                        setCanSwipe(false);
                        recyclerView.setNestedScrollingEnabled(false);
                        if (mRoomScrollListener!=null){
                            mRoomScrollListener.closeRefresh();
                        }
                        return false;
                    }
                  ...
                }
                break;
        }
        return canSwipe && super.onInterceptTouchEvent(ev);
    }

    private boolean canSwipe = true;

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mDownY == -1) {
            mDownY = ev.getRawY();
        }
        if (mLastY == -1) {
            mLastY = ev.getRawY();
        }
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                float x = ev.getRawX();
                float y = ev.getRawY();
                Log.e(TAG, "onTouchEvent  ACTION_DOWN   " + x + "  y坐标   " + y);
                return false;
            case MotionEvent.ACTION_MOVE:
                if (mRoomScrollListener!=null){
                    mRoomScrollListener.showRefresh();
                }
                Log.e(TAG, "onTouchEvent  ACTION_MOVE");
                float deltaY = ev.getRawY() - mDownY;
                if (deltaY > 0 && !recyclerView.canScrollVertically(-1)) {
                    tvTip.setText("没有更多直播啦");
                    tvTip.setY(deltaY / DRAG_RATE - TEXT_MARGIN);
                    recyclerView.setY(deltaY / DRAG_RATE);
                    isPulling = true;
                } else if (deltaY < 0 && !recyclerView.canScrollVertically(1)) {
                    tvTip.setText("已经到底啦");
                    tvTip.setY(getHeight() + deltaY / DRAG_RATE + TEXT_MARGIN);
                    recyclerView.setY(deltaY / DRAG_RATE);
                    isPulling = true;
                }
                mLastY = ev.getRawY();
                break;
            default:
                mDownY = -1;
                mLastY = -1;
                if (isPulling) {
                    TranslateAnimation animation;
                    // TextView 归位
                    if (recyclerView.getY() > 0) {
                        animation = new TranslateAnimation(0, 0, tvTip.getY(), -TEXT_MARGIN - tvTip.getHeight());
                    } else {
                        animation = new TranslateAnimation(0, 0, tvTip.getY(), getHeight() + TEXT_MARGIN);
                    }
                    animation.setDuration(300);
                    animation.setAnimationListener(new Animation.AnimationListener() {
                        @Override
                        public void onAnimationStart(Animation animation) {
                        }

                        @Override
                        public void onAnimationEnd(Animation animation) {
                            tvTip.setY(-TEXT_MARGIN - tvTip.getHeight());
                        }

                        @Override
                        public void onAnimationRepeat(Animation animation) {
                        }
                    });
                    tvTip.setY(0);
                    tvTip.startAnimation(animation);
                    // RecyclerView 归位
                    TranslateAnimation animation1 = new TranslateAnimation(0, 0, recyclerView.getY(), 0);
                    animation1.setDuration(300);
                    animation1.setAnimationListener(new Animation.AnimationListener() {
                        @Override
                        public void onAnimationStart(Animation animation) {
                        }

                        @Override
                        public void onAnimationEnd(Animation animation) {
                            recyclerView.setY(0);
                        }

                        @Override
                        public void onAnimationRepeat(Animation animation) {
                        }
                    });
                    recyclerView.setY(0);
                    recyclerView.startAnimation(animation1);

                    isPulling = false;
                }
                break;
        }
        return canSwipe && super.onTouchEvent(ev);
    }
}

public class NestRecyclerView extends RecyclerView {
    private static final String TAG = NestRecyclerView.class.getSimpleName();
    private float downX, downY;

    public NestRecyclerView(@NonNull Context context) {
        super(context);
    }

    public NestRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public NestRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = ev.getX();
                downY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                int width = getWidth();
                int height = getHeight();

                if (downX > width/11.0769 && downX < width/1.6 && downY > height/1.813 && downY < height/1.1138) {
                    Log.e(TAG, "onInterceptTouchEvent  ACTION_MOVE 在消息列表区域");
                    //消息区域
                    return false;
                }
        }
        return super.onInterceptTouchEvent(ev);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个小狼娃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值