RecyclerView - получить представление в определенной позиции

146

У меня есть активность с a RecyclerViewи an ImageView. Я использую, RecyclerViewчтобы показать список изображений по горизонтали. Когда я нажимаю на изображении в в деятельности должна показать более широкую картину изображения. Пока все работает нормально.RecyclerViewImageView

Теперь ImageButtonsв активности есть еще два : imageButton_leftи imageButton_right. Когда я нажимаю на imageButton_left, изображение ImageViewдолжно повернуться влево, а также эскиз в нем RecyclerViewдолжен отражать это изменение. То же самое и с imageButton_right.

Я могу повернуть ImageView. Но как я могу повернуть миниатюру в RecyclerView? Как я могу получить ViewHolder«S ImageView?

Код:

XML активности:

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

Мой код активности:

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

Мой пользовательский адаптер для 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
Вы должны сделать это в своем конструкторе viewHolder, добавить свойство, которое сообщит вам, нужно ли повернуть изображение, затем просто измените это свойство в своих данных и вызовите notifyDataSetChanged, чтобы перезагрузить recyclerView
Nanoc
Решает ли какой-либо из ответов вашу проблему?
SMBiggs 08
ViewHolder должен быть статическим для снижения производительности. Все переменные из внешнего класса должны передаваться через конструктор.
AppiDevo
В качестве дополнения к ответу @ Scott Нажмите здесь
mut tony

Ответы:

155

Я полагаю, вы используете a, LinearLayoutManagerчтобы показать список. Он имеет метод хороший называемый , findViewByPositionчто

Находит вид, который представляет заданное положение адаптера.

Все, что вам нужно, это положение адаптера интересующего вас элемента.

изменить : как отметил Пол Войташек в комментариях, findViewByPositionэто метод, LayoutManagerпоэтому он будет работать со всеми LayoutManager (т.е. StaggeredGridLayoutManagerи т. д.)

stan0
источник
10
На самом деле это метод, LayoutManagerпоэтому он будет работать для всех подклассов LayoutManager.
Пол Войташек 05
Посмотрел исходный код. LinearLayoutManger ищет через directAccess в резервном массиве, когда он не находится в предварительном размещении, тогда как метод recyclerView всегда выполняет обход. Так что этот метод, вероятно, более эффективен.
NameSpace
Привет, @stanO, не могли бы вы помочь мне с этим stackoverflow.com/questions/49952965/…
3
Возвращает: View - Дочернее представление, которое представляет данную позицию или null, если позиция не выложена ... recyclerview -> большинство вещей возвращают null
me_
1
Имейте в виду, что вам нужно подождать, пока адаптер будет добавлен в список, подробности см. В моем ответе ниже
Константин Конопко
252

Это то, что вы ищете .

У меня тоже была эта пробема. И, как и вам, ответ найти очень сложно. Но ЕСТЬ простой способ получить ViewHolder из определенной позиции (то, что вы, вероятно, много будете делать в адаптере).

myRecyclerView.findViewHolderForAdapterPosition(pos);

ПРИМЕЧАНИЕ. Если представление было переработано, оно вернется null. Спасибо Майклу за то, что он быстро уловил мое важное упущение.

SMBiggs
источник
3
Это именно то, что я искал в LayoutManager
Эд Ли
12
Однако это работает, если объект, на который ViewHolderвы пытаетесь сослаться, был «переработан», он вернется null.
Michael
5
@ Майкл, хороший улов! Это предупреждение для всех вас, сначала проверьте значение null (да, как и все остальное в Android)!
SMBiggs
1
Можем ли мы сделать это изнутри адаптера?
Mauker
2
@Mauker: вы можете использовать это из всего, что может получить доступ к экземпляру RecyclerView.
SMBiggs 02
27

Если вы хотите View, убедитесь, что вы itemViewполучили доступ к свойству ViewHolder следующим образом:myRecyclerView.findViewHolderForAdapterPosition(pos).itemView;

Горацио
источник
14

Вы можете использовать оба

recyclerViewInstance.findViewHolderForAdapterPosition(adapterPosition) и recyclerViewInstance.findViewHolderForLayoutPosition(layoutPosition). Убедитесь, что представление RecyclerView использует два типа позиций

Положение адаптера: Положение элемента в адаптере. Это позиция с точки зрения адаптера. Расположение макета: положение элемента при последнем расчете макета. Это позиция с точки зрения LayoutManager. Вы должны использовать getAdapterPosition()для findViewHolderForAdapterPosition(adapterPosition)и getLayoutPosition()для findViewHolderForLayoutPosition(layoutPosition).

Возьмите переменную-член, чтобы удерживать ранее выбранную позицию элемента в адаптере recyclerview, и другую переменную-член, чтобы проверить, щелкает ли пользователь в первый раз или нет.

Образец кода и снимки экрана прилагаются для получения дополнительной информации внизу.

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

} выбран первый элемент второй элемент выбран, и ранее выбранный элемент становится невыделенным выбран пятый элемент, и ранее выбранный элемент становится невыделенным

Викаш Кумар Тивари
источник
И я думал, что это обсуждение исчерпано! Спасибо за дальнейшее разъяснение этого вопроса.
SMBiggs 05
2
Здесь я получаю исключение NullPointer в блоке else, когда я пытаюсь изменить цвет предыдущего элемента, поэтому я добавил нулевую проверку, теперь он никогда не входит в этот блок, если он имеет исключение NullPointer, поэтому цвет предыдущего элемента никогда не меняется. теперь в моем представлении Recycler есть элементы с несколькими цветными элементами. как мне решить эту проблему?
Абхилаш Редди
8

Вы можете сделать ArrayList из ViewHolder:

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

и все хранят ViewHolder (ы) в списке, например:

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

        myViewHolders.add(position,holder);
}

и добавьте / удалите другой ViewHolder в ArrayList в соответствии с вашими требованиями.

Тарун Уматх
источник
Спасибо, что я изо всех сил пытался получить переработанные держатели примерно 2 дня.
nimi0112
7

Вы можете просто использовать метод findViewHolderForAdapterPosition для представления recycler, и вы получите объект viewHolder из него, а затем введите тип этого держателя просмотра в держатель просмотра адаптера, чтобы вы могли напрямую получить доступ к представлениям вашего зрителя.

Ниже приведен пример кода для котлина

 val viewHolder = recyclerView.findViewHolderForAdapterPosition(position)
 val textview=(viewHolder as YourViewHolder).yourTextView
Маянк Шарма
источник
6

Вы можете получить представление для конкретной позиции в recyclerview, используя следующие

int position = 2;
RecyclerView.ViewHolder viewHolder = recyclerview.findViewHolderForItemId(position);
View view = viewHolder.itemView;
ImageView imageView = (ImageView)view.findViewById(R.id.imageView);
Сандей Джи Акинсете
источник
5

Имейте в виду, что вам нужно подождать, пока адаптер будет добавлен в список, после чего вы можете попробовать получить представление по позиции

final int i = 0;
recyclerView.setAdapter(adapter);
recyclerView.post(new Runnable() {
    @Override
    public void run() {
        View view = recyclerView.getLayoutManager().findViewByPosition(i);
    }
});
Константин Конопко
источник
2
Это не всегда работает, если вы добавляете несколько элементов (например, если у вас есть кнопка, которая добавляет их и быстро нажимает). Вы должны добавить runnable с задержкой.
Alex Burdusel 03
4

Чтобы получить представление из определенной позиции recyclerView, вам необходимо вызвать findViewHolderForAdapterPosition (position) для объекта recyclerView, который вернет RecyclerView.ViewHolder

из возвращенного объекта RecyclerView.ViewHolder с помощью itemView вы можете получить доступ ко всем представлениям в этой конкретной позиции

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

ImageView iv_wish = rv_view.itemView.findViewById(R.id.iv_item);
тушар
источник
2

Чтобы получить конкретное представление из списка представлений ресайклера ИЛИ показать ошибку в тексте редактирования представления ресайклера.

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 !");
}
Чираг Праджапати
источник
1

Вы также можете сделать это, это поможет, когда вы захотите изменить представление после нажатия элемента позиции recyclerview.

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

        }
Мут Тони
источник
0

Если вы, ребята, получаете null при каждой попытке получить представление с любой позицией int, попробуйте добавить новый параметр конструктора в свой адаптер, например, так:

class RecyclerViewTableroAdapter(
private val fichas: Array<MFicha?>,
private val activity: View.OnClickListener,
private val indicesGanadores:MutableList<Int>
) : RecyclerView.Adapter<RecyclerViewTableroAdapter.ViewHolder>() {
//CODE 
}

Я добавил indexGanadores, чтобы раскрасить фон моей карты, если моя игра выиграна.

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
//CODE
if(indicesGanadores.contains(position)){
        holder.cardViewFicha.setCardBackgroundColor((activity as MainActivity).resources.getColor(R.color.DarkGreen))
    }
//MORE CODE
}  

Если мне еще не нужно раскрашивать фон, я просто отправляю пустой изменяемый список следующим образом:

binding.recyclerViewMain.adapter = RecyclerViewTableroAdapter(fichasTablero, this@MainActivity, mutableListOf<Int>())

Удачного кодирования! ...

Рамиро GM
источник