1

How to destroy retain Fragments or unused Fragments(Fragment with number 1-4) in ViewPager2 when right swap. I want only retain one Fragment in left of visible Fragment (Fragment 5)?

private static class SettingAdapter extends FragmentStateAdapter {

    public SettingAdapter(@NonNull Fragment fragment) {
        super(fragment);
    }
    
    public void destroyUnusedFragment(){
        // ?
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        Log.i("xxx", "createFragment: " + position);
        switch (position) {
            default:
            case 0:
                return new Fragment1();
            case 1:
                return new Fragment2();
            case 2:
                return new Fragment3();
            case 3:
                return new Fragment4();
            case 4:
                return new Fragment5();
            case 5:
                return new DispenserFragment();
            case 6:
                return new Fragment7();
            case 7:
                return new Fragment8();
            case 8:
                return new Fragment9();
            case 9:
                return new Fragment10();
            case 10:
                return new Fragment11();
            case 11:
                return new Fragment12();
            case 12:
                return new Fragment13();
            case 13:
                return new Fragment14();
            case 14:
                return new Fragment15();
        }
    }

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


// call above from onCreateView in Fragment

String[] titles = getSettingTabTitlesRes(context);
settingAdapter = new SettingAdapter(this);

binding.settingViewpager.setOffscreenPageLimit(1);
binding.settingViewpager.setAdapter(settingAdapter);

new TabLayoutMediator(binding.settingTablayout, binding.settingViewpager, (tab, position) -> {
    tab.setText(titles[position]);
}).attach();

Retain Fragment in ViewPager2

Retain Fragment in ViewPager(old version)

package id.ctech.dispenser_pos.ui.fragment.setting.dispenser.view;

import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;

import java.util.List;

import id.ctech.dispenser_pos.R;
import id.ctech.dispenser_pos.databinding.FragmentDispenserBinding;
import id.ctech.dispenser_pos.db.local.datasource.setting.dispenser.dispenser.DispenserDataSource;
import id.ctech.dispenser_pos.db.local.datasource.setting.dispenser.dispenser_brand.DispenserBrandDataSource;
import id.ctech.dispenser_pos.db.local.datasource.setting.nozzle.NozzleDataSourceHelper;
import id.ctech.dispenser_pos.db.local.datasource.setting.work_schedule.shift_work.ShiftWorkDataSourceHelper;
import id.ctech.dispenser_pos.db.local.datasource.setting.work_schedule.work_schedule.WorkScheduleDataSourceHelper;
import id.ctech.dispenser_pos.etc.ChooseColor;
import id.ctech.dispenser_pos.etc.ThreadExecutors;
import id.ctech.dispenser_pos.ui.activity.main.MainActivity;
import id.ctech.dispenser_pos.ui.compound.table.listeners.ITableDataClickListener;
import id.ctech.dispenser_pos.ui.compound.table.listeners.ITableDataLongClickListener;
import id.ctech.dispenser_pos.ui.fragment.etc.DebouncedOnClickListener;
import id.ctech.dispenser_pos.ui.fragment.setting.SettingFragment;
import id.ctech.dispenser_pos.ui.fragment.setting.dispenser.model.Dispenser;
import id.ctech.dispenser_pos.ui.fragment.setting.dispenser.model.DispenserBrand;
import id.ctech.dispenser_pos.ui.fragment.setting.dispenser.presenter.DispenserPresenter;
import id.ctech.dispenser_pos.ui.fragment.setting.dispenser.presenter.IDispenserPresenter;
import id.ctech.dispenser_pos.ui.fragment.setting.dispenser.view.table.dispenser.DispenserDataAdapter;
import id.ctech.dispenser_pos.ui.fragment.setting.dispenser.view.table.dispenser_brand.DispenserBrandDataAdapter;
import id.ctech.dispenser_pos.ui.fragment.setting.nozzle.model.Nozzle;
import id.ctech.dispenser_pos.ui.fragment.setting.user_account.model.UserAccount;
import id.ctech.dispenser_pos.ui.fragment.setting.work_schedule.model.ShiftWork;
import id.ctech.dispenser_pos.ui.fragment.setting.work_schedule.model.WorkSchedule;

import static android.widget.Toast.LENGTH_SHORT;
import static id.ctech.dispenser_pos.dev.dispenser.ENozzleStatus.IN_HOLSTER;
import static id.ctech.dispenser_pos.etc.Common.ADMIN;
import static id.ctech.dispenser_pos.etc.Common.getDispenser;
import static id.ctech.dispenser_pos.etc.Common.getNozzles;
import static id.ctech.dispenser_pos.etc.Common.getShiftWorks;
import static id.ctech.dispenser_pos.etc.Common.getWorkSchedules;
import static id.ctech.dispenser_pos.etc.CommonColor.readThemeFromPref;
import static id.ctech.dispenser_pos.etc.CommonDate.toShortTime24;
import static id.ctech.dispenser_pos.ui.compound.alert_dialog.SweetAlertDialog.showConfirmDialog;
import static id.ctech.dispenser_pos.ui.compound.snackbar.SweetSnackbar.showSnackbarError;
import static id.ctech.dispenser_pos.ui.compound.snackbar.SweetSnackbar.showSnackbarInfo;
import static id.ctech.dispenser_pos.ui.compound.snackbar.SweetSnackbar.showSnackbarSuccess;


public class DispenserFragment extends Fragment implements IDispenserView {
    private final static int PREV_PAGE = 3;
    private final static int NEXT_PAGE = 5;
    private Context context;
    private IDispenserPresenter iDispenserPresenter;
    // Dispenser
    private DispenserDataAdapter dispenserDataAdapter;
    private Dispenser dispenserClicked = null, dispenserLongClicked = null;
    private int dispenserClickedRowIndex = -1, dispenserLongClickedRowIndex = -1;
    private int hiddenNewID;
    // DispenserBrand
    private DispenserBrandDataAdapter dispenserBrandDataAdapter;
    private DispenserBrand dispenserBrandClicked = null, dispenserBrandLongClicked = null;
    private int dispenserBrandClickedRowIndex = -1, dispenserBrandLongClickedRowIndex = -1;
    private int dispenserBrandNewID;
    private FragmentDispenserBinding binding;
    private ThreadExecutors threadExecutors;
    private UserAccount signInAccount;
    //
    private Dispenser dispenser;
    private Nozzle[] nozzles;
    private ShiftWork[] shiftWorks;
    private WorkSchedule[] workSchedules;
    private int nozzleQty;
    private int shiftWorkQty;

    public DispenserFragment() {
    }
    
    private void readDispenser() {
        try {
            dispenser = getDispenser(context);
            if (dispenser == null) {
                showSnackbarError(getString(R.string.message_failed_to_load_dispenser_module), LENGTH_SHORT);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private void readNozzles() {
        try {
            nozzles = getNozzles(context);
            if (nozzles.length == 0) {
                showSnackbarError(getString(R.string.message_failed_to_load_nozzle_module), LENGTH_SHORT);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private void readShiftWorks() {
        try {
            shiftWorks = getShiftWorks(context);
            if (shiftWorks.length == 0) {
                showSnackbarError(getString(R.string.message_failed_to_load_shift_work_module), LENGTH_SHORT);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private void readWorkSchedules() {
        try {
            workSchedules = getWorkSchedules(context);
            if (workSchedules.length == 0) {
                showSnackbarError(getString(R.string.message_failed_to_load_work_schedule_module), LENGTH_SHORT);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private void nozzleSync() {
        try {
            readDispenser();
            readNozzles();

            // nozzle sync
            if (dispenser != null && nozzles != null) {
                nozzleQty = dispenser.getNozzleQty(); // references
                if (nozzleQty < nozzles.length) {
                    for (int i = nozzleQty + 1; i <= nozzles.length; i++) {
                        NozzleDataSourceHelper.deleteNozzle(context, i);
                    }
                } else if (nozzleQty > nozzles.length) {
                    for (int i = nozzles.length + 1; i <= nozzleQty; i++) {
                        Nozzle nozzle = new Nozzle(i, i, 0, 0, 0, 0, IN_HOLSTER);
                        NozzleDataSourceHelper.createNozzle(context, nozzle);
                    }
                }
                nozzles = getNozzles(context);

                // nozzle validation
                nozzleQty = dispenser.getNozzleQty();
                if (nozzleQty != nozzles.length) {
                    showSnackbarError(getString(R.string.message_nozzle_sync_failed), LENGTH_SHORT);
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private void shiftWorkSync() {
        try {
            readDispenser();
            readShiftWorks();

            // shift work sync
            if (dispenser != null && shiftWorks != null) {
                shiftWorkQty = dispenser.getShiftWorkQty(); // references
                if (shiftWorkQty < shiftWorks.length) {
                    for (int i = shiftWorkQty + 1; i <= shiftWorks.length; i++) {
                        ShiftWorkDataSourceHelper.deleteShiftWork(context, i);
                    }
                } else if (shiftWorkQty > shiftWorks.length) {
                    for (int i = shiftWorks.length + 1; i <= shiftWorkQty; i++) {
                        String strTime24 = "";
                        switch (i) {
                            case 1:
                                strTime24 = "06:00";
                                break;
                            case 2:
                                strTime24 = "14:00";
                                break;
                            case 3:
                                strTime24 = "22:00";
                                break;
                        }
                        // create shift work
                        ShiftWork shiftWork = new ShiftWork(i, toShortTime24(strTime24));
                        ShiftWorkDataSourceHelper.createShiftWork(context, shiftWork);
                    }
                }
                shiftWorks = getShiftWorks(context);

                // shiftWorks validation
                shiftWorkQty = dispenser.getShiftWorkQty();
                if (shiftWorkQty != shiftWorks.length) {
                    showSnackbarError(getString(R.string.message_shiftworks_sync_failed), LENGTH_SHORT);
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private void workScheduleSync() {
        try {
            readDispenser();
            readWorkSchedules();

            // work schedule sync
            if (dispenser != null && workSchedules != null) {
                nozzleQty = dispenser.getNozzleQty(); // references
                if (nozzleQty < workSchedules.length) {
                    for (int i = nozzleQty + 1; i <= workSchedules.length; i++) {
                        WorkScheduleDataSourceHelper.deleteWorkSchedule(context, i);
                    }
                } else if (nozzleQty > workSchedules.length) {
                    for (int i = workSchedules.length + 1; i <= nozzleQty; i++) {
                        WorkSchedule workSchedule = new WorkSchedule(i, i, 1, 1, 1);
                        WorkScheduleDataSourceHelper.createWorkSchedule(context, workSchedule);
                    }
                }
                workSchedules = getWorkSchedules(context);

                // work schedule validation
                nozzleQty = dispenser.getNozzleQty();
                if (nozzleQty != workSchedules.length) {
                    showSnackbarError(getString(R.string.message_workschedule_sync_failed), LENGTH_SHORT);
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private void doSyncs() {
        try {
            nozzleSync();
            shiftWorkSync();
            workScheduleSync();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        try {
            Log.i("xxx", "DispenserFragment onCreateView");
            context = getActivity();
            if (context != null) {

                binding = FragmentDispenserBinding.inflate(inflater, container, false);
                View rootView = binding.getRoot();

                threadExecutors = ThreadExecutors.getInstance();

                doSyncs();

                DispenserDataSource dispenserDataSource = DispenserDataSource.getInstance(context);
                DispenserBrandDataSource dispenserBrandDataSource = DispenserBrandDataSource.getInstance(context);
                new DispenserPresenter(context, dispenserDataSource, dispenserBrandDataSource, this);

                // DispenserBrand
                binding.dispenserBrandAddButton.setOnClickListener(new DispenserBrandAddButtonClickListener());
                binding.dispenserBrandEditButton.setOnClickListener(new DispenserBrandEditButtonClickListener());
                binding.dispenserBrandDeleteButton.setOnClickListener(new DispenserBrandDeleteButtonClickListener());

                iDispenserPresenter.readDispenserBrands();

                // dispenser
                binding.dispenserPrevButton.setOnClickListener(new DispenserPrevButtonClickListener());
                binding.dispenserNextButton.setOnClickListener(new DispenserNextButtonClickListener());
                binding.dispenserAddButton.setOnClickListener(new DispenserAddButtonClickListener());
                binding.dispenserEditButton.setOnClickListener(new DispenserEditButtonClickListener());
                binding.dispenserDeleteButton.setOnClickListener(new DispenserDeleteButtonClickListener());

                signInAccount = ((MainActivity) getActivity()).getSignInAccount();

                iDispenserPresenter.readDispensers();

                setTheme(context);

                return rootView;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    private void setTheme(@NonNull Context context) {
        try {
            // set theme from pref
            ChooseColor chooseColor = readThemeFromPref(context);
            if (chooseColor != null) {
                context.setTheme(chooseColor.getTheme());     // app theme (default: R.style.app_theme_red)
            }

            Activity activity = getActivity();
            if (activity != null) {
                int colorPrimary = ((MainActivity) activity).getColorPrimary();

                binding.dispenserAddButton.setBackgroundColor(colorPrimary);
                binding.dispenserDeleteButton.setBackgroundColor(colorPrimary);
                binding.dispenserEditButton.setBackgroundColor(colorPrimary);

                binding.dispenserPrevButton.setBackgroundColor(colorPrimary);
                binding.dispenserNextButton.setBackgroundColor(colorPrimary);

                binding.dispenserBrandAddButton.setBackgroundColor(colorPrimary);
                binding.dispenserBrandDeleteButton.setBackgroundColor(colorPrimary);
                binding.dispenserBrandEditButton.setBackgroundColor(colorPrimary);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();

        Log.i("xxx", "DispenserFragment onDestroyView");

        binding = null;
        threadExecutors = null;
        dispenserBrandDataAdapter = null;
        dispenserBrandClicked = null;
        dispenserBrandLongClicked = null;
        dispenserDataAdapter = null;
        dispenserClicked = null;
        dispenserLongClicked = null;
    }

    @Override
    public void setPresenter(@NonNull IDispenserPresenter iPresenter) {
        iDispenserPresenter = iPresenter;
    }

    // DispenserBrand
    @Override
    public void onCreateDispenserBrand(boolean result) {
        Runnable r = () -> {

            try {
                if (binding == null) {
                    return;
                }

                if (result) {
                    showSnackbarSuccess(getString(R.string.message_successfully_to_save_data), LENGTH_SHORT);
                    iDispenserPresenter.readDispenserBrands();
                    binding.dispenserBrandAddButton.setText(getString(R.string.button_add));
                    binding.dispenserBrandEditButton.setEnabled(true);
                    binding.dispenserBrandDeleteButton.setEnabled(true);
                    binding.dispenserBrandAddButton.setTextColor(Color.WHITE);
                } else {
                    showSnackbarError(getString(R.string.message_failed_to_save_data), LENGTH_SHORT);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        };
        threadExecutors.getMainThreadExecutor().execute(r);
    }

    @Override
    public void onReadDispenserBrands(@NonNull List<DispenserBrand> dispenserBrandList) {
        Runnable r = () -> {

            try {
                if (binding == null) {
                    return;
                }

                dispenserBrandDataAdapter = new DispenserBrandDataAdapter(context, dispenserBrandList, binding.dispenserBrandTable);
                dispenserBrandDataAdapter.setSelectedColor(-1);
                binding.dispenserBrandTable.setDataAdapter(dispenserBrandDataAdapter);
                binding.dispenserBrandTable.sort(0, true);
                binding.dispenserBrandTable.invalidate();
                binding.dispenserBrandTable.removeDataClickListener(new DispenserBrandClickListener());
                binding.dispenserBrandTable.removeDataLongClickListener(new DispenserBrandLongClickListener());
                binding.dispenserBrandTable.addDataClickListener(new DispenserBrandClickListener());
                binding.dispenserBrandTable.addDataLongClickListener(new DispenserBrandLongClickListener());
                dispenserBrandClickedRowIndex = -1;
                dispenserBrandClicked = null;
                dispenserBrandLongClickedRowIndex = -1;
                dispenserBrandLongClicked = null;
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        };
        threadExecutors.getMainThreadExecutor().execute(r);
    }

    @Override
    public void onUpdateDispenserBrand(boolean result) {
        Runnable r = () -> {

            try {
                if (binding == null) {
                    return;
                }

                if (result) {
                    showSnackbarSuccess(getString(R.string.message_successfully_to_save_data), LENGTH_SHORT);
                    iDispenserPresenter.readDispenserBrands();
                    binding.dispenserBrandEditButton.setText(getString(R.string.button_edit));
                    binding.dispenserBrandAddButton.setEnabled(true);
                    binding.dispenserBrandDeleteButton.setEnabled(true);
                    binding.dispenserBrandEditButton.setTextColor(Color.WHITE);
                } else {
                    showSnackbarError(getString(R.string.message_failed_to_save_data), LENGTH_SHORT);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        };
        threadExecutors.getMainThreadExecutor().execute(r);
    }

    @Override
    public void onDeleteDispenserBrand(boolean result) {
        Runnable r = () -> {

            try {
                if (binding == null) {
                    return;
                }

                if (result) {
                    showSnackbarSuccess(getString(R.string.message_data_deleted), LENGTH_SHORT);
                } else {
                    showSnackbarError(getString(R.string.message_failed_to_delete_data), LENGTH_SHORT);
                }
                iDispenserPresenter.readDispenserBrands();
                binding.dispenserBrandAddButton.setEnabled(true);
                binding.dispenserBrandEditButton.setEnabled(true);
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        };
        threadExecutors.getMainThreadExecutor().execute(r);
    }

    @Override
    public void onNewIDDispenserBrand(int ID) {
        Runnable r = () -> {

            try {
                if (binding == null) {
                    return;
                }

                dispenserBrandNewID = ID;
                dispenserBrandDataAdapter.add(new DispenserBrand(dispenserBrandNewID, ""));
                dispenserBrandDataAdapter.notifyDataSetChanged();
                binding.dispenserBrandTable.enterEditMode();
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        };
        threadExecutors.getMainThreadExecutor().execute(r);
    }

    @Override
    public void onErrorDispenserBrand(@NonNull Throwable result) {
        Runnable r = () -> {

            try {
                String strMessage = result.getMessage();
                if (strMessage != null) {
                    showSnackbarError(strMessage, LENGTH_SHORT);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        };
        threadExecutors.getMainThreadExecutor().execute(r);
    }

    // Dispenser
    @Override
    public void onCreateDispenser(boolean result) {
        Runnable r = () -> {

            try {
                if (binding == null) {
                    return;
                }

                if (result) {
                    showSnackbarSuccess(getString(R.string.message_successfully_to_save_data), LENGTH_SHORT);
                    iDispenserPresenter.readDispensers();
                    binding.dispenserAddButton.setText(getString(R.string.button_add));
                    binding.dispenserEditButton.setEnabled(true);
                    binding.dispenserDeleteButton.setEnabled(true);
                    binding.dispenserAddButton.setTextColor(Color.WHITE);

                    doSyncs();
                } else {
                    showSnackbarError(getString(R.string.message_failed_to_save_data), LENGTH_SHORT);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        };
        threadExecutors.getMainThreadExecutor().execute(r);
    }

    @Override
    public void onReadDispensers(@NonNull List<Dispenser> result) {
        Runnable r = () -> {

            try {
                if (binding == null) {
                    return;
                }

                dispenserDataAdapter = new DispenserDataAdapter(context, result, binding.dispenserTable);
                dispenserDataAdapter.setSelectedColor(-1);
                binding.dispenserTable.setDataAdapter(dispenserDataAdapter);
                binding.dispenserTable.sort(0, true);
                binding.dispenserTable.invalidate();
                binding.dispenserTable.removeDataClickListener(new DispenserClickListener());
                binding.dispenserTable.removeDataLongClickListener(new DispenserLongClickListener());
                binding.dispenserTable.addDataClickListener(new DispenserClickListener());
                binding.dispenserTable.addDataLongClickListener(new DispenserLongClickListener());
                dispenserClickedRowIndex = -1;
                dispenserClicked = null;
                dispenserLongClickedRowIndex = -1;
                dispenserLongClicked = null;
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        };
        threadExecutors.getMainThreadExecutor().execute(r);
    }

    @Override
    public void onUpdateDispenser(boolean result) {
        Runnable r = () -> {

            try {
                if (binding == null) {
                    return;
                }

                if (result) {
                    showSnackbarSuccess(getString(R.string.message_successfully_to_save_data), LENGTH_SHORT);
                    iDispenserPresenter.readDispensers();
                    binding.dispenserEditButton.setText(getString(R.string.button_edit));
                    binding.dispenserAddButton.setEnabled(true);
                    binding.dispenserDeleteButton.setEnabled(true);
                    binding.dispenserEditButton.setTextColor(Color.WHITE);

                    doSyncs();

                } else {
                    showSnackbarError(getString(R.string.message_failed_to_save_data), LENGTH_SHORT);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        };
        threadExecutors.getMainThreadExecutor().execute(r);
    }

    @Override
    public void onDeleteDispenser(boolean result) {
        Runnable r = () -> {

            try {
                if (binding == null) {
                    return;
                }

                if (result) {
                    showSnackbarSuccess(getString(R.string.message_data_deleted), LENGTH_SHORT);

                    doSyncs();

                } else {
                    showSnackbarError(getString(R.string.message_failed_to_delete_data), LENGTH_SHORT);
                }
                iDispenserPresenter.readDispensers();
                binding.dispenserAddButton.setEnabled(true);
                binding.dispenserEditButton.setEnabled(true);
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        };
        threadExecutors.getMainThreadExecutor().execute(r);
    }

    @Override
    public void onNewIDDispenser(int ID) {
        Runnable r = () -> {

            try {
                if (binding == null) {
                    return;
                }

                hiddenNewID = ID;
                // create default dispenser (dispenserId:1, dispenserType:new, nozzleQty:8, workScheduleQty:3)
                dispenserDataAdapter.add(new Dispenser(hiddenNewID, 1, 2, 8, 3));
                dispenserDataAdapter.notifyDataSetChanged();
                binding.dispenserTable.enterEditMode();
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        };
        threadExecutors.getMainThreadExecutor().execute(r);
    }


    @Override
    public void onErrorDispenser(@NonNull Throwable result) {
        Runnable r = () -> {

            try {
                String strMessage = result.getMessage();
                if (strMessage != null) {
                    showSnackbarError(strMessage, LENGTH_SHORT);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        };
        threadExecutors.getMainThreadExecutor().execute(r);
    }

    // DispenserBrand
    private class DispenserBrandAddButtonClickListener extends DebouncedOnClickListener {

        @Override
        public void onDebouncedClick(final View v) {
        }
    }

    private class DispenserBrandEditButtonClickListener extends DebouncedOnClickListener {

        @Override
        public void onDebouncedClick(View v) {
        }
    }

    private class DispenserBrandDeleteButtonClickListener extends DebouncedOnClickListener {

        @Override
        public void onDebouncedClick(View v) {
        }
    }


    private class DispenserBrandClickListener implements ITableDataClickListener<DispenserBrand> {
        @Override
        public void onDataClicked(int rowIndex, DispenserBrand clickedData) {
        }
    }

    private class DispenserBrandLongClickListener implements ITableDataLongClickListener<DispenserBrand> {
        @Override
        public boolean onDataLongClicked(int rowIndex, DispenserBrand clickedData) {
            return false;
        }
    }


    // Dispenser
    private class DispenserPrevButtonClickListener extends DebouncedOnClickListener {

        @Override
        public void onDebouncedClick(View v) {
        }
    }

    private class DispenserNextButtonClickListener extends DebouncedOnClickListener {

        @Override
        public void onDebouncedClick(View v) {
        }
    }


    private class DispenserAddButtonClickListener extends DebouncedOnClickListener {

        @Override
        public void onDebouncedClick(final View v) {
        }
    }

    private class DispenserEditButtonClickListener extends DebouncedOnClickListener {

        @Override
        public void onDebouncedClick(View v) {
        }
    }

    private class DispenserDeleteButtonClickListener extends DebouncedOnClickListener {

        @Override
        public void onDebouncedClick(View v) {
        }
    }


    private class DispenserClickListener implements ITableDataClickListener<Dispenser> {
        @Override
        public void onDataClicked(int rowIndex, Dispenser clickedData) {
        }
    }

    private class DispenserLongClickListener implements ITableDataLongClickListener<Dispenser> {
        @Override
        public boolean onDataLongClicked(int rowIndex, Dispenser clickedData) {
            return false;
        }
    }

}
Sabrina N
  • 150
  • 11
  • `setOffscreenPageLimit(1)` already means that only the fragments directly to the side of your current fragment will exist (only the saved state of other fragments is kept; the fragment instances themselves *do* receive a call to `onDestroy()`). – ianhanniballake Jul 04 '21 at 00:21
  • I've used setOffscreenPageLimit(1) but it didn't solve my problem. My problem is sometimes my data appears and sometimes the data does not appear when I swipe right and left. It happens because ViewPager2 still retains the Fragment which is far from the visible Fragment. I compare with ViewPager(previous version), ViewPager will call onDestroy of Fragments as soon as it is not used and keep one fragment on left and one fragment on right of visible fragment. My program runs normally when using ViewPager, but when using ViewPager2, there is a problem as I said above. – Sabrina N Jul 04 '21 at 05:11
  • how to call onDestroy of fragments which far from the view-point (I want to keep one fragment before and after the visibled fragment, and delete everything retained as soon as possible). Is it possible to intercept in the FragmentStateAdapter for that need, before the fragment is visible? – Sabrina N Jul 04 '21 at 05:12
  • Result of ViewPager2(not worked): https://i.stack.imgur.com/IOg0P.jpg Result of ViewPager(worked): https://i.stack.imgur.com/HCpGx.jpg – Sabrina N Jul 04 '21 at 05:21
  • It sounds like all of your problems are due to your fragment code, not ViewPager or ViewPager2. Can you include your fragment code so that we can recommend the appropriate fixes there? – ianhanniballake Jul 04 '21 at 06:17
  • I've added DispenserFragment.java. – Sabrina N Jul 04 '21 at 09:50
  • The problem has been found, ViewPager2 does not immediately call onDestroy of retain fragments, and leaves retain fragment up to 4 on left, when it is more than 4 retain fragments then it calls onDestroy for the farthest retain fragment. That behavior is problematic. – Sabrina N Jul 05 '21 at 10:39
  • ViewPager(old version) leaves retain fragment only one on left and one on right. When its more than that, then will call onDestroy of farthest retain fragment asap. – Sabrina N Jul 05 '21 at 10:41
  • ViewPager2 video: https://drive.google.com/file/d/1loDLzmRyqeQmT0JvTQkZPtgwBIsH6si3/view?usp=sharing – Sabrina N Jul 05 '21 at 16:37
  • ViewPager(worked) video: https://drive.google.com/file/d/1--AacZwt8pb_hsiH-gR_zT9rlay46fUz/view?usp=sharing – Sabrina N Jul 05 '21 at 16:37
  • ViewPager2 video (above) uses ViewPager2. Sometime work, sometime failed. ViewPager video (below) uses ViewPager (old version). And it works normally. Both use the same fragments. I think the problem is in ViewPager2. – Sabrina N Jul 05 '21 at 16:42
  • I've got the answer for the ViewPager2 bug. Thanks – Sabrina N Jul 09 '21 at 00:01

1 Answers1

3

Its bug of ViewPager2 and I get solution as follows:

/**
 * Sets whether the LayoutManager should be queried for views outside of
 * its viewport while the UI thread is idle between frames.
 *
 * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
 * view system traversals on devices running API 21 or greater. Default value is true.</p>
 *
 * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
 * to RenderThread and the starting up its next frame at the next VSync pulse. By
 * prefetching out of window views in this time period, delays from inflation and view
 * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
 *
 * <p>While prefetch is enabled, it will have the side effect of expanding the effective
 * size of the View cache to hold prefetched views.</p>
 *
 * @param enabled <code>True</code> if items should be prefetched in between traversals.
 *
 * @see #isItemPrefetchEnabled()
 */
RecyclerView.LayoutManager layoutManager =  ((RecyclerView)(binding.settingViewpager.getChildAt(0))).getLayoutManager();
if(layoutManager != null) {
    layoutManager.setItemPrefetchEnabled(false);
}


/**
 * Set the number of offscreen views to retain before adding them to the potentially shared
 * {@link #getRecycledViewPool() recycled view pool}.
 *
 * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
 * a LayoutManager to reuse those views unmodified without needing to return to the adapter
 * to rebind them.</p>
 *
 * @param size Number of views to cache offscreen before returning them to the general
 *             recycled view pool
 */ 
RecyclerView recyclerView=  ((RecyclerView)(binding.settingViewpager.getChildAt(0)));
if(recyclerView != null) {
    recyclerView.setItemViewCacheSize(0);
}
Sabrina N
  • 150
  • 11