Android notifyDataSetChanged不在RecyclerView上工作
我从服务器获取数据,然后对其进行解析并将其存储在列表中。我将此列表用于RecyclerView的适配器。我用的是碎片 我正在使用Nexus5和KitKat。我正在为此使用支持库。这会有所不同吗 以下是我的代码:(使用问题的虚拟数据) 成员变量:Android notifyDataSetChanged不在RecyclerView上工作,android,android-5.0-lollipop,android-recyclerview,Android,Android 5.0 Lollipop,Android Recyclerview,我从服务器获取数据,然后对其进行解析并将其存储在列表中。我将此列表用于RecyclerView的适配器。我用的是碎片 我正在使用Nexus5和KitKat。我正在为此使用支持库。这会有所不同吗 以下是我的代码:(使用问题的虚拟数据) 成员变量: List<Business> mBusinesses = new ArrayList<Business>(); RecyclerView recyclerView; RecyclerView.LayoutManager mLay
List<Business> mBusinesses = new ArrayList<Business>();
RecyclerView recyclerView;
RecyclerView.LayoutManager mLayoutManager;
BusinessAdapter mBusinessAdapter;
从服务器获取数据后,将调用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();
}
我的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;
}
}
在这之后我没有任何日志。适配器中的
getItemCount()
是否应该再次调用?在parseResponse()
中,您正在创建BusinessAdapter
类的一个新实例,但实际上您并没有在任何地方使用它,因此您的RecyclerView
不知道新实例存在
您需要:
- 再次调用
更新recyclerView的适配器引用,以指向您的新适配器recyclerView.setAdapter(mBusinessAdapter)
- 或者只需删除
继续使用现有适配器。由于您尚未更改mBusinessAdapter=newbusinessadapter(mbusiness)
引用,因此适配器仍将使用该数组列表,并且在调用mbusiness
时应正确更新notifyDataSetChanged()
List<Business> mBusinesses2 = mBusinesses;
mBusinesses.clear();
mBusinesses.addAll(mBusinesses2);
//and do the notification
List mBusinesses2=mBusinesses;
mbusiness.clear();
mbusiness.addAll(mBusinesses2);
//然后做通知
有点费时,但应该可以用。我也有同样的问题。我只是在类的
onCreate
之前声明adapter
public来解决这个问题
PostAdapter postAdapter;
之后
postAdapter = new PostAdapter(getActivity(), posts);
recList.setAdapter(postAdapter);
最后我打电话给:
@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();
}
这可能会对您有所帮助。只是为了补充其他答案,因为我认为这里没有人提到过:
notifydatasetchange()
应该在主线程上执行notify
当然还有RecyclerView.Adapter
的其他方法)
从我收集的信息来看,由于解析过程和对notifyDataSetChanged()
的调用都在同一个块中,所以要么是从工作线程调用它,要么是在主线程上进行JSON解析(我相信您也知道这也是一个否定的问题)。因此,正确的方法是:
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();
}
});
protectedvoidparseresponse(JSONArray响应,字符串url){
//为演示插入虚拟数据
//
mBusinessAdapter=新业务适配器(mbusiness);
//或者只需使用recyclerView.post()或[Fragment]getView().post()即可
//取而代之的是,但要确保视图在运行时没有被破坏
//解析
新的处理程序(Looper.getMainLooper()).post(新的Runnable()){
公开募捐{
mBusinessAdapter.notifyDataSetChanged();
}
});
}
PS奇怪的是,我不认为您从IDE或运行时日志中得到任何关于主线程的指示。这仅仅是我个人的观察结果:如果我从工作线程调用notifyDataSetChanged()
,我不会得到必须的消息,只有创建视图层次结构的原始线程可以接触其视图消息或类似的消息-它只是默默地失败(在我的例子中,一次主线程调用甚至会阻止后续的主线程调用正常运行,可能是因为某种竞争条件)
此外,目前(现在是2017年),无论是官方还是相关官员都没有明确提到主线程要求,Android Studio lint检查规则似乎也没有涉及这一问题
但是,这是作者自己写的清除旧的viewmodel并将新数据设置到适配器,然后调用notifyDataSetChanged()
虽然有点奇怪,但是如果不为适配器设置新值,notifyDataSetChanged
就不能真正工作。因此,您应该:
array = getNewItems();
((MyAdapter) mAdapter).setValues(array); // pass the new list to adapter !!!
mAdapter.notifyDataSetChanged();
这对我很有用。在我的情况下,在主ui线程中强制运行#notifyDataSetChanged将修复此问题
public void refresh() {
clearSelection();
// notifyDataSetChanged must run in main ui thread, if run in not ui thread, it will not update until manually scroll recyclerview
((Activity) ctx).runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
}
});
}
感谢您的帮助!!!明白了..刚刚删除了再次创建新实例的行。将在8分钟内接受您的答案。这对我来说不起作用,我必须重新创建适配器并将其重新设置为recylerviewI。我遵循您的第二点,使用新的BusinessAdapter(MBusiness)再次创建适配器。我必须以任何方式释放旧适配器吗?如果我每次只创建一个新适配器,这会导致内存泄漏吗?只要您不静态引用任何内容或以其他方式保留引用,就可以释放旧引用。只需使用int index查找您在列表中添加或删除数据的位置=list.indexOf(object)
并且根据该索引,您可以使用adapter.notifyItemChanged(index)通知数据
这句话:List mbusinesse2=mBusinesses;并没有对列表进行深度复制。所以你不是在克隆列表——这是一件事,第二件事,我不知道这如何解决原来的问题……事实上,我也遇到了类似的问题,并以这种方式解决了它:@kosiara BartoszKosarzycki,但它确实做了,非常感谢我尝试过的东西,你的完美无瑕。@Maximus很高兴我能帮上忙,matedamn!notifyDataset更改侦听器不需要从主线程调用吗,完美,+1
array = getNewItems();
((MyAdapter) mAdapter).setValues(array); // pass the new list to adapter !!!
mAdapter.notifyDataSetChanged();
public void refresh() {
clearSelection();
// notifyDataSetChanged must run in main ui thread, if run in not ui thread, it will not update until manually scroll recyclerview
((Activity) ctx).runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
}
});
}