问题描述: 常见于 发布一篇帖子, 帖子带有几张图, 图是9宫格的RecyclerView展示的,
假如此时用户只传了4张图片, 此时就会存在缺角的部分没有事件响应, 如何来响应这一部分的事件呢?
- 如何处理呢?
- 如果点击区域在Item区域, 则RecyclerView事件不变;
- 如果点击区域在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();
}
}