RecyclerView - Uzyskaj widok w określonej pozycji

140

Mam aktywność z a RecyclerViewi ImageView. Używam, RecyclerViewaby wyświetlić listę zdjęć w poziomie. Po kliknięciu na zdjęcie w w działalności powinny wykazywać większe zdjęcie obrazu. Jak dotąd wszystko działa dobrze.RecyclerViewImageView

Teraz w ćwiczeniu są jeszcze dwa ImageButtons: imageButton_lefti imageButton_right. Kiedy kliknę imageButton_left, obraz w ImageViewpowinien obrócić się w lewo, a miniatura w RecyclerViewpowinna odzwierciedlać tę zmianę. Podobnie jest z imageButton_right.

Jestem w stanie obrócić ImageView. Ale jak mogę obrócić miniaturę w RecyclerView? Jak mogę dostać ViewHolder„s ImageView?

Kod:

XML aktywności:

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

Kod mojej aktywności:

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

Mój niestandardowy adapter dla 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
źródło
1
Powinieneś zrobić to w swoim konstruktorze viewHolder, dodać właściwość, która powie ci, czy obraz wymaga obrócenia, a następnie po prostu zmień tę właściwość w swoich danych i wywołaj notifyDataSetChanged, aby przeładować
recyclingView
Czy któraś z odpowiedzi rozwiązuje Twój problem?
Scott Biggs
ViewHolder powinien być statyczny ze względu na problem z wydajnością. Wszystkie zmienne z klasy zewnętrznej powinny być przekazywane przez konstruktor.
AppiDevo
Jako uzupełnienie odpowiedzi @ Scotta Kliknij tutaj
mut tony

Odpowiedzi:

149

Przypuszczam, że używasz a, LinearLayoutManageraby wyświetlić listę. Ma fajną metodę o nazwie findViewByPositionto

Znajduje widok, który reprezentuje daną pozycję adaptera.

Wszystko, czego potrzebujesz, to pozycja adaptera interesującego Cię przedmiotu.

edycja : jak zauważył Paul Woitaschek w komentarzach, findViewByPositionjest to metoda, LayoutManagerwięc działałaby ze wszystkimi LayoutManagerami (tj. StaggeredGridLayoutManageritp.)

stan0
źródło
9
W rzeczywistości jest to metoda, LayoutManagerwięc będzie działać dla wszystkich podklas LayoutManager.
Paul Woitaschek
Spojrzałem na kod źródłowy. LinearLayoutManger wyszukuje za pośrednictwem directAccess w tablicy zapasowej, gdy nie jest w trybie prelayout, podczas gdy metoda recyclinglerView zawsze wykonuje traversal. Więc ta metoda jest prawdopodobnie bardziej wydajna.
NameSpace
Hej @stanO, czy możesz mi pomóc z tym stackoverflow.com/questions/49952965/ ...
3
Zwraca: Widok - Widok podrzędny, który reprezentuje daną pozycję lub wartość null, jeśli pozycja nie jest określona ... recyclinglerview -> większość rzeczy zwraca wartość null
me_
Pamiętaj, że musisz poczekać, aż adapter zostanie dodany do listy, zobacz moją odpowiedź poniżej po szczegóły
Konstantin Konopko
241

To jest to, czego szukasz .

Ja też miałem ten problem. I tak jak ty, odpowiedź jest bardzo trudna do znalezienia. Ale jest łatwy sposób na pobranie ViewHolder z określonej pozycji (coś, co prawdopodobnie zrobisz dużo w adapterze).

myRecyclerView.findViewHolderForAdapterPosition(pos);

UWAGA: Jeśli widok został poddany recyklingowi, powróci null. Dziękuję Michaelowi za szybkie złapanie mojego ważnego przeoczenia.

Scott Biggs
źródło
3
To jest dokładnie to, czego szukałem w LayoutManager
Ed Lee,
12
Działa to jednak, jeśli element ViewHolder, do którego próbujesz się odwołać, został „poddany recyklingowi”, zostanie zwrócony null.
Michael,
5
@Michael, dobry chwyt! To ostrzeżenie dla was wszystkich, najpierw przetestujcie pod kątem wartości null (tak, jak wszystko inne w Androidzie)!
Scott Biggs,
1
Czy możemy to zrobić z wnętrza adaptera?
Mauker
2
@Mauker: Możesz tego użyć z dowolnego miejsca, które ma dostęp do instancji RecyclerView.
Scott Biggs
26

Jeśli chcesz View, upewnij się, że masz dostęp do itemViewwłaściwości ViewHolder w następujący sposób:myRecyclerView.findViewHolderForAdapterPosition(pos).itemView;

Horatio
źródło
11

Możesz użyć obu

recyclerViewInstance.findViewHolderForAdapterPosition(adapterPosition) i recyclerViewInstance.findViewHolderForLayoutPosition(layoutPosition). Upewnij się, że widok RecyclerView używa dwóch typów pozycji

Pozycja adaptera: pozycja elementu w adapterze. To jest pozycja z perspektywy Adaptera. Pozycja układu: pozycja elementu w ostatnich obliczeniach układu. To jest pozycja z perspektywy LayoutManagera. Powinieneś używać getAdapterPosition()dla findViewHolderForAdapterPosition(adapterPosition)i getLayoutPosition()dla findViewHolderForLayoutPosition(layoutPosition).

Weź zmienną składową, aby zachować poprzednio wybraną pozycję elementu w adapterze recyclinglerview i inną zmienną składową, aby sprawdzić, czy użytkownik kliknął po raz pierwszy, czy nie.

Przykładowy kod i zrzuty ekranu znajdują się na dole, aby uzyskać więcej informacji.

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

} wybrany pierwszy element Drugi element jest zaznaczony, a poprzednio wybrany element staje się odznaczony wybrany piąty element i poprzednio wybrany element nie jest zaznaczony

Vikash Kumar Tiwari
źródło
I myślałem, że ta dyskusja została wyczerpana! Dziękuję za dalsze wyjaśnienie tej kwestii.
Scott Biggs,
2
Tutaj otrzymuję wyjątek NullPointer w bloku else, gdy próbuję zmienić kolor poprzedniego elementu, więc dodałem sprawdzenie zerowe, teraz nigdy nie wchodzi do tego bloku, jeśli ma wyjątek NullPointer, więc kolor poprzedniego elementu nigdy się nie zmienia. teraz mój widok Recycler zawiera elementy z wieloma kolorowymi elementami. jak mam to rozwiązać
Abhilash Reddy
8

Możesz utworzyć ArrayList of ViewHolder:

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

i wszystkie przechowują ViewHolder na liście, na przykład:

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

        myViewHolders.add(position,holder);
}

i dodaj / usuń inne ViewHolder w ArrayList zgodnie z wymaganiami.

Tarun Umath
źródło
Dziękuję, że walczyłem o zdobycie uchwytów z recyklingu przez około 2 dni.
nimi0112
5

Możesz uzyskać widok dla określonej pozycji w widoku recyklingu, korzystając z poniższego

int position = 2;
RecyclerView.ViewHolder viewHolder = recyclerview.findViewHolderForItemId(position);
View view = viewHolder.itemView;
ImageView imageView = (ImageView)view.findViewById(R.id.imageView);
Niedziela G Akinsete
źródło
5

Możesz po prostu użyć metody " findViewHolderForAdapterPosition " widoku recyklera, a otrzymasz z niego obiekt viewHolder, a następnie typecast tego elementu widoku do elementu widoku adaptera, dzięki czemu uzyskasz bezpośredni dostęp do widoków elementu widoku

poniżej znajduje się przykładowy kod dla kotlin

 val viewHolder = recyclerView.findViewHolderForAdapterPosition(position)
 val textview=(viewHolder as YourViewHolder).yourTextView
Mayank Sharma
źródło
4

Pamiętaj, że musisz poczekać, aż adapter zostanie dodany do listy, wtedy możesz spróbować uzyskać widok według pozycji

final int i = 0;
recyclerView.setAdapter(adapter);
recyclerView.post(new Runnable() {
    @Override
    public void run() {
        View view = recyclerView.getLayoutManager().findViewByPosition(i);
    }
});
Konstantin Konopko
źródło
2
To nie zawsze działa, jeśli dodajesz wiele elementów (np .: jeśli masz przycisk, który je dodaje i szybko go naciśniesz). Powinieneś dodać runnable z opóźnieniem.
Alex Burdusel
3

Aby uzyskać widok z określonej pozycji w RecyclerView, należy wywołać funkcję findViewHolderForAdapterPosition (pozycja) na obiekcie recyclinglerView, który zwróci RecyclerView.ViewHolder

z zwróconego obiektu RecyclerView.ViewHolder z itemView można uzyskać dostęp do wszystkich widoków w tej konkretnej pozycji

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

ImageView iv_wish = rv_view.itemView.findViewById(R.id.iv_item);
tushar
źródło
1

Możesz to również zrobić, to pomoże, gdy chcesz zmodyfikować widok po kliknięciu pozycji widoku recyklingu

@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
źródło
1

Aby uzyskać określony widok z listy widoków recyklera LUB pokaż błąd w edytorze widoku recyklera.

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
źródło