如何正确突出显示 RecyclerView 上的选定项目?

Posted

技术标签:

【中文标题】如何正确突出显示 RecyclerView 上的选定项目?【英文标题】:How to properly highlight selected item on RecyclerView? 【发布时间】:2015-01-27 10:20:32 【问题描述】:

我正在尝试使用RecyclerView 作为水平ListView。我想弄清楚如何突出显示所选项目。当我单击其中一个项目时,它会被选中并正确突出显示,但是当我单击另一个项目时,第二个项目会与旧项目一起突出显示。

这是我的 onClick 函数:

@Override
public void onClick(View view) 

    if(selectedListItem!=null)
        Log.d(TAG, "selectedListItem " + getPosition() + " " + item);
        selectedListItem.setBackgroundColor(Color.RED);
    
    Log.d(TAG, "onClick " + getPosition() + " " + item);
    viewHolderListener.onIndexChanged(getPosition());
    selectedPosition = getPosition();
    view.setBackgroundColor(Color.CYAN); 
    selectedListItem = view;

这里是onBindViewHolder

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position)    
    viewHolder.setItem(fruitsData[position]);
    if(selectedPosition == position)
        viewHolder.itemView.setBackgroundColor(Color.CYAN);    
    else
        viewHolder.itemView.setBackgroundColor(Color.RED);


【问题讨论】:

使用可聚焦视图对于尝试跟踪所选项目不是一个好主意。检查我的答案以获得完整的解决方案 这可以帮助你amolsawant88.blogspot.in/2015/08/… 回收站查看项目选择:***.com/a/38501676/2648035 当你还没有任何东西工作时,这很难理解,这很糟糕,因为答案会跟着标记,并且没有详细说明去哪里。 【参考方案1】:

这是非常简单的方法。

在 RecyclerView Adapter 类中有一个private int selectedPos = RecyclerView.NO_POSITION;,在 onBindViewHolder 方法下试试:

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position)    
    viewHolder.itemView.setSelected(selectedPos == position);


并在您的 OnClick 事件中修改:

@Override
public void onClick(View view) 
     notifyItemChanged(selectedPos);
     selectedPos = getLayoutPosition();
     notifyItemChanged(selectedPos); 

对于导航抽屉和其他 RecyclerView 项目适配器来说就像一个魅力。

注意:请务必使用 colabug 等选择器在布局中使用背景色:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@color/pressed_color" android:state_pressed="true"/>
  <item android:drawable="@color/selected_color" android:state_selected="true"/>
  <item android:drawable="@color/focused_color" android:state_focused="true"/>
</selector>

否则 setSelected(..) 将什么都不做,使这个解决方案毫无用处。

【讨论】:

我将此解决方案与在我的行视图上设置的背景可绘制/选择器一起使用:&lt;selector xmlns:android="http://schemas.android.com/apk/res/android"&gt; &lt;item android:drawable="@color/pressed_color" android:state_pressed="true"/&gt; &lt;item android:drawable="@color/selected_color" android:state_selected="true"/&gt; &lt;item android:drawable="@color/focused_color" android:state_focused="true"/&gt; &lt;/selector&gt; @zIronManBox:不错,简单的方法! onClick 方法中的“selectedPosition”应该是“selectedPos”吗? getLayoutPosition() 我无法使用。 不要只使用-1,使用RecyclerView.NO_POSITION; (即-1) @ka3ak: getLayoutPosition 是 ViewHolder 类的方法,其对象作为绑定视图方法中的第一个参数传递。所以vieHolder.getLayoutPosition可以访问到【参考方案2】:

更新 [2017 年 7 月 26 日]:

正如Pawan 在评论中提到的关于那个 IDE 警告不要使用它 固定位置,我刚刚修改了我的代码,如下所示。点击 听众被移动到ViewHolder,我得到了这个位置 使用getAdapterPosition()方法

int selected_position = 0; // You have to set this globally in the Adapter class

@Override
public void onBindViewHolder(ViewHolder holder, int position) 
    Item item = items.get(position);

    // Here I am just highlighting the background
    holder.itemView.setBackgroundColor(selected_position == position ? Color.GREEN : Color.TRANSPARENT);


public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener 

    public ViewHolder(View itemView) 
        super(itemView);
        itemView.setOnClickListener(this);
    

    @Override
    public void onClick(View v) 
        // Below line is just like a safety check, because sometimes holder could be null,
        // in that case, getAdapterPosition() will return RecyclerView.NO_POSITION
        if (getAdapterPosition() == RecyclerView.NO_POSITION) return;

        // Updating old as well as new positions
        notifyItemChanged(selected_position);
        selected_position = getAdapterPosition();
        notifyItemChanged(selected_position);

        // Do your another stuff for your onClick
    

希望这会有所帮助。

【讨论】:

太棒了!我有点困惑,你能帮我实现一个方法,扩展这个答案,当你点击一个已经选择的项目时,如何在 else 语句中做任何事情,以便你取消选择它:else holder.itemView.setBackgroundColor(Color.TRANSPARENT); 当然可以。 @iBobb。对于未选择的项目,即其位置不等于我们的 'selected_position' 变量将必须没有背景。我使用它是因为 RecyclerView,顾名思义,回收每个项目,所以它在我们滚动时为随机行设置绿色背景,这就是我放置 ELSE 部分的原因。 (您可以通过注释掉 ELSE 部分来尝试,然后滚动您的 Recycler-view) 我认为您没有理解我的问题。您知道在任何应用程序中,当您选择某些内容时,它会突出显示。如果再次按住它,它将被取消选择。我们将如何实现它?现在我们只能使用您的代码进行选择,并且触摸并按住相同的项目没有任何作用。 有可能,但是你必须重写onLongClickListener而不是onClickListener,现在我很忙所以不能提供完整的代码,但你应该基本上这样做。 干得好,满足。对于此处未选择的代码,它对我有用,int previousselectedPosition = -2; // 全局设置。然后在这些行之后进行 notifyItemChanged(selected_position);添加下面的行, if(previousselectedPosition == selected_position) selected_position = -1; notifyItemChanged(selected_position); 其他 上一个选择位置 = selected_position; 试试这个,它会工作的。【参考方案3】:

我编写了一个基础适配器类来使用 RecyclerView 自动处理项目选择。只需从中派生适配器并使用带有 state_selected 的可绘制状态列表,就像使用列表视图一样。

我有一个关于它的Blog Post Here,但这里是代码:

public abstract class TrackSelectionAdapter<VH extends TrackSelectionAdapter.ViewHolder> extends RecyclerView.Adapter<VH> 
    // Start with first item selected
    private int focusedItem = 0;

    @Override
    public void onAttachedToRecyclerView(final RecyclerView recyclerView) 
        super.onAttachedToRecyclerView(recyclerView);

        // Handle key up and key down and attempt to move selection
        recyclerView.setOnKeyListener(new View.OnKeyListener() 
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) 
                RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();

                // Return false if scrolled to the bounds and allow focus to move off the list
                if (event.getAction() == KeyEvent.ACTION_DOWN) 
                    if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) 
                        return tryMoveSelection(lm, 1);
                     else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) 
                        return tryMoveSelection(lm, -1);
                    
                

                return false;
            
        );
    

    private boolean tryMoveSelection(RecyclerView.LayoutManager lm, int direction) 
        int tryFocusItem = focusedItem + direction;

        // If still within valid bounds, move the selection, notify to redraw, and scroll
        if (tryFocusItem >= 0 && tryFocusItem < getItemCount()) 
            notifyItemChanged(focusedItem);
            focusedItem = tryFocusItem;
            notifyItemChanged(focusedItem);
            lm.scrollToPosition(focusedItem);
            return true;
        

        return false;
    

    @Override
    public void onBindViewHolder(VH viewHolder, int i) 
        // Set selected state; use a state list drawable to style the view
        viewHolder.itemView.setSelected(focusedItem == i);
    

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

            // Handle item click and set the selection
            itemView.setOnClickListener(new View.OnClickListener() 
                @Override
                public void onClick(View v) 
                    // Redraw the old selection and the new
                    notifyItemChanged(focusedItem);
                    focusedItem = getLayoutPosition();
                    notifyItemChanged(focusedItem);
                
            );
        
    
 

【讨论】:

mRecyclerView 未在任何地方声明。我们是否应该将它作为构造函数中的参数传递并存储在字段中? 很抱歉。我的适配器是我的片段的内部类,因此它可以访问回收器视图字段。否则,是的,您可以将其作为参数传递。或者更好的是,处理 onRecyclerViewAttached 并将其存储在其中的成员变量中。 不错的答案,但为什么要使用 getChildPosition()?还有一种方法developer.android.com/reference/android/support/v7/widget/… 对不起,您的意思是我只需要导入您的TrackSelectionAdapter 类并在我的列表中使用它吗?我如何“从你的班级派生我的适配器”?请问你能回答我的问题吗?我被深深地困住了:***.com/questions/29695811/… 我试过这个,发现对 NotifyItemChanged() 的两个背靠背调用杀死了在较慢硬件上平滑滚动的任何表面。在棒棒糖更新之前,Fire TV 上的表现尤其糟糕【参考方案4】:

如in this linked question 所述,应在 onCreateViewHolder 中设置 viewHolders 的侦听器。也就是说,下面的实现最初是针对多选的,但是我在 sn-p 中抛出了一个 hack 来强制单选。(*1)

// an array of selected items (Integer indices) 
private final ArrayList<Integer> selected = new ArrayList<>();

// items coming into view
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) 
    // each time an item comes into view, its position is checked
    // against "selected" indices
    if (!selected.contains(position))
        // view not selected
        holder.parent.setBackgroundColor(Color.LTGRAY);
    
    else
        // view is selected
        holder.parent.setBackgroundColor(Color.CYAN);


// selecting items
@Override
public boolean onLongClick(View v) 
        
        // select (set color) immediately.
        v.setBackgroundColor(Color.CYAN);

        // (*1)
        // forcing single selection here...
        if (selected.isEmpty())
            selected.add(position); // (done - see note)
        else 
            int oldSelected = selected.get(0);
            selected.clear(); // (*1)... and here.
            selected.add(position);
            // note: We do not notify that an item has been selected
            // because that work is done here.  We instead send
            // notifications for items which have been deselected.
            notifyItemChanged(oldSelected);
        
        return false;

【讨论】:

holder中没有父字段。 @FractalBob 显然在我的实现中。无论如何,这些代码行不是答案逻辑的一部分。这就是您将自己的逻辑放在选择/取消选择时想要发生的事情的地方。【参考方案5】:

我想,我已经找到了关于如何将 RecyclerView 与我们需要的所有基本功能(单选+多选、突出显示、波纹、多选中的单击和删除等)一起使用的最佳教程。

在这里 --> http://enoent.fr/blog/2015/01/18/recyclerview-basics/

基于此,我能够创建一个库“FlexibleAdapter”,它扩展了一个 SelectableAdapter。 我想这一定是Adapter的责任,其实你不需要每次都重写Adapter的基本功能,让一个库来做,你可以重用相同的实现。

这个适配器非常快,开箱即用(你不需要扩展它);您为所需的每种视图类型自定义项目; ViewHolder 已预定义:常见事件已实现:单击和长按;它保持旋转后的状态,更多

请查看并随时在您的项目中实施它。

https://github.com/davideas/FlexibleAdapter

也可以使用 Wiki。

【讨论】:

编写该适配器的工作很棒,它似乎非常有用。唯一的问题是它确实需要一些基本的示例和文档,我什至觉得启动和运行它有点令人困惑。不过可能很棒! 是的,我没有找到足够的信息来开始。即使代码似乎已考虑到这一点,也找不到 API 参考。示例应用程序似乎 - 虽然内容广泛且内容丰富 - 如果没有图书馆的先验知识,很难理解。所有用例都绑定在一起,几乎没有迹象表明什么演示了什么,类在不同的场景中被重用,这导致它们被信息超载。我在这里用这些建议创建了一个新问题:github.com/davideas/FlexibleAdapter/issues/120 Wiki 已经完全重写,即将完成。【参考方案6】:

看看我的解决方案。我想您应该在持有人中设置选定的位置并将其作为视图标签传递。 视图应该在 onCreateViewHolder(...) 方法中设置。也有正确的地方为视图设置监听器,例如 OnClickListener 或 LongClickListener。

请查看下面的示例并阅读 cmets 到代码。

public class MyListAdapter extends RecyclerView.Adapter<MyListAdapter.ViewHolder> 
    //Here is current selection position
    private int mSelectedPosition = 0;
    private OnMyListItemClick mOnMainMenuClickListener = OnMyListItemClick.NULL;

    ...

    // constructor, method which allow to set list yourObjectList

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        //here you prepare your view 
        // inflate it
        // set listener for it
        final ViewHolder result = new ViewHolder(view);
        final View view =  LayoutInflater.from(parent.getContext()).inflate(R.layout.your_view_layout, parent, false);
        view.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                //here you set your current position from holder of clicked view
                mSelectedPosition = result.getAdapterPosition();

                //here you pass object from your list - item value which you clicked
                mOnMainMenuClickListener.onMyListItemClick(yourObjectList.get(mSelectedPosition));

                //here you inform view that something was change - view will be invalidated
                notifyDataSetChanged();
            
        );
        return result;
    

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) 
        final YourObject yourObject = yourObjectList.get(position);

        holder.bind(yourObject);
        if(mSelectedPosition == position)
            holder.itemView.setBackgroundColor(Color.CYAN);
        else
            holder.itemView.setBackgroundColor(Color.RED);
    

    // you can create your own listener which you set for adapter
    public void setOnMainMenuClickListener(OnMyListItemClick onMyListItemClick) 
        mOnMainMenuClickListener = onMyListItemClick == null ? OnMyListItemClick.NULL : onMyListItemClick;
    

    static class ViewHolder extends RecyclerView.ViewHolder 


        ViewHolder(View view) 
            super(view);
        

        private void bind(YourObject object)
            //bind view with yourObject
        
    

    public interface OnMyListItemClick 
        OnMyListItemClick NULL = new OnMyListItemClick() 
            @Override
            public void onMyListItemClick(YourObject item) 

            
        ;

        void onMyListItemClick(YourObject item);
    

【讨论】:

你认为 mSelectedPosition 可以声明为静态来维护配置更改吗? 不!将其设为静态是错误的,这是非常错误的 是的。这很危险。但是为了保持配置更改(尤其是屏幕旋转),我们可以在活动中声明这个变量并从那里获取它,对吧? 有很多解决方案...您可以使用getter和setter,您可以为适配器创建自己的方法saveInstanceState并从Activity/Fragment在saveInstanceState中调用它【参考方案7】:

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"

【讨论】:

这没什么用。它只会在按下时改变颜色。【参考方案8】:

接口和回调的决策。 创建具有选择和取消选择状态的界面:

public interface ItemTouchHelperViewHolder 
    /**
     * Called when the @link ItemTouchHelper first registers an item as being moved or swiped.
     * Implementations should update the item view to indicate it's active state.
     */
    void onItemSelected();


    /**
     * Called when the @link ItemTouchHelper has completed the move or swipe, and the active item
     * state should be cleared.
     */
    void onItemClear();

在 ViewHolder 中实现接口:

   public static class ItemViewHolder extends RecyclerView.ViewHolder implements
            ItemTouchHelperViewHolder 

        public LinearLayout container;
        public PositionCardView content;

        public ItemViewHolder(View itemView) 
            super(itemView);
            container = (LinearLayout) itemView;
            content = (PositionCardView) itemView.findViewById(R.id.content);

        

               @Override
    public void onItemSelected() 
        /**
         * Here change of item
         */
        container.setBackgroundColor(Color.LTGRAY);
    

    @Override
    public void onItemClear() 
        /**
         * Here change of item
         */
        container.setBackgroundColor(Color.WHITE);
    

在回调时运行状态更改:

public class ItemTouchHelperCallback extends ItemTouchHelper.Callback 

    private final ItemTouchHelperAdapter mAdapter;

    public ItemTouchHelperCallback(ItemTouchHelperAdapter adapter) 
        this.mAdapter = adapter;
    

    @Override
    public boolean isLongPressDragEnabled() 
        return true;
    

    @Override
    public boolean isItemViewSwipeEnabled() 
        return true;
    

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) 
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlags = ItemTouchHelper.END;
        return makeMovementFlags(dragFlags, swipeFlags);
    

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) 
        ...
    

    @Override
    public void onSwiped(final RecyclerView.ViewHolder viewHolder, int direction) 
        ...
    

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) 
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) 
            if (viewHolder instanceof ItemTouchHelperViewHolder) 
                ItemTouchHelperViewHolder itemViewHolder =
                        (ItemTouchHelperViewHolder) viewHolder;
                itemViewHolder.onItemSelected();
            
        
        super.onSelectedChanged(viewHolder, actionState);
    

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) 
        super.clearView(recyclerView, viewHolder);
        if (viewHolder instanceof ItemTouchHelperViewHolder) 
            ItemTouchHelperViewHolder itemViewHolder =
                    (ItemTouchHelperViewHolder) viewHolder;
            itemViewHolder.onItemClear();
        
       

使用回调创建 RecyclerView(示例):

mAdapter = new BuyItemsRecyclerListAdapter(MainActivity.this, positionsList, new ArrayList<BuyItem>());
positionsList.setAdapter(mAdapter);
positionsList.setLayoutManager(new LinearLayoutManager(this));
ItemTouchHelper.Callback callback = new ItemTouchHelperCallback(mAdapter);
mItemTouchHelper = new ItemTouchHelper(callback);
mItemTouchHelper.attachToRecyclerView(positionsList);

在 iPaulPro 的文章中查看更多信息:https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd#.6gh29uaaz

【讨论】:

【参考方案9】:

这是我的解决方案,您可以在一个项目(或一个组)上设置并再次单击取消选择它:

 private final ArrayList<Integer> seleccionados = new ArrayList<>();
@Override
    public void onBindViewHolder(final ViewHolder viewHolder, final int i) 
        viewHolder.san.setText(android_versions.get(i).getAndroid_version_name());
        if (!seleccionados.contains(i)) 
            viewHolder.inside.setCardBackgroundColor(Color.LTGRAY);
        
        else 
            viewHolder.inside.setCardBackgroundColor(Color.BLUE);
        
        viewHolder.itemView.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                if (seleccionados.contains(i))
                    seleccionados.remove(seleccionados.indexOf(i)); 
                    viewHolder.inside.setCardBackgroundColor(Color.LTGRAY);
                 else  
                    seleccionados.add(i);
                    viewHolder.inside.setCardBackgroundColor(Color.BLUE);
                
            
        );
    

【讨论】:

【参考方案10】:

@zIronManBox 答案完美无缺。虽然它不具备 recyclerView 中取消选择和取消选择项的能力。

所以

像以前一样添加一个私有 int selectedPos = RecyclerView.NO_POSITION;在 RecyclerView Adapter 类中,在 onBindViewHolder 方法下:

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position)    
    viewHolder.itemView.setSelected(selectedPos == position);


还有在您的 OnClick 事件中:

@Override
public void onClick(View view) 
     notifyItemChanged(selectedPos);
     selectedPos = getLayoutPosition();
     notifyItemChanged(selectedPos); 

还在你的布局中添加以下选择器(可绘制),其中包括一个带有透明颜色的 state_selected="false":

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@color/pressed_color" android:state_pressed="true"/>
  <item android:drawable="@color/selected_color" android:state_selected="true"/>
  <item android:drawable="@color/focused_color" android:state_focused="true"/>
  <item android:drawable="@android:color/transparent" android:state_selected="false"/>
</selector>

否则 setSelected(..) 将什么都不做,使这个解决方案毫无用处。

【讨论】:

【参考方案11】:

我有同样的问题,我通过以下方式解决它:

用于在 createViewholder 中创建 Row 的 xml 文件,只需添加以下行:

 android:clickable="true"
 android:focusableInTouchMode="true"
 android:background="?attr/selectableItemBackgroundBorderless"

或者,如果您使用 frameLayout 作为行项的父级,则:

android:clickable="true"
android:focusableInTouchMode="true"
android:foreground="?attr/selectableItemBackgroundBorderless"

在您在点击监听器上添加的视图持有者内的 java 代码中:

@Override
   public void onClick(View v) 

    //ur other code here
    v.setPressed(true);
 

【讨论】:

【参考方案12】:

我在网上找不到解决这个问题的好方法,我自己解决了。有很多人正在遭受这个问题的困扰。因此,我想在这里分享我的解决方案。

滚动时,行被回收。因此,选中的复选框和突出显示的行无法正常工作。我通过编写下面的适配器类解决了这个问题。

我还实施了一个完整的项目。在这个项目中,您可以选择多个复选框。包括选定复选框的行被突出显示。更重要的是,这些在滚动时不会丢失。您可以从链接下载它:

https://www.dropbox.com/s/ssm58w62gw32i29/recyclerView_checkbox_highlight.zip?dl=0

    public class RV_Adapter extends RecyclerView.Adapter<RV_Adapter.ViewHolder> 
        public ArrayList<String> list;
        boolean[] checkBoxState;
        MainActivity mainActivity;
        MyFragment myFragment;
        View firstview;

        private Context context;

        FrameLayout framelayout;

        public RV_Adapter() 

      

        public RV_Adapter(Context context, MyFragment m, ArrayList<String> list ) 
          this.list = list;
          myFragment = m;
          this.context = context;
          mainActivity = (MainActivity) context;
          checkBoxState = new boolean[list.size()];
          // relativeLayoutState = new boolean[list.size()];
        

        public class ViewHolder extends RecyclerView.ViewHolder  
            public TextView textView;
            public CheckBox checkBox;
            RelativeLayout relativeLayout;
            MainActivity mainActivity;
            MyFragment myFragment;
            public ViewHolder(View v,MainActivity mainActivity,MyFragment m) 
                super(v);
                textView = (TextView) v.findViewById(R.id.tv_foodname);
                /**/
                checkBox= (CheckBox) v.findViewById(R.id.checkBox);
                relativeLayout = (RelativeLayout)v.findViewById(R.id.relativelayout);
                this.mainActivity = mainActivity;
                this.myFragment = m;
                framelayout = (FrameLayout) v.findViewById(R.id.framelayout);
                framelayout.setOnLongClickListener(m);
            

        

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            firstview = inflater.inflate(R.layout.row, parent, false);
            return new ViewHolder(firstview,mainActivity, myFragment);
        

        @Override
        public void onBindViewHolder( final ViewHolder holder,  final int position) 

            holder.textView.setText(list.get(position));

            holder.itemView.setOnClickListener(new View.OnClickListener() 
              @Override
              public void onClick(View v) 

              
            );

            // When action mode is active, checkboxes are displayed on each row, handle views(move icons) on each row are disappered.
            if(!myFragment.is_in_action_mode)
            

              holder.checkBox.setVisibility(View.GONE);
            
            else
            
              holder.checkBox.setVisibility(View.VISIBLE);
              holder.checkBox.setChecked(false);
            

              holder.checkBox.setTag(position);

              holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
                @Override
                public void onCheckedChanged(CompoundButton compoundButton, boolean b) 
                  if(compoundButton.isPressed()) // ekrandan kaybolan checkbox'lar otomatik olarak state degistiriyordu ve bu listener method cagiriliyordu, bunu onlemek icin isPressed() method'u ile kullanici mi basmis diye kontrol ediyorum.
                  
                    int getPosition = (Integer) compoundButton.getTag();  // Here we get the position that we have set for the checkbox using setTag.
                    checkBoxState[getPosition] = compoundButton.isChecked(); // Set the value of checkbox to maintain its state.
                    //relativeLayoutState[getPosition] = compoundButton.isChecked();

                  if(checkBoxState[getPosition] && getPosition == position )
                    holder.relativeLayout.setBackgroundResource(R.color.food_selected); /** Change background color of the selected items in list view  **/
                  else
                    holder.relativeLayout.setBackgroundResource(R.color.food_unselected); /** Change background color of the selected items in list view  **/
                    myFragment.prepareselection(compoundButton, getPosition, holder.relativeLayout);

                  
                
              );
              holder.checkBox.setChecked(checkBoxState[position]);

              if(checkBoxState[position]  )
                holder.relativeLayout.setBackgroundResource(R.color.food_selected); /** Change background color of the selected items in list view  **/
              else
                holder.relativeLayout.setBackgroundResource(R.color.food_unselected);
        



        @Override
        public int getItemCount() 
            return list.size();
        

        public void updateList(ArrayList<String> newList)
          this.list = newList;
          checkBoxState = new boolean[list.size()+1];
        

      public void resetCheckBoxState()
        checkBoxState = null;
        checkBoxState = new boolean[list.size()];
      

    

应用截图:

【讨论】:

【参考方案13】:

以编程方式

注意:不使用任何文件选择器。

涉及的步骤:

    创建一个全局位置变量,即int selectedPosition = -1;

整数:

-1 默认不选择

0 用于默认的第一项选择

    检查变量是否等于当前位置position

    public void onBindViewHolder(@NonNull ViewHolder holder, int position)
    
        if(selectedPosition == position)
            holder.itemView.setBackgroundColor(Color.GREEN); //selected
        else
            holder.itemView.setBackgroundColor(Color.BLACK); //not selected
        ....
    
    

    使用setOnClickListener 方法在项目之间导航

    holder.itemView.setOnClickListener(v ->
    
        if(selectedPosition == position)
        
            selectedPosition = -1;
            notifyDataSetChanged();
            return;
        
    
        selectedPosition = position;
        notifyDataSetChanged();
    
        // TODO: Put your itemview clicked functionality below
    );
    

注意

使用此代码在项目上创建切换效果:

      if(selectedPosition == position)
      
          selectedPosition = -1;
          notifyDataSetChanged();
          return;
      

      selectedPosition = position;
      notifyDataSetChanged();

仅使用此代码创建列表的经典选择方式(一次一个项目

      selectedPosition = position;
      notifyDataSetChanged();

【讨论】:

我认为这不是一个好主意,因为当您取消选择一个项目时,onClickListener 不会被调用,这意味着您的片段将不知道您的 RecyclerView 中发生了什么。 【参考方案14】:

设置private int selected_position = -1; 以防止在开始时选择任何项目。

 @Override
 public void onBindViewHolder(final OrdersHolder holder, final int position) 
    final Order order = orders.get(position);
    holder.bind(order);
    if(selected_position == position)
        //changes background color of selected item in RecyclerView
        holder.itemView.setBackgroundColor(Color.GREEN);
     else 
        holder.itemView.setBackgroundColor(Color.TRANSPARENT);
        //this updated an order property by status in DB
        order.setProductStatus("0");
    
    holder.itemView.setOnClickListener(new View.OnClickListener() 
        @Override
        public void onClick(View v) 
            //status switch and DB update
            if (order.getProductStatus().equals("0")) 
                order.setProductStatus("1");
                notifyItemChanged(selected_position);
                selected_position = position;
                notifyItemChanged(selected_position);
              else 
                if (order.getProductStatus().equals("1"))
                    //calls for interface implementation in
                    //MainActivity which opens a new fragment with 
                    //selected item details 
                    listener.onOrderSelected(order);
                
             
         
     );

【讨论】:

【参考方案15】:

如果您没有背景颜色,只需添加 android:background="?attr/selectableItemBackgroundBorderless" 即可,但不要忘记使用 setSelected 方法。如果你有不同的背景颜色,我就用这个(我正在使用数据绑定);

在onClick函数中设置isSelected

b.setIsSelected(true);

并将其添加到 xml;

android:background="@ isSelected ? @color/color selected : @color/color not selected "

【讨论】:

【参考方案16】:

制作选择器:

 <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/Green_10" android:state_activated="true" />
    <item android:drawable="@color/Transparent" />
</selector>

在列表项布局中将其设置为背景

   <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:background="@drawable/selector_attentions_list_item"
        android:layout_
        android:layout_>

在您的适配器中将 OnClickListener 添加到视图(onBind 方法)

 @Suppress("UNCHECKED_CAST")
    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) 

        fun bindItems(item: T) 
            initItemView(itemView, item)
            itemView.tag = item
            if (isClickable) 
                itemView.setOnClickListener(onClickListener)
            
        
    

在 onClick 事件中激活视图:

 fun onItemClicked(view: View)
        view.isActivated = true
    

【讨论】:

这有两个问题:1)您没有提供任何方法来撤消回收器中其他项目的激活。 2) 视图被重用。因此,当滚动时,相同的激活视图可能会按顺序显示。

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

RecyclerView 显示两个突出显示的项目

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

将数据从 Firebase 检索到嵌入在 Fragment 上的 RecyclerView

为啥在recyclerview android中滚动后突出显示的项目丢失

ASP.net MVC 验证突出显示和不正确字段上的图标 Jquery

如何正确构建突出当前页面的导航菜单