ViewPager2 предварительно загружает следующий фрагмент, даже если по умолчанию установлен offsetlimit (0) - PullRequest
0 голосов
/ 16 февраля 2020

У меня довольно странная проблема с viewPager2. Я немного прочитал и обнаружил, что viewPager2 имеет предел смещения по умолчанию 0, который идеально подходит для моего приложения. Я использую его с макетом вкладки, и у меня есть 3 фрагмента (Home, Profile, Notification). Когда загружается действие и загружается первый фрагмент (Home), я вижу в своем logcat, что следующий фрагмент (Profile) загружен не так, как ожидалось. Но когда я нажимаю на вкладку профиля, происходит что-то странное, следующая вкладка (Уведомление) предварительно загружается. Вызваны методы onAttach, onCreate, onCreateView, onActivityCreated, onStart для вкладки «Уведомления». Почему это так и как это исправить? Снимок экрана Logcat Я приложил снимок экрана с моим logcat здесь. Заранее благодарю.

1 Ответ

1 голос
/ 17 февраля 2020

I Предполагается, что вы имеете в виду OFFSCREEN_PAGE_LIMIT_DEFAULT, а не offsetlimit, поскольку вы говорите о проблеме предварительной загрузки фрагментов.

И значение по умолчанию -1 не ноль https://developer.android.com/reference/androidx/viewpager2/widget/ViewPager2.html#OFFSCREEN_PAGE_LIMIT_DEFAULT

и значение по умолчанию означает

Значение, указывающее, что механизм кэширования по умолчанию RecyclerView должен использоваться вместо явной предварительной выборки и сохранения страниц по обе стороны от текущей страницы.

Поскольку это оптимизация производительности просмотра переработчика, я бы сказал, что это не является гарантией того, что он не будет предварительно загружать ваши фрагменты, он просто остается за механизмом кэширования обзора переработчика.

Существует ряд факторов, которые могут повлиять на механизм кэширования в просмотрнике.

Если предзагрузка вашего фрагмента является проблемой, потому что у вас есть динамические c данные, которые вы хотите загружать только когда страница показано, что было бы лучше переместить ваш фрагмент, чтобы использовать метод «отложенной загрузки», т.е. загружать данные только тогда, когда они отображаются.

У меня был Аналогичная проблема с оригинальным viewpager и решена с помощью «ленивой загрузки». Если проблема заключается в том, что время загрузки ваших динамических c данных является проблемой, обновите вопрос, и тогда я смогу наметить возможное решение.

Обновление: 3

It Кажется, что Viewpager2 на самом деле корректно работает с жизненным циклом фрагментов, в отличие от исходного Viewpager, поэтому вы можете вызвать updateView(), как показано в примере update2, из метода Fragments onResume без необходимости использовать обратный вызов pageSelected через адаптер для запуска обновления the View.

Обновление:

Я считаю, что фактическая причина просмотра кода viewpager2 заключается в том, что при выборе вкладки выполняется фальшивое перетаскивание с плавной прокруткой и плавной прокруткой выбранный элемент +1 к кешу, если вы проведете пальцем от Tab0 к Tab1 самостоятельно, это не создаст Tab2

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

Основная идея с оригинальным ViewPager была обновлять представление ТОЛЬКО в том случае, если страница была выбрана с помощью onPageChangeListener, но ViewPager2 вместо нее использует обратный вызов

Поэтому добавьте следующее после создания ViewPager2 (обычно в Activity onCreate)

        viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                super.onPageSelected(position);
                // Tell the recyclerview that position 2 has changed when selected
                // Thus it recreates it updating the dynamic data
                if (position == 2) {
                    // the adapter for the ViewPager needs to a member of the Activity class so accessible here
                    adapter.notifyItemChanged(position);
                }
            }
        });

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

Update2: Более эффективное добавление к Первый метод больше похож на мой оригинальный подход

Это полный рабочий пример, поскольку его легче объяснить.

Основная идея заключается в вашем фрагменте с динамическими данными c, которые вы ТОЛЬКО используете загрузка при отображении означает создание пустого элемента представления «заполнитель», и вы не заполняете его данными во фрагментах onViewCreated, в данном примере это второй textview без текста, но он может быть Вторичное представление с нулевыми объектами или любым другим типом представления.

В своем фрагменте вы затем создаете метод для обновления "заполнителя" с данными (в этом случае метод называется updateView(), который устанавливает текстовый вид текста на текущую дату и время)

Затем в вашем адаптере фрагментов вы сохраняете ссылку на каждый фрагмент, который он создает, в ArrayList (это позволяет получить фрагмент обратно), а затем вы создаете метод updateFragment() в адаптере, который использует позицию, чтобы получить возможность фрагмента вызвать updateView() для него.

Наконец, снова вы используете onPageSelected для вызова updateFragment с позиции, которую вы хотите динамически обновить.

Таким образом, textview1 показывает данные и время создания фракции, а textview2 отображается только на третьей вкладке. и имеет дату и время, когда страница была выбрана. Обратите внимание, что texview1 на «Tab 2» и «Tab 3» - это то же самое время, когда вы нажимаете на «Tab 2» заголовки для изменения вкладок (это проблема в вопросе)

MainActivity. java

package com.test.viewpager2;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;

import android.os.Bundle;

import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;

public class MainActivity extends AppCompatActivity {

    TabLayout tabLayout;
    ViewPager2 viewPager2;
    ViewPager2Adapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        viewPager2 = findViewById(R.id.viewpager2);
        tabLayout = findViewById(R.id.tabLayout);

        viewPager2.setAdapter(createCardAdapter());

        new TabLayoutMediator(tabLayout, viewPager2,
                new TabLayoutMediator.TabConfigurationStrategy() {
                    @Override public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                        tab.setText("Tab " + (position + 1));
                    }
                }).attach();

        viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                super.onPageSelected(position);
                // Tell the recyclerview that position 2 has changed when selected
                // Thus it recreates it updating the dynamic data
                if (position == 2) {
                    adapter.updateFragment(position);
                }
            }
        });
    }

    private ViewPager2Adapter createCardAdapter() {
        adapter = new ViewPager2Adapter(this);
        return adapter;
    }

}

ViewPager2Adapter. java

package com.test.viewpager2;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;

import java.util.ArrayList;

public class ViewPager2Adapter extends FragmentStateAdapter {

    private static final int numOfTabs = 3;
    private ArrayList<Fragment> fragments = new ArrayList<>();

    public ViewPager2Adapter(@NonNull FragmentActivity fragmentActivity) {
        super(fragmentActivity);
    }

    @NonNull @Override public Fragment createFragment(int position){
        Fragment fragment = TextFragment.newInstance(position);
        fragments.add(fragment);
        return fragment;
    }

    @Override
    public int getItemCount(){
        return numOfTabs;
    }

    public void updateFragment(int position){
        Fragment fragment = fragments.get(position);
        // Check fragment type to make sure it is one we know has an updateView Method
        if (fragment instanceof TextFragment){
            TextFragment textFragment = (TextFragment) fragment;
            textFragment.updateView();
        }
    }
}

TextFragment. java

package com.test.viewpager2;

import android.content.Context;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;

public class TextFragment extends Fragment {
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";

    private int mParam1;
    private View view;


    public TextFragment() {
        // Required empty public constructor
    }

    public static TextFragment newInstance(int param1) {
        TextFragment fragment = new TextFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_PARAM1, param1);
        fragment.setArguments(args);
        Log.d("Frag", "newInstance");
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getInt(ARG_PARAM1);
        }
        Log.d("Frag", "onCreate");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        Log.d("Frag", "onCreateView");
        view = inflater.inflate(R.layout.fragment_text, container, false);

        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        Bundle args = getArguments();
        TextView textView1 = view.findViewById(R.id.textview1);
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss.sss", Locale.US);
        String dt = df.format(Calendar.getInstance().getTime());
        textView1.setText(dt);
    }


    public void updateView(){
        Log.d("Frag", "updateView");
        TextView textView2 = view.findViewById(R.id.textview2);
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss.sss", Locale.US);
        String dt = df.format(Calendar.getInstance().getTime());
        textView2.setText(dt);
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        Log.d("Frag", "onAttach");
    }

    @Override
    public void onDetach() {
        super.onDetach();
        Log.d("Frag", "onDetach");
    }
}

activity_main. xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewpager2"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tabLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".TextFragment">

    <TextView
        android:id="@+id/textview1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textview2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/textview1" />/>

</androidx.constraintlayout.widget.ConstraintLayout>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...