您当前的位置: 首页 > 
  • 0浏览

    0关注

    674博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

ItemTouchHelper实现拖拽和侧滑删除自定义篇(二)

沙漠一只雕得儿得儿 发布时间:2020-05-02 20:10:14 ,浏览量:0

上一篇中我们学习了使用itemTouchHelper来上下拖拽和侧滑删除view,但是如果需要在item侧滑后响应我们的按钮点击事件是无法生效的,因为ItemTouchHelper并没有进行子view的事件分发,导致无法响应在recyclerView中的子view的点击事件。这就需要我们自己完成对子view的事件分发了,整体实现效果如图,完整项目

在这个recyclerView中我么定义了三种布局的item,里面都有点击删除事件,我们侧滑出来之后,再点击删除可以完成删除操作,就表示我们的子view响应了点击事件。本篇主要讲解思路如何将源码中的ItemTouchHelper这个类支持我们的事件分发

我们需要自己完成ItemTouchHelper中的事件分发,我们把代码copy一份到自己项目中,这个文件唯一依赖的是ItemTouchUIUtilImpl,因此我们将这两个类拷贝出来。我们核心需要添加的就两步:

  • 1、找到当前的这个按钮
  • 2、开始事件分发
步骤一:首先找到我们需要响应点击事件的view内部的子view:
/**
     * 分发按键事件给子控件
     * 根据坐标找到自控控件
     *
     * @param event
     */
    private void doChildClickEvent(MotionEvent event) {
        // mSelected 当前选择的需要上下滑动或者左右删除的元素view
        if (mSelected == null) return;

        View consumeEventView = mSelected.itemView;

        if (consumeEventView instanceof ViewGroup) {
            consumeEventView = findConsumeView((ViewGroup) consumeEventView, event.getRawX(),
                    event.getRawY());
        }

        /**
         * 找到了最终的view,分发click事件
         */
        if (consumeEventView != null) {
            consumeEventView.performClick();
        }
    }

    /**
     * 通过递归来找当前需要响应的view
     */
    private View findConsumeView(ViewGroup parent, float x, float y) {
        for (int i = 0; i < parent.getChildCount(); i++) {
            View child = parent.getChildAt(i);
            if (child instanceof ViewGroup && child.getVisibility() == View.VISIBLE) {
                View view = findConsumeView((ViewGroup) child, x, y);
                if (view != null) {
                    return view;
                }
            } else {
                if (isInBounds((int) x, (int) y, child)) {
                    return child;
                }
            }
        }

        if (isInBounds((int) x, (int) y, parent)) {
            return parent;
        }
        return null;
    }

    /**
     * 判断是不是在当前的这个xy区域
     */
    private boolean isInBounds(int x, int y, View child) {
        int[] location = new int[2];
        child.getLocationOnScreen(location);
        Rect rect = new Rect(location[0], location[1], location[0] + child.getWidth(), location[1] + child.getHeight());
        if (rect.contains(x, y) && ViewCompat.hasOnClickListeners(child) &&
                child.getVisibility() == View.VISIBLE) {
            return true;
        }
        return false;
    }

整体思路比较清晰:在当前选择的需要上下滑动或者左右删除的元素view中,利用递归viewgroup去找到需要响应的子view元素

步骤二:事件分发出去:

在onInterceptTouchEvent这个方法中,我们设置两个变量,mClick表示是否点击,initX表示当前手指的x坐标位置;整体思路就是要是点击状态,并且是ACTION_UP则表示我们完成了这次点击,需要调用doChildClickEvent(event),去响应我们的子view的点击事件了。例如:

else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
                if (mClick && action == MotionEvent.ACTION_UP) {
                    doChildClickEvent(event);
                }
                case MotionEvent.ACTION_UP:
                    if (mClick) {
                        doChildClickEvent(event);
                    }
                    mClick = false;

整体的响应点击事件的就这两处;

为了防止手的抖动造成左右滑动时误操作,我们利用initX进行滑动距离判断:

                case MotionEvent.ACTION_MOVE: {
                    // Find the index of the active pointer and fetch its position
                    if (activePointerIndex >= 0) {
                        float abs = Math.abs(initX - event.getX(0));
                        if (abs > 50) {
                            mClick = false;
                        }
步骤三:已经侧滑出来的view和其他侧滑view以及整个recyclerView上下滑动时的互斥操作

我们已经侧滑出来的view需要有互斥操作,分为下面两个场景:

  • 需要在整体recyclerView上下滑动时关闭掉;
  • 已经侧滑出来的view此时要是有其他view侧滑出来则已经侧滑出来的view同样需要关闭掉。

场景一:在源码attachToRecyclerView方法内添加整体recyclerView的滑动监听即可:

            /**
             * 自定义:添加滑动监听
             */
            mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    if (newState == RecyclerView.SCROLL_STATE_DRAGGING && mPreOpened != null) {
                        closeOpenedPreItem();
                    }
                }
            });

场景二:在源码checkSelectForSwipe方法内添加:

        /**
         * 完成了侧滑操作,并且不是当前的viewHolder,我们将原本的view close掉
         */
        if (mPreOpened != null && mPreOpened != vh) {
            closeOpenedPreItem();
        }

下面的就是需要关闭的动画:

    /**
     * 已经侧滑出来的view和其他侧滑view以及整个recyclerView上下滑动时的互斥操作的动画
     */
    private void closeOpenedPreItem() {
        final View view = getItemFrontView(mPreOpened);
        if (mPreOpened == null || view == null) return;
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, "translationX", view.getTranslationX(), 0f);
        objectAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                if (mPreOpened != null) mCallback.clearView(mRecyclerView, mPreOpened);
                if (mPreOpened != null) mPendingCleanup.remove(mPreOpened.itemView);
                endRecoverAnimation(mPreOpened, true);
                mPreOpened = mSelected;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
            }
        });
        objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        objectAnimator.start();
    }

    public View getItemFrontView(RecyclerView.ViewHolder viewHolder) {
        if (viewHolder == null) return null;
        if (viewHolder.itemView instanceof ViewGroup &&
                ((ViewGroup) viewHolder.itemView).getChildCount() > 1) {
            ViewGroup viewGroup = (ViewGroup) viewHolder.itemView;
            return viewGroup.getChildAt(viewGroup.getChildCount() - 1);
        } else {
            return viewHolder.itemView;
        }
    }

由于这个类源码有2000多行,我们完成后的源码在这里。https://github.com/buder-cp/CustomView/blob/master/buder_DN_view/buderdn11extension/src/main/java/com/test/buderdn11extension/itemTouchHelper/ItemTouchHelper.java

具体使用方法就是将我们上一节中的import替换成我们修改后的这个类即可。完整项目见:https://github.com/buder-cp/CustomView/tree/master/buder_DN_view/buderdn11extension

关注
打赏
1657159701
查看更多评论
立即登录/注册

微信扫码登录

0.0482s