如何在RecyclerView上正确突出显示所选项目?

 jiangzh 发布于 2022-12-09 09:38

我正在尝试使用a 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);

}

zIronManBox.. 143

这是一种非常简单的方法.

private 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); 
}

就像Navigtional Drawer和其他RecyclerView物品适配器的魅力一样.

注意:务必使用像colabug澄清的选择器在布局中使用背景颜色:


  
  
  

否则setSelected(..)将不执行任何操作,使此解决方案无效.

6 个回答
  • 看看我的解决方案.我想你应该在持有人中设置选定的位置并将其作为标记传递.应该在onCreateViewHolder(...)方法中设置视图.还有正确的位置来设置侦听器的视图,如OnClickListener或LongClickListener.

    请查看下面的示例并阅读代码注释.

    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);
        }
    }
    

    2022-12-11 02:02 回答
  • 更新[26/Jul/2017]:

    正如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
        }
    }
    

    希望这会有所帮助.

    2022-12-11 02:07 回答
  • 这是一种非常简单的方法.

    private 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); 
    }
    

    就像Navigtional Drawer和其他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(..)将不执行任何操作,使此解决方案无效.

    2022-12-11 02:10 回答
  • 如果您将内容滚出视图然后返回视图,则您的实现可能会有效.当我提出你的问题时,我遇到了类似的问题.

    以下文件片段对我有用.我的实现是针对多种选择,但我在那里投入了一个强制单一选择.(*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) {
    
            // set color immediately.
            v.setBackgroundColor(Color.CYAN);
    
            // (*1)
            // forcing single selection here
            if (selected.isEmpty()){
                selected.add(position);
            }else {
                int oldSelected = selected.get(0);
                selected.clear();
                selected.add(position);
                // we do not notify that an item has been selected
                // because that work is done here.  we instead send
                // notifications for items to be deselected
                notifyItemChanged(oldSelected);
            }
            return false;
    }
    

    正如指出的在这个链接的问题,为viewHolders设置听众应该onCreateViewHolder完成.我以前忘了提这个.

    2022-12-11 02:11 回答
  • 我写了一个基础适配器类来自动处理使用RecyclerView的项目选择.只需从中派生适配器并使用state_selected的可绘制状态列表,就像使用列表视图一样.

    我有一篇关于它的博客文章,但这里是代码:

    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);
                    }
                });
            }
        }
    } 
    

    2022-12-11 02:11 回答
  • 我想,我已经找到了关于如何使用RecyclerView以及我们需要的所有基本功能的最佳教程(单选+多选,高亮,涟漪,点击和删除多选等等).

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

    基于此,我能够创建一个库"FlexibleAdapter",它扩展了SelectableAdapter.我认为这必须是Adapter的责任,实际上你不需要每次都重写Adapter的基本功能,让库来做,所以你可以重用相同的实现.

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

    请在您的项目中实现它.

    https://github.com/davideas/FlexibleAdapter

    Wiki也可用.

    2022-12-11 02:56 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有