美文网首页高级UI
RecyclerView中实现单选和长按复选

RecyclerView中实现单选和长按复选

作者: 缺牙青蛙 | 来源:发表于2018-04-08 21:28 被阅读0次

    个人原创,转载请注明出处:https://www.jianshu.com/p/36740a1ac404

    概述

    最近写app时需要实现一个recyclerview的单选和复选功能,查了下资料大概整理出两种思路。一种是给列表的javabean增加一个boolean型的变量isSelected,然后利用databinding将该变量与itemview中的选中标志绑定,再在item点击事件中去操作该变量。另一种是维护一个hashset,通过将item的position加入或移除这个set来控制item的选中状态。由于第一种方法需要修改javabean,对项目整体可能造成更多影响,于是权衡了一下决定采用第二种方法,下面是具体实现。

    Item

    首先是JavaBean:

    public class Fruit {
    
        private String name;
        private int imageId;
    
        public Fruit(String name, int imageId){
            this.name = name;
            this.imageId = imageId;
        }
        public String getName(){
            return name;
        }
        public int getImageId(){
            return imageId;
        }
    }
    

    很简单,只包含两个变量。然后是xml:

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/item_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="5dp">
    
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="3dp"  /><!--留出3dp的空间用于显示选中效果-->
    
            <ImageView
                android:id="@+id/fruit_image"
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:scaleType="centerCrop" />
    
            <TextView
                android:id="@+id/fruit_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginTop="10dp"/>
    
        </LinearLayout>
        
    </FrameLayout>
    

    布局也很简单。注意这里用一个linearlayout外套一个framelayout并且中间留3dp的空间,是为了通过改变item_layout的底色,从而使item被选中时显示一个有色的框,进而表示选中效果。当然用其他控件表示选中效果也行,这里只是用了一种简单的方法。

    Activity

    xml:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </LinearLayout>
    

    就是一个recyclerview。下面重点来了,mainactivity:

    public class MainActivity extends AppCompatActivity {
    
        public static Set<Integer> positionSet = new HashSet<>();
        private FruitAdapter adapter;
        private List<Fruit> fruitList = new ArrayList<>();
        private boolean selectMode;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initFruits();
            RecyclerView recyclerView = findViewById(R.id.recycler_view);
            StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
            recyclerView.setLayoutManager(layoutManager);
            adapter = new FruitAdapter(fruitList);
            recyclerView.setAdapter(adapter);
            adapter.setOnItemClickListener(new FruitAdapter.OnItemClickListener() {
                @Override
                public void onItemClick(View view, int position) {
                    if (selectMode) {
                        // 如果当前处于多选状态,则进入多选状态的逻辑
                        // 维护当前已选的position
                        addOrRemove(position);
                    } else {
                        // 如果不是多选状态,则进入单选事件的业务逻辑
                        if (!positionSet.contains(position)){
                            // 选择不同的单位时取消之前选中的单位
                            positionSet.clear();
                        }
                        addOrRemove(position);
                    }
                }
    
                @Override
                public void onItemLongClick(View view, int position) {
                    if (!selectMode) {
                        selectMode = true;
                        positionSet.clear();
                    }
                }
            });
        }
    
        private void addOrRemove(int position) {
            if (positionSet.contains(position)) {
                // 如果包含,则撤销选择
                positionSet.remove(position);
            } else {
                // 如果不包含,则添加
                positionSet.add(position);
            }
            if (positionSet.size() == 0) {
                // 如果没有选中任何的item,则退出多选模式
                adapter.notifyDataSetChanged();
                selectMode = false;
            } else {
                // 更新列表界面,否则无法显示已选的item
                adapter.notifyDataSetChanged();
            }
        }
    
        private void initFruits(){
            Fruit apple = new Fruit("apple",R.drawable.apple);
            fruitList.add(apple);
            Fruit banana = new Fruit("banana",R.drawable.banana);
            fruitList.add(banana);
            Fruit cherry = new Fruit("cherry",R.drawable.cherry);
            fruitList.add(cherry);
            Fruit grape = new Fruit("grape",R.drawable.grape);
            fruitList.add(grape);
            Fruit mango = new Fruit("mango",R.drawable.mango);
            fruitList.add(mango);
            Fruit orange = new Fruit("orange",R.drawable.orange);
            fruitList.add(orange);
            Fruit pear = new Fruit("pear",R.drawable.pear);
            fruitList.add(pear);
            Fruit pineapple = new Fruit("pineapple",R.drawable.pineapple);
            fruitList.add(pineapple);
            Fruit strawberry = new Fruit("strawberry",R.drawable.strawberry);
            fruitList.add(strawberry);
            Fruit watermelon = new Fruit("watermelon",R.drawable.watermelon);
            fruitList.add(watermelon);
        }
    
    }
    
    

    这里用一个boolean变量selectMode来判断是否进入了复选模式,默认false(单选),通过longclick进入复选。当然用android自带的actionMode也行,但稍微麻烦点,本例选用了一种轻量级的实现方法。
    这段代码的核心在于用addOrRemove()方法来维护用于一个记录item是否选中的positionSet(用ArrayList也行,不过由于对插入删除的顺序不敏感,显然HashSet效率更高)。
    另外click和longclick下逻辑判断的不同也需要注意。

    Adapter

    public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
    
        private List<Fruit> mFruitList;
        private OnItemClickListener onItemClickListener;
        private Context mContext;
    
        public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
            this.onItemClickListener = onItemClickListener;
        }
    
        public interface OnItemClickListener{
            void onItemClick(View view, int position);
            void onItemLongClick(View view,int position);
        }
    
        static class ViewHolder extends RecyclerView.ViewHolder{
            ImageView fruitImage;
            TextView fruitName;
            FrameLayout itemLayout;
    
            public ViewHolder(View view){
                super(view);
                fruitImage = view.findViewById(R.id.fruit_image);
                fruitName = view.findViewById(R.id.fruit_name);
                itemLayout = view.findViewById(R.id.item_layout);
            }
        }
    
        public FruitAdapter(List<Fruit> fruitList){
            mFruitList = fruitList;
        }
    
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if (mContext == null){
                mContext = parent.getContext();
            }
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
            ViewHolder holder = new ViewHolder(view);
            return holder;
        }
    
        @Override
        public void onBindViewHolder(ViewHolder holder, final int position) {
            Set<Integer> positionSet = MainActivity.positionSet;
            //检查set里是否包含position,包含则显示选中的背景色,不包含则反之
            if (positionSet.contains(position)) {
                holder.itemLayout.setBackgroundColor(ContextCompat.getColor(mContext,R.color.selected));
            } else {
                holder.itemLayout.setBackgroundColor(ContextCompat.getColor(mContext,R.color.unSelected));
            }
            Fruit fruit = mFruitList.get(position);
            Glide.with(mContext).load(fruit.getImageId()).into(holder.fruitImage);
            holder.fruitName.setText(fruit.getName());
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onItemClickListener.onItemClick(v,position);
                }
            });
            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    onItemClickListener.onItemLongClick(v,position);
                    return false;
                }
            });
        }
    
        @Override
        public int getItemCount() {
            return mFruitList.size();
        }
    }
    

    在adapter里需要导入FruitActivity里的positionSet,并通过检查set里是否包含当前item的position来设置itemlayout的背景色。这里R.color.selected是选中的颜色R.color.unSelected是未选中的颜色,都定义在color.xml文件中。

    效果图

    Animation.gif

    总结

    本例的核心在于三点:
    1.boolean变量selectMode控制单选/复选
    2.positionSet记录选中item,addOrRemove()方法维护positionSet
    3.click/longclick下不同的逻辑判断

    本例稍加拓展,也很容易实现全选、反选和批量删除等功能。

    相关文章

      网友评论

        本文标题:RecyclerView中实现单选和长按复选

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