Подтвердить что ты не робот

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() в адаптере?

4b9b3361

Ответ 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();       

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