zoukankan      html  css  js  c++  java
  • Android开发 RecyclerView实现拖动与滑动ItemTouchHelper

    前言

      RecyclerView依靠ItemTouchHelper,实现item的拖动与滑动功能。

    了解重写方法

      ItemTouchHelper提供了大量的重写方法,让你自己实现需要的组合。需要一一了解。

    是否开启长按拖动

        @Override
        public boolean isLongPressDragEnabled() {
            return false;
        }

    是否开启ItemView的滑动

        @Override
        public boolean isItemViewSwipeEnabled() {
            return true;
        }

    触发拖动的百分比

        // 针对drag状态,当滑动超过多少就可以触发onMove()方法(这里指onMove()方法的调用,并不是随手指移动的View)
        @Override
        public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
            return super.getMoveThreshold(viewHolder);
        }

    触发滑动的百分比

        // 侧滑事件的滑动距离触发值
        @Override
        public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
            return super.getSwipeThreshold(viewHolder);
        }

    触发滑动的滑动速度

        // 侧滑事件的速度触发值
        @Override
        public float getSwipeEscapeVelocity(float defaultValue) {
            return super.getSwipeEscapeVelocity(defaultValue);
        }

    设置支持的拖动或者滑动的方向

    这是一个重要的方法,设置此方法可以实现只能上拖还是下拖 或者 都可以。 也可以设置又支持拖动又支持滑动

        @Override
        public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
            int dragFlags = ItemTouchHelper.ACTION_STATE_IDLE;//设置拖动为空闲
            int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;//设置左右都能滑动
            return makeMovementFlags(dragFlags, swipeFlags);
        }

    正在拖动的回调方法

    这是重要的方法

        /**
         * onMove 
         */
        @Override
        public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
            return false;
        }

    正在滑动的方法

        /**
         * 在刷动
         */
        @Override
        public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
        }

    正在ItemView的动画绘制方法

        /**
         * 针对swipe和drag状态,整个过程中一直会调用这个函数,随手指移动的view就是在super里面做到的(和ItemDecoration里面的onDraw()函数对应)
         */
        @Override
        public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        }

    动画的持续时间

        /**
         * 针对swipe和drag状态,当手指离开之后,view回到指定位置动画的持续时间(swipe可能是回到原位,也有可能是swipe掉)
         */
        @Override
        public long getAnimationDuration(@NonNull RecyclerView recyclerView, int animationType, float animateDx, float animateDy) {
            return super.getAnimationDuration(recyclerView, animationType, animateDx, animateDy);
        }

    执行完拖动或者滑动后,对应清理View的方法

        /**
         * 针对swipe和drag状态,当一个item view在swipe、drag状态结束的时候调用
         * drag状态:当手指释放的时候会调用
         * swipe状态:当item从RecyclerView中删除的时候调用,一般我们会在onSwiped()函数里面删除掉指定的item view
         */
        @Override
        public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
            super.clearView(recyclerView, viewHolder);
        }

    一些不常用的方法

        /**
         * 针对drag状态,当前target对应的item是否允许移动
         * 我们一般用drag来做一些换位置的操作,就是当前对应的target对应的Item可以移动
         */
        @Override
        public boolean canDropOver(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder current, @NonNull RecyclerView.ViewHolder target) {
            return super.canDropOver(recyclerView, current, target);
        }
    
        /**
         * 针对drag状态,当itemView滑动到RecyclerView边界的时候(比如下面边界的时候),RecyclerView会scroll,
         * 同时会调用该函数去获取scroller距离(不用我们处理 直接super)
         */
        @Override
        public int interpolateOutOfBoundsScroll(@NonNull RecyclerView recyclerView, int viewSize, int viewSizeOutOfBounds, int totalSize, long msSinceStartScroll) {
            return super.interpolateOutOfBoundsScroll(recyclerView, viewSize, viewSizeOutOfBounds, totalSize, msSinceStartScroll);
        }
    
        /**
         * 针对swipe和drag状态,当swipe或者drag对应的ViewHolder改变的时候调用
         * 我们可以通过重写这个函数获取到swipe、drag开始和结束时机,viewHolder 不为空的时候是开始,空的时候是结束
         */
        @Override
        public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
            super.onSelectedChanged(viewHolder, actionState);
    
        }

    使用ItemTouchHelper实现上下拖动的例子

    首先我们需要继承重写 ItemTouchHelper.Callback

    复制代码
    public class QuickReplyItemTouchCallback extends ItemTouchHelper.Callback {
        private QuickReplyAdapter mAdapter;
        private boolean mIsLongPressDragEnabled = true;
    
        public QuickReplyItemTouchCallback(QuickReplyAdapter adapter) { //传入适配器
            mAdapter = adapter;
    
        }
    
        public void setLongPressDragEnabled(boolean isLongPressDragEnabled) {
            mIsLongPressDragEnabled = isLongPressDragEnabled;
        }
    
    
        @Override
        public boolean isItemViewSwipeEnabled() { //是否启用左右滑动
            return false;
        }
    
        @Override
        public boolean isLongPressDragEnabled() { //返回设置是否长按拖动Item上下移动
            return mIsLongPressDragEnabled;
        }
    
        @Override
        public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
            //在这个回调方法里我们返回我们需要的使用的动作功能
            int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;  //拖动  这里设置的UP 与 DOWN 表示允许上下拖动
            int swipeFlags = ItemTouchHelper.ACTION_STATE_IDLE;         //滑动  这里设置的ACTION_STATE_IDLE 表示我们将滑动动作设置为空闲
            return makeMovementFlags(dragFlags, swipeFlags);
       
        }
    
        @Override
        public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
            //用于上下移动Item的回调方法,在这个方法里我们要主动将Adapter里的数据互相替换位置
            mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
            //返回 true表示我们已经将Adapter里的数据互相替换位置
            return true;
        }
    
        @Override
        public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
            //因为我们不需要处理滑动,所以此处不写逻辑
    
        }
    }
    复制代码

    Adapter更换位置的实现

    复制代码
        /**
         * 提供给QuickReplyItemTouchCallback类使用的移动Item位置的方法
         * @param fromPosition
         * @param toPosition
         */
        public void onItemMove(int fromPosition, int toPosition) {
            Collections.swap(mList, fromPosition, toPosition);//更换我们数据List的位置
            notifyItemMoved(fromPosition, toPosition);     //更换Adapter Item的视图位置
            if (mOnChangeDataPositionListener != null){
                mOnChangeDataPositionListener.onChange(mList);  
            }
        }
    复制代码

    在Activity里给RecyclerView添加ItemTouchHelper.Callback

    复制代码
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
            mQuickReplyRecyclerView.setLayoutManager(linearLayoutManager);
            mQuickReplyAdapter = new QuickReplyAdapter();
            mQuickReplyRecyclerView.setAdapter(mQuickReplyAdapter);
            mCallback = new QuickReplyItemTouchCallback(mQuickReplyAdapter);
            ItemTouchHelper itemTouchHelper = new ItemTouchHelper(mCallback);
            itemTouchHelper.attachToRecyclerView(mQuickReplyRecyclerView);
    复制代码

    使用ItemTouchHelper实现左右滑动的例子

    下面的这个例子是改变需要出现的View的宽度

    滚动整个View来实现,这个效果的关键点是那个需要隐藏或者显示的View 需要在父类布局的外面

    布局例子:

    关键点 app:layout_constraintLeft_toRightOf="parent"

    复制代码
        <ImageButton
            android:id="@+id/delete_btn"
            android:layout_width="80dp"
            android:layout_height="0dp"
            android:background="@color/color_text_yellow"
            android:src="@drawable/ic_delete_2"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toRightOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    复制代码

    代码

        @Override
        public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
            if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
                Context context = recyclerView.getContext();
                if (mWidth == 0) {
                    mWidth = UnitConversionUtil.dip2px(context, 80);
                }
                if (dX == 0){ //有时候点击也会被触发成swipe,这里判断不发生偏移量就跳过
                    return;
                }
                boolean isLeft = dX < 0;
                SwitchListAdapter.ViewHolder itemViewHolder = (SwitchListAdapter.ViewHolder) viewHolder;
                if (isLeft) {
                    itemViewHolder.rootView.setScrollX(Math.min((int) Math.abs(dX), mWidth));
                } else {
                    itemViewHolder.rootView.setScrollX(Math.max((int) (mWidth - dX), 0));
                }
            } else {
                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            }
    
        }

    End

    // 侧滑事件的滑动距离触发值
    @Override
    public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
    return super.getSwipeThreshold(viewHolder);
    }
  • 相关阅读:
    Ext.Net 1.2.0_利用 Ext.Net 自定义 GridPanel Ajax 控件
    ASP.NET_0404_ASP.NET 重定向:页面传值
    程序设计_洗牌程序
    表单/验证表单——千万不要做一个只会拖控件、“照猫画虎”、copy/paste 程序员
    ASP.NET_0204_ASP.NET 重定向:如何将用户重定向到另一页
    Oracle 11g R1(11.1) Joins表连接
    隐藏 iframe 技术——Ajax 时代一个重要的环节
    Ext.Net 1.2.0_改变 Ext.Net.GridPanel 某行或某列的式样
    数据结构冒泡排序和直接插入排序
    XMLHttpRequest——Ajax 时代的到来
  • 原文地址:https://www.cnblogs.com/guanxinjing/p/13036680.html
Copyright © 2011-2022 走看看