程序员Android知识Android开发

BaseRecyclerViewAdapterHelper开源项

2017-03-25  本文已影响273人  Angels_安杰

version:2.8.5

今天我们主要来分析BaseRecyclerViewAdapterHelper为view提供监听点击事件能力的相关源码。

 public abstract class SimpleClickListener implements RecyclerView.OnItemTouchListener {
private GestureDetectorCompat mGestureDetector;
private RecyclerView recyclerView;
protected BaseQuickAdapter baseQuickAdapter;
public static String TAG = "SimpleClickListener";
private boolean mIsPrepressed = false;
private boolean mIsShowPress = false;
private View mPressedView = null;

@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
    Log.i(TAG,">>>>onInterceptTouchEvent e"+e.getActionMasked());
    if (recyclerView == null) {
        this.recyclerView = rv;
        this.baseQuickAdapter = (BaseQuickAdapter) recyclerView.getAdapter();
        mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(), new ItemTouchHelperGestureListener(recyclerView));
    }else if (recyclerView!=rv){
        this.recyclerView = rv;
        this.baseQuickAdapter = (BaseQuickAdapter) recyclerView.getAdapter();
        mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(), new ItemTouchHelperGestureListener(recyclerView));
    }
    if (!mGestureDetector.onTouchEvent(e) && e.getActionMasked() == MotionEvent.ACTION_UP && mIsShowPress) {
        if (mPressedView!=null){
            BaseViewHolder vh = (BaseViewHolder) recyclerView.getChildViewHolder(mPressedView);
            if (vh == null ||!isHeaderOrFooterView(vh.getItemViewType())) {
                mPressedView.setPressed(false);
            }
        }
        mIsShowPress = false;
        mIsPrepressed = false;
    }
    return false;
}

@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
    Log.i(TAG,">>>>onTouchEvent e"+e.getActionMasked());
    mGestureDetector.onTouchEvent(e);
}

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    Log.i(TAG,">>>>onRequestDisallowInterceptTouchEvent disallowIntercept"+disallowIntercept);
}

private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {

    private RecyclerView recyclerView;


    ItemTouchHelperGestureListener(RecyclerView recyclerView) {
        this.recyclerView = recyclerView;
    }
    @Override
    public boolean onDown(MotionEvent e) {
        mIsPrepressed = true;
        mPressedView = recyclerView.findChildViewUnder(e.getX(), e.getY());
        super.onDown(e);
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {
        Log.i(TAG,">>>>onShowPress e"+e);
        if (mIsPrepressed && mPressedView != null) {
    //    mPressedView.setPressed(true);
            mIsShowPress = true;
        }
        super.onShowPress(e);
    }


    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        Log.i(TAG,">>>>onSingleTapUp e"+e);
        if (mIsPrepressed && mPressedView != null) {
            if (recyclerView.getScrollState()!=RecyclerView.SCROLL_STATE_IDLE){
                return false;
            }
            final View pressedView = mPressedView;
            BaseViewHolder vh = (BaseViewHolder) recyclerView.getChildViewHolder(pressedView);

            if (isHeaderOrFooterPosition(vh.getLayoutPosition())) {
                return false;
            }
            Set<Integer> childClickViewIds = vh.getChildClickViewIds();
            Set<Integer> nestViewIds = vh.getNestViews();
            if (childClickViewIds != null && childClickViewIds.size() > 0) {
                for (Integer childClickViewId : childClickViewIds) {
                    View childView = pressedView.findViewById(childClickViewId);
                    if (childView != null) {
                        if (inRangeOfView(childView, e) && childView.isEnabled()) {

                            if (nestViewIds!=null&&nestViewIds.contains(childClickViewId)){
                                return false;
                            }

                            setPressViewHotSpot(e, childView);
                            childView.setPressed(true);
                            onItemChildClick(baseQuickAdapter, childView, vh.getLayoutPosition() - baseQuickAdapter.getHeaderLayoutCount());
                            resetPressedView(childView);
                            return true;
                        } else {
                            childView.setPressed(false);
                        }
                    }

                }
                setPressViewHotSpot(e,pressedView);
                mPressedView.setPressed(true);
                for (Integer childClickViewId : childClickViewIds) {
                    View childView = pressedView.findViewById(childClickViewId);
                    if (childView!=null){
                        childView.setPressed(false);
                    }
                }
                onItemClick(baseQuickAdapter, pressedView, vh.getLayoutPosition() - baseQuickAdapter.getHeaderLayoutCount());
            } else {
                setPressViewHotSpot(e,pressedView);
                mPressedView.setPressed(true);
                if (childClickViewIds != null && childClickViewIds.size() > 0) {
                    for (Integer childClickViewId : childClickViewIds) {
                        View childView = pressedView.findViewById(childClickViewId);
                        if (childView!=null){
                            childView.setPressed(false);
                        }
                    }
                }

                onItemClick(baseQuickAdapter, pressedView, vh.getLayoutPosition() - baseQuickAdapter.getHeaderLayoutCount());
            }
            resetPressedView(pressedView);

        }
        return true;
    }

    private void resetPressedView(final View pressedView) {
        if (pressedView!=null){
            pressedView.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (pressedView!=null){
                        pressedView.setPressed(false);
                    }

                }
            }, 100);
        }

        mIsPrepressed = false;
        mPressedView = null;
    }

    @Override
    public void onLongPress(MotionEvent e) {
        Log.i(TAG,">>>>onLongPress e"+e);
        boolean isChildLongClick =false;
        if (recyclerView.getScrollState()!=RecyclerView.SCROLL_STATE_IDLE){
            return ;
        }
        if (mIsPrepressed && mPressedView != null) {
            mPressedView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
            BaseViewHolder vh = (BaseViewHolder) recyclerView.getChildViewHolder(mPressedView);
            if (!isHeaderOrFooterPosition(vh.getLayoutPosition())) {
                Set<Integer> longClickViewIds = vh.getItemChildLongClickViewIds();
                Set<Integer> nestViewIds = vh.getNestViews();
                if (longClickViewIds != null && longClickViewIds.size() > 0) {
                    for (Integer longClickViewId : longClickViewIds) {
                        View childView = mPressedView.findViewById(longClickViewId);
                        if (inRangeOfView(childView, e) && childView.isEnabled()) {
                            if (nestViewIds!=null&&nestViewIds.contains(longClickViewId)){
                                isChildLongClick=true;
                                break;
                            }
                            setPressViewHotSpot(e, childView);
                            onItemChildLongClick(baseQuickAdapter, childView, vh.getLayoutPosition() - baseQuickAdapter.getHeaderLayoutCount());
                            childView.setPressed(true);
                            mIsShowPress = true;
                            isChildLongClick = true;
                            break;
                        }
                    }
                }
                if (!isChildLongClick){

                    onItemLongClick(baseQuickAdapter, mPressedView, vh.getLayoutPosition() - baseQuickAdapter.getHeaderLayoutCount());
                    setPressViewHotSpot(e,mPressedView);
                    mPressedView.setPressed(true);
                    if (longClickViewIds != null) {
                        for (Integer longClickViewId : longClickViewIds) {
                            View childView = mPressedView.findViewById(longClickViewId);
                            childView.setPressed(false);
                        }
                    }
                    mIsShowPress = true;
                }

            }

        }
    }


}

private void setPressViewHotSpot(final MotionEvent e,final  View mPressedView) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        /**
         * when   click   Outside the region  ,mPressedView is null
         */
        if (mPressedView !=null && mPressedView.getBackground() != null) {
            mPressedView.getBackground().setHotspot(e.getRawX(), e.getY()-mPressedView.getY());
        }
    }
}

/**
 * Callback method to be invoked when an item in this AdapterView has
 * been clicked.
 *
 * @param view     The view within the AdapterView that was clicked (this
 *                 will be a view provided by the adapter)
 * @param position The position of the view in the adapter.
 */
public abstract void onItemClick(BaseQuickAdapter adapter, View view, int position);

/**
 * callback method to be invoked when an item in this view has been
 * click and held
 *
 * @param view     The view whihin the AbsListView that was clicked
 * @param position The position of the view int the adapter
 * @return true if the callback consumed the long click ,false otherwise
 */
public abstract void onItemLongClick(BaseQuickAdapter adapter, View view, int position);

public abstract void onItemChildClick(BaseQuickAdapter adapter, View view, int position);

public abstract void onItemChildLongClick(BaseQuickAdapter adapter, View view, int position);

public boolean inRangeOfView(View view, MotionEvent ev) {
    int[] location = new int[2];
    if (view==null||!view.isShown()){
        return false;
    }
    view.getLocationOnScreen(location);
    int x = location[0];
    int y = location[1];
    if (ev.getRawX() < x
            || ev.getRawX() > (x + view.getWidth())
            || ev.getRawY() < y
            || ev.getRawY() > (y + view.getHeight())) {
        return false;
    }
    return true;
}

private boolean isHeaderOrFooterPosition(int position) {
    /**
     *  have a headview and EMPTY_VIEW FOOTER_VIEW LOADING_VIEW
     */
    if (baseQuickAdapter==null){
        if (recyclerView!=null){
            baseQuickAdapter= (BaseQuickAdapter) recyclerView.getAdapter();
        }else {
            return false;
        }
    }
    int type = baseQuickAdapter.getItemViewType(position);
    return (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type == LOADING_VIEW);
}
private boolean isHeaderOrFooterView(int type) {

    return (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type == LOADING_VIEW);
}}

用过BVAH框架中的点击事件的小伙伴们应该对以下几个接口类不陌生:

里面都提供了响应的回调接口,但是赋予他们能力的其实是SimpleClickListener 这个类。是不是开始开兴趣了?让我们一起来看下这个类的真实面目:

public abstract class SimpleClickListener implements RecyclerView.OnItemTouchListener {....}

注:点击事件的实现有多重方式,如:

类内字段解析

接下来我们根据事件的传递机制进行分析,当touch事件发生时:
onInterceptTouchEvent是第一个被调用的。

@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
    Log.i(TAG,">>>>onInterceptTouchEvent e"+e.getActionMasked());
    if (recyclerView == null) {
        this.recyclerView = rv;
        this.baseQuickAdapter = (BaseQuickAdapter) recyclerView.getAdapter();
        mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(), new ItemTouchHelperGestureListener(recyclerView));
    }else if (recyclerView!=rv){
        this.recyclerView = rv;
        this.baseQuickAdapter = (BaseQuickAdapter) recyclerView.getAdapter();
        mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(), new ItemTouchHelperGestureListener(recyclerView));
    }
    if (!mGestureDetector.onTouchEvent(e) && e.getActionMasked() == MotionEvent.ACTION_UP && mIsShowPress) {
        if (mPressedView!=null){
            BaseViewHolder vh = (BaseViewHolder) recyclerView.getChildViewHolder(mPressedView);
            if (vh == null ||!isHeaderOrFooterView(vh.getItemViewType())) {
                mPressedView.setPressed(false);
            }
        }
        mIsShowPress = false;
        mIsPrepressed = false;
    }
    return false;
}

源码中,在onInterceptTouchEvent 方法中主要做了两件事情:

然后我们就可以在onTouchEvent 方法中将touch事件交给mGestureDetector进行处理了

  @Override
   public void onTouchEvent(RecyclerView rv, MotionEvent e) {
       Log.i(TAG,">>>>onTouchEvent e"+e.getActionMasked());
       mGestureDetector.onTouchEvent(e);
   }

然后我们看在初始化mGestureDetector时绑定的ItemTouchHelperGestureListener 监听器, ItemTouchHelperGestureListener 监听器继承自SimpleOnGestureListener类,我们进入SimpleOnGestureListener的源码可以看到:

 public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener,
        OnContextClickListener {

其内部其实是实现了在个手势交互监听器,只是方法体什么都没做,这样的好处是,我们继承他,可以不用实现一堆方法,需要用到哪个方法,重写该方法就可以了。

我们这次主要用到了如下几个方法:

可以看到,使用GestureDetectorCompat的好处。

根据一个touch事件的发生过程,down->showpress->up
即压下,显示挤压效果,松开弹起。

 @Override
    public boolean onDown(MotionEvent e) {
        mIsPrepressed = true;
        mPressedView = recyclerView.findChildViewUnder(e.getX(), e.getY());
        super.onDown(e);
        return false;
    }

在down中:

@Override
public void onShowPress(MotionEvent e) {
if (mIsPrepressed && mPressedView != null) {
mIsShowPress = true;
}
super.onShowPress(e);
}
在showpress中:

如果是单机事件则会触发 onSingleTapUp(MotionEvent e)
在从onSIngleTapUp的源码中可以看到:

if (mIsPrepressed && mPressedView != null) {

接下来到了关键代码了:

 Set<Integer> childClickViewIds = vh.getChildClickViewIds();

如果在点击范围内切isEnable = true则执行下面代码

    if (nestViewIds!=null&&nestViewIds.contains(childClickViewId)){
                return false;  }

经过层层过滤之后,剩下的就是当view被点击时需要做的操作了:
如果

如果不出于点击范围内或者isEable=false 则 childView.setPressed(false);

上面处理了item下的childview 的点击,如果是item维度的点击,则执行下面代码,流程基本一致,只是回调的是onItemClick方法。

                setPressViewHotSpot(e,pressedView);
                mPressedView.setPressed(true);
                for (Integer childClickViewId : childClickViewIds) {
                    View childView = pressedView.findViewById(childClickViewId);
                    if (childView!=null){
                        childView.setPressed(false);
                    }
                }
                onItemClick(baseQuickAdapter, pressedView, vh.getLayoutPosition() - baseQuickAdapter.getHeaderLayoutCount());
            } 

之前处理的是存在childview 的情况,如果不存在childview,自然点击事件都是item的了,操作跟在存在childview但childview不处于点击范围内或者isEable=false时类似。

else {
                setPressViewHotSpot(e,pressedView);
                mPressedView.setPressed(true);
                if (childClickViewIds != null && childClickViewIds.size() > 0) {
                    for (Integer childClickViewId : childClickViewIds) {
                        View childView = pressedView.findViewById(childClickViewId);
                        if (childView!=null){
                            childView.setPressed(false);
                        }
                    }
                }

                onItemClick(baseQuickAdapter, pressedView, vh.getLayoutPosition() - baseQuickAdapter.getHeaderLayoutCount());
            }
            resetPressedView(pressedView);

长点击的实现类似,这里就不做重复的分析了。如果大家有哪里不懂的可以直接留言
总结:通过GestureDetectorCompat的使用,能很好的简化我们的编码量,在涉及到交互的操作时,都可以使用GestureDetectorCompat来进行辅助。最好的老师就是阅读优秀的源码,欢迎一起学习,共同进步!

上一篇下一篇

猜你喜欢

热点阅读