美文网首页Android学习
Android RecyclerView item 拖动、滑动删

Android RecyclerView item 拖动、滑动删

作者: 博弈史密斯 | 来源:发表于2017-06-30 10:25 被阅读150次

    在上一文中,介绍了 RecyclerView 的基本使用,
    请参考:Android RecyclerView 的基本使用
    (下面的代码都是基于上一篇,为了节省篇幅,省略了一些重复代码)。
    这一篇主要介绍 item 拖拽、滑动删除等手势操作,以及对点击、滑动事件的监听等。

    点击事件监听

    和 ListView 通过 setOnItemClickListener 设置监听不同,RecyclerView 需要我们自己添加监听事件,监听事件需要放在自定义的 Adapter中,在其 onCreateViewHolder 或者 onBindViewHolder 中设置,其本质是一样的,都是通过 view 的 setOnClickListener 方法。为了节省篇幅,省略重复代码,如下:

    /**
     * Created by zhangyb on 2017/6/22.
     */
    public class LinearRecycleAdapter extends RecyclerView.Adapter<LinearRecycleAdapter.MyViewHolder> {
    
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
            View view = mInflater.inflate(R.layout.item, viewGroup, false);
            return new MyViewHolder(view);
        }
    
        @Override
        public void onBindViewHolder(MyViewHolder viewHolder, int position) {
            viewHolder.mTextView.setText(mDataList.get(position));
            //在 onBindViewHolder 获取到 viewHolder
            //通过 viewHolder 获取到 View 来设置监听
            setClickListener(viewHolder);
        }
    
        //设置点击监听
        private void setClickListener(final MyViewHolder viewHolder) {
            if (mOnItemClickListener != null) {
    
                //通过 View 的 setOnClickListener 设置监听
                viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mOnItemClickListener.onItemClick(viewHolder.itemView, viewHolder.getLayoutPosition());
                    }
                });
    
                viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        mOnItemClickListener.onItemLongClick(viewHolder.itemView, viewHolder.getLayoutPosition());
                        return false;
                    }
                });
            }
        }
    
        //item 点击事件接口,回调到Activity
        public interface OnItemClickListener {
            void onItemClick(View view, int position);
    
            void onItemLongClick(View view, int position);
        }
    
        private OnItemClickListener mOnItemClickListener;
    
        //在Activity中调用
        public void setOnItemClickListener(OnItemClickListener listener) {
            this.mOnItemClickListener = listener;
        }
    }
    

    上面代码中,也可以在 onCreateViewHolder 中设置监听,因为 onCreateViewHolder 中也可以获取到 View,但是因为获取不到 position,所以如果需要知道 item 的 position,还是需在 onBindViewHolder 中设置。

    在 Activity 中 设置监听 并实现接口:

    adapter.setOnItemClickListener(new LinearRecycleAdapter.OnItemClickListener() {
        @Override
        public void onItemClick(View view, int position) {
        }
    
        @Override
        public void onItemLongClick(View view, int position) {
        }
    });
    

    实现 item 拖动 交换位置、滑动删除 item

    拖动 item 改变位置等操作,是通过 android.support.v7.widget.helper.ItemTouchHelper 这个类来实现的,在 CallBack 回调方法中操作,然后关联到 RecyclerView。直接看代码:

    private ItemTouchHelper itemTouchHelper;
    
    itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
    
        //用于设置拖拽和滑动的方向
        @Override
        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    
            //设置允许拖拽item的方向,线性式布局有2个方向
            int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            //设置侧滑方向为从两个方向都可以
            int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
    
            return makeMovementFlags(dragFlags, swipeFlags);
        }
    
        //长摁item拖拽到和另一个item重合时,调用
        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView
                .ViewHolder target) {
            Log.i("zyb", "onMove");
            int from = viewHolder.getAdapterPosition();
            int to = target.getAdapterPosition();
    
            String strFrom = dataList.get(from);
            dataList.remove(from);
            dataList.add(to, strFrom);
    
            adapter.notifyItemMoved(from, to);//更新适配器中item的位置
            return true;
        }
    
        //这里处理滑动删除
        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
            int position = viewHolder.getAdapterPosition();
            dataList.remove(from);
            adapter.notifyItemRemoved(position);
        }
    
        //按下和松开item时 调用
        @Override
        public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
            Log.i("zyb", "onSelectedChanged");
            super.onSelectedChanged(viewHolder, actionState);
            if (viewHolder != null) {
                viewHolder.itemView.setBackgroundColor(Color.LTGRAY); //拖拽时设置背景色为灰色
            }
        }
    
        //拖拽停止时 调用
        @Override
        public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            Log.i("zyb", "clearView");
            super.clearView(recyclerView, viewHolder);
        }
    
        //当item视图变化时调用
        @Override
        public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float
                dX, float dY, int actionState, boolean isCurrentlyActive) {
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            viewHolder.itemView.setAlpha((float) 0.5); //可以在这里设置透明度
        }
    });
    
    

    itemTouchHelper 关联到 RecyclerView

    //itemTouchHelper 关联到 RecyclerView
    itemTouchHelper.attachToRecyclerView(recyclerView);
    

    上面代码中 CallBack 回调方法,都做了注释说明,不再赘述。

    item 设置滚动监听

    可以 通过 Adapter 自带的 addOnScrollListener 设置滚动监听:

    adapter.addOnScrollListener(new ImageAutoLoadScrollListener());
    
    private class ImageAutoLoadScrollListener extends OnScrollListener {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
    
            switch (newState) {
                case SCROLL_STATE_IDLE: //item停止滚动时
                    break;
    
                case SCROLL_STATE_DRAGGING: //item正在滚动时,手指未松开
                    break;
    
                case SCROLL_STATE_SETTLING: //item根据惯性滚动时,手指已松开
                    break;
            }
        }
    }
    

    代码优化

    上面的代码 Adapter、RecyclerView的初始化,ItemTouchHelper、Click的回调,都是在 Activity中,这样代码看着很乱,所以能实现一个自定义类,继承自 RecyclerView,把所有和 RecyclerView 相关的都放在自定义的类中,而初始化工作只需调用一个封装好的方法即可,下面看这个类的代码:

    /**
     * Created by zhangyb on 2017/6/27.
     */
    public class LinearRecyclerView extends RecyclerView {
    
        private Context myContext;
        private LinearRecycleAdapter adapter;
        private ItemTouchHelper itemTouchHelper;
        private RecyclerView.LayoutManager layoutManager;
    
        public LinearRecyclerView(Context context) {
            super(context, null, 0);
            myContext = context;
        }
    
        public void initRecyclerView(List<String> dataList) {
            layoutManager = new LinearLayoutManager(myContext, VERTICAL, false);
            setLayoutManager(layoutManager);
    
            adapter = new LinearRecycleAdapter(myContext, dataList);
            setAdapter(adapter);
    
            addOnScrollListener(new ImageAutoLoadScrollListener());
            addItemMoveListener();
        }
    
        private void addItemMoveListener() {
            itemTouchHelper = new ItemTouchHelper(new ItemMoveListener());
            itemTouchHelper.attachToRecyclerView(this);
        }
    
        private class ItemMoveListener extends ItemTouchHelper.Callback {
    
            //用于设置拖拽和滑动的方向
            @Override
            public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    
                //设置允许拖拽item的方向,线性式布局有2个方向
                int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
                //设置侧滑方向为从两个方向都可以
                int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
    
                return makeMovementFlags(dragFlags, swipeFlags);
            }
    
            //长摁item拖拽到和另一个item重合时,调用
            @Override
            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView
                    .ViewHolder target) {
                Log.i("zyb", "onMove");
                int from = viewHolder.getAdapterPosition();
                int to = target.getAdapterPosition();
    
                String strFrom = adapter.getDataList().get(from);
                adapter.getDataList().remove(from);
                adapter.getDataList().add(to, strFrom);
    
                adapter.notifyItemMoved(from, to);//更新适配器中item的位置
                return true;
            }
    
            //这里处理滑动删除
            @Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
                int position = viewHolder.getAdapterPosition();
                adapter.notifyItemRemoved(position);
            }
    
            //按下和松开item时 调用
            @Override
            public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
                Log.i("zyb", "onSelectedChanged");
                super.onSelectedChanged(viewHolder, actionState);
                if (viewHolder != null) {
                    viewHolder.itemView.setBackgroundColor(Color.LTGRAY); //拖拽时设置背景色为灰色
                }
            }
    
            //拖拽停止时 调用
            @Override
            public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
                Log.i("zyb", "clearView");
                super.clearView(recyclerView, viewHolder);
            }
    
            ////当item视图变化时调用
            @Override
            public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float
                    dX, float dY, int actionState, boolean isCurrentlyActive) {
                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
                viewHolder.itemView.setAlpha((float) 0.5); //可以在这里设置透明度
            }
        }
    
        private class ImageAutoLoadScrollListener extends OnScrollListener {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
    
                switch (newState) {
                    case SCROLL_STATE_IDLE: //item停止滚动时
                        break;
    
                    case SCROLL_STATE_DRAGGING: //item正在滚动时,手指未松开
                        break;
    
                    case SCROLL_STATE_SETTLING: //item通过惯性滚动时,手指已松开
                        break;
                }
            }
        }
    }
    

    在 Activity 中调用:

    public class MainActivity extends AppCompatActivity {
    
        /*优化后*/
        private List<String> dataList;
        private LinearRecyclerView linearRecyclerView;
        
        /*优化前*/
        private RecyclerView recyclerView;
        private RecyclerView.LayoutManager layoutManager;
        private LinearRecycleAdapter adapter;
        private ItemTouchHelper itemTouchHelper;
        private List<String> dataList;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            /*优化后*/
            linearRecyclerView = (LinearRecyclerView) findViewById(R.id.main_recycle_view);
            linearRecyclerView.initRecyclerView(initData());
            
            
            /*优化前*/
            layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
            recyclerView = (RecyclerView) findViewById(R.id.main_recycle_view);
            recyclerView.setLayoutManager(layoutManager);
    
            initData();
            adapter = new LinearRecycleAdapter(this, dataList);
    
            itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
                ..... //省略
            });
    
            itemTouchHelper.attachToRecyclerView(recyclerView);
    
            adapter.setOnItemClickListener(new LinearRecycleAdapter.OnItemClickListener() {
               ..... //省略
            });
    
            recyclerView.setAdapter(adapter);
        }
        
        private List<String> initData() {
            dataList = new ArrayList<>();
            for (int i = 0; i < 50; i++) {
                dataList.add("item : " + i);
            }
            return dataList;
        }
    }
    
    
    

    对比发现,优化后只需要两行代码。我们理想的状态,就是只需要一行代码,创建出这个类,就完成这个类的初始化工作,最多传入一个或者多个参数,初始化的工作尽可能的交给构造函数来完成,也就是编译器帮你做,尽量不要让用户操心 或者让用户完成一系列的初始化。但在这个例子中,DataList,也就是 和 RecyclerView 绑定的数据,需要外面来传入,所以封装了一个 public void initRecyclerView(List<String> dataList) 方法,而添加的监听功能,如果用户不想使用,只需不去处理监听的回调函数即可。

    相关文章

      网友评论

        本文标题:Android RecyclerView item 拖动、滑动删

        本文链接:https://www.haomeiwen.com/subject/csybcxtx.html