android UI系列专题

Android小记:RecyclerView添加Item点击事件

2020-04-09  本文已影响0人  喝牛奶的小饼干

自从Android5.0,Google发布RecyclerView以来,逐渐代替了传统的ListView,因为RecyclerView更加强大,更加灵活,但是,由于官方未实现RecyclerView的Item点击事件,所以需要自行实现。下面就是我在使用RecyclerView的过程中,总结的添加点击事件的几种方法。

方式一:通过RecyclerView的addOnItemTouchListener()方法

使用RecyclerView提供的addOnItemTouchListener()方法,为Item添加触摸回调,在触摸事件处理中,实现Item点击;
通过源码得知,RecyclerView有一个添加Item触摸事件的方法:

/**
 * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
 * to child views or this view's standard scrolling behavior.
 *
 * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
 * returns true from
 * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
 * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
 * for each incoming MotionEvent until the end of the gesture.</p>
 *
 * @param listener Listener to add
 * @see SimpleOnItemTouchListener
 */
public void addOnItemTouchListener(@NonNull OnItemTouchListener listener) {
    mOnItemTouchListeners.add(listener);
}

此方法需要传入一个OnItemTouchListener,OnItemTouchListener代码如下:

public interface OnItemTouchListener {
    boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
    void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
    void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
}

同时,RecyclerView还提供了一个子类SimpleOnItemTouchListener

/**
 * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method 
 * bodies and default return values.
 * <p>
 * You may prefer to extend this class if you don't need to override all methods. Another
 * benefit of using this class is future compatibility. As the interface may change, we'll
 * always provide a default implementation on this class so that your code won't break when
 * you update to a new version of the support library.
 */
public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
    @Override
    public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
        return false;
    }
    @Override
    public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
    }
    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    }
}

我们可以通过此类实现Item点击功能;
写一个类ItemTouchHelper类继承自SimpleOnItemTouchListener,重写onInterceptTouchEvent方法,在onInterceptTouchEvent中进行事件处理;

@Override
public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView,
                                     @NonNull MotionEvent motionEvent) {
    return mGestureDetectorCompat.onTouchEvent(motionEvent);
}

这里的mGestureDetectorCompat,是一个GestureDetectorCompat辅助工具,继续观察源码,GestureDetectorCompat构造器中需要我们传入ContextOnGestureListener,下面是OnGestureListener源码:

public interface OnGestureListener {
    // 用户按下屏幕就会触发
    boolean onDown(MotionEvent e);
    // 如果是按下的时间超过瞬间,而且在按下的时候没有松开或者是拖动的,那么onShowPress就会执行
    void onShowPress(MotionEvent e);
    // 一次单独的轻击抬起操作,也就是轻击一下屏幕,就是普通点击事件
    boolean onSingleTapUp(MotionEvent e);
    // 在屏幕上拖动事件
    boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
    // 长按触摸屏,超过一定时长,就会触发这个事件
    void onLongPress(MotionEvent e);
    // 滑屏,用户按下触摸屏、快速移动后松开
    boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}

通过源码发现,onSingleTapUp方法会在触摸抬起时触发;同样,官方也为我们提供了一个OnGestureListener的简单实现类SimpleOnGestureListener,这个类实现了所有方法,但都是空实现,我们只需要重写onSingleTapUp方法,并处理事件即可:

GestureDetector.OnGestureListener gl = new GestureDetector.SimpleOnGestureListener() {
    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
        if (child != null && mListener != null) {
            mListener.onItemClick(recyclerView.getChildViewHolder(child));
            return true;
        }
        return false;
    }
}

mGestureDetectorCompat = new GestureDetectorCompat(recyclerView.getContext(), gl);

RecyclerView提供了findChildViewUnder方法,我们可以通过事件点击的坐标获取点击的Item,再通过RecyclerView的getChildViewHolder方法获取Item的ViewHolder,进而执行我们想要的操作,至此,我们可以将点击事件回调出去。方法一:通过View.setOnClickListener()方法

方式二:将点击事件添加到Item的根布局上,通过Adapter回调的方式添加事件监听:
public class MyAdapter extends RecyclerView.Adapter<ViewHolder> {
    
    ...
  
    @Override
    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, int position) {
        viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (listener != null) {
                    listener.onItemClick(viewHolder.itemView, viewHolder.getAdapterPosition());
                }
            }
        });
    }

    private OnItemClickListener listener;

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }

    public interface OnItemClickListener {
        void onItemClick(View itemView, int position);
    }
}
方式三:在Item添加到RecyclerView时添加点击事件

RecyclerView还提供了一个addOnChildAttachStateChangeListener方法,可以为RecyclerView提供一个OnChildAttachStateChangeListener,监听一个View添加到RecyclerView和离开RecyclerView,我们可以在这个时候执行一些操作来实现Item点击:

OnChildAttachStateChangeListener listener = new RecyclerView.OnChildAttachStateChangeListener() {
    @Override
    public void onChildViewAttachedToWindow(@NonNull View view) {
        final RecyclerView.ViewHolder viewHolder = mRecyclerView.getChildViewHolder(view);
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mListener != null){
                    mListener.onItemClick(viewHolder);
                }
            }
        });
    }

    @Override
    public void onChildViewDetachedFromWindow(@NonNull View view) {}
}
mRecyclerView.addOnChildAttachStateChangeListener(listener);

小结

以上三种实现方式是在开发中遇到,并在网上获取的实现方法,三种方式各有优劣,在实际开发中,可以灵活选择。

第一种直接从触摸事件入手,可举一反三,通过不同的逻辑进行其他手势的监听和处理;第二种和第三种大同小异,均是通过View类的setOnClickListener方法入手实现,同样的也可以通过其他方式实现不同效果。


本文仅代表个人看法,如果有误欢迎指正,如果您有更好的实现,欢迎指教,谢谢!

上一篇下一篇

猜你喜欢

热点阅读