Wie kann ich ausgewählte Elemente in RecyclerView richtig hervorheben?

146

Ich versuche ein RecyclerViewals Horizontale zu verwenden ListView. Ich versuche herauszufinden, wie das ausgewählte Element hervorgehoben werden kann. Wenn ich auf eines der Elemente klicke, wird es ausgewählt und richtig hervorgehoben. Wenn ich jedoch auf ein anderes klicke, wird das zweite mit dem älteren hervorgehoben.

Hier ist meine onClick-Funktion:

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

Hier ist das 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
quelle
Die Verwendung fokussierbarer Ansichten ist keine gute Idee, um ausgewählte Elemente zu verfolgen. Überprüfen Sie meine Antwort für eine vollständige Lösung
Greg Ennis
kann b diese Hilfe u amolsawant88.blogspot.in/2015/08/…
Amol Sawant 96 Kuli
Artikelauswahl für Recycler-Ansicht: stackoverflow.com/a/38501676/2648035
Alok Omkar
Dies ist schwer zu befolgen, wenn noch nichts funktioniert , was schlecht ist, da die Antworten mitgehen und nicht viel darüber angeben, was wohin geht.
FirstOne

Antworten:

61

Ich habe eine Basisadapterklasse geschrieben, um die Artikelauswahl automatisch mit einem RecyclerView durchzuführen. Leiten Sie einfach Ihren Adapter daraus ab und verwenden Sie mit state_selected zeichnbare Statuslisten, wie Sie es bei einer Listenansicht tun würden.

Ich habe einen Blog-Beitrag hier darüber, aber hier ist der Code:

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
quelle
mRecyclerViewwird nirgendwo deklariert. Sollten wir es einfach als Parameter im Konstruktor übergeben und in einem Feld speichern?
Pedro
1
Das tut mir leid. Mein Adapter ist eine innere Klasse meines Fragments, sodass er Zugriff auf das Recycler-Ansichtsfeld hat. Andernfalls könnten Sie es ja als Parameter übergeben. Oder noch besser, behandeln onRecyclerViewAttachedund speichern Sie es dort in einer Mitgliedsvariablen.
Greg Ennis
1
Schöne Antwort, aber warum getChildPosition () verwenden? Es gibt eine andere Methode developer.android.com/reference/android/support/v7/widget/…
skyfishjy
Es tut mir leid, meinst du damit, dass ich nur deine TrackSelectionAdapterKlasse importieren und in meiner Liste verwenden muss? Wie leite ich "meinen Adapter von Ihrer Klasse ab"? Könnten Sie bitte meine Frage beantworten? Ich stecke so tief fest: stackoverflow.com/questions/29695811/…
Cristiano Colacillo
2
Ich habe dies versucht und festgestellt, dass die beiden aufeinanderfolgenden Aufrufe von NotifyItemChanged () den Anschein eines reibungslosen Bildlaufs auf langsamerer Hardware zunichte gemacht haben. War besonders schlecht im Fire TV vor dem Lollipop Update
Redshirt
157

Dies ist eine sehr einfache Möglichkeit.

Haben Sie eine private int selectedPos = RecyclerView.NO_POSITION;in der RecyclerView Adapter-Klasse und versuchen Sie unter onBindViewHolder-Methode:

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

}

Und in Ihrem OnClick-Ereignis ändern Sie:

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

Funktioniert wie ein Zauber für Navigtional Drawer und andere RecyclerView Item Adapter.

Hinweis: Verwenden Sie in Ihrem Layout unbedingt eine Hintergrundfarbe, indem Sie einen Selektor wie Colabug verwenden.

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

Andernfalls führt setSelected (..) nichts aus und macht diese Lösung unbrauchbar.

zIronManBox
quelle
12
Ich habe diese Lösung mit einem Hintergrund-Zeichen- / Auswahl-Set in meiner <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>
Zeilenansicht verwendet
1
@zIronManBox: schöne, einfache Art! Soll "selectedPosition" in der onClick-Methode "selectedPos" sein?
AJW
3
getLayoutPosition () ist für mich nicht verfügbar.
Ka3ak
3
Verwenden Sie nicht nur -1, sondern RecyclerView.NO_POSITION. (das ist -1)
Martin Marconcini
8
@ ka3ak: getLayoutPosition ist eine Methode der ViewHolder-Klasse, deren Objekt als erster Parameter in der Bindungsansichtsmethode übergeben wird. So kann es vonvieHolder.getLayoutPosition
Tushar Kathuria
129

UPDATE [26 / Jul / 2017]:

Wie der Pawan im Kommentar zu dieser IDE-Warnung erwähnt hat, diese feste Position nicht zu verwenden, habe ich meinen Code wie folgt geändert. Der Klick-Listener wird verschoben ViewHolder, und dort erhalte ich die Position mithilfe der getAdapterPosition()Methode

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

hoffe das wird helfen.

Treffen Sie Vora
quelle
Das ist großartig! Ich bin allerdings etwas verwirrt. Können Sie mir helfen, einen Weg zu finden, der diese Antwort erweitert und zeigt, wie Sie alles tun, was Sie in der else-Anweisung tun, wenn Sie auf ein bereits ausgewähltes Element klicken, sodass Sie die Auswahl aufheben: else { Inhaber.itemView.setBackgroundColor (Color.TRANSPARENT); }
iBobb
Natürlich ja. @ iBobb. Für NICHT AUSGEWÄHLTE Elemente, dh deren Position nicht unserer Variablen 'selected_position' entspricht, muss KEIN Hintergrund vorhanden sein. Ich habe dies verwendet, weil RecyclerView, wie der Name schon sagt, jedes Element recycelt, sodass der GRÜNE Hintergrund für zufällige Zeilen festgelegt wird, während wir SCROLLEN. Deshalb habe ich diesen ELSE-Teil platziert. (Sie können es einfach versuchen, indem Sie diesen anderen Teil auskommentieren und dann durch Ihre Recycler-Ansicht scrollen.)
Treffen Sie Vora
Ich glaube nicht, dass du meine Frage verstanden hast. Sie wissen in jeder App, wenn Sie etwas auswählen, wird es hervorgehoben. Wenn Sie es erneut gedrückt halten, wird die Auswahl aufgehoben. Wie würden wir das umsetzen? Im Moment können wir nur mit Ihrem Code auswählen und das Berühren und Halten des gleichen Elements bewirkt nichts.
iBobb
Es kann möglich sein, aber dafür müssen Sie onLongClickListener anstelle von onClickListener überschreiben. Im Moment bin ich beschäftigt und kann daher keinen vollständigen Code bereitstellen, aber Sie sollten dies grundsätzlich auf diese Weise tun müssen.
Treffen Sie Vora
Aus irgendeinem Grund dauert es einige Zeit, nach dem Klicken etwa 100 bis 200 Millisekunden hervorzuheben. Irgendeine Idee warum? Ich habe dies auf einem Emulator und meinem Lutschertelefon erlebt
Suku
14

Ihre Implementierung funktioniert wahrscheinlich, wenn Sie den Inhalt aus der Ansicht und dann wieder in die Ansicht scrollen. Ich hatte ein ähnliches Problem, als ich auf Ihre Frage stieß.

Das folgende Datei-Snippet funktioniert für mich. Meine Implementierung war auf Mehrfachauswahl ausgerichtet, aber ich habe dort einen Hack ausgeführt, um eine Einzelauswahl zu erzwingen. (* 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;
}

Wie in dieser verknüpften Frage erwähnt , sollte das Festlegen von Listenern für viewHolders in onCreateViewHolder erfolgen. Ich habe vergessen, dies zuvor zu erwähnen.

Dustin Charles
quelle
6

Ich denke, ich habe das beste Tutorial gefunden, wie man RecyclerView mit allen grundlegenden Funktionen verwendet, die wir benötigen (Einzel + Mehrfachauswahl, Hervorheben, Welligkeit, Klicken und Entfernen bei Mehrfachauswahl usw.).

Hier ist es -> http://enoent.fr/blog/2015/01/18/recyclerview-basics/

Auf dieser Grundlage konnte ich eine Bibliothek "FlexibleAdapter" erstellen, die einen SelectableAdapter erweitert. Ich denke, dies muss in der Verantwortung des Adapters liegen. Tatsächlich müssen Sie die grundlegenden Funktionen des Adapters nicht jedes Mal neu schreiben. Lassen Sie eine Bibliothek dies tun, damit Sie dieselbe Implementierung einfach wiederverwenden können.

Dieser Adapter ist sehr schnell und funktioniert sofort (Sie müssen ihn nicht erweitern). Sie passen die Elemente für jeden Ansichtstyp an, den Sie benötigen. ViewHolder sind vordefiniert: Allgemeine Ereignisse sind bereits implementiert: Einzel- und Langklick; es behält den Zustand nach der Rotation bei und vieles mehr .

Bitte schauen Sie sich das an und implementieren Sie es in Ihre Projekte.

https://github.com/davideas/FlexibleAdapter

Ein Wiki ist ebenfalls verfügbar.

Davideas
quelle
Tolle Arbeit beim Schreiben dieses Adapters, es scheint sehr nützlich zu sein. Das einzige ist, dass es wirklich einige grundlegende Beispiele und Dokumentationen braucht. Ich finde es etwas verwirrend, es überhaupt zum Laufen zu bringen. Potenziell großartig!
Voy
Ja, ich habe dort nicht genügend Informationen gefunden, um loszulegen. API-Referenz konnte nicht gefunden werden, obwohl Code in diesem Sinne kommentiert zu sein scheint. Die Beispiel-App scheint - obwohl sie breit und informativ ist - ohne vorherige Kenntnis der Bibliothek sehr schwer zu verstehen. Alle Anwendungsfälle sind miteinander verbunden, es gibt kaum Hinweise darauf, was was demonstriert. Klassen werden in verschiedenen Szenarien wiederverwendet, was dazu führt, dass sie mit Informationen überladen sind. Ich habe hier ein neues Problem mit diesen Vorschlägen erstellt: github.com/davideas/FlexibleAdapter/issues/120
Voy
Das Wiki wurde komplett neu geschrieben und ist auf dem Weg zur Fertigstellung.
Davideas
6

Schau dir meine Lösung an. Ich nehme an, Sie sollten die ausgewählte Position im Halter festlegen und als Tag of View übergeben. Die Ansicht sollte in der Methode onCreateViewHolder (...) festgelegt werden. Es gibt auch den richtigen Ort, um den Listener für die Ansicht festzulegen, z. B. OnClickListener oder LongClickListener.

Bitte schauen Sie sich das folgende Beispiel an und lesen Sie die Kommentare zum Code.

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
quelle
Denken Sie, dass mSelectedPosition als statisch deklariert werden kann, um Konfigurationsänderungen beizubehalten?
Sathesh
Nein! es ist falsch, es als statisch zu machen, es ist sehr falsch
Konrad Krakowiak
Ja. es ist gefährlich. Aber um die Konfigurationsänderung (insbesondere Bildschirmdrehung) beizubehalten, können wir diese Variable in Aktivität deklarieren und von dort abrufen, oder?
Sathesh
Es gibt viele Lösungen ... Sie können Getter und Setter verwenden. Sie können Ihre eigene Methode saveInstanceState für den Adapter erstellen und diese in saveInstanceState von Activity / Fragment
Konrad Krakowiak
4

In RecyclerView gibt es keinen Selektor wie ListView und GridView, aber Sie versuchen unten, was bei mir funktioniert hat

Erstellen Sie einen Selektor, der wie folgt gezeichnet werden kann

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

Legen Sie dieses Zeichen dann als Hintergrund für Ihr RecyclerView-Zeilenlayout fest

android:background="@drawable/selector"
Amodkanthe
quelle
2
Das macht nichts Sinnvolles. Es ändert nur die Farbe während der Druckzeit.
Leo Droidcoder
4

Entscheidung mit Schnittstellen und Rückrufen. Schnittstelle mit ausgewählten und nicht ausgewählten Zuständen erstellen:

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

Schnittstelle in ViewHolder implementieren:

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

Statusänderung beim Rückruf ausführen:

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

Erstellen Sie RecyclerView mit Rückruf (Beispiel):

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

Weitere Informationen finden Sie im Artikel von iPaulPro: https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd#.6gh29uaaz

Mykola Tychyna
quelle
3

Dies ist meine Lösung. Sie können ein Element (oder eine Gruppe) festlegen und die Auswahl mit einem weiteren Klick aufheben:

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

Ich hatte das gleiche Problem und löse es folgendermaßen:

Die XML-Datei, die zum Erstellen einer Zeile in createViewholder verwendet wird, fügen Sie einfach die folgende Zeile hinzu:

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

ODER Wenn Sie frameLayout als übergeordnetes Element des Zeilenelements verwenden, gilt Folgendes:

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

Im Java-Code im Ansichtshalter, den Sie beim Klicken auf den Listener hinzugefügt haben:

@Override
   public void onClick(View v) {

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

Ich konnte im Web keine gute Lösung für dieses Problem finden und habe es selbst gelöst. Es gibt viele Menschen, die unter diesem Problem leiden. Daher möchte ich meine Lösung hier teilen.

Während des Bildlaufs werden Zeilen recycelt. Daher funktionieren aktivierte Kontrollkästchen und hervorgehobene Zeilen nicht ordnungsgemäß. Ich habe dieses Problem gelöst, indem ich die folgende Adapterklasse geschrieben habe.

Ich implementiere auch ein vollständiges Projekt. In diesem Projekt können Sie mehrere Kontrollkästchen aktivieren. Zeilen mit ausgewählten Kontrollkästchen werden hervorgehoben. Und was noch wichtiger ist, diese gehen beim Scrollen nicht verloren. Sie können es über den Link herunterladen:

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

    }

Screenshots der App:

screen1 screen2 screen3 screen4

oiyio
quelle
-1

Festlegen private int selected_position = -1;, um zu verhindern, dass beim Start ein Element ausgewählt wird.

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

Das Hinzufügen android:background="?attr/selectableItemBackgroundBorderless"sollte funktionieren, wenn Sie keine Hintergrundfarbe haben, aber vergessen Sie nicht, die setSelected-Methode zu verwenden. Wenn Sie eine andere Hintergrundfarbe haben, habe ich diese gerade verwendet (ich verwende Datenbindung).

Set isSelected bei der onClick-Funktion

b.setIsSelected(true);

Und füge dies zu xml hinzu;

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

Auswahl treffen:

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

Legen Sie es als Hintergrund für Ihr Listenelementlayout fest

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

Fügen Sie in Ihrem Adapter OnClickListener zur Ansicht hinzu (onBind-Methode)

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

Aktivieren Sie im Ereignis onClick die Ansicht:

 fun onItemClicked(view: View){
        view.isActivated = true
    }
i_tanova
quelle
2
Dies hat zwei Probleme: 1) Sie haben keine Möglichkeit angegeben, die Aktivierung anderer Elemente im Recycler rückgängig zu machen. 2) Ansichten werden wiederverwendet. Es ist also möglich, dass dieselbe aktivierte Ansicht beim Scrollen in der Reihenfolge angezeigt wird.
Anup Sharma