如何突出显示 Recycler View 的选定项目?

Posted

技术标签:

【中文标题】如何突出显示 Recycler View 的选定项目?【英文标题】:how to highlight the selected Item of Recycler View? 【发布时间】:2015-07-11 02:33:53 【问题描述】:

我有一个回收站视图,其中包含从内部存储加载的图像。 我想在单击时突出显示所选项目。 我尝试了很多东西,但没有奏效。 实际上,我需要的是当我单击 Recycler View 中的任何项目时,该项目必须进入 My ArrayList 并且它也应该突出显示,并且当我单击或说取消选择时它必须再次变得正常。 这是我的代码:

public class Images extends Fragment 
    private List<ImageHolder> imageList;
    Cursor imageCursor;

    RecyclerView recyclerView;
    MyImageAdapter adapter;
    ActionButton clickButton;
    List<String> listofImages;
    List<Integer> pos;
    int columnIndex;
    StringBuilder stringBuilder;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,   Bundle savedInstanceState) 
        View rootlayout = inflater.inflate(R.layout.image, container, false);
        listofImages=new ArrayList<String>();
        pos=new ArrayList<Integer>();
        stringBuilder=new StringBuilder();
        ContentResolver imageResolver = getActivity().getContentResolver();
        Uri imageUri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        String projection[]=MediaStore.Images.Thumbnails._ID,MediaStore.Images.Media.TITLE;
        imageCursor = getActivity().managedQuery(imageUri, projection, null, null, null);

        clickButton= (ActionButton) rootlayout.findViewById(R.id.action_button);

        recyclerView = (RecyclerView) rootlayout.findViewById(R.id.recycler_view_image);
        adapter = new MyImageAdapter(getActivity(), getImageList());

        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

        recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getActivity(),recyclerView,new RecyclerTouchListener.ClickListener() 
            @Override
            public void onClick(View view, int position) 
               TextView tv= (TextView) view.findViewById(R.id.list_text_all);
                    int flag=0;

                    String[] projection = MediaStore.Images.Media.DATA;
                    imageCursor = getActivity().managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                            projection, 
                            null,       
                            null,
                            null);
                    columnIndex = imageCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                    imageCursor.moveToPosition(position);
                    // Get image filename
                    String imagePath = imageCursor.getString(columnIndex);
                    if (listofImages.contains(imagePath))
                        Log.d("Contains Test","Yes");
                        listofImages.remove(imagePath);
                        pos.remove(position);
                     else 
                        listofImages.add(imagePath);
                        pos.add(position);
                        Log.d("Contains Test","No");
                    

                String s=listofImages.size()+" "+imagePath;
                Log.d("Inserted",s);
            

            @Override
            public void onLongClick(View view, int position) 
        ));

        clickButton.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                for (int i=0;i<listofImages.size();i++)
                    stringBuilder.append(listofImages.get(i)+"\n");
                
                Toast.makeText(getActivity(),stringBuilder,Toast.LENGTH_LONG).show();
            
        );

        return rootlayout;
    

    public List<ImageHolder> getImageList() 
        imageList=new ArrayList<ImageHolder>();

        if(imageCursor!=null && imageCursor.moveToFirst())

           int titleColumn = imageCursor.getColumnIndex
                    (android.provider.MediaStore.Images.Media.TITLE);
            int idColumn = imageCursor.getColumnIndex
                    (android.provider.MediaStore.Images.Media._ID);

            do 
                ImageHolder img=new ImageHolder();
                img.id=imageCursor.getLong(idColumn);
                img.title=imageCursor.getString(titleColumn);

                img.iconid= imageCursor.getInt(idColumn);


                imageList.add(img);
            
            while (imageCursor.moveToNext());
        

        return  imageList;
    

这是我的适配器类:

public class MyImageAdapter extends RecyclerView.Adapter<MyImageAdapter.MyViewHolder> 
    Context context;
    private LayoutInflater inflater;
    List<ImageHolder> data= Collections.emptyList();
    private ClickListener clickListener;
    int width,height;

    public MyImageAdapter(Context context, List<ImageHolder> data1) 
        inflater = LayoutInflater.from(context);
        this.data=data1;
        this.context=context;
    

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        View view = inflater.inflate(R.layout.all_row, parent, false);
        MyViewHolder holder=new MyViewHolder(view);
        return holder;
    

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) 
        try
            ImageHolder current=data.get(position);
            holder.title.setText(current.title);

            Log.d("Imageid:"+current.iconid,"");
            Uri IMAGE_URI = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + current.iconid);

            Bitmap bitmap = Bitmap.createScaledBitmap(decodeUri(IMAGE_URI), 200, 200, true);
            holder.img.setImageBitmap(bitmap);
        
        catch(Exception e)
    
    public void deleteRecyclerData(int position)
        data.remove(position);
        notifyItemRemoved(position);
    


    private Bitmap decodeUri(Uri selectedImage) throws FileNotFoundException 
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(
               context.getContentResolver().openInputStream(selectedImage), null, o);

        final int REQUIRED_SIZE = 100;

        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) 
            if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE) 
                break;
            
            width_tmp /= 2;
            height_tmp /= 2;
            scale *= 2;
        

        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(
                context.getContentResolver().openInputStream(selectedImage), null, o2);
    
    @Override
    public int getItemCount() 
        return data.size();
    

    public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
        TextView title;
      // TextView artist;
        ImageView img;
        CheckBox checkBox;

        public MyViewHolder(View itemView) 
            super(itemView);
            title= (TextView) itemView.findViewById(R.id.list_text_all);
            img= (ImageView) itemView.findViewById(R.id.list_image_all);
            img.setOnClickListener(this);
        

        @Override
        public void onClick(View v) 
    
    public interface ClickListener
        public void itemClicked(View view, int position);
    

【问题讨论】:

这可以帮助你amolsawant88.blogspot.in/2015/08/… 试试链接:***.com/a/46677018/1308990 可以查看this tutorial和this example in GitHub 【参考方案1】:

您可以使用StateListDrawable 来达到预期的效果。

示例

在您的drawable 目录中创建一个新的可绘制资源文件,其内容如下:

selector_row.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Color when the row is selected -->
    <item android:drawable="@android:color/darker_gray" android:state_pressed="false" android:state_selected="true" />
    <!-- Standard background color -->
    <item android:drawable="@android:color/white" android:state_selected="false" />
</selector>

现在只需将此StateListDrawable 用作RecyclerView 的行布局中的背景

row_recyclerview.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:background="@drawable/selector_row">

    <!-- row content -->

</RelativeLayout>

现在,只要调用适配器中的 onClick() 方法,您只需执行以下操作:

// myBackground is the RelativeLayout root of your row
myBackground.setSelected(true);

只要您调用myBackground.setSelected(false),行的背景就会有颜色(在本例中为darker_gray)。当然,您应该创建一个SparseBooleanArray,例如为了知道哪一行被选中,哪一行未被选中,因为这些行在滚动时会被重用。

编辑:记住所选项目SparseBooleanArray 背后的想法是记住被选中的项目。以下是如何使用它的示例:

public class MyImageAdapter extends RecyclerView.Adapter<MyImageAdapter.MyViewHolder> 

    private SparseBooleanArray selectedItems;

    // Other stuff [...]

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) 
        // Set the selected state of the row depending on the position
        holder.myBackground.setSelected(selectedItems.get(position, false));
    

    public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener

        @Override
        public void onClick(View v) 
              // Save the selected positions to the SparseBooleanArray 
              if (selectedItems.get(getAdapterPosition(), false)) 
                  selectedItems.delete(getAdapterPosition());
                  myBackground.setSelected(false);
              
              else 
                  selectedItems.put(getAdapterPosition(), true);
                  myBackground.setSelected(true);
              
        
    

【讨论】:

@reVrese,谢谢它的帮助,我在您的代码的帮助下对我的 Adapter 类进行了一些更改并且它起作用了。 嘿@reVerse 谢谢,我真的很喜欢这种使用 SparseBooleanArray 的方法。就我而言,我一次只能选择一项。选择新行后,如何取消选择先前选择的行? @DanielJulio 将所选项目的位置单独保存在变量中怎么样?选择更改后,您可以简单地取消选择旧行,因为您知道已选择的上一个项目的位置。 一旦CardView 被删除,您是否会重置selectedPosition 变量(例如重置为-1)?这将是必要的,以防止在 RecyclerView 刷新后进行选择。 @user3475052 抱歉,显然这也应该是getAdapterPosition()。我会解决的。【参考方案2】:

RecyclerView 中没有像 ListView 和 GridView 这样的选择器,但你试试下面对我有用的东西

如下创建一个可绘制的选择器

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
<item android:state_pressed="true">
   <shape>
         <solid android:color="@color/blue" />
   </shape>
</item>

<item android:state_pressed="false">
    <shape>
       <solid android:color="@android:color/transparent" />
    </shape>
</item>
</selector>

然后将此可绘制对象设置为 RecyclerView 行布局的背景

android:background="@drawable/selector"

【讨论】:

我希望每次你的问题拯救我时我都可以投票!谢谢! 确保同时添加 android:clickable="true" 到你的 RecyclerView 行布局 这适用于按下的效果,而不是选定的效果【参考方案3】:

您可以将其添加到您的 row_item.xml

android:clickable="true"
android:background="?attr/selectableItemBackground"

例如:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_
   android:layout_
   android:clickable="true"
   android:background="?attr/selectableItemBackground"

<!-- row content -->

如果 android 版本是 Lolipop 或更高版本,则选择器带有波纹。以及其他版本的亮点。希望对你有帮助

【讨论】:

这适用于按下的效果,而不是选定的效果 你没有回答这个问题,因为这只解决了按下的条件【参考方案4】:

我已经尝试了几个小时的几种方法,这是我提出的两种解决方案。 两种解决方案假设我的 RecyclerView 声明如下:

activity.xml

<android.support.v7.widget.RecyclerView
    android:id="@+id/list"
    android:layout_
    android:layout_
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

这里没有什么特别的,只是一个普通的RecyclerView 声明。现在让我们看看其他文件,从最简单可行的解决方案开始。

第一个解决方案(仅限 XML)

layout/item.xml

项目根ViewGroup 中的两个重要属性是backgroundclickable

<?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:background="@drawable/selector_item"
    android:clickable="true"
    android:gravity="center"
    android:layout_
    android:layout_
    android:orientation="horizontal"
    android:padding="16dp">

    ...

</LinearLayout>

drawable/selector_item.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:drawable="@drawable/background_item_pressed"
        android:state_pressed="true"
        />

    <item
        android:drawable="@drawable/background_item"
        />

</selector>

第二种解决方案(XML + Java)

item.xml

这里没有backgroundclickable 属性。

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:gravity="center"
    android:layout_
    android:layout_
    android:orientation="horizontal"
    android:padding="16dp">

    ...

</LinearLayout>

Adapter.java

public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> 
    public class ViewHolder extends RecyclerView.ViewHolder 
        public ViewHolder(View itemView) 
            super(itemView);

            itemView.setOnTouchListener(itemTouchListener);
        
    

    ...
    private View.OnTouchListener itemTouchListener = new View.OnTouchListener() 
        @Override
        public boolean onTouch(View v, MotionEvent event) 
            switch (event.getAction()) 
                case MotionEvent.ACTION_DOWN:
                    v.setBackgroundResource(R.drawable.background_item_event_pressed);
                    break;
                case MotionEvent.ACTION_CANCEL:
                    // CANCEL triggers when you press the view for too long
                    // It prevents UP to trigger which makes the 'pressed' background permanent which isn't what we want
                case MotionEvent.ACTION_OUTSIDE:
                    // OUTSIDE triggers when the user's finger moves out of the view
                case MotionEvent.ACTION_UP:
                    v.setBackgroundResource(R.drawable.background_item_event);
                    break;
                default:
                    break;
            

            return true;
        
    ;

    ...

我强烈建议使用第一个解决方案,因为它更易于维护且功能更强大,因为它还允许您添加涟漪效果(在drawable/background_item... XML 文件中),我认为解决方案 2 无法做到这一点。

【讨论】:

回收适配器类中始终突出显示的一项您得到答案了吗【参考方案5】:

您可以在适配器之外使用此代码

LinearLayoutManager RvLayoutManager = (LinearLayoutManager)rootlayout.getLayoutManager();
View itemSelected = RvLayoutManager.findViewByPosition(position);
itemSelected.setBackgroundColor(Color.Red);

【讨论】:

【参考方案6】:

您应该使用android:state_focused="true" 属性创建一个可绘制的选择器,如下所示

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="?attr/colorControlHighlight">
    <item>
        <selector xmlns:android="http://schemas.android.com/apk/res/android">
            <item
                android:drawable="@color/colorAccent"
                android:state_focused="true" />
        </selector>
    </item>
</ripple>

然后将此可绘制对象设置为 RecyclerView 行布局的背景

android:background="@drawable/selector"

【讨论】:

【参考方案7】:

如果您设法使用 Otto 或 AndroidRx 之类的 observable 模式风格,您可以按照上面解释的如何突出显示背景,并且对于每个 viewHolder 的 itemView,您可以订阅 observable 并在它与您的 recyclerview 分离时取消订阅,例如我在这里做了:

https://github.com/juanmendez/jm_android_dev/blob/master/01.fragments/06.fragments_with_rx/app/src/main/java/info/juanmendez/android/recyclerview/ui/listing/recyclerview/CountryHolder.java#L49

顺便说一下,我的 itemView 使用的是 linearLayout,所以很容易将背景颜色设置为黄色。

【讨论】:

【参考方案8】:

此解决方案更像是 ios 中的 tableView 的交互式外观。它将突出显示然后取消突出显示单元格。

@Override
public void onBindViewHolder(Cell holder, final int position) 
    if(requests != null) 
        holder.setView(requests.get(position), context);

        holder.itemView.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(final View v) 
                Logs.print("In OnClickListener", position + " selected");
            
        );

        holder.itemView.setOnTouchListener(new View.OnTouchListener() 
            @Override
            public boolean onTouch(View v, MotionEvent event) 
                switch (event.getAction()) 
                    case MotionEvent.ACTION_DOWN:
                        Logs.print("In Touch Handler", "A press has started");
                        v.setSelected(true);
                        break;
                    case MotionEvent.ACTION_UP:
                        Logs.print("In Touch Handler", "A press has been completed");
                        v.setSelected(false);
                        break;
                    case MotionEvent.ACTION_CANCEL:
                        Logs.print("In Touch Handler", "gesture aborted");
                        v.setSelected(false);
                        break;
                
                return true;
            
        );
    

【讨论】:

请在代码中包含 cmets 或某种解释。为什么要同时使用 Click Listener 和 Touch Listener?这将如何允许某人选择和取消选择一个项目? @SammyT 嘿,Sammy 对不起。此实现将在点击时突出显示然后取消突出显示,并在长按时保持突出显示。此外,在长按然后滚动离开时保持单元格突出显示后,它也会取消突出显示。

以上是关于如何突出显示 Recycler View 的选定项目?的主要内容,如果未能解决你的问题,请参考以下文章

如何在泥浆中突出显示选定的列表项?

Annotations Recycler View ItemClick

Android:将列表视图项设置为“选定”(突出显示)

ListView 中突出显示的选定项 Xamarin.Forms

Xamarin.Forms ListView:设置点击项的突出显示颜色

如何在 Fragment 中使用 Recycler View [重复]