Obtenha o item clicado e sua posição no RecyclerView


105

Estou substituindo o meu ListViewcom RecyclerView, lista que mostra está bem, mas eu gostaria de saber como obter item clicado e sua posição, semelhante ao método OnItemClickListener.onItemClick(AdapterView parent, View v, int position, long id)que usamos em ListView.

Obrigado pelas ideias!




maneira mais fácil para mim: CustomSelectionCallback
ATES

Respostas:


177

Com base no link: Por que o RecyclerView não tem onItemClickListener ()? e como o RecyclerView é diferente do Listview? , e também a ideia geral de @Duncan, dou minha solução aqui:

  • Defina uma interface RecyclerViewClickListenerpara passar mensagem do adaptador para Activity/ Fragment:

    public interface RecyclerViewClickListener {
        public void recyclerViewListClicked(View v, int position);
    }
  • Em Activity/ Fragmentimplemente a interface e também passe o ouvinte ao adaptador:

    @Override
    public void recyclerViewListClicked(View v, int position){... ...}
    
    //set up adapter and pass clicked listener this
    myAdapter = new MyRecyclerViewAdapter(context, this);
  • Em Adaptere ViewHolder:

    public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ItemViewHolder> {
       ... ... 
       private Context context;
       private static RecyclerViewClickListener itemListener;
    
    
       public MyRecyclerViewAdapter(Context context, RecyclerViewClickListener itemListener) {
           this.context = context;
           this.itemListener = itemListener;
           ... ...
       }
    
    
       //ViewHolder class implement OnClickListener, 
       //set clicklistener to itemView and, 
       //send message back to Activity/Fragment 
       public static class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
           ... ...
           public ItemViewHolder(View convertView) {
               super(convertView);
               ... ...
               convertView.setOnClickListener(this);
           }
    
           @Override
           public void onClick(View v) {
               itemListener.recyclerViewListClicked(v, this.getPosition());     
    
           }
       }
    }

Após o teste, ele funciona bem.


[ ATUALIZAÇÃO ]

Desde a API 22, RecyclerView.ViewHoler.getPosition()está obsoleto, portanto, em vez de getLayoutPosition().


22
use o método getLayoutPosition (), pois getPosition () está obsoleto agora.
Ankur Chaudhary

2
obrigado por este pedaço de código :) apenas uma dica: referencie o estático private static RecyclerViewClickListener itemListener;de uma forma estática no construtor.
David Artmann

1
@khurramengr Acabei adicionando um ouvinte a cada item do método onBindViewHolder. Além disso, o item da minha lista não era totalmente clicável (apenas parte do item).
Ferran Negre

1
Você não deveria usar um WeakReference aqui new MyRecyclerViewAdapter(context, this):? Isso pode causar vazamentos de memória
RamwiseMatt

2
public void recyclerViewListClicked(View v, int position);O modificador publicé redundante para métodos de interface
Joel Raju

35
public class MyRvAdapter extends RecyclerView.Adapter<MyRvAdapter.MyViewHolder>{
    public Context context;
    public ArrayList<RvDataItem> dataItems;

    ...
    constructor
    overrides
    ...

    class MyViewHolder extends RecyclerView.ViewHolder{
        public TextView textView;
        public Context context;

        public MyViewHolder(View itemView, Context context) {
            super(itemView);

            this.context = context;

            this.textView = (TextView)itemView.findViewById(R.id.textView);

            // on item click
            itemView.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v) {
                    // get position
                    int pos = getAdapterPosition();

                    // check if item still exists
                    if(pos != RecyclerView.NO_POSITION){
                        RvDataItem clickedDataItem = dataItems.get(pos);
                        Toast.makeText(v.getContext(), "You clicked " + clickedDataItem.getName(), Toast.LENGTH_SHORT).show();
                    }
                }
            });
        }
    }
}

4
Obrigado. getAdapterPosition () é o que eu precisava.
Ayaz Alifov

1
Obrigado pelo homem do código !! Salvou meu dia. Não tinha certeza de onde colocar getAdapterPosition () e, seguindo seu código, fiz funcionar ... Bom e fácil :)
Vinay Vissh

Muito obrigado cara, seu código era exatamente eu preciso disso! Homem feliz
Naveen Nirban Yadav

13

Aqui está um exemplo para definir um ouvinte de cliques.

Adapter extends  RecyclerView.Adapter<MessageAdapter.MessageViewHolder> {  ...  }

 public static class MessageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        public TextView     tv_msg;
        public TextView     tv_date;
        public TextView     tv_sendTime;
        public ImageView    sharedFile;
        public ProgressBar sendingProgressBar;

        public MessageViewHolder(View v) {
            super(v);
            tv_msg =  (TextView) v.findViewById(R.id.tv_msg);
            tv_date = (TextView)  v.findViewById(R.id.tv_date);
            tv_sendTime = (TextView)  v.findViewById(R.id.tv_sendTime);
            sendingProgressBar = (ProgressBar) v.findViewById(R.id.sendingProgressBar);
            sharedFile = (ImageView) v.findViewById(R.id.sharedFile);

            sharedFile.setOnClickListener(this);

        }

        @Override
        public void onClick(View view) {
        int position  =   getAdapterPosition();


            switch (view.getId()){
                case R.id.sharedFile:


                    Log.w("", "Selected"+position);


                    break;
            }
        }

    }

O que é getAdapterPostion (); aqui?
TheDevMan de

1
a posição da vista clicada. não é um método personalizado.
Gilberto Ibarra

Esta parece ser a maneira mais limpa de fazer isso, especialmente porque você quase sempre está interessado em obter dados usando a posição e pode não ter acesso ao objeto RecyclerView.
FrostRocket

Muito obrigado porint position = getAdapterPosition();
Shylendra Madda

Observe que chamar getAdapterPosition enquanto a lista está sendo atualizada, por exemplo, devido à chamada de notififyXYZ, retornará -1
David

11

Coloque este código onde você define a visualização do reciclador em atividade.

    rv_list.addOnItemTouchListener(
            new RecyclerItemClickListener(activity, new RecyclerItemClickListener.OnItemClickListener() {
                @Override
                public void onItemClick(View v, int position) {

                    Toast.makeText(activity, "" + position, Toast.LENGTH_SHORT).show();
                }
            })
    );

Em seguida, faça uma classe separada e coloque este código:

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {

    private OnItemClickListener mListener;
    public interface OnItemClickListener {
        public void onItemClick(View view, int position);
    }
    GestureDetector mGestureDetector;
    public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
        mListener = listener;
        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }
        });
    }
    @Override
    public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
        View childView = view.findChildViewUnder(e.getX(), e.getY());
        if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
            mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
        }
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
}

7

crie o arquivo java com o código abaixo

public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private OnItemClickListener mListener;

public interface OnItemClickListener {
    public void onItemClick(View view, int position);
}

GestureDetector mGestureDetector;

public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
    mListener = listener;
    mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
        @Override public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }
    });
}

@Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
    View childView = view.findChildViewUnder(e.getX(), e.getY());
    if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
        mListener.onItemClick(childView, view.getChildLayoutPosition(childView));
        return true;
    }
    return false;
}

@Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

}

e apenas use o listener em seu objeto RecyclerView.

recyclerView.addOnItemTouchListener(  
new RecyclerItemClickListener(context, new RecyclerItemClickListener.OnItemClickListener() {
  @Override public void onItemClick(View view, int position) {
    // TODO Handle item click
  }
}));

RecyclerItemClickListenernão é uma classe android padrão, você deve fornecer a biblioteca que está usando ou a própria classe.
Greg

@Greg, obrigado !! eu esqueci de postar!
MazRoid

5

Se você quiser o evento Click de reciclagem-Visualização da atividade / fragmento em vez do adaptador, você também pode usar o seguinte atalho.

recyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                final TextView txtStatusChange = (TextView)v.findViewById(R.id.txt_key_status);
                txtStatusChange.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Log.e(TAG, "hello text " + txtStatusChange.getText().toString() + " TAG " + txtStatusChange.getTag().toString());
                        Util.showToast(CampaignLiveActivity.this,"hello");
                    }
                });
                return false;
            }
        });

Você também pode usar outras formas longas, como usar a interface


,, olá, mas e se tivermos que obter a posição do item clicado também. ?
mfaisalhyder

Você pode apenas definir a posição do item no TAG por meio do adaptador e, em seguida, obter a tag conforme mencionado acima txtStatusChange.getTag (). ToString ()
Amandeep Rohila

@AmandeepRohila Para que o código funcione, primeiro preciso rolar até o final de uma visualização de reciclagem horizontal. Só então a torrada funciona. Não há uma maneira de fazer funcionar sem ter que rolar para frente e para trás primeiro?
iOSAndroidWindowsMobileAppsDev

4
recyclerViewObject.addOnItemTouchListener(
    new RecyclerItemClickListener(
        getContext(),
        recyclerViewObject,
        new RecyclerItemClickListener.OnItemClickListener() {
            @Override public void onItemClick(View view, int position) {
                // view is the clicked view (the one you wanted
                // position is its position in the adapter
            }
            @Override public void onLongItemClick(View view, int position) {
            }
        }
    )
);

Que feitiçaria é essa? Funciona perfeitamente.
Bolling

2

Use o código abaixo: -

public class SergejAdapter extends RecyclerView.Adapter<SergejAdapter.MyViewHolder>{

...

class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{


    @Override
    public void onClick(View v) {
        // here you use position
        int position = getAdapterPosition();
        ...

    }
}
}

1

O RecyclerView não fornece esse método.

Para gerenciar eventos de clique no RecyclerView acabei implementando onClickListener no meu adaptador, ao vincular o ViewHolder: No meu ViewHolder mantenho uma referência para a visão raiz (como você pode fazer com seus ImageViews, TextViews, etc ...) e ao vincular o viewHolder Eu defino uma tag nele com as informações que preciso para lidar com o clique (como posição) e um clicklistener


3
sim, podemos definir o ouvinte de cliques em ViewHolder, mas preciso devolver a visão e a posição do item clicado ao meu fragmento para um processo posterior
Xcihnegn

1

Esta é a maneira mais simples e fácil de encontrar a posição do item clicado:

Eu também enfrentei o mesmo problema.

Eu queria encontrar a posição do item clicado / selecionado do RecyclerView () e realizar algumas operações específicas naquele item em particular.

O método getAdapterPosition () funciona perfeitamente para esse tipo de coisa. Descobri esse método depois de um dia de longa pesquisa e depois de tentar vários outros métodos.

int position = getAdapterPosition();
Toast.makeText(this, "Position is: "+position, Toast.LENGTH_SHORT).show();

Você não precisa usar nenhum método extra. Basta criar uma variável global chamada 'position' e inicializá-la com getAdapterPosition () em qualquer um dos métodos principais do adaptador (classe ou similar).

Aqui está uma breve documentação deste link .

getAdapterPosition adicionado na versão 22.1.0 int getAdapterPosition () Retorna a posição do adaptador do item representado por este ViewHolder. Observe que isso pode ser diferente de getLayoutPosition () se houver atualizações pendentes do adaptador, mas uma nova passagem de layout ainda não aconteceu. O RecyclerView não lida com nenhuma atualização do adaptador até a próxima passagem de layout. Isso pode criar inconsistências temporárias entre o que o usuário vê na tela e o conteúdo do adaptador. Essa inconsistência não é importante, pois será menor que 16 ms, mas pode ser um problema se você quiser usar a posição ViewHolder para acessar o adaptador. Às vezes, pode ser necessário obter a posição exata do adaptador para realizar algumas ações em resposta aos eventos do usuário. Nesse caso, você deve usar este método que calculará a posição do adaptador do ViewHolder.

Feliz por ajudar. Sinta-se à vontade para tirar dúvidas.


qual é a maneira mais simples e fácil de obter valor de um item clicado do RecyclerView?
अक्षय परूळेकर

1

Minha solução simples

Make a position holder:

    public class PositionHolder {

    private int position;

    public PositionHolder(int position) {
        this.position = position;
    }

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }
}

Basta posicionar ou inserir os dados que você precisa obter da atividade.

Construtor do adaptador:

public ItemsAdapter(Context context, List<Item> items, PositionHolder positionHolder){
        this.context = context;
        this.items = items;
        this.positionHolder = positionHolder;
}

Em atividade:

 @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        selectedPosition = 0;

        positionHolder = new PositionHolder(selectedPosition);
        initView();
    }

No adaptador onClickLictener no item
em onBindViewHolder

holder.holderButton.setOnClickListener(v -> {
            positionHolder.setPosition(position);
            notifyDataSetChanged();
        });

Agora, sempre que você muda de posição no RecyclerView, ela é mantida no Holder (ou talvez deva se chamar Listener)

Espero que seja útil

Minha primeira postagem; P


0
//Create below methods into the Activity which contains RecyclerView.


private void createRecyclerView() {

final RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    MyAdapter myAdapter=new MyAdapter(dataAray,MianActivity.this);
    recyclerView.setAdapter(myAdapter);
    recyclerView.setItemAnimator(new DefaultItemAnimator());

    setRecyclerViewClickListner(recyclerView);
}

private void setRecyclerViewClickListner(RecyclerView recyclerView){

    final GestureDetector gestureDetector = new GestureDetector(MainActivity.this,new GestureDetector.SimpleOnGestureListener() {
        @Override public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }
    });


    recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
            View child =recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY());
            if(child!=null && gestureDetector.onTouchEvent(motionEvent)){
               int position=recyclerView.getChildLayoutPosition(child);
                String name=itemArray.get(position).name;

            return true;
        }

        @Override
        public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {

        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean b) {

        }
    });
}

3
Adicione uma explicação, apenas despejando o código sem introdução e uma explicação de como isso corrige o problema não é muito útil.
Mark Rotteveel de

0

Tente desta forma

Classe do adaptador:

 public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {

public interface OnItemClickListener {
    void onItemClick(ContentItem item);
}

private final List<ContentItem> items;
private final OnItemClickListener listener;

public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
    this.items = items;
    this.listener = listener;
}

@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
    return new ViewHolder(v);
}

@Override public void onBindViewHolder(ViewHolder holder, int position) {
    holder.bind(items.get(position), listener);
}

@Override public int getItemCount() {
    return items.size();
}

static class ViewHolder extends RecyclerView.ViewHolder {

    private TextView name;
    private ImageView image;

    public ViewHolder(View itemView) {
        super(itemView);
        name = (TextView) itemView.findViewById(R.id.name);
        image = (ImageView) itemView.findViewById(R.id.image);
    }

    public void bind(final ContentItem item, final OnItemClickListener listener) {
        name.setText(item.name);
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                listener.onItemClick(item);
            }
        });
    }
}

}

Em sua atividade ou fragmento:

ContentAdapter adapter = new ContentAdapter(itemList, this);

Nota: Implemente o OnItemClickListener com base no contexto fornecido por você em atividade ou fragmento e métodos de substituição.


Boa solução acima. Você pode adicionar itens acima para mostrar como o método onItemClick deve se parecer na atividade? Estou tentando passar a posição do item para um Intent (para abrir a próxima Activity) e continuo obtendo a posição errada.
AJW

0

Toda vez que uso outra abordagem. As pessoas parecem armazenar ou obter posição em uma visualização, em vez de armazenar uma referência a um objeto que é exibido por ViewHolder.

Em vez disso, uso essa abordagem e apenas armazeno-a em ViewHolder quando onBindViewHolder () é chamado e defini a referência como null em onViewRecycled ().

Cada vez que o ViewHolder se torna invisível, ele é reciclado. Portanto, isso não afeta no grande consumo de memória.

@Override
public void onBindViewHolder(final ItemViewHolder holder, int position) {
    ...
    holder.displayedItem = adapterItemsList.get(i);
    ...
}

@Override
public void onViewRecycled(ItemViewHolder holder) {
    ...
    holder.displayedItem = null;
    ...
}

class ItemViewHolder extends RecyclerView.ViewHolder {
    ...
    MySuperItemObject displayedItem = null;
    ...
}

0

A extensão curta para o Método Kotlin
retorna a posição absoluta de todos os itens (não a posição apenas de itens visíveis).

fun RecyclerView.getChildPositionAt(x: Float, y: Float): Int {
    return getChildAdapterPosition(findChildViewUnder(x, y))
}

E uso

val position = recyclerView.getChildPositionAt(event.x, event.y)

0

Use getLayoutPosition () em seu método java de interface personalizada. Isso retornará a posição selecionada de um item, verifique todos os detalhes em

https://becody.com/get-clicked-item-and-its-position-in-recyclerview/


1
Bem-vindo ao Stack Overflow! Embora os links sejam uma ótima maneira de compartilhar conhecimento, eles não responderão realmente à pergunta se forem quebrados no futuro. Adicione à sua resposta o conteúdo essencial do link que responde à pergunta. Caso o conteúdo seja muito complexo ou grande para caber aqui, descreva a ideia geral da solução proposta. Lembre-se de manter sempre um link de referência para o site da solução original. Veja: Como escrevo uma boa resposta?
sɐunıɔ ןɐ qɐp

0

No designer, você pode definir a onClickpropriedade do listItem como um método definido com um único parâmetro. Eu tenho um método de exemplo definido abaixo. O método getAdapterPosition fornecerá o índice do listItem selecionado.

public void exampleOnClickMethod(View view){
    myRecyclerView.getChildViewHolder(view).getAdapterPosition());
}

Para obter informações sobre como configurar um RecyclerView, consulte a documentação aqui: https://developer.android.com/guide/topics/ui/layout/recyclerview


0
//simply check if the adapter position you get not less than zero

holder.btnDelItem.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if(holder.getAdapterPosition()>=0){
            list.remove(holder.getAdapterPosition());
            notifyDataSetChanged();
        }
    }
});

3
Ao responder a uma pergunta antiga, sua resposta seria muito mais útil para outros usuários do StackOverflow se você incluísse algum contexto para explicar como sua resposta ajuda, especialmente para uma pergunta que já tem uma resposta aceita. Veja: Como escrevo uma boa resposta .
David Buck

0

Depois de muita tentativa e erro, descobri que há uma maneira muito fácil de fazer isso que é criando uma interface na classe do adaptador e implementando isso no fragmento agora vem a reviravolta, instansiei o modelo de visualização dentro da minha função de substituição presente no fragmento agora você pode enviar os dados dessa função para viemodel e de lá em qualquer lugar.

Não sei se este método é um bom método de codificação, mas por favor, deixe-me saber em um comentário e se alguém quiser ver o deixe-me saber na seção de comentários, eu regularmente abro o fluxo de pilha.


0
recyclerView.addOnItemTouchListener(object : AdapterView.OnItemClickListener,
                RecyclerView.OnItemTouchListener {
                override fun onItemClick(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
                    TODO("Not yet implemented")
                }

                override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
                    TODO("Not yet implemented")
                }

                override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
                    TODO("Not yet implemented")
                }

                override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
                    TODO("Not yet implemented")
                }

            })

Se usar Kotlin


Embora este código possa resolver a questão, incluir uma explicação de como e por que isso resolve o problema realmente ajudaria a melhorar a qualidade da sua postagem e provavelmente resultaria em mais votos positivos. Lembre-se de que você está respondendo às perguntas dos leitores no futuro, não apenas da pessoa que está perguntando agora. Por favor edite sua resposta para adicionar explicações e dar uma indicação do que limitações e premissas se aplicam.
Богдан Опир

-4

Em onViewHolder, defina onClickListiner para qualquer visualização e, no clique lateral, use este código:

Toast.makeText (Drawer_bar.this, "posição" + posição, Toast.LENGTH_SHORT) .show ();

Substitua Drawer_Barpelo nome da sua atividade.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.