RecyclerView - Visualizza in una posizione particolare


140

Ho un'attività con a RecyclerViewe an ImageView. Sto usando il RecyclerViewper mostrare un elenco di immagini in orizzontale. Quando clicco su un'immagine nella RecyclerViewla ImageViewnell'attività dovrebbe mostrare un quadro più ampio dell'immagine. Finora tutto funziona bene.

Ora ce ne sono altri due ImageButtonsnell'attività: imageButton_lefte imageButton_right. Quando faccio clic su imageButton_left, l'immagine in ImageViewdovrebbe girare a sinistra e anche, l'anteprima in RecyclerViewdovrebbe riflettere questo cambiamento. Simile è il caso di imageButton_right.

Sono in grado di ruotare il ImageView. Ma come posso ruotare l'anteprima in RecyclerView? Come posso ottenere le ViewHolder's ImageView?

Codice:

XML attività:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp" />


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/original_image"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:scaleType="fitXY"
            android:src="@drawable/image_not_available_2" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center_horizontal"
            android:orientation="horizontal">


            <ImageButton
                android:id="@+id/imageButton_left"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="20dp"
                android:background="@drawable/rotate_left_icon" />

            <ImageButton
                android:id="@+id/imageButton_right"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/rotate_right_icon" />

        </LinearLayout>
    </LinearLayout>
</LinearLayout>

Il mio codice di attività:

public class SecondActivity extends AppCompatActivity implements IRecyclerViewClickListener {


    RecyclerView mRecyclerView;
    LinearLayoutManager mLayoutManager;
    RecyclerViewAdapter mRecyclerViewAdapter;
    List<String> urls = new ArrayList<String>();
    ImageView mOriginalImageView;
    ImageButton mLeftRotate, mRightRotate;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        urls.clear();

        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        mLayoutManager = new LinearLayoutManager(this, android.support.v7.widget.LinearLayoutManager.HORIZONTAL, false);
        mLayoutManager.setOrientation(android.support.v7.widget.LinearLayoutManager.HORIZONTAL);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerViewAdapter = new RecyclerViewAdapter(this, urls);
        mRecyclerView.setAdapter(mRecyclerViewAdapter);

        mOriginalImageView = (ImageView) findViewById(R.id.original_image);
        mLeftRotate = (ImageButton) findViewById(R.id.imageButton_left);
        mLeftRotate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mOriginalImageView.setRotation(mOriginalImageView.getRotation() - 90);
            }
        });


        mRightRotate = (ImageButton) findViewById(R.id.imageButton_right);
        mRightRotate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mOriginalImageView.setRotation(mOriginalImageView.getRotation() + 90);
            }
        });

        Intent intent = getIntent();
        if (intent != null) {

            String portfolio = intent.getStringExtra("portfolio");

            try {

                JSONArray jsonArray = new JSONArray(portfolio);

                for (int i = 0; i < jsonArray.length(); i++) {

                    JSONObject jsonObject = jsonArray.getJSONObject(i);

                    String url = jsonObject.getString("url");
                    urls.add(url);
                }

                Log.d(Const.DEBUG, "URLs: " + urls.toString());

                mRecyclerViewAdapter.notifyDataSetChanged();

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }


    @Override
    public void onItemClick(int position) {
        Picasso.with(this).load(urls.get(position)).into(mOriginalImageView);
    }
}

Il mio adattatore personalizzato per RecyclerView:

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {

    Context context;
    List<String> mUrls = new ArrayList<String>();

    IRecyclerViewClickListener mIRecyclerViewClickListener;

    public int position;

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }



    public RecyclerViewAdapter(Context context, List<String> urls) {
        this.context = context;
        this.mUrls.clear();
        this.mUrls = urls;

        Log.d(Const.DEBUG, "Urls Size: " + urls.size());
        Log.d(Const.DEBUG, urls.toString());

        if (context instanceof IRecyclerViewClickListener)
            mIRecyclerViewClickListener = (IRecyclerViewClickListener) context;
        else
            Log.d(Const.DEBUG, "Implement IRecyclerViewClickListener in Activity");
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.item_horizontal_recyclerview, parent, false);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Picasso.with(context).load(mUrls.get(position)).into(holder.mImageView);
    }

    @Override
    public int getItemCount() {
        return mUrls.size();
    }


    public void rotateThumbnail() {


    }

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        public ImageView mImageView;
        public View v;

        public ViewHolder(View v) {
            super(v);
            v.setTag(getAdapterPosition());
            v.setOnClickListener(this);
            this.mImageView = (ImageView) v.findViewById(R.id.image);
        }

        @Override
        public void onClick(View v) {
            this.v = v;
            mIRecyclerViewClickListener.onItemClick(getAdapterPosition());
        }
    }


}

1
Dovresti farlo nel tuo viewHolder costruttore, aggiungere una proprietà che ti dirà se l'immagine deve essere ruotata, quindi basta cambiare quella proprietà sui tuoi dati e chiamare notificationDataSetChanged per far ricaricare il
recyclerView

Qualcuno delle risposte risolve il tuo problema?
SMBiggs

ViewHolder deve essere statico per problemi di prestazioni. Tutte le variabili della classe esterna devono essere passate attraverso il costruttore.
AppiDevo,

Come supplemento alla risposta di @ Scott Fare clic qui
mut tony

Risposte:


149

Suppongo che tu stia usando a LinearLayoutManagerper mostrare l'elenco. Ha un bel metodo chiamato findViewByPositioncosì

Trova la vista che rappresenta la posizione dell'adattatore specificata.

Tutto ciò di cui hai bisogno è la posizione dell'adattatore dell'articolo che ti interessa.

modifica : come notato da Paul Woitaschek nei commenti, findViewByPositionè un metodo LayoutManagerper cui funzionerebbe con tutti i LayoutManager (cioè StaggeredGridLayoutManager, ecc.)


9
Questo è in realtà un metodo per LayoutManagercui questo funzionerà per tutte le sottoclassi di LayoutManager.
Paul Woitaschek,

Guardato il codice sorgente. LinearLayoutManger cerca tramite directAccess sull'array di supporto quando non è in prelayout, mentre il metodo recyclerView esegue sempre l'attraversamento. Quindi questo metodo è probabilmente più efficiente.
Nome Spazio

Hey @stanO Potete per favore mi aiuto con questo stackoverflow.com/questions/49952965/...

3
Restituisce: Visualizza - La vista figlio che rappresenta la posizione data o nulla se la posizione non è definita ... recyclerview -> la maggior parte delle cose restituisce null
me_

Tieni presente che devi attendere che l'adattatore
venga

241

Questo è quello che stai cercando .

Ho avuto anche questo problema. E come te, la risposta è molto difficile da trovare. Ma c'è un modo semplice per ottenere ViewHolder da una posizione specifica (qualcosa che probabilmente farai molto nell'adattatore).

myRecyclerView.findViewHolderForAdapterPosition(pos);

NOTA: se la vista è stata riciclata, verrà restituita null. Grazie a Michael per aver colto rapidamente la mia importante omissione.


3
Questo è esattamente quello che stavo cercando nel LayoutManager
Ed Lee,

12
Questo funziona, tuttavia, se ViewHoldersi sta tentando di fare riferimento a "riciclato", verrà restituito null.
Michael,

5
@Michael, buona cattura! Questo è un avvertimento per tutti voi là fuori, test prima di null (sì, come tutto il resto in Android)!
SMBiggs

1
Possiamo farlo dall'interno dell'adattatore?
Mauker,

2
@Mauker: puoi usarlo da qualsiasi cosa possa accedere all'istanza di RecyclerView.
SMBiggs

26

Se lo desideri View, assicurati di accedere alla itemViewproprietà di ViewHolder in questo modo:myRecyclerView.findViewHolderForAdapterPosition(pos).itemView;


11

Puoi usare entrambi

recyclerViewInstance.findViewHolderForAdapterPosition(adapterPosition) e recyclerViewInstance.findViewHolderForLayoutPosition(layoutPosition). Assicurarsi che la vista RecyclerView utilizzi due tipi di posizioni

Posizione adattatore: posizione di un articolo nell'adattatore. Questa è la posizione dal punto di vista dell'adattatore. Posizione layout: posizione di un elemento nell'ultimo calcolo del layout. Questa è la posizione dal punto di vista del LayoutManager. Dovresti usare getAdapterPosition()per findViewHolderForAdapterPosition(adapterPosition)e getLayoutPosition()per findViewHolderForLayoutPosition(layoutPosition).

Prendi una variabile membro per mantenere la posizione dell'articolo precedentemente selezionata nell'adattatore recyclerview e l'altra variabile membro per verificare se l'utente fa clic per la prima volta o meno.

Codice di esempio e schermate sono allegati per ulteriori informazioni in fondo.

public class MainActivity extends AppCompatActivity {     

private RecyclerView mRecyclerList = null;    
private RecyclerAdapter adapter = null;    

@Override    
protected void onCreate(Bundle savedInstanceState) {    
    super.onCreate(savedInstanceState);    
    setContentView(R.layout.activity_main);    

    mRecyclerList = (RecyclerView) findViewById(R.id.recyclerList);    
}    

@Override    
protected void onStart() {    
    RecyclerView.LayoutManager layoutManager = null;    
    String[] daysArray = new String[15];    
    String[] datesArray = new String[15];    

    super.onStart();    
    for (int i = 0; i < daysArray.length; i++){    
        daysArray[i] = "Sunday";    
        datesArray[i] = "12 Feb 2017";    
    }    

    adapter = new RecyclerAdapter(mRecyclerList, daysArray, datesArray);    
    layoutManager = new LinearLayoutManager(MainActivity.this);    
    mRecyclerList.setAdapter(adapter);    
    mRecyclerList.setLayoutManager(layoutManager);    
}    
}    


public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyCardViewHolder>{          

private final String TAG = "RecyclerAdapter";        
private Context mContext = null;        
private TextView mDaysTxt = null, mDateTxt = null;    
private LinearLayout mDateContainerLayout = null;    
private String[] daysArray = null, datesArray = null;    
private RecyclerView mRecyclerList = null;    
private int previousPosition = 0;    
private boolean flagFirstItemSelected = false;    

public RecyclerAdapter(RecyclerView mRecyclerList, String[] daysArray, String[] datesArray){    
    this.mRecyclerList = mRecyclerList;    
    this.daysArray = daysArray;    
    this.datesArray = datesArray;    
}    

@Override    
public MyCardViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {    
    LayoutInflater layoutInflater = null;    
    View view = null;    
    MyCardViewHolder cardViewHolder = null;    
    mContext = parent.getContext();    
    layoutInflater = LayoutInflater.from(mContext);    
    view = layoutInflater.inflate(R.layout.date_card_row, parent, false);    
    cardViewHolder = new MyCardViewHolder(view);    
    return cardViewHolder;    
}    

@Override    
public void onBindViewHolder(MyCardViewHolder holder, final int position) {    
    mDaysTxt = holder.mDaysTxt;    
    mDateTxt = holder.mDateTxt;    
    mDateContainerLayout = holder.mDateContainerLayout;    

    mDaysTxt.setText(daysArray[position]);    
    mDateTxt.setText(datesArray[position]);    

    if (!flagFirstItemSelected){    
        mDateContainerLayout.setBackgroundColor(Color.GREEN);    
        flagFirstItemSelected = true;    
    }else {    
        mDateContainerLayout.setBackground(null);    
    }    
}    

@Override    
public int getItemCount() {    
    return daysArray.length;    
}    

class MyCardViewHolder extends RecyclerView.ViewHolder{    
    TextView mDaysTxt = null, mDateTxt = null;    
    LinearLayout mDateContainerLayout = null;    
    LinearLayout linearLayout = null;    
    View view = null;    
    MyCardViewHolder myCardViewHolder = null;    

    public MyCardViewHolder(View itemView) {    
        super(itemView);    
        mDaysTxt = (TextView) itemView.findViewById(R.id.daysTxt);    
        mDateTxt = (TextView) itemView.findViewById(R.id.dateTxt);    
        mDateContainerLayout = (LinearLayout) itemView.findViewById(R.id.dateContainerLayout);    

        mDateContainerLayout.setOnClickListener(new View.OnClickListener() {    
            @Override    
            public void onClick(View v) {    
                LinearLayout linearLayout = null;    
                View view = null;    

                if (getAdapterPosition() == previousPosition){    
                    view = mRecyclerList.findViewHolderForAdapterPosition(previousPosition).itemView;    
                    linearLayout = (LinearLayout) view.findViewById(R.id.dateContainerLayout);    
                    linearLayout.setBackgroundColor(Color.GREEN);    
                    previousPosition = getAdapterPosition();    

                }else {    
                    view = mRecyclerList.findViewHolderForAdapterPosition(previousPosition).itemView;    
                    linearLayout = (LinearLayout) view.findViewById(R.id.dateContainerLayout);    
                    linearLayout.setBackground(null);    

                    view = mRecyclerList.findViewHolderForAdapterPosition(getAdapterPosition()).itemView;    
                    linearLayout = (LinearLayout) view.findViewById(R.id.dateContainerLayout);    
                    linearLayout.setBackgroundColor(Color.GREEN);    
                    previousPosition = getAdapterPosition();    
                }    
            }    
        });    
    }    
}       

} primo elemento selezionato il secondo elemento selezionato e l'elemento precedentemente selezionato non viene selezionato il quinto elemento selezionato e l'elemento precedentemente selezionato diventa deselezionato


E ho pensato che questa discussione fosse esaurita! Grazie per chiarire ulteriormente questo problema.
SMBiggs

2
Qui sto ottenendo l'eccezione NullPointer nel blocco else quando sto cercando di cambiare il colore dell'articolo precedente, quindi ho aggiunto il controllo null, ora non entra mai in quel blocco se ha l'eccezione NullPointer, quindi il colore dell'articolo precedente non è mai cambiato. ora la mia vista Riciclatore ha oggetti con più oggetti colorati. come
posso

8

Puoi creare ArrayList di ViewHolder:

 ArrayList<MyViewHolder> myViewHolders = new ArrayList<>();
 ArrayList<MyViewHolder> myViewHolders2 = new ArrayList<>();

e, tutti memorizzano ViewHolder (s) nell'elenco come:

 @Override
    public void onBindViewHolder(@NonNull final MyViewHolder holder, final int position) {
        final String str = arrayList.get(position);

        myViewHolders.add(position,holder);
}

e aggiungi / rimuovi altri ViewHolder nella ArrayList secondo i tuoi requisiti.


Grazie per aver lottato per ottenere supporti riciclati per circa 2 giorni.
nimi0112

5

È possibile ottenere una vista per una posizione particolare su una vista riciclo utilizzando quanto segue

int position = 2;
RecyclerView.ViewHolder viewHolder = recyclerview.findViewHolderForItemId(position);
View view = viewHolder.itemView;
ImageView imageView = (ImageView)view.findViewById(R.id.imageView);

5

Puoi semplicemente usare il metodo " findViewHolderForAdapterPosition " per la vista del riciclo e otterrai un oggetto viewHolder da quel punto, quindi scrivi quel viewholder nel tuo viewholder dell'adattatore in modo da poter accedere direttamente alle viste del tuo viewholder

di seguito è riportato il codice di esempio per kotlin

 val viewHolder = recyclerView.findViewHolderForAdapterPosition(position)
 val textview=(viewHolder as YourViewHolder).yourTextView

4

Tieni presente che devi attendere fino a quando l'adattatore non verrà aggiunto all'elenco, quindi puoi provare a visualizzare per posizione

final int i = 0;
recyclerView.setAdapter(adapter);
recyclerView.post(new Runnable() {
    @Override
    public void run() {
        View view = recyclerView.getLayoutManager().findViewByPosition(i);
    }
});

2
Questo non funziona sempre se aggiungi più elementi (es: se hai un pulsante che li aggiunge e lo premi velocemente). È necessario aggiungere il runnable con ritardo.
Alex Burdusel,

3

Per ottenere una vista da una posizione specifica di recyclerView è necessario chiamare findViewHolderForAdapterPosition (posizione) sull'oggetto recyclerView che restituirà RecyclerView.ViewHolder

dall'oggetto RecyclerView.ViewHolder restituito con itemView è possibile accedere a tutte le viste in quella particolare posizione

RecyclerView.ViewHolder rv_view = recyclerView.findViewHolderForAdapterPosition(position);

ImageView iv_wish = rv_view.itemView.findViewById(R.id.iv_item);

1

Puoi anche farlo, questo ti aiuterà quando vuoi modificare una vista dopo aver fatto clic su un elemento di posizione riciclata

@Override
public void onClick(View view, int position) {

            View v =  rv_notifications.getChildViewHolder(view).itemView;
            TextView content = v.findViewById(R.id.tv_content);
            content.setText("Helloo");

        }

1

Per ottenere una vista specifica dall'elenco delle visualizzazioni del riciclatore O mostrare un errore al punto di vista della vista del riciclatore.

private void popupErrorMessageAtPosition(int itemPosition) {

    RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(itemPosition);
    View view = viewHolder.itemView;
    EditText etDesc = (EditText) view.findViewById(R.id.et_description);
    etDesc.setError("Error message here !");
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.