如何更新RecyclerView适配器数据?

时间:2015-07-12 12:08:49

标签: android android-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);
    }
    

    一切都保持不变。

  5. 这是我的适配器:

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

    那么,我如何实际更新适配器数据以显示新收到的项目?

    更新:问题在于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作为父级布局。

14 个答案:

答案 0 :(得分:259)

我正在使用RecyclerView,删除和更新工作都很顺利。

1)删除: 从RecyclerView中删除项目有4个步骤

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

这些代码对我有用。

2)更新数据: 我唯一需要做的就是

mAdapter.notifyDataSetChanged();

您必须在Actvity / Fragment代码中执行所有这些操作,而不是在RecyclerView适配器代码中。

答案 1 :(得分:196)

这是未来访客的一般答案。解释了更新适配器数据的各种方法。该过程每次包括两个主要步骤:

  1. 更新数据集
  2. 通知适配器更改
  3. 插入单个项目

    添加&#34;猪&#34;在索引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());
    

    删除单个项目

    删除&#34; Pig&#34;从列表中。

    Remove single item

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

    删除多个项目

    删除&#34; Camel&#34;和#34;绵羊&#34;从列表中。

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

    用新列表替换旧列表

    清除旧列表,然后添加一个新列表。

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

    adapter引用了data,因此我没有将data设置为新对象非常重要。相反,我清除了data中的旧项目,然后添加了新项目。

    更新单个项目

    改变&#34;绵羊&#34;项目,因此它说&#34;我喜欢绵羊。&#34;

    Update single item

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

    移动单个项目

    移动&#34;绵羊&#34;从位置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

    进一步研究

答案 2 :(得分:40)

这对我有用:

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

创建一个包含更新列表的新适配器(在我的情况下,它是一个转换为ArrayList的数据库)并将其设置为适配器后,我尝试recyclerView.invalidate()并且它有效。

答案 3 :(得分:15)

你有2个选项可以做到这一点: 从适配器刷新UI:

mAdapter.notifyDataSetChanged();

或从recyclerView本身刷新它:

recyclerView.invalidate();

答案 4 :(得分:9)

另一种选择是使用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);

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

如果它是一个很大的列表,我会在主线程中执行calculateDiff工作。

答案 5 :(得分:8)

更新listview,gridview和recyclerview的数据

mAdapter.notifyDataSetChanged();

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

答案 6 :(得分:5)

将新数据添加到当前数据的最佳和最酷的方法是

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

答案 7 :(得分:5)

我以不同的方式解决了同样的问题。 我没有从后台线程等待它的数据,所以从一个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();
        }

就是这样。

答案 8 :(得分:2)

我发现重新加载RecyclerView的一个非常简单的方法就是调用

recyclerView.removeAllViews();

这将首先删除RecyclerView的所有内容,然后使用更新的值再次添加。

答案 9 :(得分:1)

很长一段时间后我得到了答案

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

答案 10 :(得分:1)

如果上述评论中没有提及,则对您有用。这可能意味着问题出在其他地方。

我发现解决方案的一个地方就是我将列表设置为适配器的方式。在我的活动中,列表是一个实例变量,我在更改任何数据时直接更改它。由于它是一个参考变量,有一些奇怪的事情发生。所以我将引用变量更改为本地变量并使用另一个变量来更新数据,然后传递给上面答案中提到的addAll()函数。

答案 11 :(得分:1)

这些方法非常有效,可以开始使用基本的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();
}

您可以在Adapter Class或Fragment或Activity中实现它们,但在这种情况下,您必须实例化Adapter才能调用通知方法。在我的情况下,我通常在适配器中实现它。

答案 12 :(得分:1)

我会使用 Kotlin,适配器。

class ProductAdapter(var apples: List<Apples>?= null) : RecyclerView.Adapter<ProductViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {...}

    override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {...}

    override fun getItemCount(): Int {...}

    fun setData(newApples: List<Apples>) {
        apples = newApples
    }
}

在片段/活动中

val appleAdapter = ProductAdapter()

val recyclerView = binding.appleRecycler // or findViewById or synthetics or whatever. 
recyclerView.adapter = appleAdapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())

现在处理片段中的数据变化

fun updateRecyclerData(apples: List<Apples>){
    adapter.setData(apples)
    adapter.notifyDataSetChanged()
}

updateRecyclerData(applesList)

答案 13 :(得分:0)

我强烈建议您使用[DiffUtil.ItemCallback][1]来处理RecyclerView.Adapter中的更改


 fun setData(data: List<T>) {
        val calculateDiff = DiffUtil.calculateDiff(DiffUtilCallback(items, data))
        items.clear()
        items += data
        calculateDiff.dispatchUpdatesTo(this)
    }

它在后台用AdapterListUpdateCallback处理大多数事情:

/**
 * ListUpdateCallback that dispatches update events to the given adapter.
 *
 * @see DiffUtil.DiffResult#dispatchUpdatesTo(RecyclerView.Adapter)
 */
public final class AdapterListUpdateCallback implements ListUpdateCallback {
    @NonNull
    private final RecyclerView.Adapter mAdapter;

    /**
     * Creates an AdapterListUpdateCallback that will dispatch update events to the given adapter.
     *
     * @param adapter The Adapter to send updates to.
     */
    public AdapterListUpdateCallback(@NonNull RecyclerView.Adapter adapter) {
        mAdapter = adapter;
    }

    /** {@inheritDoc} */
    @Override
    public void onInserted(int position, int count) {
        mAdapter.notifyItemRangeInserted(position, count);
    }

    /** {@inheritDoc} */
    @Override
    public void onRemoved(int position, int count) {
        mAdapter.notifyItemRangeRemoved(position, count);
    }

    /** {@inheritDoc} */
    @Override
    public void onMoved(int fromPosition, int toPosition) {
        mAdapter.notifyItemMoved(fromPosition, toPosition);
    }

    /** {@inheritDoc} */
    @Override
    public void onChanged(int position, int count, Object payload) {
        mAdapter.notifyItemRangeChanged(position, count, payload);
    }
}