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

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

我写了一个基类适配器类来自动处理与RecyclerView的项目select。 只需从中获取适配器,并使用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); } }); } } } 

这是很简单的方法来做到这一点。

有一个private int selectedPos = 0; 在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抽屉和其他RecyclerView项目适配器的魅力。

我没有使用任何额外的方法😉:

UPDATE [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 } } 

希望这会有所帮助。

如果您将内容从视图中滚动回到视图中,您的实现可能会起作用。 当我提到你的问题时,我也有类似的问题。

以下文件片段正在为我工​​作。 我的实现是针对多种select的,但是我在那里做了一个黑客攻击来强制单一select(* 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中完成。 我以前忘了提这个。

我想,我已经find了关于如何使用我们需要的所有基本function(单选+多选,突出显示,波纹,点击并在多选中删除等等)的RecyclerView的最佳教程。

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

基于此,我能够创build一个库“FlexibleAdapter”,它扩展了SelectableAdapter。 我认为这一定是Adapter的责任,实际上你不需要每次都重写Adapter的基本function,让一个库去做,所以你可以重复使用相同的实现。

这个适配器速度非常快,可以直接使用(不需要扩展)。 您可以为您需要的每种视图types自定义项目; ViewHolder是预定义的:公共事件已经实现:单击和长按; 它保持旋转之后的状态以及更多

请看一看,并随意在您的项目中实现它。

https://github.com/davideas/FlexibleAdapter

Wiki也是可用的。

看看我的解决scheme。 我想你应该在持有人中设置选定的位置,并将其作为视图标签传递。 视图应该在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); } } 

在RecyclerView中没有像ListView和GridView那样的select器,但是你可以在下面试试它为我工作的东西

创build一个如下所示的select器

 <?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" 

这是我的解决scheme,你可以设置一个项目(或一个组),并通过另一次点击取消select:

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

接口和callback的决定。 使用select和unselect状态创build接口:

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

在callback中运行状态更改:

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

使用callback创buildRecyclerView(示例):

 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

我有同样的问题,我解决它的方式如下:

用于在createViewholder中创build一个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); } 

我找不到在这个问题上的一个很好的解决scheme,并自己解决。 有很多人遭受这个问题。 因此我想在这里分享我的解决scheme。

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

我也执行一个完整的项目。 在这个项目中,你可以select多个checkbox。 包含选定checkbox的行将突出显示。 更重要的是,这些在滚动时不会丢失。 您可以从链接下载:

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

应用程序的屏幕截图:

屏幕截图1 画面2 SCREEN3 screen4

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

在onClick函数中设置isSelected

 b.setIsSelected(true); 

并添加到XML;

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

设置private int selected_position = -1; 防止从开始select任何项目。

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