Android RecyclerÖğe ekleme ve kaldırma


144

Bir TextView metin kutusu ve çapraz düğmesi ImageView ile bir RecyclerView var. Recyclerview dışında çapraz düğme ImageView görünür / gitti yapan bir düğme var.

ImageView basıldığında bu öğeler çapraz düğmesine basıldığında, bir öğe recylerview kaldırmak için arıyorum.

Adaptörüm:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener, View.OnLongClickListener {

    private ArrayList<String> mDataset;
    private static Context sContext;

    public MyAdapter(Context context, ArrayList<String> myDataset) {
        mDataset = myDataset;
        sContext = context;
    }

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

        ViewHolder holder = new ViewHolder(v);
        holder.mNameTextView.setOnClickListener(MyAdapter.this);
        holder.mNameTextView.setOnLongClickListener(MyAdapter.this);

        holder.mNameTextView.setTag(holder);

        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {

        holder.mNameTextView.setText(mDataset.get(position));

    }

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


    @Override
    public void onClick(View view) {
        ViewHolder holder = (ViewHolder) view.getTag();
        if (view.getId() == holder.mNameTextView.getId()) {
            Toast.makeText(sContext, holder.mNameTextView.getText(), Toast.LENGTH_SHORT).show();
        }
    }


    @Override
    public boolean onLongClick(View view) {
        ViewHolder holder = (ViewHolder) view.getTag();
        if (view.getId() == holder.mNameTextView.getId()) {
            mDataset.remove(holder.getPosition());

            notifyDataSetChanged();

            Toast.makeText(sContext, "Item " + holder.mNameTextView.getText() + " has been removed from list",
                    Toast.LENGTH_SHORT).show();
        }
        return false;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mNumberRowTextView;
        public TextView mNameTextView;


        public ViewHolder(View v) {
            super(v);

            mNameTextView = (TextView) v.findViewById(R.id.nameTextView);
        }
    }
}

Düzenim:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:gravity="center_vertical"
    android:id="@+id/layout">

    <TextView
        android:id="@+id/nameTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:padding="5dp"
        android:background="@drawable/greyline"/>

    <ImageView
        android:id="@+id/crossButton"
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:visibility="gone"
        android:layout_marginLeft="50dp"
        android:src="@drawable/cross" />
</LinearLayout>

CrossButton ImageView için çalışan bir onClick gibi bir şeyi nasıl edinebilirim? Daha iyi bir yol var mı? Belki öğenin tamamını değiştirerek öğeyi kaldırmak için tıklayın? Geri dönüşüm görünümünde, düzenlenmesi gereken konumların bir listesi gösterilir. En iyi uygulama ile ilgili herhangi bir teknik tavsiye veya yorum / öneri çok takdir edilecektir.


Bir bak LINK 1 LINK 2 yardımcı olabilir U
SaravanaRaja


1
Görebilirsiniz Github içinde bu örneği Mutlu kodu !!!!
Cabezas

Yanıtlar:


267

Benzer bir şey yaptım. Sizin MyAdapter:

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
    public CardView mCardView;
    public TextView mTextViewTitle;
    public TextView mTextViewContent;
    public ImageView mImageViewContentPic;

    public ImageView imgViewRemoveIcon;
    public ViewHolder(View v) {
        super(v);
        mCardView = (CardView) v.findViewById(R.id.card_view);
        mTextViewTitle = (TextView) v.findViewById(R.id.item_title);
        mTextViewContent = (TextView) v.findViewById(R.id.item_content);
        mImageViewContentPic = (ImageView) v.findViewById(R.id.item_content_pic);
        //......
        imgViewRemoveIcon = (ImageView) v.findViewById(R.id.remove_icon);

        mTextViewContent.setOnClickListener(this);
        imgViewRemoveIcon.setOnClickListener(this);
        v.setOnClickListener(this);
        mTextViewContent.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                if (mItemClickListener != null) {
                    mItemClickListener.onItemClick(view, getPosition());
                }
                return false;
            }
        });
    }


    @Override
    public void onClick(View v) {
        //Log.d("View: ", v.toString());
        //Toast.makeText(v.getContext(), mTextViewTitle.getText() + " position = " + getPosition(), Toast.LENGTH_SHORT).show();
        if(v.equals(imgViewRemoveIcon)){
            removeAt(getPosition());
        }else if (mItemClickListener != null) {
            mItemClickListener.onItemClick(v, getPosition());
        }
    }
}

public void setOnItemClickListener(final OnItemClickListener mItemClickListener) {
    this.mItemClickListener = mItemClickListener;
}
public void removeAt(int position) {
    mDataset.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position, mDataSet.size());
}

Bu yardımcı olur umarım.

Düzenle:

getPosition()artık kullanımdan kaldırıldı, getAdapterPosition()bunun yerine kullanın.


50
Ayrıca notifyItemRangeChanged(getPosition, mDataSet.size());, öğeyi kaldırdıktan sonra kaldırılan öğenin altındaki tüm görünümlerin buna göre ayarlanması için paradite yanıtına eklemek isteyeceksiniz .
Chris Angell

3
aynı sorun var ama getPosition()şimdi kullanımdan kaldırıldı
çip

4
Teşekkürler, görünüşe göre sadece aramak notifyItemRemoved(position)yeterli değildi.
Ektos974

3
@chip getAdapterPosition () kullanın
Tyler Pfaff

2
Bu kod, hem üstten hem de alttan öğeleri hızlı bir şekilde kaldırdığınızda bir sorunu çözmez.
Vito Valov

60

her şeyden önce, öğe listeden kaldırılmalıdır!

  mDataSet.remove(getAdapterPosition());

sonra:

  notifyItemRemoved(getAdapterPosition());
  notifyItemRangeChanged(getAdapterPosition(),mDataSet.size());

2
teşekkür ederim overmuch, varsayalım ben daha fazla değer eklemek zorunda bunun için ne yapabilirim anlamına gelir.
MohanRaj S

adaptörünüzdeki listenin boyutuyla ilgilidir. @ Hammad Nasir
Zahra.HY

Ben de aynıydım, ama geri dönüşüm görüntüleme yüksekliği dinamik olarak değişmedi. mNotes.remove (pozisyon); (pozisyon) notifyItemRemoved; notifyItemRangeChanged (position, getItemCount ()); @ Zahra.HY bir şey kaçırdım.
Hitesh Dhamshaniya

22

eğer hala öğe kaldırılmadıysa bu sihirli yöntemi kullanın :)

private void deleteItem(int position) {
        mDataSet.remove(position);
        notifyItemRemoved(position);
        notifyItemRangeChanged(position, mDataSet.size());
        holder.itemView.setVisibility(View.GONE);
}

Kotlin versiyonu

private fun deleteItem(position: Int) {
    mDataSet.removeAt(position)
    notifyItemRemoved(position)
    notifyItemRangeChanged(position, mDataSet.size)
    holder.itemView.visibility = View.GONE
}

@Mostafa: Diyelim ki 10 öğem var ve sonuncuyu kaldırıyorum, bu senaryoda holder.itemView.setVisibility (View.GONE), Tüm verileri gizle,
Hitesh Dhamshaniya

@HiteshDhamshaniya hayır o sadece gizlemek ve son görünümü gitti
Mostafa Anter

Görünümü gizlemeye gerek yok. dataSet.remove (position) iyi çalışıyor, ancak notifyDatasetChamged () veya notifyItemRemoved (dataset.size () - 1)
7

holder.itemView.setVisibility (View.GONE) için teşekkürler;
K.Hayoev

10

Sorun

RecyclerViewvarsayılan olarak, veri kümesi değişikliklerinizin farkında değildir. Bu, veri listenizde her silme / ekleme yaptığınızda, bu değişikliklerin doğrudan RecyclerView cihazınıza yansıtılmayacağı anlamına gelir.

Çözüm

RecyclerView, veri kümesi durumunuzu paylaşmak için bazı yöntemler sunar. Bunun nedeni, veri kümenizdeki bir değişikliğin görünüm değişikliği gerektirmediği veya tam tersi olduğu için olur. Standart android API'leri, veri kaldırma işlemini ( sorunun amacı için) görüntüleme kaldırma işlemi ile bağlamanızı sağlar .

notifyItemChanged(index: Int)
notifyItemInserted(index: Int)
notifyItemRemoved(index: Int)
notifyItemRangeChanged(startPosition: Int, itemCount: Int)
notifyItemRangeInserted(startPosition: Int, itemCount: Int)
notifyItemRangeRemoved(startPosition: Int, itemCount: Int)

En İyi Yaklaşım

Öğeleri eklediğinizde / kaldırdığınızda (görünümler hakkında konuşurken) ne olacağını düzgün bir şekilde belirtmezseniz, RecyclerView çocukları, farklı görünümleri listenin etrafında nasıl taşıyacağınız konusunda bilgi eksikliği nedeniyle yanıt vermeden canlandırılır.

Bunun yerine, aşağıdaki kod, sadece kaldırılmakta olan çocukta animasyonu tam olarak oynatacaktır (Ve bir yan not olarak, benim IndexOutOfBoundExceptioniçin stacktrace tarafından "veri tutarsızlığı" olarak işaretlenen herhangi bir s düzeltildi )

void remove(position: Int) {
    dataset.removeAt(position)
    notifyItemChanged(position)
    notifyItemRangeRemoved(position, 1)
}

Kaputun altına RecyclerViewbakarsak, geçmemiz gereken ikinci parametrenin , toplam öğe sayısı değil, veri kümesinden kaldırılan öğe sayısı olduğunu açıklayan belgeler bulabiliriz (Diğer bazı bilgi kaynaklarında yanlış belirtildiği gibi).

    /**
     * Notify any registered observers that the <code>itemCount</code> items previously
     * located at <code>positionStart</code> have been removed from the data set. The items
     * previously located at and after <code>positionStart + itemCount</code> may now be found
     * at <code>oldPosition - itemCount</code>.
     *
     * <p>This is a structural change event. Representations of other existing items in the data
     * set are still considered up to date and will not be rebound, though their positions
     * may be altered.</p>
     *
     * @param positionStart Previous position of the first item that was removed
     * @param itemCount Number of items removed from the data set
     */
    public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
        mObservable.notifyItemRangeRemoved(positionStart, itemCount);
    }

7

Muhtemelen yinelenen bir cevap ama benim için oldukça yararlı. Aşağıda verilen yöntemi uygulayabilir ve RecyclerView.Adapter<RecyclerView.ViewHolder> gereksinimlerinize göre bu yöntemi kullanabilirsiniz, umarım sizin için çalışır

public void removeItem(@NonNull Object object) {
        mDataSetList.remove(object);
        notifyDataSetChanged();
    }

5

İşte bazı görsel ek örnekler. Bkz benim dolgun cevabı eklenmesi ve bir dizi çıkarma örnekleri için.

Tek öğe ekle

Dizine "Domuz" ekleyin 2.

Tek öğe ekle

String item = "Pig";
int insertIndex = 2;
data.add(insertIndex, item);
adapter.notifyItemInserted(insertIndex);

Tek bir öğeyi kaldır

"Domuz" listeden kaldırın.

Tek bir öğeyi kaldır

int removeIndex = 2;
data.remove(removeIndex);
adapter.notifyItemRemoved(removeIndex);

4

Yukarıdaki tüm cevapları denedim, ancak geri dönüşüm görünümüne öğe ekleme veya kaldırma dataSet konumdaki soruna neden olur. delete(getAdapterPosition());Öğelerin konumunu bulmak için harika çalışan viewHolder içinde sona erdi .


1
Sadece bir dinleyici ayarlayın ve görünüm tutucu içinde getAdapterPosition () yöntemini kullanın. Öğenin konumunu döndürür.
Arun

4

Sahip olduğum sorun, bağdaştırıcınızda böyle bir yöntem uygulayabildiğinizden emin olmak için doğru bağdaştırıcıyı değiştirdiğinizden emin olmak için artık bağdaştırıcıyla ilişkili olmayan bir öğeyi listeden kaldırıyordu:

public void removeItemAtPosition(int position) {
    items.remove(position);
}

Ve bunu fragmanınızda veya aktivitenizde şöyle söyleyin:

adapter.removeItemAtPosition(position);

2
  public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private List<cardview_widgets> list;

public MyAdapter(Context context, List<cardview_widgets> list) {
    this.context = context;
    this.list = list;
}

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
    View view = LayoutInflater.from(this.context).inflate(R.layout.fragment1_one_item,
            viewGroup, false);
    return new MyViewHolder(view);
}

public static class MyViewHolder extends RecyclerView.ViewHolder {
    TextView txt_value;
    TextView txt_category;
    ImageView img_inorex;
    ImageView img_category;
    TextView txt_date;

    public MyViewHolder(@NonNull View itemView) {
        super(itemView);
        txt_value = itemView.findViewById(R.id.id_values);
        txt_category = itemView.findViewById(R.id.id_category);
        img_inorex = itemView.findViewById(R.id.id_inorex);
        img_category = itemView.findViewById(R.id.id_imgcategory);
        txt_date = itemView.findViewById(R.id.id_date);
    }
}

@NonNull
@Override
public void onBindViewHolder(@NonNull final MyViewHolder myViewHolder, int i) {

    myViewHolder.txt_value.setText(String.valueOf(list.get(i).getValuee()));
    myViewHolder.txt_category.setText(list.get(i).getCategory());
    myViewHolder.img_inorex.setBackgroundColor(list.get(i).getImg_inorex());
    myViewHolder.img_category.setImageResource(list.get(i).getImg_category());
    myViewHolder.txt_date.setText(list.get(i).getDate());
    myViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            list.remove(myViewHolder.getAdapterPosition());
            notifyDataSetChanged();
            return false;
        }
    });
}

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

Umarım bu sana yardımcı olur.


1

öğeyi kaldırmak istiyorsanız bunu yapmalısınız: önce öğeyi kaldır:

phones.remove(position);

sonraki adımda, geri dönüşüm adaptörünüze bu kodu kullanarak bir öğeyi kaldırdığınızı bildirmelisiniz:

notifyItemRemoved(position);
notifyItemRangeChanged(position, phones.size());

ancak bir öğeyi değiştirirseniz bunu yapın: önce nesnenizin parametresini şu şekilde değiştirin:

Service s = services.get(position);
s.done = "Cancel service";
services.set(position,s);

ya da böyle yeni:

Service s = new Service();
services.set(position,s);

ardından geri dönüşüm adaptörünüze bu kodu kullanarak bir öğeyi değiştirdiğinizi bildirin:

notifyItemChanged(position);
notifyItemRangeChanged(position, services.size());

umut size yardımcı olur.


1
  String str = arrayList.get(position);
  arrayList.remove(str);
  MyAdapter.this.notifyDataSetChanged();

Bu gerçekten kötü bir fikir, çünkü öğelerin tam olarak yeniden çekilmesini zorlayacaksınız, çok fazla öğeniz varsa muhtemelen göze çarpan gecikme. Yapılacak doğru şey aramaktır notifyItemRemoved(position).
Minas Mina

ya senin doğru biz de notifyItemRemoved (pozisyon) instaify notifyDataSetChanged ()
Tarun Umath

1

Yöntem onBindViewHolderBu Kodu Yazmak İçin

holder.remove.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Cursor del=dbAdapter.ExecuteQ("delete from TblItem where Id="+values.get(position).getId());
                values.remove(position);
                notifyDataSetChanged();
            }
        });

1

Incase Herkes böyle bir şey bağdaştırıcı sınıfı yerine ana sınıfta uygulamak istiyor, kullanabilirsiniz:

public void removeAt(int position) {
    peopleListUser.remove(position);

    friendsListRecycler.getAdapter().notifyItemRemoved(position);
    friendsListRecycler.getAdapter().notifyItemRangeChanged(position, peopleListUser.size());
}

Burada friendsListRecycler Bağdaştırıcı adıdır


0
 //////// set the position
 holder.cancel.setTag(position);


///// click to remove an item from recycler view and an array list
holder.cancel.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {

            int positionToRemove = (int)view.getTag(); //get the position of the view to delete stored in the tag
            mDataset.remove(positionToRemove);
            notifyDataSetChanged();
                }
            });

0

özel adaptör sınıfına arayüz oluşturma ve geri dönüşüm görünümünde click olayı işleme ..

 onItemClickListner onItemClickListner;

public void setOnItemClickListner(CommentsAdapter.onItemClickListner onItemClickListner) {
    this.onItemClickListner = onItemClickListner;
}

public interface onItemClickListner {
    void onClick(Contact contact);//pass your object types.
}
    @Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
    // below code handle click event on recycler view item.
    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            onItemClickListner.onClick(mContectList.get(position));
        }
    });
}

sonra adaptörü tanımlayın ve aşağıdaki kod denilen geri dönüşüm görünümüne bağlayın ..

        adapter.setOnItemClickListner(new CommentsAdapter.onItemClickListner() {
        @Override
        public void onClick(Contact contact) {
            contectList.remove(contectList.get(contectList.indexOf(contact)));
            adapter.notifyDataSetChanged();
        }
    });
}

0

Yaptığım gibi merak ediyorsanız getadapterposition () yönteminde adaptör konumunu nereden bulabiliriz; izleyici nesnesinde. yani kodunuzu böyle koymanız gerekiyor

mdataset.remove(holder.getadapterposition());

0

Faaliyette:

mAdapter.updateAt(pos, text, completed);
mAdapter.removeAt(pos);

Adaptörünüzde:

void removeAt(int position) {
    list.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position, list.size());
}

void updateAt(int position, String text, Boolean completed) {
    TodoEntity todoEntity = list.get(position);
    todoEntity.setText(text);
    todoEntity.setCompleted(completed);
    notifyItemChanged(position);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.