Я пытаюсь использовать 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);
}
android
android-recyclerview
recycler-adapter
user65721
источник
источник
Ответы:
Я написал базовый класс адаптера для автоматической обработки выбора элемента с помощью 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); } }); } } }
источник
mRecyclerView
нигде не заявлено. Должны ли мы просто передать его как параметр в конструктор и сохранить в поле?onRecyclerViewAttached
и сохраните его в переменной-члене.TrackSelectionAdapter
класс и использовать его в моем списке? Как мне «унаследовать мой адаптер от вашего класса»? Не могли бы вы ответить на мой вопрос? Я так глубоко застрял: stackoverflow.com/questions/29695811/…Это очень простой способ сделать это.
Имейте
private int selectedPos = RecyclerView.NO_POSITION;
в классе адаптера RecyclerView, а в методе 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 clear:
<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 (..) ничего не сделает, что сделает это решение бесполезным.
источник
<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>
vieHolder.getLayoutPosition
ОБНОВЛЕНИЕ [26 / июл / 2017]:
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 } }
надеюсь, это поможет.
источник
Ваша реализация, вероятно, будет работать, если вы прокрутите контент из поля зрения, а затем обратно в поле зрения. У меня была аналогичная проблема, когда я столкнулся с вашим вопросом.
У меня работает следующий фрагмент файла. Моя реализация была нацелена на множественный выбор, но я применил хитрость, чтобы принудительно выбрать один. (* 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. Я забыл упомянуть об этом ранее.
источник
Думаю, я нашел лучший учебник по использованию RecyclerView со всеми необходимыми нам базовыми функциями (одиночный + множественный выбор, выделение, рябь, щелчок и удаление в множественном выборе и т. Д.).
Вот он -> http://enoent.fr/blog/2015/01/18/recyclerview-basics/
На основе этого мне удалось создать библиотеку «FlexibleAdapter», которая расширяет SelectableAdapter. Я думаю, что это должно быть обязанностью адаптера, на самом деле вам не нужно каждый раз переписывать основные функции адаптера, пусть это делает библиотека, поэтому вы можете просто повторно использовать ту же реализацию.
Адаптер очень быстрый, работает «из коробки» (расширять не нужно); вы настраиваете элементы для каждого типа просмотра, который вам нужен; ViewHolder предопределены: стандартные события уже реализованы: одиночный и долгий щелчок; сохраняет состояние после вращения и многое другое .
Пожалуйста, посмотрите и не стесняйтесь применять его в своих проектах.
https://github.com/davideas/F flexibleAdapter
Также доступна Wiki.
источник
Посмотрите на мое решение. Я предполагаю, что вы должны установить выбранную позицию в держателе и передать ее как Tag of View. Представление должно быть установлено в методе 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, но вы попробуйте ниже, что сработало для меня
создайте селектор, который можно рисовать, как показано ниже
<?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"
источник
Решение с интерфейсами и обратными вызовами. Создать интерфейс с состояниями выбора и отмены выбора:
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
источник
это мое решение, вы можете установить элемент (или группу) и отменить выбор другим щелчком мыши:
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); } } }); }
источник
У меня была такая же проблема, и я решаю ее следующим образом:
XML-файл, который используется для создания строки внутри createViewholder, просто добавьте строку ниже:
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); }
источник
Я не смог найти в Интернете хорошего решения этой проблемы и решил сам. Многие страдают от этой проблемы. Поэтому я хочу поделиться здесь своим решением.
При прокрутке строки перерабатываются. Таким образом, отмеченные флажки и выделенные строки не работают должным образом. Я решил эту проблему, написав приведенный ниже класс адаптера.
Также реализую полноценный проект. В этом проекте вы можете установить несколько флажков. Строки, содержащие выбранные флажки, выделяются. И что еще более важно, они не теряются при прокрутке. Вы можете скачать его по ссылке:
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()]; } }
Скриншоты приложения:
источник
Ответ @zIronManBox работает безупречно. Хотя у него нет возможности отмены выбора и невыделенных элементов в recyclerView.
ТАК
добавить, как и раньше, частный int selectedPos = RecyclerView.NO_POSITION; в классе адаптера RecyclerView и в методе 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 (..) ничего не сделает, что сделает это решение бесполезным.
источник
Установите,
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); } } } }); }
источник
Простое добавление
android:background="?attr/selectableItemBackgroundBorderless"
должно сработать, если у вас нет цвета фона, но не забудьте использовать метод setSelected. Если у вас другой цвет фона, я просто использовал это (я использую привязку данных);Установить isSelected в функции onClick
b.setIsSelected(true);
И добавьте это в xml;
android:background="@{ isSelected ? @color/{color selected} : @color/{color not selected} }"
источник
Сделайте селектор:
<?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 }
источник