Как правильно выделить выбранный элемент в RecyclerView?

148

Я пытаюсь использовать 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);

}
user65721
источник
Использование видов с фокусировкой - не лучшая идея для отслеживания выбранного элемента. Проверьте мой ответ, чтобы найти полное решение
Грег Эннис
Может быть, эта помощь вам amolsawant88.blogspot.in/2015/08/…
Амол Савант 96 Кули
Выбор элемента просмотра Recycler: stackoverflow.com/a/38501676/2648035
Алок Омкар
За этим сложно следить, когда у вас ничего не работает , что плохо, поскольку ответы сопровождаются тегами и не содержат подробных сведений о том, что и куда идет.
FirstOne

Ответы:

61

Я написал базовый класс адаптера для автоматической обработки выбора элемента с помощью 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нигде не заявлено. Должны ли мы просто передать его как параметр в конструктор и сохранить в поле?
Педро
1
Прости за это. Мой адаптер - это внутренний класс моего фрагмента, поэтому он имеет доступ к полю просмотра ресайклера. В противном случае да, вы можете передать его в качестве параметра. Или, что еще лучше, обработайте onRecyclerViewAttachedи сохраните его в переменной-члене.
Грег Эннис,
1
Хороший ответ, но зачем использовать getChildPosition ()? Есть еще один метод developer.android.com/reference/android/support/v7/widget/…
skyfishjy
Извините, вы имеете в виду, что мне просто нужно импортировать ваш TrackSelectionAdapterкласс и использовать его в моем списке? Как мне «унаследовать мой адаптер от вашего класса»? Не могли бы вы ответить на мой вопрос? Я так глубоко застрял: stackoverflow.com/questions/29695811/…
Криштиану Коласильо
2
Я попробовал это и обнаружил, что два последовательных вызова NotifyItemChanged () убивают любое подобие плавной прокрутки на более медленном оборудовании. Особенно плохо было на Fire TV перед обновлением Lollipop
Redshirt
166

Это очень простой способ сделать это.

Имейте 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 (..) ничего не сделает, что сделает это решение бесполезным.

zIronManBox
источник
12
Я использовал это решение с фоновым рисунком / селектором, установленным в моем представлении строки: <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>
colabug
1
@zIronManBox: хороший, простой способ! Является ли «selectedPosition» в методе onClick «selectedPos»?
AJW 08
3
getLayoutPosition () мне недоступен.
ka3ak
3
Не используйте просто -1, используйте RecyclerView.NO_POSITION; (что -1)
Мартин Маркончини
9
@ ka3ak: getLayoutPosition - это метод класса ViewHolder, объект которого передается в качестве первого параметра в методе просмотра привязки. Так что к нему может получить доступvieHolder.getLayoutPosition
Тушар Катурия
132

ОБНОВЛЕНИЕ [26 / июл / 2017]:

Как упоминал Паван в комментарии к предупреждению 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
Да конечно. @iBobb. Для НЕ ВЫБРАННЫХ элементов, т.е. чья позиция не равна нашей переменной selected_position, не будет фона. Я использовал это, потому что RecyclerView, как следует из названия, перерабатывает каждый элемент, поэтому он устанавливает ЗЕЛЕНЫЙ фон для случайных строк во время ПРОКРУТКИ, поэтому я разместил эту часть ELSE. (Вы можете просто попробовать это, закомментировав эту часть ELSE, а затем прокрутите свой Recycler-view)
Встречайте Vora
Не думаю, что вы поняли мой вопрос. Вы знаете, что в любом приложении, когда вы выбираете что-то, оно выделяется. Если вы удерживаете его снова, он будет отключен. Как бы мы это реализовали? Сейчас мы можем выбрать только с помощью вашего кода, а касание и удерживание того же элемента ничего не делает.
iBobb
Это может быть возможно, но для этого вам нужно переопределить onLongClickListener вместо onClickListener, прямо сейчас я занят, поэтому не могу предоставить полный код, но вы должны в основном это делать.
Встречайте Vora
1
Хорошая работа Знакомьтесь. Для НЕ ВЫБРАННЫХ здесь приведенные ниже коды работают для меня, int previousselectedPosition = -2; // установить глобально. затем следуйте этим строкам notifyItemChanged (selected_position); добавьте следующие строки, если (previousselectedPosition == selected_position) {selected_position = -1; notifyItemChanged (selected_position); } else {previousselectedPosition = selected_position; } попробуйте этот, он будет работать.
GNSRIDHAR
14

Ваша реализация, вероятно, будет работать, если вы прокрутите контент из поля зрения, а затем обратно в поле зрения. У меня была аналогичная проблема, когда я столкнулся с вашим вопросом.

У меня работает следующий фрагмент файла. Моя реализация была нацелена на множественный выбор, но я применил хитрость, чтобы принудительно выбрать один. (* 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. Я забыл упомянуть об этом ранее.

Дастин Чарльз
источник
6

Думаю, я нашел лучший учебник по использованию RecyclerView со всеми необходимыми нам базовыми функциями (одиночный + множественный выбор, выделение, рябь, щелчок и удаление в множественном выборе и т. Д.).

Вот он -> http://enoent.fr/blog/2015/01/18/recyclerview-basics/

На основе этого мне удалось создать библиотеку «FlexibleAdapter», которая расширяет SelectableAdapter. Я думаю, что это должно быть обязанностью адаптера, на самом деле вам не нужно каждый раз переписывать основные функции адаптера, пусть это делает библиотека, поэтому вы можете просто повторно использовать ту же реализацию.

Адаптер очень быстрый, работает «из коробки» (расширять не нужно); вы настраиваете элементы для каждого типа просмотра, который вам нужен; ViewHolder предопределены: стандартные события уже реализованы: одиночный и долгий щелчок; сохраняет состояние после вращения и многое другое .

Пожалуйста, посмотрите и не стесняйтесь применять его в своих проектах.

https://github.com/davideas/F flexibleAdapter

Также доступна Wiki.

Давидеас
источник
Отличная работа по написанию этого адаптера, он кажется очень полезным. Единственное, что ему действительно нужны некоторые базовые примеры и документация, я нахожу немного запутанным даже запускать его. Хотя потенциально отлично!
Voy
Да, я не нашел там достаточно информации, чтобы начать работу. Не удалось найти ссылку на API, хотя код, похоже, прокомментирован с учетом этого. Образец приложения кажется - хотя и обширным и информативным - очень трудным для понимания без предварительного знания библиотеки. Все варианты использования связаны друг с другом, мало что указывает на то, что демонстрирует, классы повторно используются в разных сценариях, что приводит к их перегрузке информацией. Я создал новую проблему с этими предложениями здесь: github.com/davideas/F flexibleAdapter
issues/
Wiki была полностью переписана и находится в стадии завершения.
Davideas 06
6

Посмотрите на мое решение. Я предполагаю, что вы должны установить выбранную позицию в держателе и передать ее как 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);
    }
}
Конрад Краковяк
источник
Как вы думаете, можно ли объявить mSelectedPosition как static для сохранения изменений конфигурации?
Sathesh
Нет! неправильно делать его статичным, это очень неправильно
Конрад Краковяк
Да уж. это опасно. Но чтобы сохранить изменение конфигурации (особенно поворот экрана), мы можем объявить эту переменную в действии и получить ее оттуда, верно?
Sathesh
Есть много решений ... Вы можете использовать геттер и сеттер, вы можете создать свой собственный метод saveInstanceState для адаптера и вызвать его в saveInstanceState из Activity / Fragment
Конрад Краковяк
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>

затем установите этот рисунок в качестве фона макета строки RecyclerView как

android:background="@drawable/selector"
Амодканте
источник
2
Это не делает ничего полезного. Он просто изменит цвет во время печати.
Leo Droidcoder,
4

Решение с интерфейсами и обратными вызовами. Создать интерфейс с состояниями выбора и отмены выбора:

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

Николай Тычина
источник
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);
                }
            }
        });
    }
Сантьяго Кастеллано
источник
2

У меня была такая же проблема, и я решаю ее следующим образом:

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);
 }
Викас Тивари
источник
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()];
      }

    }

Скриншоты приложения:

экран1 экран2 экран3 экран4

Oiyio
источник
0

Ответ @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 (..) ничего не сделает, что сделает это решение бесполезным.

Мигель Сильва
источник
-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);
                }
             }
         }
     });
}
ЛеонД
источник
-1

Простое добавление android:background="?attr/selectableItemBackgroundBorderless"должно сработать, если у вас нет цвета фона, но не забудьте использовать метод setSelected. Если у вас другой цвет фона, я просто использовал это (я использую привязку данных);

Установить isSelected в функции onClick

b.setIsSelected(true);

И добавьте это в xml;

android:background="@{ isSelected ? @color/{color selected} : @color/{color not selected} }"
Мохамед
источник
-2

Сделайте селектор:

 <?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
    }
i_tanova
источник
2
У этого есть 2 проблемы: 1) Вы не предоставили никакого способа отменить активацию других элементов в ресайклере. 2) Просмотры используются повторно. Таким образом, возможно, что тот же активированный вид будет виден вниз по порядку при прокрутке.
Ануп Шарма,