九宫格缺角RecyclerView点击事件的处理

本文详细介绍了在Android开发中,如何使用RecyclerView实现特定的事件拦截与处理机制,尤其是在处理9宫格图片展示时,对于未填充图片区域点击事件的响应。通过自定义RecyclerView并重写onInterceptTouchEvent和onTouchEvent方法,可以实现对空白区域点击事件的监听。

问题描述: 常见于 发布一篇帖子, 帖子带有几张图, 图是9宫格的RecyclerView展示的,
假如此时用户只传了4张图片, 此时就会存在缺角的部分没有事件响应, 如何来响应这一部分的事件呢?

  • 如何处理呢?
  1. 如果点击区域在Item区域, 则RecyclerView事件不变;
  2. 如果点击区域在Item之外, 则RecyclerView拦截事件自己处理.
  • 拦截事件处理
    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        return needIntercept(e) || super.onInterceptTouchEvent(e);
    }
    
    /**
     * 计算 是否拦截当前recyclerView的事件.
     *
     * @param ev 事件
     * @return 是否拦截
     */
    private boolean needIntercept(MotionEvent ev) {
        final int action = ev.getAction();
        if (action == MotionEvent.ACTION_DOWN && getChildCount() > 0) {
            final float x = ev.getX();
            final float y = ev.getY();
            int firstVisibleItemPos = getChildLayoutPosition(getChildAt(0));
            int lastVisibleItemPos = getChildLayoutPosition(getChildAt(getChildCount() - 1));
            for (int i =  firstVisibleItemPos; i <= lastVisibleItemPos; i++) {
                int realPos = i - firstVisibleItemPos;
                // 事件落在item的view上, 分发事件下去
                if (pointInView(x, y, getChildAt(realPos))) {
                    calculateIntercept = false;
                    break;
                } else {
                    calculateIntercept = true;
                }
            }
        }
        return calculateIntercept;
    }
    
    /**
     * 判断当前点是否在 child 上
     */
    private boolean pointInView(float localX, float localY, View child) {
        return localX >= child.getLeft() && localX <= child.getRight() &&
                localY >= child.getTop() && localY <= child.getBottom();
    }
  • 设置点击事件
    mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                if (mOnEmptyClickListener != null) {
                    mOnEmptyClickListener.onContentClicked(InterceptRecyclerView.this);
                }
                return super.onSingleTapConfirmed(e);
            }
        });
        
    @Override
    public boolean onTouchEvent(MotionEvent e) {
        if (calculateIntercept) {
            mGestureDetector.onTouchEvent(e);
        }
        return super.onTouchEvent(e);
    }
  • 完整代码如下
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;

/**
 *
 * @author haomini
 * @see #onInterceptTouchEvent(MotionEvent)
 * @since 2019/4/16
 */
public class InterceptRecyclerView extends RecyclerView {

    /**
     * 触摸事件doctor
     */
    protected GestureDetector mGestureDetector;
    /**
     * empty click listener
     */
    protected OnEmptyClickListener mOnEmptyClickListener;
    /**
     * 上一次记录的是否拦截事件
     */
    protected boolean calculateIntercept;

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

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

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

    protected void init() {
        mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                if (mOnEmptyClickListener != null) {
                    mOnEmptyClickListener.onContentClicked(InterceptRecyclerView.this);
                }
                return super.onSingleTapConfirmed(e);
            }
        });
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        return needIntercept(e) || super.onInterceptTouchEvent(e);
    }

    @Override
    @SuppressLint("ClickableViewAccessibility")
    public boolean onTouchEvent(MotionEvent e) {
        if (calculateIntercept) {
            mGestureDetector.onTouchEvent(e);
        }
        return super.onTouchEvent(e);
    }

    /**
     * 设置当事件被拦截时整体的点击事件回调.
     *
     * @param onEmptyClickListener 回调
     */
    public void setOnEmptyClickListener(OnEmptyClickListener onEmptyClickListener) {
        mOnEmptyClickListener = onEmptyClickListener;
    }

    public interface OnEmptyClickListener {
        /**
         * 当事件被拦截下来后, 事件的处理
         *
         * @param v 当前RecyclerView
         */
        void onContentClicked(InterceptRecyclerView v);
    }

    /**
     * 计算 是否拦截当前recyclerView的事件.
     *
     * @param ev 事件
     * @return 是否拦截
     */
    private boolean needIntercept(MotionEvent ev) {
        final int action = ev.getAction();
        if (action == MotionEvent.ACTION_DOWN && getChildCount() > 0) {
            final float x = ev.getX();
            final float y = ev.getY();
            int firstVisibleItemPos = getChildLayoutPosition(getChildAt(0));
            int lastVisibleItemPos = getChildLayoutPosition(getChildAt(getChildCount() - 1));
            for (int i =  firstVisibleItemPos; i <= lastVisibleItemPos; i++) {
                int realPos = i - firstVisibleItemPos;
                // 事件落在item的view上, 分发事件下去
                if (pointInView(x, y, getChildAt(realPos))) {
                    calculateIntercept = false;
                    break;
                } else {
                    calculateIntercept = true;
                }
            }
        }
        return calculateIntercept;
    }

    /**
     * 判断当前点是否在 child 上
     */
    private boolean pointInView(float localX, float localY, View child) {
        return localX >= child.getLeft() && localX <= child.getRight() &&
                localY >= child.getTop() && localY <= child.getBottom();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值