¿Cómo resaltar correctamente el elemento seleccionado en RecyclerView?

146

Estoy tratando de usar a RecyclerViewcomo horizontal ListView. Estoy tratando de descubrir cómo resaltar el elemento seleccionado. Cuando hago clic en uno de los elementos, se selecciona y se resalta correctamente, pero cuando hago clic en otro, el segundo se resalta con el anterior.

Aquí está mi función 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;
}

Aquí está el 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);

}
usuario65721
fuente
Usar vistas enfocables no es una buena idea para tratar de rastrear el elemento seleccionado. Compruebe mi respuesta para obtener una solución completa
Greg Ennis
puede b esto te ayuda amolsawant88.blogspot.in/2015/08/…
Amol Sawant 96 Kuli
Selección de elementos de vista de reciclador: stackoverflow.com/a/38501676/2648035
Alok Omkar
Esto es difícil de seguir cuando aún no tienes nada funcione , lo cual es malo ya que las respuestas acompañan y no especifican mucho sobre qué va a dónde.
FirstOne

Respuestas:

61

Escribí una clase de adaptador base para manejar automáticamente la selección de elementos con un RecyclerView. Simplemente deriva tu adaptador de él y usa listas de estado dibujables con state_selected, como lo harías con una vista de lista.

Tengo una publicación de blog aquí , pero aquí está el 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
fuente
mRecyclerViewNo se declara en ninguna parte. ¿Deberíamos pasarlo como un parámetro en el constructor y almacenarlo en un campo?
Pedro
1
Lo siento por eso. Mi adaptador es una clase interna de mi fragmento, por lo que tiene acceso al campo de vista del reciclador. De lo contrario, sí, podría pasarlo como parámetro. O incluso mejor, manipúlelo onRecyclerViewAttachedy guárdelo en una variable miembro allí.
Greg Ennis
1
Buena respuesta, pero ¿por qué usar getChildPosition ()? Hay otro método developer.android.com/reference/android/support/v7/widget/…
skyfishjy
Lo siento, ¿quieres decir que solo necesito importar tu TrackSelectionAdapterclase y usarla en mi lista? ¿Cómo "obtengo mi adaptador de tu clase"? Por favor, ¿podrías responder a mi pregunta? Estoy atascado tan profundamente: stackoverflow.com/questions/29695811/…
Cristiano Colacillo
2
Intenté esto y encontré que las dos llamadas consecutivas a NotifyItemChanged () mataron cualquier parecido de desplazamiento suave en hardware más lento. Fue particularmente malo en Fire TV antes de la actualización de Lollipop
Redshirt
158

Esta es una forma muy simple de hacerlo.

Tenga un private int selectedPos = RecyclerView.NO_POSITION;en la clase RecyclerView Adapter, y en el método onBindViewHolder intente:

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

}

Y en su evento OnClick modifique:

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

Funciona de maravilla para el cajón de navegación y otros adaptadores de elementos RecyclerView.

Nota: Asegúrese de usar un color de fondo en su diseño usando un selector como Colabug aclarado:

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

De lo contrario, setSelected (..) no hará nada, por lo que esta solución será inútil.

zIronManBox
fuente
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>
Usé
1
@zIronManBox: ¡manera agradable y simple! ¿Se supone que "selectedPosition" en el método onClick es "selectedPos"?
AJW
3
getLayoutPosition () no está disponible para mí.
ka3ak
3
No solo use -1, use RecyclerView.NO_POSITION; (que es -1)
Martin Marconcini
8
@ ka3ak: getLayoutPosition es un método de la clase ViewHolder cuyo objeto se pasa como primer parámetro en el método de vista de enlace. Por lo tanto, se puede acceder porvieHolder.getLayoutPosition
Tushar Kathuria
129

ACTUALIZACIÓN [26 / Jul / 2017]:

Como Pawan mencionó en el comentario sobre esa advertencia IDE sobre no usar esa posición fija, acabo de modificar mi código como se muestra a continuación. El escucha de clics se mueve a ViewHolder, y allí obtengo la posición usando el 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 esto ayude.

Conoce a Vora
fuente
¡Eso es genial! Sin embargo, estoy un poco confundido, ¿podría ayudarme a implementar una forma, ampliando esta respuesta, cómo hacer lo que está haciendo en la instrucción else cuando hace clic en un elemento que ya está seleccionado, para que lo anule la selección: else { holder.itemView.setBackgroundColor (Color.TRANSPARENT); }
iBobb
Por supuesto que sí. @iBobb. Para los elementos NO SELECCIONADOS, es decir, cuya posición no es igual a nuestra variable 'selected_position' no tendrá fondo. Usé esto porque RecyclerView, como su nombre lo indica, recicla cada elemento, por lo que establece un fondo VERDE para filas aleatorias mientras nos DESPLAZAMOS, por eso coloqué esa parte ELSE. (Puede probarlo comentando esa parte ELSE y luego desplazar su vista de Reciclador)
Conozca a Vora
No creo que hayas entendido mi pregunta. En cualquier aplicación, cuando selecciona algo, se resalta. Si lo mantiene de nuevo, se deselecciona. ¿Cómo implementaríamos eso? En este momento solo podemos seleccionar usando su código y tocar y mantener presionado el mismo elemento no hace nada.
iBobb
Puede ser posible, pero para eso tienes que anular onLongClickListener en lugar de onClickListener, ahora estoy ocupado, así que no puedo proporcionar el código completo, pero básicamente debes hacerlo de esa manera.
Conoce a Vora
Por alguna razón, lleva tiempo resaltar después del clic, alrededor de 100 a 200 milisegundos. ¿Alguna idea de por qué? Experimenté esto en un emulador y mi teléfono piruleta
suku
14

Es probable que su implementación funcione si desplaza el contenido fuera de la vista y luego lo vuelve a ver. Estaba teniendo un problema similar cuando me encontré con tu pregunta.

El siguiente fragmento de archivo está funcionando para mí. Mi implementación estaba dirigida a la selección múltiple, pero lancé un truco allí para forzar la selección ú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;
}

Como se señaló en esta pregunta vinculada , la configuración de los oyentes para viewHolders debe hacerse en onCreateViewHolder. Olvidé mencionar esto anteriormente.

Dustin Charles
fuente
6

Creo que he encontrado el mejor tutorial sobre cómo usar RecyclerView con todas las funciones básicas que necesitamos (simple + multiselección, resaltado, ondulación, clic y eliminar en multiselección, etc.).

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

En base a eso, pude crear una biblioteca "Adaptador flexible", que extiende un Adaptador seleccionable. Creo que esto debe ser responsabilidad del Adaptador, en realidad no es necesario reescribir las funcionalidades básicas del Adaptador cada vez, deje que una biblioteca lo haga, para que pueda reutilizar la misma implementación.

Este adaptador es muy rápido, funciona de fábrica (no es necesario extenderlo); puede personalizar los elementos para cada tipo de vista que necesite; ViewHolder están predefinidos: los eventos comunes ya están implementados: clic simple y largo; mantiene el estado después de la rotación y mucho más .

Eche un vistazo y siéntase libre de implementarlo en sus proyectos.

https://github.com/davideas/FlexibleAdapter

Un Wiki también está disponible.

Davideas
fuente
Gran trabajo escribiendo ese adaptador, parece muy útil. Lo único es que realmente necesita algunos ejemplos básicos y documentación, me resulta un poco confuso incluso ponerlo en funcionamiento. ¡Potencialmente genial sin embargo!
Voy
Sí, no encontré suficiente información para comenzar. No se pudo encontrar la referencia de API a pesar de que el código parece estar comentado con eso en mente. La aplicación de muestra parece, aunque amplia e informativa, muy difícil de entender sin un conocimiento previo de la biblioteca. Todos los casos de uso están unidos, hay poca indicación de lo que demuestra qué, las clases se reutilizan en diferentes escenarios, lo que hace que se sobrecarguen de información. Creé
Voy
Wiki ha sido reescrito por completo y está en camino de completarse.
Davideas
6

Mira mi solución. Supongo que debe establecer la posición seleccionada en el soporte y pasarla como Etiqueta de vista. La vista debe establecerse en el método onCreateViewHolder (...). También hay un lugar correcto para configurar el oyente para la vista, como OnClickListener o LongClickListener.

Mire el siguiente ejemplo y lea los comentarios al 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
fuente
¿Crees que mSelectedPosition se puede declarar como estático para mantener los cambios de configuración?
Sábado
¡No! está mal hacerlo estático, está muy mal
Konrad Krakowiak
Si. es peligroso. Pero para mantener el cambio de configuración (especialmente la rotación de la pantalla) podemos declarar esta variable en actividad y obtenerla desde allí, ¿verdad?
Sábado
Hay muchas soluciones ... Puedes usar getter y setter, puedes crear tu propio método saveInstanceState para adaptador y llamarlo en saveInstanceState desde Activity / Fragment
Konrad Krakowiak
4

no hay un selector en RecyclerView como ListView y GridView pero intentas a continuación lo que funcionó para mí

crear un selector dibujable como se muestra a continuación

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

luego establezca este dibujo como fondo de su diseño de fila RecyclerView como

android:background="@drawable/selector"
amodkanthe
fuente
2
Esto no hace nada útil. Simplemente cambiará el color haciendo el tiempo de impresión.
Leo Droidcoder
4

Decisión con interfaces y devoluciones de llamada. Crear interfaz con estados de selección y anulación de selección:

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

Ejecute el cambio de estado en la devolución de llamada:

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

Crear RecyclerView con devolución de llamada (ejemplo):

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

Ver más en el artículo de iPaulPro: https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd#.6gh29uaaz

Mykola Tychyna
fuente
3

Esta es mi solución, puede configurar un elemento (o un grupo) y deseleccionarlo con otro clic:

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

Tuve el mismo problema y lo soluciono de la siguiente manera:

El archivo xml que se usa para crear una Fila dentro de createViewholder, simplemente agregue la siguiente línea:

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

O bien, si usa frameLayout como elemento primario del elemento de fila, entonces:

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

En el código de Java dentro del soporte de la vista donde agregó el escucha de clics:

@Override
   public void onClick(View v) {

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

No pude encontrar una buena solución en la web para este problema y lo resolví yo mismo. Hay muchas personas que sufren este problema. Por lo tanto, quiero compartir mi solución aquí.

Mientras se desplaza, las filas se reciclan. Por lo tanto, las casillas marcadas y las filas resaltadas no funcionan correctamente. Resolví este problema escribiendo la siguiente clase de adaptador.

También implemento un proyecto completo. En este proyecto, puede seleccionar varias casillas de verificación. Las filas que incluyen casillas seleccionadas están resaltadas. Y lo más importante, estos no se pierden durante el desplazamiento. Puedes descargarlo desde el enlace:

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 pantalla de la aplicación:

pantalla1 pantalla2 pantalla3 pantalla4

oiyio
fuente
-1

Establezca private int selected_position = -1;para evitar que se seleccione cualquier elemento al inicio.

 @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
fuente
-1

Solo agregar android:background="?attr/selectableItemBackgroundBorderless"debería funcionar si no tiene color de fondo, pero no olvide usar el método setSelected. Si tiene un color de fondo diferente, acabo de usar esto (estoy usando enlace de datos);

Establecer isSelected en la función onClick

b.setIsSelected(true);

Y agregue esto a xml;

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

Hacer selector:

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

Establecer como fondo en el diseño de elementos de la 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">

En su adaptador, agregue OnClickListener a la vista (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)
            }
        }
    }

En el evento onClick, active la vista:

 fun onItemClicked(view: View){
        view.isActivated = true
    }
i_tanova
fuente
2
Esto tiene 2 problemas: 1) No ha proporcionado ninguna forma de deshacer la activación de otros elementos en el reciclador. 2) Las vistas se reutilizan. Por lo tanto, es posible que la misma vista activada sea visible en el orden cuando se desplaza.
Anup Sharma