NotifyDataSetChanged не работает с RecyclerView
Я получаю данные с сервера, а затем разбираю его и сохраняю в списке. Я использую этот список для адаптера RecyclerView. Я использую фрагменты.
Я использую Nexus 5 с KitKat. Для этого я использую библиотеку поддержки. Будет ли это иметь значение?
Вот мой код: (Использование фиктивных данных для вопроса)
Элементные переменные:
List<Business> mBusinesses = new ArrayList<Business>();
RecyclerView recyclerView;
RecyclerView.LayoutManager mLayoutManager;
BusinessAdapter mBusinessAdapter;
Мой 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;
}
После получения данных с сервера вызывается 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();
}
Мой бизнес-адаптер:
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;
}
}
Но я не получаю данные, отображаемые в представлении. Что я делаю неправильно?
Вот мой журнал,
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
После этого я не получаю никаких журналов. Не следует ли снова вызывать getItemCount()
в адаптере?
Ответы
Ответ 1
В вашем parseResponse()
вы создаете новый экземпляр класса BusinessAdapter
, но вы на самом деле его не используете, поэтому ваш RecyclerView
не знает, что новый экземпляр существует.
Вам либо нужно:
- Вызовите
recyclerView.setAdapter(mBusinessAdapter)
снова, чтобы обновить ссылку адаптера RecyclerView, чтобы указать на новую.
- Или просто удалите
mBusinessAdapter = new BusinessAdapter(mBusinesses);
, чтобы продолжить использование существующего адаптера. Поскольку вы не изменили ссылку mBusinesses
, адаптер все равно будет использовать этот список массивов и должен корректно обновляться при вызове notifyDataSetChanged()
.
Ответ 2
Попробуйте этот метод:
List<Business> mBusinesses2 = mBusinesses;
mBusinesses.clear();
mBusinesses.addAll(mBusinesses2);
//and do the notification
немного времени, но он должен работать.
Ответ 3
У меня была такая же проблема. Я просто решил это с объявлением adapter
public перед onCreate
класса.
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();
}
Пусть это поможет вам.
Ответ 4
Просто чтобы дополнить другие ответы, поскольку я не думаю, что кто-то упомянул об этом здесь: notifyDataSetChanged()
должен выполняться в основном потоке (другие notify<Something>
методы 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();
}
});
}
PS Странно то, что я не думаю, что у вас есть какие-либо указания на предмет основного потока из IDE или журналов времени выполнения. Это только из моих личных наблюдений: если я вызываю notifyDataSetChanged()
из рабочего потока, я не получаю обязательный Только исходный поток, который создал иерархию представлений, может коснуться своего сообщения о представлении или что-то в этом роде - он просто терпит неудачу (и в моем случае один вызов вне основного потока может даже предотвратить правильное выполнение последующих вызовов основного потока, возможно, из-за какого-то состояния гонки)
Кроме того, ссылка RecyclerView.Adapter api или соответствующий официальный dev guide прямо упоминает основное требование к потоку на данный момент (на данный момент - 2017 год), и ни один из правил проверки линз Android Studio, похоже, не касается и этой проблемы.
Но, вот объяснение этого самого автора
Ответ 5
Очистите старую модель просмотра и установите новые данные для адаптера и вызовите notifyDataSetChanged()
Ответ 6
Я просто добавлю к ответу @pradeep kumar. Да, notifyDataSetChanged
действительно не работает без установки новых значений для адаптера. Итак, вы должны:
array = getNewItems();
((MyAdapter) mAdapter).setValues(array); // pass the new list to adapter !!!
mAdapter.notifyDataSetChanged();
Это сработало для меня.