当前位置: 首页 > 工具软件 > RefreshView > 使用案例 >

RefreshView左滑删除编辑效果

戚奇略
2023-12-01
RefreshView左滑效果

最近项目中有需求要做列表item的左滑效果,需要左滑时显示操作按钮。以为很容易,做起来也是磕磕绊绊,特地保存下来,也为了能够得到建议优化方案。

初步思路:

1、利用RefreshView加载数据

2、在item样式xml文件中添加左滑按钮

3、补充item点击事件和左滑事件

查阅了一些相关博主的资料后,东改西凑写了一个RecyclerView自定义view:

public class SwipeRecyclerView extends RecyclerView {
    private int maxLength, mTouchSlop;
    private int xDown, yDown, xMove, yMove;
    /**
     * 当前选中的item索引(这个很重要)
     */
    private int curSelectPosition;
    private Scroller mScroller;

    private LinearLayout mCurItemLayout, mLastItemLayout;
    private LinearLayout mLlHidden;//隐藏部分
    private TextView mItemContent;
    private TextView mTvDelete, mTvEdit;
    private LinearLayout mItemDelete;

    /**
     * 隐藏部分长度
     */
    private int mHiddenWidth;
    /**
     * 记录连续移动的长度
     */
    private int mMoveWidth = 0;
    /**
     * 是否是第一次touch
     */
    private boolean isFirst = true;
    private boolean isItemClick = true;
    //上下滑动标识
    private boolean isDragging  = false;
    private Context mContext;

    /**
     * 编辑的监听事件
     */
    private OnItemClickListener mItemListener;

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

    /**
     * 删除的监听事件
     */
    private OnRightClickListener mRightListener;

    public void setRightClickListener(OnRightClickListener listener) {
        this.mRightListener = listener;
    }

    /**
     * 编辑的监听事件
     */
    private OnEditClickListener mEditListener;

    public void setEditClickListener(OnEditClickListener listener) {
        this.mEditListener = listener;
    }


    public SwipeRecyclerView(Context context) {
        this(context, null);
    }

    public SwipeRecyclerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SwipeRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        //滑动到最小距离
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        //滑动的最大距离
        maxLength = ((int) (180 * context.getResources().getDisplayMetrics().density + 0.5f));
        //初始化Scroller
        mScroller = new Scroller(context, new LinearInterpolator(context, null));
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        int x = (int) e.getX();
        int y = (int) e.getY();
        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //记录当前按下的坐标 (onTouchEvent会最先拦截触摸事件,所以这里要获取按下时选中的item)
                xDown = x;
                yDown = y;
                View view = findChildViewUnder(xDown, yDown);
                if (view == null) {
                    renew();
                    return false;
                }

                //计算选中哪个Item
                int firstPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
                Rect itemRect = new Rect();

                final int count = getChildCount();
                for (int i = 0; i < count; i++) {
                    final View child = getChildAt(i);
                    if (child.getVisibility() == View.VISIBLE) {
                        child.getHitRect(itemRect);
                        if (itemRect.contains(x, y)) {
                            curSelectPosition = firstPosition + i;
                            break;
                        }
                    }
                }

                if (isFirst) {//第一次时,不用重置上一次的Item
                    isFirst = false;
                } else {
                    //屏幕再次接收到点击时,恢复上一次Item的状态
                    renew();
                }

                //取到当前选中的Item,赋给mCurItemLayout,以便对其进行左移
                int select = curSelectPosition - firstPosition;
                if (select == 0) {
                    return false;
                }
                View item = getChildAt(curSelectPosition - firstPosition);
                if (item != null) {
                    //获取当前选中的Item
                    GroupAdapter.GroupHolder viewHolder = (GroupAdapter.GroupHolder) getChildViewHolder(item);
                    mCurItemLayout = viewHolder.llayout;
                    //找到具体元素(这与实际业务相关了~~)
                    mLlHidden = (LinearLayout) mCurItemLayout.findViewById(R.id.ll_hidden);
                    mItemDelete = (LinearLayout) mCurItemLayout.findViewById(R.id.ll_hidden);
                    mTvDelete = (TextView) mCurItemLayout.findViewById(R.id.tv_item_delete);
                    mTvEdit = (TextView) mCurItemLayout.findViewById(R.id.tv_item_edit);

                    mTvEdit.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (mEditListener != null) {
                                //将Item右移,恢复原位
                                scrollRight(mLastItemLayout, (0 - mMoveWidth));
                                //清空变量
                                mHiddenWidth = 0;
                                mMoveWidth = 0;
                                mEditListener.onEditClick(curSelectPosition, "");
                            }
                        }
                    });

                    mItemDelete.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (mRightListener != null) {
                                //删除
                                //将Item右移,恢复原位
                                scrollRight(mLastItemLayout, (0 - mMoveWidth));
                                //清空变量
                                mHiddenWidth = 0;
                                mMoveWidth = 0;
                                mRightListener.onRightClick(curSelectPosition, "");
                            }
                        }
                    });

                    //这里将删除按钮的宽度设为可以移动的距离
                    mHiddenWidth = mLlHidden.getWidth();
                }
                break;

            case MotionEvent.ACTION_MOVE:
                xMove = x;
                yMove = y;
                int dx = xMove - xDown;//为负时:手指向左滑动;为正时:手指向右滑动。这与Android的屏幕坐标定义有关
                int dy = yMove - yDown;//

                //左滑
                if (dx < 0 && Math.abs(dx) > mTouchSlop && Math.abs(dy) < mTouchSlop) {
                    int newScrollX = Math.abs(dx);
                    if (mMoveWidth >= mHiddenWidth) {//超过了,不能再移动了
                        newScrollX = 0;
                    } else if (mMoveWidth + newScrollX > mHiddenWidth) {//这次要超了,
                        newScrollX = mHiddenWidth - mMoveWidth;
                    }
                    //左滑,每次滑动手指移动的距离
                    scrollLeft(mCurItemLayout, newScrollX);
                    //对移动的距离叠加
                    mMoveWidth = mMoveWidth + newScrollX;
                } else if (dx > 0) {//右滑
                    //执行右滑,这里没有做跟随,瞬间恢复
                    scrollRight(mCurItemLayout, 0 - mMoveWidth);
                    mMoveWidth = 0;
                }

                break;
            case MotionEvent.ACTION_UP://手抬起时
                int scrollX = 0;
                if (mCurItemLayout != null) {
                    scrollX = mCurItemLayout.getScrollX();
                }
                //判断时点击事件调用点击事件回调
                if (scrollX < 6 && isItemClick && !isDragging) {
                    mItemListener.onItemClick(curSelectPosition, "");
                } else {
                    if (mHiddenWidth > mMoveWidth) {
                        int toX = (mHiddenWidth - mMoveWidth);
                        if (scrollX > mHiddenWidth / 2) {//超过一半长度时松开,则自动滑到左侧
                            scrollLeft(mCurItemLayout, toX);
                            mMoveWidth = mHiddenWidth;
                        } else {//不到一半时松开,则恢复原状
                            scrollRight(mCurItemLayout, 0 - mMoveWidth);
                            mMoveWidth = 0;
                        }
                    }
                }
                mLastItemLayout = mCurItemLayout;
                break;


        }
        return super.onTouchEvent(e);
    }

    private void renew(){
        if (mLastItemLayout != null && mMoveWidth > 0) {
            //将Item右移,恢复原位
            isItemClick = false;
            scrollRight(mLastItemLayout, (0 - mMoveWidth));
            //清空变量
            mHiddenWidth = 0;
            mMoveWidth = 0;
        } else {
            isItemClick = true;
        }
    }



    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {

            Log.e(TAG, "computeScroll getCurrX ->" + mScroller.getCurrX());
            mCurItemLayout.scrollBy(mScroller.getCurrX(), 0);
            invalidate();
        }
    }

    @Override
    public void onScrollStateChanged(int state) {
        super.onScrollStateChanged(state);
        isDragging = state == SCROLL_STATE_DRAGGING;
    }

    /**
     * 向左滑动
     */
    private void scrollLeft(View item, int scorllX) {
        Log.e(TAG, " scroll left -> " + scorllX);
        if (item != null)
            item.scrollBy(scorllX, 0);
    }

    /**
     * 向右滑动
     */
    private void scrollRight(View item, int scorllX) {
        Log.e(TAG, " scroll right -> " + scorllX);
        if (item != null)
            item.scrollBy(scorllX, 0);
    }

    public interface OnRightClickListener {
        void onRightClick(int position, String id);

    }

    public interface OnEditClickListener {
        void onEditClick(int position, String id);

    }

    public interface OnItemClickListener {
        void onItemClick(int position, String id);

    }
}

在布局文件中引用自定义view:

<com.***.SwipeRecyclerView
    android:id="@+id/groups"
    android:focusable="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:cacheColorHint="#00000000"
    android:fadingEdge="none"
    android:listSelector="#00000000"
    android:scrollbars="none"/>

在对应activity中声明对应view的使用

SwipeRefreshLayout mRefreshView;
 ***       
mGroupsView.setAdapter(groupAdapter);
mGroupsView.setLayoutManager(new LinearLayoutManager(this));
//  mGroupsView.setItemAnimator(new DefaultItemAnimator());//自适应item添加删除动画
mGroupsView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));

 在对应的adapter中间中定义自定义的itemView文件:

@Override
public GroupHolder onCreateHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_group, parent, false);
    return new GroupHolder(view);
}

 对应的itemView文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/ll_item"
    android:background="@drawable/bg_selector_primary"
    android:focusable="true"
    android:orientation="horizontal">

        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:padding="8dp">

            <ImageView
                android:id="@+id/head"
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:src="@mipmap/logo"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="8dp"
                android:text="ZenoZhang"
                android:textColor="@android:color/white"
                android:textSize="@dimen/textArticle"
                app:layout_constraintBottom_toTopOf="@+id/online"
                app:layout_constraintLeft_toRightOf="@id/head"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/online"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/in_online_member"
                android:textColor="@android:color/white"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="@id/name"
                app:layout_constraintTop_toBottomOf="@id/name" />

            <TextView
                android:id="@+id/count"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="count"
                android:textColor="@color/textColorGreen"
                android:textSize="@dimen/textArticle"
                app:layout_constraintLeft_toRightOf="@+id/online"
                app:layout_constraintTop_toTopOf="@+id/online" />

        </android.support.constraint.ConstraintLayout>

        <LinearLayout
            android:id="@+id/ll_hidden"
            android:layout_width="160dp"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/tv_item_edit"
                android:layout_width="80dp"
                android:layout_height="match_parent"
                android:layout_marginBottom="2dp"
                android:background="#ffa042"
                android:gravity="center"
                android:padding="15dp"
                android:text="编辑" />

            <TextView
                android:id="@+id/tv_item_delete"
                android:layout_width="80dp"
                android:layout_height="match_parent"
                android:layout_marginBottom="2dp"
                android:background="#ff0000"
                android:gravity="center"
                android:padding="15dp"
                android:text="删除" />

        </LinearLayout>
</LinearLayout>

 接着在对用的Holder中声明itemView中的按钮

mTvDelete = itemView.findViewById(R.id.tv_item_delete);
mTvEdit = itemView.findViewById(R.id.tv_item_edit);

最后在使用的activity中定义好对应的点击事件即可:

mGroupsView.setRightClickListener(new SwipeRecyclerView.OnRightClickListener() {
    @Override
    public void onRightClick(int position, String id) {
    }
});

mGroupsView.setEditClickListener(new SwipeRecyclerView.OnEditClickListener() {
    @Override
    public void onEditClick(int position, String id) {
    }
});

mGroupsView.setItemClickListener(new SwipeRecyclerView.OnItemClickListener() {
    @Override
    public void onItemClick(int position, String id) {
     
           }

            @Override
            public void onFailure(Group group) {
          
            }
        });
    }
});

 

注意点:  

SwipeRecyclerView中的onTouchEvent方法会最先获取item的点击事件,此时如果监听了down事件会导致adapter中的itemClick事件无法接受,具体listview事件分发机制感兴趣的可以自行搜索,网上说的详细的文章很多,当up事件触发后需要判断好是否时单个item的点击还是上下滑动从而决定是否出发itemclick事件,还要注意点击滑动其他item时复原最初改变位置的item。

如果有涉及侵占他人权益,请及时沟通。如侵比删。

以上,over。

 类似资料: