Como destacar corretamente o item selecionado no RecyclerView?

146

Estou tentando usar um RecyclerViewcomo horizontal ListView. Estou tentando descobrir como destacar o item selecionado. Quando clico em um dos itens, ele é selecionado e é destacado corretamente, mas quando clico em outro, o segundo é destacado com o mais antigo.

Aqui está minha função 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;
}

Aqui está o 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
fonte
Usar vistas focáveis ​​não é uma boa ideia para tentar rastrear o item selecionado. Verifique a minha resposta para uma solução completa
Greg Ennis
pode ser útil
Amol Sawant 96 Kuli
Recycler seleção de itens vista: stackoverflow.com/a/38501676/2648035
Alok Omkar
Isso é difícil de seguir quando você ainda não tem nada funcionando , o que é ruim, pois as respostas acompanham e não especificam muito sobre o que vai aonde.
precisa saber é o seguinte

Respostas:

61

Eu escrevi uma classe de adaptador base para lidar automaticamente com a seleção de itens com um RecyclerView. Basta derivar seu adaptador e usar listas de estados extraíveis com state_selected, como você faria com uma exibição de lista.

Eu tenho uma postagem no blog aqui sobre isso, mas aqui está o código:

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);
                }
            });
        }
    }
} 
Greg Ennis
fonte
mRecyclerViewnão é declarado em nenhum lugar. Devemos apenas passá-lo como um parâmetro no construtor e armazená-lo em um campo?
Pedro Pedro
1
Me desculpe por isso. Meu adaptador é uma classe interna do meu fragmento, para que ele tenha acesso ao campo de exibição do reciclador. Caso contrário, sim, você pode passá-lo como um parâmetro. Ou melhor ainda, manipule onRecyclerViewAttachede armazene-o em uma variável de membro lá.
Greg Ennis
1
Boa resposta, mas por que usar getChildPosition ()? Existe outro método developer.android.com/reference/android/support/v7/widget/…
skyfishjy 15/04/2015
Sinto muito, você quer dizer que eu só preciso importar sua TrackSelectionAdapterclasse e usá-la na minha lista? Como faço para "derivar meu adaptador da sua classe"? Por favor, você poderia responder à minha pergunta? Estou preso tão profundamente: stackoverflow.com/questions/29695811/...
Cristiano Colacillo
2
Tentei isso e descobri que as duas chamadas consecutivas para NotifyItemChanged () eliminaram qualquer aparência de rolagem suave em hardware mais lento. Foi particularmente ruim na TV fogo antes da atualização Lollipop
Redshirt
158

Esta é uma maneira muito simples de fazer isso.

Tenha um private int selectedPos = RecyclerView.NO_POSITION;na classe do adaptador RecyclerView e, no método onBindViewHolder, tente:

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {   
    viewHolder.itemView.setSelected(selectedPos == position);

}

E no seu evento OnClick, modifique:

@Override
public void onClick(View view) {
     notifyItemChanged(selectedPos);
     selectedPos = getLayoutPosition();
     notifyItemChanged(selectedPos); 
}

Funciona como um encanto para a Gaveta de navegação e outros adaptadores de itens do RecyclerView.

Nota: Certifique-se de usar uma cor de fundo no layout usando um seletor como colabug esclarecido:

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

Caso contrário, setSelected (..) não fará nada, tornando esta solução inútil.

zIronManBox
fonte
12
Eu usei esta solução com um conjunto de seleção / desenho de segundo plano na minha exibição de linha: <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: maneira agradável e simples! "SelectedPosition" no método onClick deveria ser "selectedPos"?
AJW
3
getLayoutPosition () não está disponível por mim.
ka3ak
3
Não use apenas -1, use RecyclerView.NO_POSITION; (que é -1)
Martin Marconcini 13/17
8
@ ka3ak: getLayoutPosition é o método da classe ViewHolder cujo objeto é passado como primeiro parâmetro no método de visualização de ligação. Para que ele possa ser acessado porvieHolder.getLayoutPosition
Tushar Kathuria
129

ATUALIZAÇÃO [26 / Jul / 2017]:

Como o Pawan mencionou no comentário sobre o aviso do IDE sobre não usar essa posição fixa, apenas modifiquei meu código como abaixo. O ouvinte de clique é movido para ViewHolder, e eu estou recebendo a posição usando o getAdapterPosition()método

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

espero que isso ajude.

Conheça Vora
fonte
Fantástico! Estou um pouco confuso, no entanto, você poderia me ajudar a implementar uma maneira, expandindo esta resposta, de como fazer o que estiver fazendo na instrução else quando você clicar em um item que já está selecionado, para desmarcá-lo: else { holder.itemView.setBackgroundColor (Color.TRANSPARENT); }
iBobb 27/02
Claro que sim. @iBobb. Para itens NÃO SELECIONADOS, ou seja, cuja posição não é igual à nossa variável 'posição_selecionada', NÃO haverá fundo. Eu usei isso porque o RecyclerView, como o nome sugere, recicla cada item, por isso define o plano de fundo VERDE para linhas aleatórias enquanto ROLAMOS, foi por isso que coloquei essa parte ELSE. (Você pode apenas tentar comentando a parte ELSE e depois rolar a tela de reciclagem)
Meet Vora
Eu não acho que você entendeu minha pergunta. Você sabe em qualquer aplicativo, quando você seleciona algo, ele é destacado. Se você segurá-lo novamente, ele será desmarcado. Como implementaríamos isso? No momento, só podemos selecionar usando seu código e tocar e segurar o mesmo item não faz nada.
IBobb 28/02
Pode ser possível, mas para isso você precisa substituir onLongClickListener em vez de onClickListener, agora estou ocupado, então não posso fornecer código completo, mas você deve basicamente fazer dessa maneira.
Conheça Vora
Por algum motivo, leva um tempo para destacar após o clique, cerca de 100 a 200 milissegundos. Alguma idéia do porquê? Eu experimentei isso em um emulador e meu telefone pirulito
suku
14

É provável que sua implementação funcione se você rolar o conteúdo para fora da visualização e voltar à visualização. Eu estava tendo um problema semelhante quando me deparei com sua pergunta.

O seguinte snippet de arquivo está funcionando para mim. Minha implementação foi direcionada à seleção múltipla, mas lancei um hack para forçar a seleção única. (* 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;
}

Conforme observado nesta pergunta vinculada , a configuração de listeners para viewHolders deve ser feita no onCreateViewHolder. Eu esqueci de mencionar isso anteriormente.

Dustin Charles
fonte
6

Acho que encontrei o melhor tutorial de como usar o RecyclerView com todas as funções básicas necessárias (única + multisseleção, destaque, ondulação, clique e remover na multisseleção, etc ...).

Aqui está -> http://enoent.fr/blog/2015/01/18/recyclerview-basics/

Com base nisso, consegui criar uma biblioteca "FlexibleAdapter", que estende um SelectableAdapter. Eu acho que isso deve ser uma responsabilidade do adaptador; na verdade, você não precisa reescrever as funcionalidades básicas do adaptador todas as vezes, deixe uma biblioteca fazer isso, para que você possa reutilizar a mesma implementação.

Este adaptador é muito rápido, funciona imediatamente (você não precisa estendê-lo); você personaliza os itens para todos os tipos de exibição necessários; O ViewHolder é predefinido: eventos comuns já estão implementados: clique único e longo; mantém o estado após a rotação e muito mais .

Dê uma olhada e sinta-se à vontade para implementá-lo em seus projetos.

https://github.com/davideas/FlexibleAdapter

Um Wiki também está disponível.

Davideas
fonte
Ótimo trabalho escrevendo esse adaptador, parece muito útil. A única coisa é que ele realmente precisa de alguns exemplos e documentação básicos, acho um pouco confuso até colocá-lo em funcionamento. Potencialmente ótimo!
Voy
Sim, não encontrei informações suficientes para começar. Não foi possível encontrar a referência da API, embora o código pareça ser comentado com isso em mente. O aplicativo de amostra parece - embora amplo e informativo - muito difícil de entender sem o conhecimento prévio da biblioteca. Todos os casos de uso são unidos, há pouca indicação do que demonstra o que, as classes são reutilizadas em diferentes cenários, o que resulta na sobrecarga de informações. Criei um novo problema com estas sugestões aqui: github.com/davideas/FlexibleAdapter/issues/120
Voy
O Wiki foi completamente reescrito e está a caminho da conclusão.
Davideas 6/06
6

Veja a minha solução. Suponho que você deve definir a posição selecionada no suporte e passá-la como Tag of View. A visualização deve ser definida no método onCreateViewHolder (...). Também há um local correto para definir o ouvinte para exibição, como OnClickListener ou LongClickListener.

Por favor, veja o exemplo abaixo e leia os comentários no código.

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);
    }
}
Konrad Krakowiak
fonte
Você acha que mSelectedPosition pode ser declarado como estático para manter as alterações na configuração?
Sathesh
Não! é errado fazê-lo como estático é muito errado
Konrad Krakowiak
Sim. é perigoso. Mas, para manter a alteração na configuração (especialmente a rotação da tela), podemos declarar essa variável em atividade e obtê-la a partir daí, certo?
Sathesh 18/03/2015
Há muitos solução ... Você pode usar getter e setter, Você pode criar seu próprio método saveInstanceState para o adaptador e chamá-lo em saveInstanceState de Atividade / Fragment
Konrad Krakowiak
4

não há seletor no RecyclerView como o ListView e o GridView, mas você tenta abaixo o que funcionou para mim

crie um drawer seletor como abaixo

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

defina esse desenho como plano de fundo do layout da linha do RecyclerView como

android:background="@drawable/selector"
amodkanthe
fonte
2
Isso não faz nada útil. Apenas mudará a cor fazendo o tempo de impressão.
Leo Droidcoder
4

Decisão com interfaces e retornos de chamada. Crie uma interface com estados de seleção e desmarcação:

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

Implementar interface no 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);
    }
}

Execute a alteração de estado no retorno de chamada:

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

Crie o RecyclerView com retorno de chamada (exemplo):

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

Veja mais no artigo do iPaulPro: https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd#.6gh29uaaz

Mykola Tychyna
fonte
3

esta é a minha solução, você pode definir um item (ou um grupo) e desmarcar com outro clique:

 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);
                }
            }
        });
    }
Santiago Castellano
fonte
2

Eu tive o mesmo problema e resolvo-o da seguinte maneira:

O arquivo xml que está usando para criar uma linha dentro do createViewholder, basta adicionar a linha abaixo:

 android:clickable="true"
 android:focusableInTouchMode="true"
 android:background="?attr/selectableItemBackgroundBorderless"

OU Se você estiver usando o frameLayout como pai do item de linha, então:

android:clickable="true"
android:focusableInTouchMode="true"
android:foreground="?attr/selectableItemBackgroundBorderless"

No código java dentro do suporte da vista em que você adicionou no ouvinte de clique:

@Override
   public void onClick(View v) {

    //ur other code here
    v.setPressed(true);
 }
Vikas Tiwari
fonte
1

Não consegui encontrar uma boa solução na Web para esse problema e resolvi-a. Há muitas pessoas que sofrem com esse problema. Assim, eu quero compartilhar minha solução aqui.

Durante a rolagem, as linhas são recicladas. Portanto, as caixas de seleção marcadas e as linhas destacadas não funcionam corretamente. Resolvi esse problema escrevendo a classe do adaptador abaixo.

Eu também implemento um projeto completo. Neste projeto, você pode selecionar várias caixas de seleção. Linhas incluindo caixas de seleção selecionadas são destacadas. E o mais importante, eles não são perdidos durante a rolagem. Você pode baixá-lo no link:

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

    }

Capturas de tela do aplicativo:

screen1 screen2 screen3 screen4

oiyio
fonte
-1

Defina private int selected_position = -1;para impedir que qualquer item seja selecionado no início.

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

Apenas adicionar android:background="?attr/selectableItemBackgroundBorderless"deve funcionar se você não tiver a cor de fundo, mas não se esqueça de usar o método setSelected. Se você tem uma cor de fundo diferente, usei isso (estou usando ligação de dados);

Definir isSelected na função onClick

b.setIsSelected(true);

E adicione isso ao xml;

android:background="@{ isSelected ? @color/{color selected} : @color/{color not selected} }"
Mohamed
fonte
-1

Faça o seletor:

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

Defina-o como plano de fundo no layout do item da lista

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

No seu adaptador, adicione OnClickListener à visualização (método 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)
            }
        }
    }

No evento onClick, ative a visualização:

 fun onItemClicked(view: View){
        view.isActivated = true
    }
i_tanova
fonte
2
Isso tem dois problemas: 1) Você não forneceu nenhuma maneira de desfazer a ativação de outros itens na recicladora. 2) As visualizações são reutilizadas. Portanto, é possível que a mesma exibição ativada fique visível na ordem quando rolada.
Anup Sharma