RecyclerView Bağdaştırıcı Verileri nasıl güncellenir?


275

Güncellemenin RecyclerViewAdaptörüyle ilgili sorunun ne olduğunu anlamaya çalışmak .

Yeni bir Ürün Listesi aldıktan sonra şunları denedim:

  1. Oluşturulan ArrayListparçayı güncelleyin, recyclerViewadaptöre yeni veri ayarlayın, sonra arayın adapter.notifyDataSetChanged(); işe yaramadı.

  2. Diğerleri gibi yeni bir adaptör oluşturun ve onlar için çalıştı, ancak benim için değişiklik yok: recyclerView.setAdapter(new RecyclerViewAdapter(newArrayList))

  3. AdapterVerileri aşağıdaki gibi güncelleyen bir yöntem oluşturun :

    public void updateData(ArrayList<ViewModel> viewModels) {
       items.clear();
       items.addAll(viewModels);
       notifyDataSetChanged();
    }

    Veri listesini güncellemek istediğimde bu yöntemi çağırıyorum; işe yaramadı.

  4. Ben herhangi bir şekilde recyclerView değiştirmek ve ben en azından bir öğeyi kaldırmaya çalıştım kontrol etmek için:

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

    Her şey olduğu gibi kaldı.

İşte benim Adaptör:

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> implements View.OnClickListener {

    private ArrayList<ViewModel> items;
    private OnItemClickListener onItemClickListener;

    public RecyclerViewAdapter(ArrayList<ViewModel> items) {
        this.items = items;
    }


    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

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

    public void updateData(ArrayList<ViewModel> viewModels) {
        items.clear();
        items.addAll(viewModels);
        notifyDataSetChanged();
    }
    public void addItem(int position, ViewModel viewModel) {
        items.add(position, viewModel);
        notifyItemInserted(position);
    }

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



    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        ViewModel item = items.get(position);
        holder.title.setText(item.getTitle());
        Picasso.with(holder.image.getContext()).load(item.getImage()).into(holder.image);
        holder.price.setText(item.getPrice());
        holder.credit.setText(item.getCredit());
        holder.description.setText(item.getDescription());

        holder.itemView.setTag(item);
    }

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

    @Override
    public void onClick(final View v) {
        // Give some time to the ripple to finish the effect
        if (onItemClickListener != null) {
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    onItemClickListener.onItemClick(v, (ViewModel) v.getTag());
                }
            }, 0);
        }
    }

    protected static class ViewHolder extends RecyclerView.ViewHolder {
        public ImageView image;
        public TextView price, credit, title, description;

        public ViewHolder(View itemView) {
            super(itemView);
            image = (ImageView) itemView.findViewById(R.id.image);
            price = (TextView) itemView.findViewById(R.id.price);
            credit = (TextView) itemView.findViewById(R.id.credit);
            title = (TextView) itemView.findViewById(R.id.title);
            description = (TextView) itemView.findViewById(R.id.description);
        }
    }

    public interface OnItemClickListener {

        void onItemClick(View view, ViewModel viewModel);

    }
}

Ve ben RecyclerViewşöyle başlıyorum:

recyclerView = (RecyclerView) view.findViewById(R.id.recycler);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 5));
adapter = new RecyclerViewAdapter(items);
adapter.setOnItemClickListener(this);
recyclerView.setAdapter(adapter);

Peki, yeni alınan öğeleri görüntülemek için adaptör verilerini gerçekten nasıl güncelleyebilirim?


Güncelleme: Sorun, gridView'un göründüğü mizanpajın şöyle olmasıydı:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:tag="catalog_fragment"
    android:layout_height="match_parent">

    <FrameLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="false"/>

        <ImageButton
            android:id="@+id/fab"
            android:layout_gravity="top|end"
            style="@style/FabStyle"/>

    </FrameLayout>
</LinearLayout>

Sonra kaldırdım LinearLayoutve FrameLayoutüst düzen olarak yaptım .


items.clear(); items.addAll(newItems);çirkin bir örüntüdür. Gerçekten savunma kopyalamaya ihtiyacınız varsa, items = new ArrayList(newItems);daha az çirkin olur.
Miha_x64

2
@ miha-x64 Haklısın - daha az çirkin olur. Sorun şu ki, bu işe yaramıyor. Bağdaştırıcı referansa referans almıyor. Referansı kendisi alır. Dolayısıyla, yeni bir veri kümesi oluşturursanız, bağdaştırıcı yalnızca eskisini bilirken yeni bir referansı vardır.
İnanılmaz Ocak

Yanıtlar:


326

RecyclerView ile çalışıyorum ve hem kaldırma hem de güncelleme iyi çalışıyor.

1) KALDIR: Bir öğeyi RecyclerView'dan kaldırmak için 4 adım vardır

list.remove(position);
recycler.removeViewAt(position);
mAdapter.notifyItemRemoved(position);                 
mAdapter.notifyItemRangeChanged(position, list.size());

Bu kodlar benim için çalışıyor.

2) VERİLERİ GÜNCELLEME: Yapmam gereken tek şey

mAdapter.notifyDataSetChanged();

Tüm bunları RecyclerView Adapter kodunda değil Actvity / Fragment kodunda yapmak zorundaydınız.


2
Teşekkür ederim. Lütfen güncellemeyi kontrol edin. Her nasılsa, doğrusal düzen RecylerView güncelleme listesine izin vermez.
Filip Luchianenco

40
sadece şu iki satıra ihtiyacınız vardır: list.remove (position); (pozisyon) mAdapter.notifyItemRemoved;
feisal

3
Benim için çizgiler recycler.removeViewAt(position);(ya da daha doğrusu recycler.removeAllViews()) çok önemliydi.
MSpeed

2
Silme sırasında sinir bozucu aksaklıklardan kaçınmak için önemli bir nokta: Bu satırı recycler.removeViewAt (position) olarak adlandırmanız gerekmez;
Angelo Nodari

7
NotifyDataSetChanged, özellikle ÇOK uzun listelerle uğraşıyorsanız aşırı kiloludur. Birkaç öğe için her şeyi yeniden yüklensin mi? Hayır teşekkürler. Ayrıca, recycler.removeViewAt (konum)? Gerekli değil. Hem "mAdapter.notifyItemRemoved (position)" hem de "mAdapter.notifyItemRangeChanged (position, list.size ())" öğesini çağırmanıza gerek yoktur. Bazılarını veya birini çıkarın. ONCE'ye bildir. Onlardan birini arayın.
Vitor Hugo Schwaab

328

Bu, gelecekteki ziyaretçiler için genel bir cevaptır. Adaptör verilerini güncellemenin çeşitli yolları açıklanmaktadır. Süreç her seferinde iki ana adım içerir:

  1. Veri kümesini güncelleme
  2. Adaptöre değişikliği bildirin

Tek öğe ekle

Dizine "Domuz" ekleyin 2.

Tek öğe ekle

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

Birden çok öğe ekle

Dizine üç hayvan daha ekleyin 2.

Birden çok öğe ekle

ArrayList<String> items = new ArrayList<>();
items.add("Pig");
items.add("Chicken");
items.add("Dog");
int insertIndex = 2;
data.addAll(insertIndex, items);
adapter.notifyItemRangeInserted(insertIndex, items.size());

Tek bir öğeyi kaldır

"Domuz" listeden kaldırın.

Tek bir öğeyi kaldır

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

Birden çok öğeyi kaldırma

"Deve" ve "Koyun" listeden kaldırın.

Birden çok öğeyi kaldırma

int startIndex = 2; // inclusive
int endIndex = 4;   // exclusive
int count = endIndex - startIndex; // 2 items will be removed
data.subList(startIndex, endIndex).clear();
adapter.notifyItemRangeRemoved(startIndex, count);

Tüm öğeleri kaldır

Tüm listeyi temizle.

Tüm öğeleri kaldır

data.clear();
adapter.notifyDataSetChanged();

Eski listeyi yeni listeyle değiştir

Eski listeyi temizleyin ve ardından yeni bir liste ekleyin.

Eski listeyi yeni listeyle değiştir

// clear old list
data.clear();

// add new list
ArrayList<String> newList = new ArrayList<>();
newList.add("Lion");
newList.add("Wolf");
newList.add("Bear");
data.addAll(newList);

// notify adapter
adapter.notifyDataSetChanged();

adapterBir referansı vardır databen set vermedi önemlidir, böylece datayeni bir nesneye. Bunun yerine eski öğeleri temizledim ve datayenilerini ekledim.

Tek bir öğeyi güncelle

"Koyun" öğesini, "Koyunları severim" yazacak şekilde değiştirin.

Tek bir öğeyi güncelle

String newValue = "I like sheep.";
int updateIndex = 3;
data.set(updateIndex, newValue);
adapter.notifyItemChanged(updateIndex);

Tek öğeyi taşı

"Koyun" 3yı bir konumdan başka bir yere taşıyın 1.

Tek öğeyi taşı

int fromPosition = 3;
int toPosition = 1;

// update data array
String item = data.get(fromPosition);
data.remove(fromPosition);
data.add(toPosition, item);

// notify adapter
adapter.notifyItemMoved(fromPosition, toPosition);

kod

İşte referans için proje kodu. RecyclerView Adapter kodunu bu cevapta bulabilirsiniz .

MainActivity.java

public class MainActivity extends AppCompatActivity implements MyRecyclerViewAdapter.ItemClickListener {

    List<String> data;
    MyRecyclerViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // data to populate the RecyclerView with
        data = new ArrayList<>();
        data.add("Horse");
        data.add("Cow");
        data.add("Camel");
        data.add("Sheep");
        data.add("Goat");

        // set up the RecyclerView
        RecyclerView recyclerView = findViewById(R.id.rvAnimals);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
                layoutManager.getOrientation());
        recyclerView.addItemDecoration(dividerItemDecoration);
        adapter = new MyRecyclerViewAdapter(this, data);
        adapter.setClickListener(this);
        recyclerView.setAdapter(adapter);
    }

    @Override
    public void onItemClick(View view, int position) {
        Toast.makeText(this, "You clicked " + adapter.getItem(position) + " on row number " + position, Toast.LENGTH_SHORT).show();
    }

    public void onButtonClick(View view) {
        insertSingleItem();
    }

    private void insertSingleItem() {
        String item = "Pig";
        int insertIndex = 2;
        data.add(insertIndex, item);
        adapter.notifyItemInserted(insertIndex);
    }

    private void insertMultipleItems() {
        ArrayList<String> items = new ArrayList<>();
        items.add("Pig");
        items.add("Chicken");
        items.add("Dog");
        int insertIndex = 2;
        data.addAll(insertIndex, items);
        adapter.notifyItemRangeInserted(insertIndex, items.size());
    }

    private void removeSingleItem() {
        int removeIndex = 2;
        data.remove(removeIndex);
        adapter.notifyItemRemoved(removeIndex);
    }

    private void removeMultipleItems() {
        int startIndex = 2; // inclusive
        int endIndex = 4;   // exclusive
        int count = endIndex - startIndex; // 2 items will be removed
        data.subList(startIndex, endIndex).clear();
        adapter.notifyItemRangeRemoved(startIndex, count);
    }

    private void removeAllItems() {
        data.clear();
        adapter.notifyDataSetChanged();
    }

    private void replaceOldListWithNewList() {
        // clear old list
        data.clear();

        // add new list
        ArrayList<String> newList = new ArrayList<>();
        newList.add("Lion");
        newList.add("Wolf");
        newList.add("Bear");
        data.addAll(newList);

        // notify adapter
        adapter.notifyDataSetChanged();
    }

    private void updateSingleItem() {
        String newValue = "I like sheep.";
        int updateIndex = 3;
        data.set(updateIndex, newValue);
        adapter.notifyItemChanged(updateIndex);
    }

    private void moveSingleItem() {
        int fromPosition = 3;
        int toPosition = 1;

        // update data array
        String item = data.get(fromPosition);
        data.remove(fromPosition);
        data.add(toPosition, item);

        // notify adapter
        adapter.notifyItemMoved(fromPosition, toPosition);
    }
}

notlar

  • Kullanırsanız notifyDataSetChanged(), animasyon gerçekleştirilmez. Bu da pahalı bir işlem olabilir, bu nedenle notifyDataSetChanged()yalnızca tek bir öğeyi veya bir dizi öğeyi güncellüyorsanız kullanmanız önerilmez .
  • Bir listede büyük veya karmaşık değişiklikler yapıyorsanız DiffUtil'e göz atın .

İlerideki çalışma


5
Müthiş cevap! Farklı bir görünüm türüyle sonuçlanan bir öğeyi değiştirirken izlenecek adımlar nelerdir? Sadece notifyItemChanged mi yoksa birleşik kaldırma ve ardından bir ekleme mi?
Semaphor

Minimum örneğinizle, notifyItemChanged yeterli gibi görünüyor. Değiştirilen öğe için onCreateViewHolder ve onBindViewHolder'ı çağırır ve farklı görünüm gösterilir. Bir uygulamada farklı bir görünümle sonuçlanacak bir öğeyi güncellerken sorun yaşadım, ancak başka bir sorun var gibi görünüyor.
Semaphor

@Suragch sorum hakkında herhangi bir fikir: stackoverflow.com/questions/57332222/… Ben bu konuya listelenen her şeyi denedim ama boşuna :(

Değişiklik yapmak için DiffUtils'i ne zaman kullanırız? Manuel olarak çağırmak gibi akıcı değildirler. <Action> () 'a bildirin.
GuilhE

1
Gerçekten top altından düştü Update single item, olması gereken kaplumbağa gibi I
ardnew

46

Benim için işe yarayan buydu:

recyclerView.setAdapter(new RecyclerViewAdapter(newList));
recyclerView.invalidate();

Güncellenmiş listeyi içeren yeni bir bağdaştırıcı oluşturduktan sonra (benim durumumda bir ArrayList'e dönüştürülmüş bir veritabanıydı) ve bunu bağdaştırıcı olarak ayarladıktan sonra denedim recyclerView.invalidate()ve çalıştı.


12
bu yalnızca değişen öğeleri güncellemek yerine tüm görünümü yenilemez mi?
TWilly

13
Her seferinde yeni bir bağdaştırıcı yapmak yerine, yaptığım şey yeni listeyi ayarlamak için özel bağdaştırıcı sınıfımda bir ayarlayıcı yöntemi oluşturmaktı. Bundan sonra, sadece YourAdapter adapter = (YourAdapter) recyclerView.getAdapter(); adapter.yourSetterMethod(newList); adapter.notifyDataSetChanged();Bu söyleniyor, OP'nin ilk önce denediği gibi görünüyor (sadece bunu bir yorum olarak eklemek, böylece benim durumumda çalıştığı gibi başka birine yardımcı olabilir).
Kevin Lee

2
Okuyucu, buna razı olma. Bu cevap işe yarıyor, ancak daha etkili bir yol var. Tüm verileri tekrar yüklemek yerine , en verimli yol adaptöre yeni veri eklemektir.
Sebastialonso

Evet @TWilly ile hemfikirim, kullanıcı arayüzünde tam görünümü yeniden çizecek.
Rahul

18

bunu yapmak için 2 seçeneğiniz vardır: UI'yi adaptörden yenileyin:

mAdapter.notifyDataSetChanged();

veya recyclerView'den yenileyin:

recyclerView.invalidate();

15

Başka bir seçenek diffutil kullanmaktır . Orijinal listeyi yeni listeyle karşılaştırır ve değişiklik varsa yeni listeyi güncelleme olarak kullanır.

Temel olarak, eski verileri yeni verilerle karşılaştırmak ve notifyItemRangeRemoved'i çağırmasına ve sizin adınıza notifyItemRangeChanged ve notifyItemRangeInserted'i çağırmasına izin vermek için DiffUtil'i kullanabiliriz.

NotifyDataSetChanged yerine diffUtil kullanımına hızlı bir örnek:

DiffResult diffResult = DiffUtil
                .calculateDiff(new MyDiffUtilCB(getItems(), items));

//any clear up on memory here and then
diffResult.dispatchUpdatesTo(this);

//and then, if necessary
items.clear()
items.addAll(newItems)

Büyük bir liste olması durumunda ana iş parçacığı calculateDiff iş yapmak.


1
bu doğru olmasına rağmen, bağdaştırıcınızın içindeki verileri nasıl güncelleyebilirim? Bağdaştırıcıda List <Object> görüntülediğimi ve <Object> Listesi ile yeni bir güncelleme aldığımı varsayalım. DiffUtil bir fark hesaplar ve adaptörde gönderir, ancak orijinal veya yeni listeyle hiçbir şey yapmaz (sizin durumunuzda getItems(). Orijinal listeden hangi verilerin kaldırılması / eklenmesi / güncellenmesi gerektiğini nereden biliyorsunuz?
vanomart

1
Belki bu makale yardımcı olabilir. .. medium.com/@nullthemall/…
j2emanue

@vanomart, normalde yaptığınız gibi yalnızca yerel liste alanınızı yeni liste değeriyle doğrudan değiştirmeniz gerekir, bu yaklaşımdaki tek fark, görünümü güncellemek için DiffUtil'i kullanmak yerine notifyDataSetChanged () kullanmaktır.
Nisan 17'de carrizo

benim hakkımda bir fikrin var mı? burada listelenen her şeyi denedim ama şans yok: stackoverflow.com/questions/57332222/…

Cevabınız için teşekkür ederim! Neden dispatchUpdatesTo sonra "açık" & "addAll" koymak gerektiğini açıklayabilir misiniz? Tnx!
Adi Azarya

10

Liste görünümü, ızgara görünümü ve geri dönüşüm görünümü verilerini Güncelle

mAdapter.notifyDataSetChanged();

veya

mAdapter.notifyItemRangeChanged(0, itemList.size());

Bu, kullanıcı kaydırmaya başlayana kadar verilerimi güncellemiyor gibi görünüyor. Her yere baktım ve nedenini anlayamıyorum ..
SudoPlz

@SudoPlz, kaydırmayı algılamak için özel kaydırma dinleyicisini kullanabilir ve stackoverflow.com/a/31174967/4401692
Pankaj Talaviya

Teşekkürler Pankaj ama tam olarak kaçınmaya çalışıyorum. Kullanıcının ekrana dokunmadan her şeyi güncellemek istiyorum.
SudoPlz

5

Mevcut verilere yeni veri eklemenin en iyi ve en havalı yolu

 ArrayList<String> newItems = new ArrayList<String>();
 newItems = getList();
 int oldListItemscount = alcontainerDetails.size();
 alcontainerDetails.addAll(newItems);           
 recyclerview.getAdapter().notifyItemChanged(oldListItemscount+1, al_containerDetails);

2
HERŞEY için "notifyDataSetChanged" kullanan insanlar görüyorum, bu aşırı değil mi? Demek istediğim, birkaç değişiklik olduğu veya eklendiği için tüm listeyi yeniden yükle ... Bu yaklaşımı beğendim, şu anda "notifyItemRangeInserted" yöntemi ile uyguluyordum.
Vitor Hugo Schwaab

5

Aynı sorunu farklı bir şekilde çözdüm. Ben arka plan iş parçacığından onu bekliyorum veri yok bu yüzden bir emty listesi ile başlayın.

        mAdapter = new ModelAdapter(getContext(),new ArrayList<Model>());
   // then when i get data

        mAdapter.update(response.getModelList());
  //  and update is in my adapter

        public void update(ArrayList<Model> modelList){
            adapterModelList.clear(); 
            for (Product model: modelList) {
                adapterModelList.add(model); 
            }
           mAdapter.notifyDataSetChanged();
        }

Bu kadar.


2

Bu yöntemler etkili ve bir temel kullanmaya başlamak için iyidir RecyclerView.

private List<YourItem> items;

public void setItems(List<YourItem> newItems)
{
    clearItems();
    addItems(newItems);
}

public void addItem(YourItem item, int position)
{
    if (position > items.size()) return;

    items.add(item);
    notifyItemInserted(position);
}

public void addMoreItems(List<YourItem> newItems)
{
    int position = items.size() + 1;
    newItems.addAll(newItems);
    notifyItemChanged(position, newItems);
}

public void addItems(List<YourItem> newItems)
{
    items.addAll(newItems);
    notifyDataSetChanged();
}

public void clearItems()
{
    items.clear();
    notifyDataSetChanged();
}

public void addLoader()
{
    items.add(null);
    notifyItemInserted(items.size() - 1);
}

public void removeLoader()
{
    items.remove(items.size() - 1);
    notifyItemRemoved(items.size());
}

public void removeItem(int position)
{
    if (position >= items.size()) return;

    items.remove(position);
    notifyItemRemoved(position);
}

public void swapItems(int positionA, int positionB)
{
    if (positionA > items.size()) return;
    if (positionB > items.size()) return;

    YourItem firstItem = items.get(positionA);

    videoList.set(positionA, items.get(positionB));
    videoList.set(positionB, firstItem);

    notifyDataSetChanged();
}

Bunları bir Bağdaştırıcı Sınıfının içine veya Parça ya da Etkinliğinize uygulayabilirsiniz, ancak bu durumda bildirim yöntemlerini çağırmak için Bağdaştırıcıyı başlatmanız gerekir. Benim durumumda genellikle Adaptörde uygularım.


2

RecyclerView yeniden yüklemek için gerçekten basit bir yol sadece aramak olduğunu öğrendim

recyclerView.removeAllViews();

Bu, önce RecyclerView öğesinin tüm içeriğini kaldıracak ve ardından güncellenmiş değerlerle tekrar ekleyecektir.


2
Lütfen bunu kullanmayın. Bela isteyeceksin.
dell116

1

uzun bir süre sonra cevabı aldım

  SELECTEDROW.add(dt);
                notifyItemInserted(position);
                SELECTEDROW.remove(position);
                notifyItemRemoved(position);

0

Yukarıdaki yorumlarda belirtilen hiçbir şey sizin için işe yaramıyorsa. Bu, sorunun başka bir yerde yattığı anlamına gelebilir.

Çözümü bulduğum bir yer, listeyi adaptöre ayarlama şeklimdi. Etkinliğimde liste bir örnek değişkendi ve herhangi bir veri değiştiğinde doğrudan değiştiriyordum. Referans değişken olması nedeniyle garip bir şey oldu. Bu yüzden referans değişkenini yerel bir değişkenle değiştirdim ve verileri güncellemek için başka bir değişken kullandım ve sonra addAll()yukarıdaki cevaplarda belirtilen fonksiyona geçtim .

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.