RecyclerView - Obtenez une vue à une position particulière

140

J'ai une activité avec un RecyclerViewet un ImageView. J'utilise RecyclerViewpour afficher une liste d'images horizontalement. Lorsque je clique sur une image dans leRecyclerView la ImageViewdans l'activité devrait afficher une image plus grande de l'image. Jusqu'à présent, tout fonctionne bien.

Maintenant, il y en a deux autres ImageButtonsdans l'activité: imageButton_leftet imageButton_right. Lorsque je clique sur imageButton_left, l'image dans le ImageViewdoit tourner à gauche et aussi, la vignette dans le RecyclerViewdoit refléter ce changement. Le cas est similaire avec imageButton_right.

Je peux faire pivoter le fichier ImageView. Mais, comment puis-je faire pivoter la vignette dans le RecyclerView? Comment puis-je obtenir les ViewHolder's ImageView?

Code:

XML d'activité:

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

Mon code d'activité:

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

Mon adaptateur personnalisé pour 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());
        }
    }


}
Vamsi Challa
la source
1
Vous devriez le faire dans votre constructeur viewHolder, ajouter une propriété qui vous dira si l'image doit être pivotée, puis changez simplement cette propriété sur vos données et appelez notifyDataSetChanged pour que recyclerView se recharge
Nanoc
Certaines des réponses résolvent-elles votre problème?
SMBiggs
ViewHolder doit être statique pour des problèmes de performances. Toutes les variables de la classe externe doivent être passées via le constructeur.
AppiDevo
En complément de la réponse de @ Scott Cliquez ici
mut tony

Réponses:

149

Je suppose que vous utilisez un LinearLayoutManagerpour afficher la liste. Il a une belle méthode appelée findViewByPositionça

Recherche la vue qui représente la position d'adaptateur donnée.

Tout ce dont vous avez besoin est la position de l'adaptateur de l'article qui vous intéresse.

edit : comme indiqué par Paul Woitaschek dans les commentaires, findViewByPositionest une méthode LayoutManagerpour qu'elle fonctionne avec tous les LayoutManagers (c'est StaggeredGridLayoutManager-à- dire , etc.)

stan0
la source
9
C'est en fait une méthode du, LayoutManagerdonc cela fonctionnera pour toutes les sous-classes de LayoutManager.
Paul Woitaschek
Regardé le code source. LinearLayoutManger recherche via directAccess sur le tableau de sauvegarde lorsqu'il n'est pas en prélayage, alors que la méthode recyclerView effectue toujours une traversée. Cette méthode est donc probablement plus efficace.
NameSpace
Hey @stanO pouvez-vous s'il vous plaît m'aider avec ce stackoverflow.com/questions/49952965/…
3
Renvoie: View - La vue enfant qui représente la position donnée ou null si la position n'est pas disposée ... recyclerview -> la plupart des choses renvoient nul
me_
Gardez à l'esprit que vous devez attendre que l'adaptateur soit ajouté à la liste, voir ma réponse ci-dessous pour plus de détails
Konstantin Konopko
241

C'est ce que vous recherchez .

J'ai eu ce problème également. Et comme vous, la réponse est très difficile à trouver. Mais il existe un moyen simple d'obtenir le ViewHolder à partir d'une position spécifique (ce que vous ferez probablement beaucoup dans l'adaptateur).

myRecyclerView.findViewHolderForAdapterPosition(pos);

REMARQUE: si la vue a été recyclée, elle reviendra null. Merci à Michael d'avoir rapidement compris mon importante omission.

SMBiggs
la source
3
C'est exactement ce que je cherchais dans le LayoutManager
Ed Lee
12
Cela fonctionne, cependant, si le que ViewHoldervous essayez de référencer était «recyclé», il reviendra null.
Michael
5
@Michael, bonne prise! C'est un avertissement pour vous tous, testez d'abord null (ouais, comme tout le reste dans Android)!
SMBiggs
1
Pouvons-nous faire cela depuis l'intérieur de l'adaptateur?
Mauker
2
@Mauker: Vous pouvez l'utiliser à partir de tout ce qui peut accéder à l'instance RecyclerView.
SMBiggs
26

Si vous le souhaitez View, assurez-vous d'accéder à la itemViewpropriété de ViewHolder comme suit:myRecyclerView.findViewHolderForAdapterPosition(pos).itemView;

Horatio
la source
11

Vous pouvez utiliser les deux

recyclerViewInstance.findViewHolderForAdapterPosition(adapterPosition) et recyclerViewInstance.findViewHolderForLayoutPosition(layoutPosition). Assurez-vous que la vue RecyclerView utilise deux types de positions

Position de l'adaptateur: position d'un élément dans l'adaptateur. C'est la position du point de vue de l'adaptateur. Position de mise en page: position d'un élément dans le dernier calcul de mise en page. Il s'agit de la position du point de vue du LayoutManager. Vous devez utiliser getAdapterPosition()pour findViewHolderForAdapterPosition(adapterPosition)et getLayoutPosition()pourfindViewHolderForLayoutPosition(layoutPosition) .

Prenez une variable membre pour conserver la position d'élément précédemment sélectionnée dans l'adaptateur recyclerview et une autre variable membre pour vérifier si l'utilisateur clique pour la première fois ou non.

Des exemples de code et des captures d'écran sont joints pour plus d'informations en bas.

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

} premier élément sélectionné le deuxième élément sélectionné et l'élément précédemment sélectionné devient désélectionné le cinquième élément sélectionné et l'élément précédemment sélectionné devient désélectionné

Vikash Kumar Tiwari
la source
Et je pensais que cette discussion était épuisée! Merci d'avoir clarifié cette question.
SMBiggs
2
Ici, j'obtiens l'exception NullPointer dans le bloc else lorsque j'essaie de changer la couleur de l'élément précédent, j'ai donc ajouté la vérification null, maintenant il n'entre jamais dans ce bloc s'il a une exception NullPointer, donc la couleur de l'élément précédent n'est jamais changé. maintenant, ma vue Recycleur contient des éléments avec plusieurs éléments colorés. comment résoudre ce problème
Abhilash Reddy
8

Vous pouvez créer ArrayList de ViewHolder:

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

et, tous stockent ViewHolder (s) dans la liste comme:

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

        myViewHolders.add(position,holder);
}

et ajoutez / supprimez d'autres ViewHolder dans la ArrayList selon vos besoins.

Tarun Umath
la source
Merci d'avoir eu du mal à obtenir des supports recyclés pendant environ 2 jours.
nimi0112
5

Vous pouvez obtenir une vue pour une position particulière sur une vue de recyclage en utilisant les éléments suivants

int position = 2;
RecyclerView.ViewHolder viewHolder = recyclerview.findViewHolderForItemId(position);
View view = viewHolder.itemView;
ImageView imageView = (ImageView)view.findViewById(R.id.imageView);
Dimanche G Akinsete
la source
5

Vous pouvez simplement utiliser " findViewHolderForAdapterPosition méthode " de la vue recycleur et vous obtiendrez un objet viewHolder à partir de celui-ci puis transposez cette visionneuse dans la visionneuse de votre adaptateur afin que vous puissiez accéder directement aux vues de votre visionneuse

voici l'exemple de code pour kotlin

 val viewHolder = recyclerView.findViewHolderForAdapterPosition(position)
 val textview=(viewHolder as YourViewHolder).yourTextView
Mayank Sharma
la source
4

Gardez à l'esprit que vous devez attendre que l'adaptateur soit ajouté à la liste, vous pouvez alors essayer d'obtenir vue par position

final int i = 0;
recyclerView.setAdapter(adapter);
recyclerView.post(new Runnable() {
    @Override
    public void run() {
        View view = recyclerView.getLayoutManager().findViewByPosition(i);
    }
});
Konstantin Konopko
la source
2
Cela ne fonctionne pas toujours si vous ajoutez plusieurs éléments (par exemple: si vous avez un bouton qui les ajoute et appuyez dessus rapidement). Vous devez ajouter le runnable avec délai.
Alex Burdusel
3

Pour obtenir une vue à partir d'une position spécifique de recyclerView, vous devez appeler findViewHolderForAdapterPosition (position) sur l'objet recyclerView qui renverra RecyclerView.ViewHolder

à partir de l'objet RecyclerView.ViewHolder renvoyé avec itemView, vous pouvez accéder à toutes les vues à cette position particulière

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

ImageView iv_wish = rv_view.itemView.findViewById(R.id.iv_item);
Tushar
la source
1

Vous pouvez également le faire, cela vous aidera lorsque vous souhaitez modifier une vue après avoir cliqué sur un élément de position de la vue de recyclage

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

        }
mut tony
la source
1

Pour obtenir une vue spécifique à partir de la liste des vues du recycleur OU afficher une erreur à edittext de la vue du recycleur.

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 !");
}
Chirag Prajapati
la source