RecyclerView - veja a posição em particular

140

Eu tenho uma atividade com a RecyclerViewe an ImageView. Estou usando o RecyclerViewpara mostrar uma lista de imagens horizontalmente. Quando eu clicar em uma imagem na RecyclerViewdo ImageViewna atividade deve mostrar um quadro maior da imagem. Até agora tudo funciona bem.

Agora, existem mais dois ImageButtonsna atividade: imageButton_lefte imageButton_right. Quando clico em imageButton_left, a imagem no ImageViewdeve virar à esquerda e a miniatura no RecyclerViewdeve refletir essa alteração. Semelhante é o caso com imageButton_right.

Eu sou capaz de girar o ImageView. Mas, como posso girar a miniatura no RecyclerView? Como posso obter os ViewHolder's ImageView?

Código:

XML da atividade:

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

Código da minha atividade:

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

Meu 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
fonte
1
Você deve fazê-lo em seu construtor viewHolder, adicionar uma propriedade que informe se a imagem precisa ser rotacionada, basta alterar essa propriedade em seus dados e chamar notifyDataSetChanged para fazer o recyclerView recarregar
Nanoc
Alguma das respostas resolve seu problema?
Scott Biggs
O ViewHolder deve estar estático para problemas de desempenho. Todas as variáveis ​​da classe externa devem ser passadas pelo construtor.
AppiDevo
Como complemento à resposta de @ Scott Clique aqui
mut tony

Respostas:

149

Suponho que você esteja usando a LinearLayoutManagerpara mostrar a lista. Ele tem um bom método chamado findViewByPositionque

Localiza a vista que representa a posição do adaptador fornecida.

Tudo o que você precisa é a posição do adaptador do item em que está interessado.

edit : como observado por Paul Woitaschek nos comentários, findViewByPositioné um método LayoutManagerpara trabalhar com todos os LayoutManagers (por exemplo StaggeredGridLayoutManager, etc.)

stan0
fonte
9
Na verdade, esse é um método LayoutManagerpara que isso funcione para todas as subclasses de LayoutManager.
Paul Woitaschek
Olhou para o código fonte. O LinearLayoutManger pesquisa via directAccess na matriz de apoio quando não está na pré-disposição, enquanto o método recyclerView sempre faz a travessia. Portanto, esse método é provavelmente mais eficiente.
NameSpace
Hey @stanO você pode por favor me ajude com este stackoverflow.com/questions/49952965/...
3
Retorna: Visualização - A vista da criança que representa a posição ou nulo, dado se a posição não é colocado para fora ... recyclerview -> a maioria das coisas retornar nulo
me_
Tenha em mente que você tem que esperar até que adaptador adicionado à lista, ver minha resposta abaixo para mais detalhes
Konstantin Konopko
241

Isto é o que você está procurando .

Eu tive esse problema também. E como você, a resposta é muito difícil de encontrar. Mas existe uma maneira fácil de obter o ViewHolder de uma posição específica (algo que você provavelmente fará muito no adaptador).

myRecyclerView.findViewHolderForAdapterPosition(pos);

NOTA: Se a Vista tiver sido reciclada, isso retornará null. Agradeço a Michael por captar rapidamente minha importante omissão.

Scott Biggs
fonte
3
Isto é exatamente o que eu estava procurando no LayoutManager
Ed Lee
12
Isso funciona, no entanto, se o que ViewHoldervocê está tentando fazer referência foi "reciclado", ele retornará null.
Michael
5
@ Michael, boa captura! Isso é um aviso para todos que estão por aí, teste primeiro o null (sim, como tudo no Android)!
22817 Scott Scott
1
Podemos fazer isso de dentro do adaptador?
Mauker
2
@Mauker: Você pode usar isso de qualquer coisa que possa acessar a instância do RecyclerView.
Scott Biggs
26

Se você quiser View, não deixe de acessar a itemViewpropriedade do ViewHolder da seguinte forma:myRecyclerView.findViewHolderForAdapterPosition(pos).itemView;

Horatio
fonte
11

Você pode usar os dois

recyclerViewInstance.findViewHolderForAdapterPosition(adapterPosition) e recyclerViewInstance.findViewHolderForLayoutPosition(layoutPosition). Certifique-se de que a exibição RecyclerView use dois tipos de posições

Posição do adaptador: posição de um item no adaptador. Esta é a posição da perspectiva do adaptador. Posição do layout: posição de um item no último cálculo de layout. Esta é a posição da perspectiva do LayoutManager. Você deve usar getAdapterPosition()para findViewHolderForAdapterPosition(adapterPosition)egetLayoutPosition() para findViewHolderForLayoutPosition(layoutPosition).

Pegue uma variável de membro para manter a posição do item selecionado anteriormente no adaptador de revisão de reciclagem e outra variável de membro para verificar se o usuário está clicando pela primeira vez ou não.

Código de amostra e capturas de tela estão anexados para obter mais informações na 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();    
                }    
            }    
        });    
    }    
}       

} primeiro elemento selecionado o segundo elemento selecionado e o item selecionado anteriormente ficam desmarcados o quinto elemento selecionado e o item selecionado anteriormente ficam desmarcados

Vikash Kumar Tiwari
fonte
E eu pensei que essa discussão estava esgotada! Obrigado por esclarecer melhor este problema.
Scott Biggs
2
Aqui estou recebendo a exceção NullPointer no bloco else quando estou tentando alterar a cor do item anterior, então adicionei a verificação nula, agora ele nunca está entrando nesse bloco se estiver tendo exceção NullPointer, portanto cor do item anterior nunca é alterado. agora minha visualização do Reciclador possui itens com vários itens coloridos. Como faço para resolver isso
Abhilash Reddy
8

Você pode criar o ArrayList do ViewHolder:

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

e todas as lojas ViewHolder (s) na lista, como:

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

        myViewHolders.add(position,holder);
}

e adicione / remova outro ViewHolder no ArrayList conforme sua exigência.

Tarun Umath
fonte
Obrigado por ter lutado para obter suportes reciclados por dois dias.
Nimi0112
5

Você pode obter uma visão de uma posição específica em uma revisão de reciclagem usando o seguinte

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

Você pode simplesmente usar " findViewHolderForAdapterPosition método " " da visualização do reciclador e você obterá um objeto viewHolder a partir dele e depois converterá esse visualizador no visualizador do adaptador para poder acessar diretamente as visualizações do visualizador.

a seguir está o código de exemplo para o kotlin

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

Lembre-se de que você precisa aguardar até que o adaptador seja adicionado à lista e tentar obter a visualização por posição

final int i = 0;
recyclerView.setAdapter(adapter);
recyclerView.post(new Runnable() {
    @Override
    public void run() {
        View view = recyclerView.getLayoutManager().findViewByPosition(i);
    }
});
Konstantin Konopko
fonte
2
Isso nem sempre funciona se você adicionar vários itens (por exemplo: se você tiver um botão que os adicione e pressione rapidamente). Você deve adicionar o executável com atraso.
21819 Alex Burdusel
3

Para obter uma View de uma posição específica de recyclerView, é necessário chamar findViewHolderForAdapterPosition (position) no objeto recyclerView que retornará RecyclerView.ViewHolder

do objeto RecyclerView.ViewHolder retornado com itemView, você pode acessar todas as vistas nessa posição específica

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

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

Você também pode fazer isso, pois isso ajudará quando você desejar modificar uma exibição depois de clicar em um item de posição de reciclagem.

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

Para obter uma visualização específica da lista de visualizações da recicladora OU mostre um erro no edittext da visualização da recicladora.

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
fonte