Просмотр пейджера, элементы просмотра recycler удваиваются во фрагменте, если возвращаются из другого действия / фрагмента

#android #android-fragments #android-recyclerview #android-viewpager2

#Android #android-фрагменты #android-recyclerview #android-viewpager2

Вопрос:

У меня есть основное действие, в котором размещены 5 фрагментов с view pager 2 (не сдвинутый) и нижней навигацией. Имя первого фрагмента — home fragment. Фрагмент Home содержит

1 просмотр пейджера: где показаны некоторые фотографии из firebase (используется адаптер пейджера)

2 просмотра в режиме рециркуляции: где некоторые элементы отображаются также из firebase (при нажатии на другое действие)

Проблема: Когда я просматриваю фрагменты с нижней навигацией, это работает нормально. Но когда я перехожу от 5-го фрагмента (самого последнего) к 1-му фрагменту (домашний фрагмент), иногда элементы в view pager, recycler view удваиваются с мерцанием (кажется, что это повторно добавленные элементы). То же самое происходит, когда я перехожу к другому действию и возвращаюсь к нему через некоторое время.

Адаптер просмотра Recycler

 public class CourseListAdapter extends RecyclerView.Adapter<CourseListAdapter.CourseListViewHolder> {

    public static final int HOME_PAGE = 1;
    public static final int DISPLAY_COURSE = 2;

    private Picasso mPicasso;

    private List<DisplayCourse> courseList;

    private static final String TAG = "CourseListAdapter";

    private OnItemClickListener mListener;

    public interface OnItemClickListener {
        void onItemClick(int position, View view);
    }

    private String mSender;

    public void setOnItemClickListener(OnItemClickListener listener) {
        mListener = listener;
    }

    public CourseListAdapter(List<DisplayCourse> courseList, String sender) {
        this.courseList = courseList;
        this.mSender = sender;
        mPicasso = Picasso.get();
    }

    @NonNull
    @Override
    public CourseListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());

        switch (viewType) {
            case 1:
                View view1 = inflater.inflate(R.layout.recom_course_home_layout, parent, false);
                return new CourseListViewHolder(view1, mListener);
            case 2:
                View view2 = inflater.inflate(R.layout.display_course_layout, parent, false);
                return new CourseListViewHolder(view2, mListener);

            default:
                View view3 = inflater.inflate(R.layout.section_video_item_layout, parent, false);
                return new CourseListViewHolder(view3, mListener);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull CourseListViewHolder holder, int position) {

        holder.title.setText(courseList.get(position).getCourseTitle());

        mPicasso.load(courseList.get(position).getThumbnailURL())
                .networkPolicy(NetworkPolicy.OFFLINE)
                .into(holder.thumbnail, new Callback() {
                    @Override
                    public void onSuccess() {

                    }

                    @Override
                    public void onError(Exception e) {
                        mPicasso.load(courseList.get(position).getThumbnailURL())
                                .error(R.drawable.ofklogo)
                                .into(holder.thumbnail, new Callback() {
                                    @Override
                                    public void onSuccess() {

                                    }

                                    @Override
                                    public void onError(Exception e) {

                                    }
                                });
                    }
                });
    }

    @Override
    public int getItemCount() {
        return courseList.size();
    }

    public static class CourseListViewHolder extends RecyclerView.ViewHolder {

        ImageView thumbnail;
        TextView title;

        public CourseListViewHolder(@NonNull View itemView, final OnItemClickListener listener) {
            super(itemView);

            title = itemView.findViewById(R.id.courseTitle);
            thumbnail = itemView.findViewById(R.id.courseThumbNailImageView);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    int position = getAdapterPosition();

                    if (listener != null) {
                        if (position != RecyclerView.NO_POSITION) {
                            listener.onItemClick(position, view);
                        }
                    }
                }
            });
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (mSender.equals("home_page")) {
            return HOME_PAGE;
        } else if (mSender.equals("displayCourse")) {
            return DISPLAY_COURSE;
        }
        return -1;
    }
}
  

В домашнем фрагменте onCreateView() Я использую этот адаптер

 mainActivityViewModel.getRandomCourseLiveData_2().observe(this, new Observer<DataSnapshot>() {
            @Override
            public void onChanged(DataSnapshot dataSnapshot) {
                DisplayCourse course = dataSnapshot.getValue(DisplayCourse.class);
                randomCourse_2.add(course);
                recom_course_2 = new CourseListAdapter(randomCourse_2, "home_page");
                binding.randomCourseRecyclerView2.setAdapter(recom_course_2);

                recom_course_2.setOnItemClickListener(new CourseListAdapter.OnItemClickListener() {
                    @Override
                    public void onItemClick(int position, View view) {
                        Intent intent = new Intent(getActivity(), CourseActivity.class);
                        intent.putExtra("section_name", "Arts");
                        intent.putExtra("course_name", randomCourse_2.get(position).getCourseTitle());
                        intent.putExtra("course_name_english", randomCourse_2.get(position).getCourseTitleEnglish());
                        intent.putExtra("from", "home");
                        startActivity(intent);
                        Log.d(TAG, "onItemClick: "   randomCourse_2.get(position).getCourseTitle());
                        Log.d(TAG, "onItemClick: "   randomCourse_2.get(position).getCourseTitleEnglish());
                    }
                });
            }
        });
  

Комментарии:

1. Пожалуйста, предоставьте свои примеры кода, возможно, RecyclerView. Реализация адаптера и то, как вы с ними работаете.

2. Я добавил код адаптера. пожалуйста, проверьте. Я пытаюсь решить это в течение 2 дней, но безуспешно

3. Похоже, с вашим адаптером все в порядке. Пожалуйста, покажите, где и как вы используете адаптер

4. @Link182 Я использую view model для извлечения данных из firebase, используя запрос firebase live data, подобный этому примеру ссылка

Ответ №1:

Вы создаете и заполняете представление recycler, наблюдая за LiveData объектом. LiveData в некоторых случаях старые события запускаются снова при воссоздании компонента Android, даже если модель представления не воссоздана. Это обычная практика для Google, использующих полурабочие решения, половина библиотек jetpack — блестящее дерьмо.

Чтобы решить эту проблему, вам нужно переключиться на лучшую библиотеку observer, такую как RxJava, или выполнить некоторые обходные пути, например, обернуть полезную нагрузку LiveData в объект, подобный этому https://github.com/Link184/ArchitectureMVVM/blob/master/library/src/main/java/com/link184/architecture/mvvm/common/Event.kt и наблюдать за этим, как здесь https://github.com/Link184/ArchitectureMVVM/blob/master/library/src/main/java/com/link184/architecture/mvvm/base/MvvmContext.kt

 myLiveData.postValue(Event(MyPaload()))

myLiveData.observe(myLifecycleOwner) { event: Event<MyPayload> ->
    event.getContentIfNotHandled()?.let {
        handleEvent(it)
    }
}
  

Другая возможная проблема, возможно, вы случайно запускаете событие дважды, трудно сказать, не видя общей картины.

Также быстрым обходным путем является удаление дубликатов из адаптера, для получения которого вам нужно только изменить свой ArrayList на HashSet

 private Set<DisplayCourse> courseList
  

Просто передайте элементы в конструкторе адаптера как HashSet

Комментарии:

1. спасибо за ваш ответ. не могли бы вы, пожалуйста, подсказать мне или указать направление, где я могу узнать об использовании set в классе адаптера? У меня нет никакого опыта работы с наборами.

2. Я пробовал с наборами. Я получаю данные в виде наборов и отправляю их на адаптер. там я преобразую набор в список массивов, чтобы получить содержимое с указанием позиции. список работает нормально. но он ведет себя так же, как и раньше. Я не понимаю. set хранит только уникальные данные. тогда как это происходит? Я так потерян @link182

3. dataSnapshot.getValue(DisplayCourse.class) возвращает правильный список объектов? Я имею в виду, без дубликатов? Попробуйте использовать debugger, чтобы увидеть, где / когда добавляются дубликаты? Также я вижу, что вы звоните randomCourse_2.add(course) пожалуйста, дважды проверьте, где / как используется адаптер