我正在尝试将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);
}
答案 0 :(得分:135)
这是一种非常简单的方法。
在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);
}
就像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(..)将不执行任何操作,使此解决方案无效。
答案 1 :(得分:111)
正如关于该IDE的评论中提到的Pawan警告不使用该警告 固定位置,我刚刚修改了我的代码如下。点击 听众被转移到
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
}
}
希望这会有所帮助。
答案 2 :(得分:55)
我写了一个基础适配器类,用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);
}
});
}
}
}
答案 3 :(得分:13)
如果您将内容滚出视图然后返回视图,则您的实现可能会有效。当我提出你的问题时,我遇到了类似的问题。
以下文件片段对我有用。我的实现是针对多项选择,但我在那里投入了一个强制单选的黑客。(* 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;
}
如上所述in this linked question,设置viewHolders的侦听器应该在onCreateViewHolder中完成。我以前忘了提这个。
答案 4 :(得分: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);
}
}
答案 5 :(得分:6)
我想,我已经找到了关于如何使用RecyclerView以及我们需要的所有基本功能的最佳教程(单选+多选,高亮,涟漪,点击和删除多选等等)。
这是 - &gt; http://enoent.fr/blog/2015/01/18/recyclerview-basics/
基于此,我能够创建一个库&#34; FlexibleAdapter&#34;,它扩展了SelectableAdapter。 我认为这必须是适配器的责任,实际上你不需要每次都重写Adapter的基本功能,让库来做,所以你可以重用相同的实现。
这个适配器非常快,它开箱即用(你不需要扩展它);您可以为所需的每种视图类型自定义项目; ViewHolder是预定义的:已经实现了常见事件:单击和长击;它在轮换后保持状态,更多。
请在项目中实现它。
https://github.com/davideas/FlexibleAdapter
也可以使用Wiki。
答案 6 :(得分:4)
在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>
然后将此drawable设置为RecyclerView行布局的背景
android:background="@drawable/selector"
答案 7 :(得分:3)
这是我的解决方案,你可以设置一个项目(或一个组),然后再点击一次取消选择它:
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);
}
}
});
}
答案 8 :(得分:2)
使用接口和回调进行决策。 使用选择和取消选择状态创建界面:
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();
}
}
}
使用回调创建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 :(得分:1)
我有同样的问题,我按照以下方式解决:
用于在createViewholder中创建一行的xml文件,只需添加以下行:
android:clickable="true"
android:focusableInTouchMode="true"
android:background="?attr/selectableItemBackgroundBorderless"
或者如果您使用frameLayout作为行项的父项,则:
android:clickable="true"
android:focusableInTouchMode="true"
android:foreground="?attr/selectableItemBackgroundBorderless"
在您在click侦听器中添加的视图持有者中的java代码中:
@Override
public void onClick(View v) {
//ur other code here
v.setPressed(true);
}
答案 10 :(得分:1)
我无法在网络上找到解决此问题的好方法并自行解决。很多人都患有这个问题。因此,我想在这里分享我的解决方案。
滚动时,行会被回收。因此,选中的复选框和突出显示的行无法正常工作。我通过编写下面的适配器类解决了这个问题。
我还实施了一个完整的项目。在此项目中,您可以选择多个复选框。包括所选复选框的行将突出显示。更重要的是,这些在滚动时不会丢失。您可以从以下链接下载:
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()];
}
}
该应用的屏幕截图:
答案 11 :(得分:0)
制作选择器:
<?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_width="match_parent"
android:layout_height="64dp">
在适配器中,将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
}
答案 12 :(得分:0)
@zIronManBox的答案完美无缺。尽管它不具备在recyclerView中取消选择和取消选择项目的功能。
SO
像以前一样添加一个私有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(..)将不执行任何操作,从而使该解决方案无用。
答案 13 :(得分:-1)
设置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);
}
}
}
});
}
答案 14 :(得分:-1)
如果您没有背景颜色,只需添加android:background="?attr/selectableItemBackgroundBorderless"
即可,但不要忘记使用setSelected方法。如果你有不同的背景颜色,我只是使用它(我正在使用数据绑定);
在onClick函数
中设置isSelectedb.setIsSelected(true);
并将其添加到xml;
android:background="@{ isSelected ? @color/{color selected} : @color/{color not selected} }"