Android notifyDataSetChanged 不适用于 RecyclerView
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24740557/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
notifyDataSetChanged not working on RecyclerView
提问by Vamsi Challa
I am getting data from server and then parsing it and storing it in a List. I am using this list for the RecyclerView's adapter. I am using Fragments.
我从服务器获取数据,然后解析它并将其存储在一个列表中。我将此列表用于 RecyclerView 的适配器。我正在使用片段。
I am using a Nexus 5 with KitKat. I am using support library for this. Will this make a difference?
我正在使用带有 KitKat 的 Nexus 5。我正在为此使用支持库。这会有所作为吗?
Here is my code: (Using dummy data for the question)
这是我的代码:(对问题使用虚拟数据)
Member Variables:
成员变量:
List<Business> mBusinesses = new ArrayList<Business>();
RecyclerView recyclerView;
RecyclerView.LayoutManager mLayoutManager;
BusinessAdapter mBusinessAdapter;
My onCreateView()
:
我的onCreateView()
:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Getting data from server
getBusinessesDataFromServer();
View view = inflater.inflate(R.layout.fragment_business_list,
container, false);
recyclerView = (RecyclerView) view
.findViewById(R.id.business_recycler_view);
recyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
mBusinessAdapter = new BusinessAdapter(mBusinesses);
recyclerView.setAdapter(mBusinessAdapter);
return view;
}
After getting data from server, parseResponse()
is called.
从服务器获取数据后,parseResponse()
调用。
protected void parseResponse(JSONArray response, String url) {
// insert dummy data for demo
mBusinesses.clear();
Business business;
business = new Business();
business.setName("Google");
business.setDescription("Google HeadQuaters");
mBusinesses.add(business);
business = new Business();
business.setName("Yahoo");
business.setDescription("Yahoo HeadQuaters");
mBusinesses.add(business);
business = new Business();
business.setName("Microsoft");
business.setDescription("Microsoft HeadQuaters");
mBusinesses.add(business);
Log.d(Const.DEBUG, "Dummy Data Inserted\nBusinesses Length: "
+ mBusinesses.size());
mBusinessAdapter = new BusinessAdapter(mBusinesses);
mBusinessAdapter.notifyDataSetChanged();
}
My BusinessAdapter:
我的业务适配器:
public class BusinessAdapter extends
RecyclerView.Adapter<BusinessAdapter.ViewHolder> {
private List<Business> mBusinesses = new ArrayList<Business>();
// Provide a reference to the type of views that you are using
// (custom viewholder)
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextViewName;
public TextView mTextViewDescription;
public ImageView mImageViewLogo;
public ViewHolder(View v) {
super(v);
mTextViewName = (TextView) v
.findViewById(R.id.textView_company_name);
mTextViewDescription = (TextView) v
.findViewById(R.id.textView_company_description);
mImageViewLogo = (ImageView) v
.findViewById(R.id.imageView_company_logo);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public BusinessAdapter(List<Business> myBusinesses) {
Log.d(Const.DEBUG, "BusinessAdapter -> constructor");
mBusinesses = myBusinesses;
}
// Create new views (invoked by the layout manager)
@Override
public BusinessAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
Log.d(Const.DEBUG, "BusinessAdapter -> onCreateViewHolder()");
// create a new view
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_business_list, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
Log.d(Const.DEBUG, "BusinessAdapter -> onBindViewHolder()");
Business item = mBusinesses.get(position);
holder.mTextViewName.setText(item.getName());
holder.mTextViewDescription.setText(item.getDescription());
holder.mImageViewLogo.setImageResource(R.drawable.ic_launcher);
}
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
Log.d(Const.DEBUG, "BusinessAdapter -> getItemCount()");
if (mBusinesses != null) {
Log.d(Const.DEBUG, "mBusinesses Count: " + mBusinesses.size());
return mBusinesses.size();
}
return 0;
}
}
But I don't get the data displayed in the view. What am I doing wrong?
但是我没有得到视图中显示的数据。我究竟做错了什么?
Here is my log,
这是我的日志,
07-14 21:15:35.669: D/xxx(2259): Dummy Data Inserted
07-14 21:15:35.669: D/xxx(2259): Businesses Length: 3
07-14 21:26:26.969: D/xxx(2732): BusinessAdapter -> constructor
I don't get any logs after this. Shouldn't getItemCount()
in adapter should be called again?
此后我没有收到任何日志。不应该getItemCount()
在适配器中再次调用吗?
回答by Bryan Herbst
In your parseResponse()
you are creating a new instance of the BusinessAdapter
class, but you aren't actually using it anywhere, so your RecyclerView
doesn't know the new instance exists.
在您parseResponse()
创建类的新实例时BusinessAdapter
,您实际上并未在任何地方使用它,因此您RecyclerView
不知道新实例存在。
You either need to:
您要么需要:
- Call
recyclerView.setAdapter(mBusinessAdapter)
again to update the RecyclerView's adapter reference to point to your new one - Or just remove
mBusinessAdapter = new BusinessAdapter(mBusinesses);
to continue using the existing adapter. Since you haven't changed themBusinesses
reference, the adapter will still use that array list and should update correctly when you callnotifyDataSetChanged()
.
recyclerView.setAdapter(mBusinessAdapter)
再次调用以更新 RecyclerView 的适配器引用以指向新的引用- 或者只是移除
mBusinessAdapter = new BusinessAdapter(mBusinesses);
以继续使用现有的适配器。由于您尚未更改mBusinesses
引用,因此适配器仍将使用该数组列表,并且在您调用notifyDataSetChanged()
.
回答by NiVeR
Try this method:
试试这个方法:
List<Business> mBusinesses2 = mBusinesses;
mBusinesses.clear();
mBusinesses.addAll(mBusinesses2);
//and do the notification
a little time consuming, but it should work.
有点费时,但它应该工作。
回答by Pratik Butani
I had same problem. I just solved it with declaring adapter
public before onCreate
of class.
我有同样的问题。我只是通过adapter
在onCreate
上课前声明public来解决它。
PostAdapter postAdapter;
after that
在那之后
postAdapter = new PostAdapter(getActivity(), posts);
recList.setAdapter(postAdapter);
at last I have called:
最后我打电话给:
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
// Display the size of your ArrayList
Log.i("TAG", "Size : " + posts.size());
progressBar.setVisibility(View.GONE);
postAdapter.notifyDataSetChanged();
}
May this will helps you.
愿这对你有帮助。
回答by Ivan Bartsov
Just to complement the other answers as I don't think anyone mentioned this here: notifyDataSetChanged()
should be executed on the main thread(other notify<Something>
methods of RecyclerView.Adapter
as well, of course)
只是为了补充其他答案,因为我认为这里没有人提到这一点: notifyDataSetChanged()
应该在主线程上执行(当然还有其他notify<Something>
方法RecyclerView.Adapter
)
From what I gather, since you have the parsing procedures and the call to notifyDataSetChanged()
in the same block, either you're calling it from a worker thread, or you're doing JSON parsing on main thread (which is also a no-no as I'm sure you know). So the proper way would be:
从我收集的信息来看,由于您notifyDataSetChanged()
在同一个块中有解析过程和对 我相信你知道)。所以正确的方法是:
protected void parseResponse(JSONArray response, String url) {
// insert dummy data for demo
// <yadda yadda yadda>
mBusinessAdapter = new BusinessAdapter(mBusinesses);
// or just use recyclerView.post() or [Fragment]getView().post()
// instead, but make sure views haven't been destroyed while you were
// parsing
new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
mBusinessAdapter.notifyDataSetChanged();
}
});
}
}
PSWeird thing is, I don't think you get any indications about the main thread thing from either IDE or run-time logs. This is just from my personal observations: if I do call notifyDataSetChanged()
from a worker thread, I don't get the obligatory Only the original thread that created a view hierarchy can touch its viewsmessage or anything like that - it just fails silently (and in my case one off-main-thread call can even prevent succeeding main-thread calls from functioning properly, probably because of some kind of race condition)
PS奇怪的是,我认为您没有从 IDE 或运行时日志中获得有关主线程事物的任何迹象。这只是来自我个人的观察:如果我确实notifyDataSetChanged()
从工作线程调用,我不会得到强制性只有创建视图层次结构的原始线程可以触摸其视图消息或类似的东西 - 它只是默默地失败(并且在在我的情况下,一个非主线程调用甚至可以阻止成功的主线程调用正常运行,可能是因为某种竞争条件)
Moreover, neither the RecyclerView.Adapter api referencenor the relevant official dev guideexplicitly mention the main thread requirement at the moment (the moment is 2017) and none of the Android Studio lint inspection rules seem to concern this issue either.
此外,RecyclerView.Adapter api 参考和相关官方开发指南目前都没有明确提到主线程要求(目前是 2017 年),而且 Android Studio lint 检查规则似乎也没有涉及这个问题。
But, here is an explanationof this by the author himself
回答by akelec
Although it is a bit strange, but the notifyDataSetChanged
does not really work without setting new values to adapter. So, you should do:
虽然有点奇怪,但是notifyDataSetChanged
如果没有为适配器设置新值,它就不会真正起作用。所以,你应该这样做:
array = getNewItems();
((MyAdapter) mAdapter).setValues(array); // pass the new list to adapter !!!
mAdapter.notifyDataSetChanged();
This has worked for me.
这对我有用。
回答by pradeep kumar
Clear your old viewmodel and set the new data to the adapter and call notifyDataSetChanged()
清除旧的视图模型并将新数据设置到适配器并调用 notifyDataSetChanged()