1048 Stimmen

Warum hat RecyclerView keine onItemClickListener()?

Ich habe RecyclerView erkundet und war überrascht zu sehen, dass RecyclerView keine onItemClickListener() hat.

Ich habe zwei Fragen.

Hauptfrage

Ich möchte wissen, warum Google onItemClickListener() entfernt hat?

Gibt es ein Leistungsproblem oder etwas anderes?

Nebenfrage

Ich habe mein Problem gelöst, indem ich onClick in meinem RecyclerView.Adapter geschrieben habe:

public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {

    public TextView txtViewTitle;
    public ImageView imgViewIcon;

    public ViewHolder(View itemLayoutView) {
        super(itemLayoutView);
        txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
        imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
    }

    @Override
    public void onClick(View v) {

    }
}

Ist das okay / gibt es einen besseren Weg?

1246voto

MLProgrammer-CiM Punkte 17123

tl;dr 2016 Verwenden Sie RxJava und ein PublishSubject, um ein Observable für die Klicks freizulegen.

public class ReactiveAdapter extends RecyclerView.Adapter {
    String[] mDataset = { "Daten", "Im", "Adapter" };

    private final PublishSubject onClickSubject = PublishSubject.create();

    @Override 
    public void onBindViewHolder(final ViewHolder holder, int position) {
        final String element = mDataset[position];

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               onClickSubject.onNext(element);
            }
        });
    }

    public Observable getPositionClicks(){
        return onClickSubject.asObservable();
    }
}

Ursprünglicher Post:

Seit der Einführung von ListView war onItemClickListener problematisch. Sobald Sie einen Klick-Listener für eines der internen Elemente hatten, würde der Rückruf nicht ausgelöst, doch dies wurde nicht benachrichtigt oder gut dokumentiert (wenn überhaupt), sodass es viele Verwirrung und SO-Fragen dazu gab.

Aufgrund der Tatsache, dass RecyclerView einen Schritt weiter geht und kein Konzept von Zeile/Spalte hat, sondern eine beliebig angeordnete Menge von Kindern, wurde der Klick an jeden von ihnen delegiert oder zur Programmiererimplementierung.

Das Recyclerview sollte nicht als 1:1-Ersatz für ListView betrachtet werden, sondern eher als ein flexibleres Komponent für komplexe Anwendungsfälle. Und wie Sie sagen, ist Ihre Lösung das, was Google von Ihnen erwartet hat. Jetzt haben Sie einen Adapter, der den Klick an ein über den Konstruktor übergebenes Interface delegieren kann, was das richtige Muster sowohl für ListView als auch für Recyclerview ist.

public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {

    public TextView txtViewTitle;
    public ImageView imgViewIcon;
    public IMyViewHolderClicks mListener;

    public ViewHolder(View itemLayoutView, IMyViewHolderClicks listener) {
        super(itemLayoutView);
        mListener = listener;
        txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
        imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
        imgViewIcon.setOnClickListener(this);
        itemLayoutView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v instanceof ImageView){
           mListener.onTomato((ImageView)v);
        } else {
           mListener.onPotato(v);
        }
    }

    public static interface IMyViewHolderClicks {
        public void onPotato(View caller);
        public void onTomato(ImageView callerImage);
    }

}

und dann in Ihrem Adapter:

public class MyAdapter extends RecyclerView.Adapter {

   String[] mDataset = { "Daten" };

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

       MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() { 
           public void onPotato(View caller) { Log.d("GEMÜSE", "Kartoffeln"); };
           public void onTomato(ImageView callerImage) { Log.d("GEMÜSE", "Tomaten"); }
        });
        return vh;
    }

    // Ersetzen Sie den Inhalt einer Ansicht (aufgerufen vom Layout-Manager) 
    @Override 
    public void onBindViewHolder(ViewHolder holder, int position) {
        // Element aus Ihrem Datensatz an dieser Position erhalten 
        // Ersetzen Sie den Inhalt der Ansicht durch dieses Element 
        // Löschen Sie die, die nicht verwendet werden
        holder.txtViewTitle.setText(mDataset[position]);
    } 

    // Geben Sie die Größe Ihres Datensatzes zurück (aufgerufen vom Layout-Manager) 
    @Override 
    public int getItemCount() { 
        return mDataset.length;
    } 
  ...

Schauen Sie sich nun diesen letzten Codeausschnitt an: onCreateViewHolder(ViewGroup parent, int viewType) die Signatur deutet bereits auf verschiedene Ansichtstypen hin. Für jeden von ihnen benötigen Sie auch einen anderen ViewHolder, und folglich kann jeder von ihnen einen unterschiedlichen Satz von Klicks haben. Oder Sie erstellen einfach einen generischen ViewHolder, der eine beliebige Ansicht und einen onClickListener benötigt und entsprechend anwendet. Oder delegieren Sie eine Ebene höher an den Orchestrierer, damit verschiedene Fragmente/Aktivitäten dieselbe Liste mit unterschiedlichem Klickverhalten haben. Nochmals, alle Flexibilität liegt bei Ihnen.

Es ist eine wirklich benötigte Komponente und ziemlich nah an dem, was unsere internen Implementierungen und Verbesserungen von ListView bis jetzt waren. Es ist gut, dass Google dies endlich anerkennt.

96voto

Abdulaziz Noor Punkte 6271

Ich mag diese Art und benutze sie

Innerhalb

public Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)

Einfügen

View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_image_and_text, parent, false);
v.setOnClickListener(new MyOnClickListener());

Und erstelle diese Klasse überall, wo du sie haben möchtest

class MyOnClickListener implements View.OnClickListener {
    @Override
    public void onClick(View v) {
       int itemPosition = recyclerView.indexOfChild(v);
       Log.e("Geklickt und Position ist ",String.valueOf(itemPosition));
    }
}

Ich habe gelesen, dass es einen besseren Weg gibt, aber ich mag diese Methode, sie ist einfach und nicht kompliziert.

41voto

Guruprasad Punkte 931

Android Recyclerview mit onItemClickListener, Warum wir das nicht versuchen sollten, funktioniert wie ListView nur.

Quelle: Link

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) {

  }
}

Und setzen Sie dies auf RecyclerView:

    recyclerView = (RecyclerView)rootView. findViewById(R.id.recyclerView);
    RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.addOnItemTouchListener(
            new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() {
                @Override
                public void onItemClick(View view, int position) {
                    // TODO Klicken des Elements behandeln
                    Log.e("@@@@@",""+position);
                }
            })
    );

26voto

Shayan_Aryan Punkte 1942

Dank @marmor, habe ich meine Antwort aktualisiert.

Ich denke, es ist eine gute Lösung, das onClick() im ViewHolder-Klassenkonstruktor zu verarbeiten und es über das OnItemClickListener-Interface an die Elternklasse weiterzugeben.

MyAdapter.java

public class MyAdapter extends RecyclerView.Adapter{

private LayoutInflater layoutInflater;
private List items;
private AdapterView.OnItemClickListener onItemClickListener;

public MyAdapter(Context context, AdapterView.OnItemClickListener onItemClickListener, List items) {
    layoutInflater = LayoutInflater.from(context);
    this.items = items;
    this.onItemClickListener = onItemClickListener;
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = layoutInflater.inflate(R.layout.my_row_layout, parent, false);
    return new ViewHolder(view);
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    MyObject item = items.get(position);
}

public MyObject getItem(int position) {
    return items.get(position);
}

class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    private TextView title;
    private ImageView avatar;

    public ViewHolder(View itemView) {
        super(itemView);
        title = itemView.findViewById(R.id.title);
        avatar = itemView.findViewById(R.id.avatar);

        title.setOnClickListener(this);
        avatar.setOnClickListener(this);
        itemView.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        //passing the clicked position to the parent class
        onItemClickListener.onItemClick(null, view, getAdapterPosition(), view.getId());
    }
}
}

Verwendung des Adapters in anderen Klassen:

MyFragment.java

public class MyFragment extends Fragment implements AdapterView.OnItemClickListener {

private RecyclerView recycleview;
private MyAdapter adapter;

    .
    .
    .

private void init(Context context) {
    //diese Fragment als OnItemClickListener an den Adapter übergeben
    adapter = new MyAdapter(context, this, items);
    recycleview.setAdapter(adapter);
}

@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
    //Du kannst das angeklickte Element aus dem Adapter anhand seiner Position erhalten
    MyObject item = adapter.getItem(position);

    //Du kannst auch herausfinden, welches View angeklickt wurde
    switch (view.getId()) {
        case R.id.title:
            //Titelansicht wurde angeklickt
            break;
        case R.id.avatar:
            //Avataransicht wurde angeklickt
            break;
        default:
            //Die gesamte Zeile wurde angeklickt
    }
}

}

21voto

waqas ali Punkte 1228

Leute verwenden diesen Code in Ihrer Hauptaktivität. Sehr effiziente Methode

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.users_list);            
UsersAdapter adapter = new UsersAdapter(users, this);
recyclerView.setAdapter(adapter);
adapter.setOnCardClickListner(this);

Hier ist Ihre Adapterklasse.

public class UsersAdapter extends RecyclerView.Adapter {
        private ArrayList mDataSet;
        OnCardClickListner onCardClickListner;

        public UsersAdapter(ArrayList mDataSet) {
            this.mDataSet = mDataSet;
        }

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

        @Override
        public void onBindViewHolder(UserViewHolder holder, final int position) {
            holder.name_entry.setText(mDataSet.get(position).getUser_name());
            holder.cardView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onCardClickListner.OnCardClicked(v, position);
                }
            });
        }

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

        @Override
        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
            super.onAttachedToRecyclerView(recyclerView);
        }

        public static class UserViewHolder extends RecyclerView.ViewHolder {
            CardView cardView;
            TextView name_entry;

            public UserViewHolder(View itemView) {
                super(itemView);
                cardView = (CardView) itemView.findViewById(R.id.user_layout);
                name_entry = (TextView) itemView.findViewById(R.id.name_entry);
             }
        }

        public interface OnCardClickListner {
            void OnCardClicked(View view, int position);
        }

        public void setOnCardClickListner(OnCardClickListner onCardClickListner) {
            this.onCardClickListner = onCardClickListner;
        }
    }

Nach diesem erhalten Sie diese Überschreibmethode in Ihrer Aktivität.

@Override
    public void OnCardClicked(View view, int position) {
        Log.d("OnClick", "Kartenposition" + position);
    }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X