RecyclerView: obtenga una vista en una posición particular

140

Tengo una actividad con ay RecyclerViewan ImageView. Estoy usando el RecyclerViewpara mostrar una lista de imágenes horizontalmente. Cuando hago clic en una imagen en el RecyclerViewla ImageViewen la actividad debe mostrar un panorama más amplio de la imagen. Hasta ahora todo funciona bien.

Ahora hay dos más ImageButtonsen la actividad: imageButton_lefty imageButton_right. Cuando hago clic en imageButton_left, la imagen en el ImageViewdebería girar a la izquierda y también, la miniatura en el RecyclerViewdebería reflejar este cambio. Similar es el caso con imageButton_right.

Soy capaz de rotar el ImageView. Pero, ¿cómo puedo rotar la miniatura en el RecyclerView? ¿Cómo puedo obtener los ViewHolder's ImageView?

Código:

XML de actividad:

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

Mi código de actividad:

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

Mi adaptador personalizado para 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
fuente
1
Debe hacerlo en su constructor viewHolder, agregue una propiedad que le dirá si la imagen necesita ser rotada, luego simplemente cambie esa propiedad en sus datos y llame a notifyDataSetChanged para hacer la recarga de recyclerView
Nanoc
¿Alguna de las respuestas resuelve su problema?
Scott Biggs
ViewHolder debe ser estático por problemas de rendimiento. Todas las variables de la clase externa deben pasarse a través del constructor.
AppiDevo
Como suplemento a la respuesta de @ Scott Haga clic aquí
mut tony

Respuestas:

149

Supongo que está utilizando a LinearLayoutManagerpara mostrar la lista. Tiene un buen método llamado findViewByPositioneso

Encuentra la vista que representa la posición del adaptador dada.

Todo lo que necesita es la posición del adaptador del artículo que le interesa.

editar : como señaló Paul Woitaschek en los comentarios, findViewByPositiones un método LayoutManagerpara que funcione con todos los LayoutManagers (es decir StaggeredGridLayoutManager, etc.)

stan0
fuente
9
Eso es realmente un método de LayoutManagerlo que funcionará para todas las subclases de LayoutManager
Paul Woitaschek
Miré el código fuente. LinearLayoutManger busca a través de directAccess en la matriz de respaldo cuando no está en prelayout, mientras que el método recyclerView siempre hace un recorrido transversal. Entonces este método es probablemente más eficiente.
NameSpace
Hola @stanO, ¿puedes ayudarme con este stackoverflow.com/questions/49952965/…
3
Devuelve: Vista - La vista secundaria que representa la posición dada o nula si la posición no se presenta ... vista de reciclaje -> la mayoría de las cosas devuelven nula
me_
Tenga en cuenta que tiene que esperar hasta que se agregue el adaptador a la lista, consulte mi respuesta a continuación para obtener más detalles
Konstantin Konopko
241

Esto es lo que estás buscando .

Yo tuve este problema también. Y como tú, la respuesta es muy difícil de encontrar. Pero hay una manera fácil de obtener el ViewHolder desde una posición específica (algo que probablemente harás mucho en el adaptador).

myRecyclerView.findViewHolderForAdapterPosition(pos);

NOTA: Si la Vista ha sido reciclada, esto volverá null. Gracias a Michael por captar rápidamente mi importante omisión.

Scott Biggs
fuente
3
Esto es exactamente lo que estaba buscando en el LayoutManager
Ed Lee
12
Esto funciona, sin embargo, si el ViewHolderque intenta hacer referencia fue "reciclado", volverá null.
Michael
55
@Michael, buena captura! Eso es una advertencia para todos ustedes, primero prueben nulo (sí, como todo lo demás en Android).
Scott Biggs
1
¿Podemos hacer esto desde el interior del adaptador?
Mauker
2
@Mauker: puede usar esto desde cualquier cosa que pueda acceder a la instancia de RecyclerView.
Scott Biggs
26

Si desea que el View, asegúrese de acceso a la itemViewpropiedad de la ViewHolder de este modo:myRecyclerView.findViewHolderForAdapterPosition(pos).itemView;

Horacio
fuente
11

Puedes usar usar ambos

recyclerViewInstance.findViewHolderForAdapterPosition(adapterPosition) y recyclerViewInstance.findViewHolderForLayoutPosition(layoutPosition). Asegúrese de que la vista RecyclerView utiliza dos tipos de posiciones

Posición del adaptador: posición de un elemento en el adaptador. Esta es la posición desde la perspectiva del Adaptador. Posición de diseño: posición de un elemento en el último cálculo de diseño. Esta es la posición desde la perspectiva del LayoutManager. Debe usar getAdapterPosition()para findViewHolderForAdapterPosition(adapterPosition)y getLayoutPosition()para findViewHolderForLayoutPosition(layoutPosition).

Tome una variable miembro para mantener la posición del elemento previamente seleccionado en el adaptador de la vista de reciclaje y otra variable miembro para verificar si el usuario hace clic por primera vez o no.

Se adjuntan códigos de muestra y capturas de pantalla para obtener más información en la parte inferior.

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

} primer elemento seleccionado el segundo elemento seleccionado y el elemento previamente seleccionado no se seleccionan quinto elemento seleccionado y el elemento previamente seleccionado no se selecciona

Vikash Kumar Tiwari
fuente
¡Y pensé que esta discusión estaba agotada! Gracias por aclarar más este problema.
Scott Biggs
2
Aquí obtengo la excepción NullPointer en el bloque else cuando intento cambiar el color del elemento anterior, así que agregué la verificación nula, ahora nunca entra en ese bloque si tiene una excepción NullPointer, por lo que el color del elemento anterior nunca se cambia ahora mi vista de Reciclador tiene elementos con múltiples elementos de colores. ¿Cómo puedo resolver esto?
Abhilash Reddy
8

Puede hacer ArrayList de ViewHolder:

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

y, todos almacenan ViewHolder (s) en la lista como:

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

        myViewHolders.add(position,holder);
}

y agregue / elimine otro ViewHolder en ArrayList según su requisito.

Tarun Umath
fuente
Gracias por haber estado luchando por obtener recipientes reciclados durante 2 días.
nimi0112
5

Puede obtener una vista para una posición particular en una vista de reciclador utilizando lo siguiente

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

Simplemente puede usar el método " findViewHolderForAdapterPosition " de la vista de reciclador y obtendrá un objeto viewHolder a partir de ese entonces, escriba ese marcador de vista en el marcador de adaptador para que pueda acceder directamente a las vistas de su marcador

el siguiente es el código de muestra para kotlin

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

Tenga en cuenta que debe esperar hasta que se agregue el adaptador a la lista, luego puede intentar obtener una vista por posición

final int i = 0;
recyclerView.setAdapter(adapter);
recyclerView.post(new Runnable() {
    @Override
    public void run() {
        View view = recyclerView.getLayoutManager().findViewByPosition(i);
    }
});
Konstantin Konopko
fuente
2
Esto no siempre funciona si agrega varios elementos (es decir, si tiene un botón que los agrega y lo presiona rápidamente). Debe agregar el ejecutable con retraso.
Alex Burdusel
3

Para obtener una vista desde una posición específica de recyclerView, debe llamar a findViewHolderForAdapterPosition (posición) en el objeto recyclerView que devolverá RecyclerView.ViewHolder

desde el objeto RecyclerView.ViewHolder devuelto con itemView puede acceder a todas las Vistas en esa posición particular

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

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

También puede hacer esto, esto ayudará cuando desee modificar una vista después de hacer clic en un elemento de posición de vista de reciclador

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

Para obtener una vista específica de la lista de vista del reciclador O mostrar un error en el texto de edición de la vista del reciclador.

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
fuente