2015-07-12 5 views
120

В течение 4 дней я не могу понять, что именно происходит с обновлением адаптера RecyclerView.Как обновить данные адаптера RecyclerView?

После того как я получить новый список продуктов, я попытался:

  1. Обновление ArrayList из фрагмента, где recyclerView создается, набор новых данных в адаптер, а затем вызвать adapter.notifyDataSetChanged(); это не работает.

  2. Создать новый адаптер, так как другие сделали, и он работал на них, но никаких изменений для меня: recyclerView.setAdapter(new RecyclerViewAdapter(newArrayList))

  3. Создать метод в Adapter, который обновляет данные следующим образом:

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

    Затем я вызываю этот метод всякий раз, когда я хочу обновить список данных; это не работает.

  4. Чтобы проверить, могу ли я изменить recyclerView в любом случае, и я попытался удалить, по меньшей мере, элемент:

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

    Все осталось, как было.

Вот мой адаптер:

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

    } 
} 

И я начинаю RecyclerView следующим образом:

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

Итак, как же я на самом деле обновления данных адаптера для отображения вновь полученные детали?


Update: проблема в том, что макет, в котором GridView был выглядел следующим образом:

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

Тогда я только что удалили LinearLayout и сделал FrameLayout в качестве родительского компоновщика.

ответ

170

Я работаю с RecyclerView, и работа по удалению и обновлению хорошо работает.

1) REMOVE: Есть 4 шага, чтобы удалить элемент из RecyclerView

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

Эти строки кодов работает для меня.

2) Обновление данных: Единственное, что я должен был сделать это

mAdapter.notifyDataSetChanged(); 

Вы должны были сделать все это в Actvity/фрагмент кода не в коде RecyclerView адаптер.

+2

спасибо. Проверьте обновление. Так или иначе, линейный макет не позволяет обновлять список RecylerView. – FilipLuch

+12

вам нужны только две строки: list.remove (position); mAdapter.notifyItemRemoved (позиция); – feisal

+1

Для меня важны строки 'recycler.removeViewAt (position);' (или, скорее, 'recycler.removeAllViews()'). –

39

Это то, что работает для меня:

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

После создания нового адаптера, который содержит обновленный список (в моем случае это была база данных преобразуется в ArrayList) и полагая, что в качестве адаптера, я попытался recyclerView.invalidate() и это сработало.

+6

не будет ли это обновлять весь вид вместо того, чтобы просто обновлять измененные предметы? – TWilly

+9

Вместо того, чтобы каждый раз создавать новый адаптер , то, что я сделал, это создать метод setter в моем настраиваемом классе адаптера, чтобы установить новый список. После этого просто позвоните адаптеру yourAdapter adapter = (YourAdapter) recyclerView.getAdapter(); adapter.yourSetterMethod (newList); notifyDataSetChanged(); 'Это, как говорится, похоже на то, что сначала пытался выполнить OP (просто добавив это как комментарий, чтобы он мог помочь кому-то другому, так как это работало в моем случае). –

+2

Читатель, не соглашайтесь на это Этот ответ работает, но есть более эффективный способ. Вместо перезагрузки * все данные снова *, наиболее эффективным способом является jus t добавьте новые данные в адаптер. – Sebastialonso

5

Лучший и самый крутой способ добавить новые данные к настоящему данных

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

Я вижу людей, использующих «notifyDataSetChanged» для КАЖДОГО, разве это не избыток? Я имею в виду, перезагрузите весь список, потому что несколько изменились или были добавлены ... Мне нравится этот подход, который был реализован прямо сейчас, с методом notifyItemRangeInserted. –

13

у вас есть 2 варианта, чтобы сделать это: обновления пользовательского интерфейса от адаптера:

mAdapter.notifyDataSetChanged(); 

или обновления из самого recyclerView:

recyclerView.invalidate(); 
6

Я решил ту же проблему по-другому. У меня нет данных, которых я жду от фонового потока, поэтому начинайте с списка emty.

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

Все.

7

Другой вариант - использовать diffutil. Он будет сравнивать исходный список с новым списком и использовать новый список в качестве обновления, если есть изменения.

В принципе, мы можем использовать DiffUtil для сравнения старых данных с новыми данными и позволить ему вызывать notifyItemRangeRemoved и notifyItemRangeChanged и notifyItemRangeInserted от вашего имени.

Быстрый пример использования diffUtil вместо notifyDataSetChanged:

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

    //any clear up on memory here and then 

     diffResult.dispatchUpdatesTo(this); 

Я делаю calclateDiff отрабатывают основной поток в случае, если это большой список.

+1

хотя это правильно, как обновить данные внутри вашего адаптера? Скажем, я показываю список в адаптере, и я получаю новое обновление со списком . DiffUtil вычислит разницу и отправит ее в адаптер, но ничего не делает с исходным или новым списком (в вашем случае 'getItems()'. Откуда вы знаете, какие данные из исходного списка нужно удалить/добавить/обновить ? – vanomart

+1

Может быть, эта статья может помочь ... https://medium.com/@nullthemall/diffutil-is-a-must-797502bc1149#.9e7v4tsv8 – j2emanue

+0

@vanomart вам нужно только сразу заменить локальное поле списка новым списком как вы обычно это делали, единственная разница в этом подходе заключается в том, что вместо notifyDataSetChanged() вы используете DiffUtil для обновления представления. – carrizo

7

Обновление данных о списке следует, GridView и recyclerview

mAdapter.notifyDataSetChanged(); 

или

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

я получил ответ после того, как долгое время

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

я вижу все эти методы, которые принимает Я нашел путь.

Я просто включил Intent о себе и называется закончить, это гарантирует, что список обновляется.

Intent intent = new Intent(context,thisActivity.class); 
startActivity(intent); 
finish(); 

Done!

+4

Убийство и перезапуск всей вашей деятельности не является хорошим решением. –

1

Если вы не указали в приведенных выше комментариях, ничего не говорится. Это может означать, что проблема лежит где-то в другом месте.

Одно место, где я нашел решение, было так, как я настраивал список адаптера.В моей деятельности список был переменной экземпляра, и я менял его непосредственно, когда какие-либо данные изменялись. Из-за того, что это контрольная переменная, было что-то странное. Поэтому я изменил ссылочную переменную на локальную и использовал другую переменную для обновления данных, а затем передал функцию addAll(), упомянутую в приведенных выше ответах.

0

Эти методы эффективны и полезны для начала использования базового 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(); 
} 

Вы можете реализовать их внутри класса адаптера или в фрагменте или деятельности, но в этом случае вы должны создать экземпляр адаптера для вызова метода уведомления. В моем случае я обычно реализую его в адаптере.

7

Это общий ответ для будущих посетителей. Объясняются различные способы обновления данных адаптера. Процесс включает в себя два основных этапа: каждый раз, когда

  1. Обновление набора данных
  2. Уведомлять адаптер изменения

Вставка одного элемента

Добавить «Pig» по индексу 2.

Insert single item

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

Вставьте несколько элементов

Вставка больше трех животных с индексом 2.

Insert multiple items

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

Удалить одиночный элемент

Удалить "Свинья" из списка.

Remove single item

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

Удалить несколько элементов

Remove "Camel" и "овцы" из списка.

Remove multiple items

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

Удалить все элементы

очистить весь список.

Remove all items

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

Заменить старый список с новым списком

Clear тогда старый список добавить новый.

Replace old list with new list

// 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(); 

The adapter имеет ссылку на data, поэтому очень важно, что я не ставил data на новый объект. Вместо этого я очистил старые предметы от data, а затем добавил новые.

Обновление одного элемента

Изменить «Sheep» вещь так, что он говорит: «Я люблю овец.»

Update single item

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

Перемещение одного элемента

Move "Sheep" из положения 3 в положение 1.

Move single item

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

Код

Вот код проекта для справки. Код адаптера RecyclerView можно найти по адресу this answer.

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

Примечание
  • Если вы используете notifyDataSetChanged(), то никакой анимации не будет. Это также может быть дорогостоящей операцией, поэтому не рекомендуется использовать notifyDataSetChanged(), если вы обновляете только один элемент или целый ряд элементов.
  • Отъезд DiffUtil Если вы делаете большие или сложные изменения в списке.

Дальнейшее изучение

+0

Человек, ваш ответ потрясающий. – Mauker

0

я узнал, что на самом деле простой способ перезагрузить RecyclerView, чтобы просто позвонить

recyclerView.removeAllViews(); 

Это будет сначала удалить все содержимое RecyclerView, а затем добавьте его снова с обновленными значениями.

Смежные вопросы