我的碎片在重新单击或导航到下一个碎片时不断重建

16

我已经在我的安卓应用上实现了新的架构组件,但是处理这些片段的状态对我来说成了一个噩梦。每当我点击片段的图标时,片段就会在每次导航时被重新创建。如何处理这种情况或者保存这些片段的状态呢?

以下是处理五个片段的主要活动:

public class MainActivityCenterofInformation extends AppCompatActivity {

    BottomNavigationView bottomNavigationView;

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

        setUpNavigation ();
    }

    public  void setUpNavigation(){
        bottomNavigationView = findViewById (R.id.bottom_nav_cict);
        NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager ()
                .findFragmentById (R.id.nav_host_fragment_cict);
        NavigationUI.setupWithNavController (bottomNavigationView, navHostFragment.getNavController ());
    }

    //adding animations to the fragment
}

我不会读 Kotlin,请直接告诉我 Java,谢谢。

3个回答

32
这是片段的正常行为。每次移除或替换它们时,它们应该被重新创建,并且您应该使用onSaveInstanceState来恢复它们的状态。
以下是一篇很好的文章,描述了如何实现:保存片段状态 除此之外,您还可以使用View Model,它是以下推荐的Android架构的一部分。它们是保留和恢复UI数据的好方法。
您可以按照这个逐步指南学习如何实现这个架构:代码实验室

Android recommended architecture

编辑:解决方案

注意:该解决方案假定您不想使用ViewModels,只想使用导航UI来隐藏或显示片段

它涵盖了以下几点:

  • 在导航期间保持片段活动
  • 实现后退键按下时的自定义导航行为(可选)

背景:

Android Navigation组件具有NavController类,您可以使用它来导航到不同的目标。 NavController使用一个Navigator来实际进行导航。 Navigator是一个抽象类,任何人都可以扩展/继承它以根据目标类型提供自定义导航。当将片段用作目标时,NavHostFragment使用FragmentNavigator,其默认实现使用FragmentTransaction.replace()替换片段,这会完全销毁先前的片段并添加新片段。因此,我们必须创建自己的导航器,并且不使用FragmentTransaction.replace(),而是使用FragmentTransaction.hide()FragmentTransaction.show()的组合来避免销毁片段。

导航UI的默认行为:

默认情况下,每当您导航到除主页片段以外的任何其他片段时,它们都不会添加到后退栈中,因此假设您按以下顺序选择片段

A -> B -> C -> D -> E

您的后退栈仅会有{{少于}}一个活动

[A, E]

正如您所看到的,片段B、C、D没有添加到后退堆栈中,因此按下后退按钮将始终返回到主页片段A。

我们现在想要的行为:

我们希望有一个简单而有效的行为。我们希望所有片段都被添加到后退堆栈中,但如果该片段已经在后退堆栈中,我们希望弹出所有片段直到选择的片段。

假设我按以下顺序选择片段

A -> B -> C -> D -> E

返回堆栈也应该是{{backstack}}。

[A, B, C, D, E]

按下返回键时,只有最后一个片段应该被弹出,回退栈应该是这样的{{示例}}。
[A, B, C, D]

但是如果我们导航到比如碎片B,由于B已经在堆栈中,那么所有在B上面的碎片都应该被弹出,我们的返回栈应该像这样

 [A, B]

我希望这种行为有意义。使用全局操作很容易实现此行为,你将在下面看到,而且比默认的更好。
好的,现在我们有两个选项:
1. 扩展 FragmentNavigator 2. 复制/粘贴 FragmentNavigator 我个人想要扩展 FragmentNavigator 并覆盖 navigate() 方法,但由于它的所有成员变量都是私有的,所以我无法实现正确的导航。
因此,我决定复制整个 FragmentNavigator 类,然后只需在整个代码中更改名称从 "FragmentNavigator" 到我想要称呼它的任何名称。
给我看步骤吧!
1. 创建自定义导航器 2. 使用自定义标签 3. 添加全局操作 4. 使用全局操作 5. 将自定义导航器添加到 NavController 中
第一步:创建自定义导航器
这是我的自定义导航器,名为 StickyCustomNavigator。所有代码与 FragmentNavigator 相同,除了 navigate() 方法。正如你所看到的,它使用 hide()、show() 和 add() 方法而不是 replace()。逻辑很简单。隐藏上一个片段并显示目标片段。如果这是我们第一次转到特定目标片段,则添加片段而不是显示它。
@Navigator.Name("sticky_fragment")
public class StickyFragmentNavigator extends Navigator<StickyFragmentNavigator.Destination> {


    private static final String TAG = "StickyFragmentNavigator";
    private static final String KEY_BACK_STACK_IDS = "androidx-nav-fragment:navigator:backStackIds";

    private final Context mContext;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    final FragmentManager mFragmentManager;
    private final int mContainerId;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
            ArrayDeque<Integer> mBackStack = new ArrayDeque<>();
    @SuppressWarnings("WeakerAccess") /* synthetic access */
            boolean mIsPendingBackStackOperation = false;

    private final FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener =
            new FragmentManager.OnBackStackChangedListener() {

                @SuppressLint("RestrictedApi")
                @Override
                public void onBackStackChanged() {
                    // If we have pending operations made by us then consume this change, otherwise
                    // detect a pop in the back stack to dispatch callback.
                    if (mIsPendingBackStackOperation) {
                        mIsPendingBackStackOperation = !isBackStackEqual();
                        return;
                    }

                    // The initial Fragment won't be on the back stack, so the
                    // real count of destinations is the back stack entry count + 1
                    int newCount = mFragmentManager.getBackStackEntryCount() + 1;
                    if (newCount < mBackStack.size()) {
                        // Handle cases where the user hit the system back button
                        while (mBackStack.size() > newCount) {
                            mBackStack.removeLast();
                        }
                        dispatchOnNavigatorBackPress();
                    }
                }
            };

    public StickyFragmentNavigator(@NonNull Context context, @NonNull FragmentManager manager,
                           int containerId) {
        mContext = context;
        mFragmentManager = manager;
        mContainerId = containerId;
    }

    @Override
    protected void onBackPressAdded() {
        mFragmentManager.addOnBackStackChangedListener(mOnBackStackChangedListener);
    }

    @Override
    protected void onBackPressRemoved() {
        mFragmentManager.removeOnBackStackChangedListener(mOnBackStackChangedListener);
    }

    @Override
    public boolean popBackStack() {
        if (mBackStack.isEmpty()) {
            return false;
        }
        if (mFragmentManager.isStateSaved()) {
            Log.i(TAG, "Ignoring popBackStack() call: FragmentManager has already"
                    + " saved its state");
            return false;
        }
        if (mFragmentManager.getBackStackEntryCount() > 0) {
            mFragmentManager.popBackStack(
                    generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
            mIsPendingBackStackOperation = true;
        } // else, we're on the first Fragment, so there's nothing to pop from FragmentManager
        mBackStack.removeLast();
        return true;
    }

    @NonNull
    @Override
    public StickyFragmentNavigator.Destination createDestination() {
        return new StickyFragmentNavigator.Destination(this);
    }

    @NonNull
    public Fragment instantiateFragment(@NonNull Context context,
                                        @SuppressWarnings("unused") @NonNull FragmentManager fragmentManager,
                                        @NonNull String className, @Nullable Bundle args) {
        return Fragment.instantiate(context, className, args);
    }

    @Nullable
    @Override
    public NavDestination navigate(@NonNull StickyFragmentNavigator.Destination destination, @Nullable Bundle args,
                                   @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
        if (mFragmentManager.isStateSaved()) {
            Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
                    + " saved its state");
            return null;
        }
        String className = destination.getClassName();
        if (className.charAt(0) == '.') {
            className = mContext.getPackageName() + className;
        }

        final FragmentTransaction ft = mFragmentManager.beginTransaction();

        int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
        int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
        int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
        int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
        if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
            enterAnim = enterAnim != -1 ? enterAnim : 0;
            exitAnim = exitAnim != -1 ? exitAnim : 0;
            popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
            popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
            ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
        }

        String tag = Integer.toString(destination.getId());
        Fragment primaryNavigationFragment = mFragmentManager.getPrimaryNavigationFragment();
        if(primaryNavigationFragment != null)
            ft.hide(primaryNavigationFragment);
        Fragment destinationFragment = mFragmentManager.findFragmentByTag(tag);
        if(destinationFragment == null) {
            destinationFragment = instantiateFragment(mContext, mFragmentManager, className, args);
            destinationFragment.setArguments(args);
            ft.add(mContainerId, destinationFragment , tag);
        }
        else
            ft.show(destinationFragment);

        ft.setPrimaryNavigationFragment(destinationFragment);

        final @IdRes int destId = destination.getId();
        final boolean initialNavigation = mBackStack.isEmpty();
        // TODO Build first class singleTop behavior for fragments
        final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
                && navOptions.shouldLaunchSingleTop()
                && mBackStack.peekLast() == destId;

        boolean isAdded;
        if (initialNavigation) {
            isAdded = true;
        } else if (isSingleTopReplacement) {
            // Single Top means we only want one instance on the back stack
            if (mBackStack.size() > 1) {
                // If the Fragment to be replaced is on the FragmentManager's
                // back stack, a simple replace() isn't enough so we
                // remove it from the back stack and put our replacement
                // on the back stack in its place
                mFragmentManager.popBackStackImmediate(
                        generateBackStackName(mBackStack.size(), mBackStack.peekLast()), 0);
                mIsPendingBackStackOperation = false;
            }
            isAdded = false;
        } else {
            ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
            mIsPendingBackStackOperation = true;
            isAdded = true;
        }
        if (navigatorExtras instanceof FragmentNavigator.Extras) {
            FragmentNavigator.Extras extras = (FragmentNavigator.Extras) navigatorExtras;
            for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
                ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
            }
        }
        ft.setReorderingAllowed(true);
        ft.commit();
        // The commit succeeded, update our view of the world
        if (isAdded) {
            mBackStack.add(destId);
            return destination;
        } else {
            return null;
        }
    }

    @Override
    @Nullable
    public Bundle onSaveState() {
        Bundle b = new Bundle();
        int[] backStack = new int[mBackStack.size()];
        int index = 0;
        for (Integer id : mBackStack) {
            backStack[index++] = id;
        }
        b.putIntArray(KEY_BACK_STACK_IDS, backStack);
        return b;
    }

    @Override
    public void onRestoreState(@Nullable Bundle savedState) {
        if (savedState != null) {
            int[] backStack = savedState.getIntArray(KEY_BACK_STACK_IDS);
            if (backStack != null) {
                mBackStack.clear();
                for (int destId : backStack) {
                    mBackStack.add(destId);
                }
            }
        }
    }

    @NonNull
    private String generateBackStackName(int backStackIndex, int destId) {
        return backStackIndex + "-" + destId;
    }

    private int getDestId(@Nullable String backStackName) {
        String[] split = backStackName != null ? backStackName.split("-") : new String[0];
        if (split.length != 2) {
            throw new IllegalStateException("Invalid back stack entry on the "
                    + "NavHostFragment's back stack - use getChildFragmentManager() "
                    + "if you need to do custom FragmentTransactions from within "
                    + "Fragments created via your navigation graph.");
        }
        try {
            // Just make sure the backStackIndex is correctly formatted
            Integer.parseInt(split[0]);
            return Integer.parseInt(split[1]);
        } catch (NumberFormatException e) {
            throw new IllegalStateException("Invalid back stack entry on the "
                    + "NavHostFragment's back stack - use getChildFragmentManager() "
                    + "if you need to do custom FragmentTransactions from within "
                    + "Fragments created via your navigation graph.");
        }
    }

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    boolean isBackStackEqual() {
        int fragmentBackStackCount = mFragmentManager.getBackStackEntryCount();
        // Initial fragment won't be on the FragmentManager's back stack so +1 its count.
        if (mBackStack.size() != fragmentBackStackCount + 1) {
            return false;
        }

        // From top to bottom verify destination ids match in both back stacks/
        Iterator<Integer> backStackIterator = mBackStack.descendingIterator();
        int fragmentBackStackIndex = fragmentBackStackCount - 1;
        while (backStackIterator.hasNext() && fragmentBackStackIndex >= 0) {
            int destId = backStackIterator.next();
            try {
                int fragmentDestId = getDestId(mFragmentManager
                        .getBackStackEntryAt(fragmentBackStackIndex--)
                        .getName());
                if (destId != fragmentDestId) {
                    return false;
                }
            } catch (NumberFormatException e) {
                throw new IllegalStateException("Invalid back stack entry on the "
                        + "NavHostFragment's back stack - use getChildFragmentManager() "
                        + "if you need to do custom FragmentTransactions from within "
                        + "Fragments created via your navigation graph.");
            }
        }

        return true;
    }

    @NavDestination.ClassType(Fragment.class)
    public static class Destination extends NavDestination {

        private String mClassName;

        public Destination(@NonNull NavigatorProvider navigatorProvider) {
            this(navigatorProvider.getNavigator(StickyFragmentNavigator.class));
        }

        public Destination(@NonNull Navigator<? extends StickyFragmentNavigator.Destination> fragmentNavigator) {
            super(fragmentNavigator);
        }

        @CallSuper
        @Override
        public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs) {
            super.onInflate(context, attrs);
            TypedArray a = context.getResources().obtainAttributes(attrs,
                    R.styleable.FragmentNavigator);
            String className = a.getString(R.styleable.FragmentNavigator_android_name);
            if (className != null) {
                setClassName(className);
            }
            a.recycle();
        }

        @NonNull
        public final StickyFragmentNavigator.Destination setClassName(@NonNull String className) {
            mClassName = className;
            return this;
        }

        @NonNull
        public final String getClassName() {
            if (mClassName == null) {
                throw new IllegalStateException("Fragment class was not set");
            }
            return mClassName;
        }
    }

    public static final class Extras implements Navigator.Extras {
        private final LinkedHashMap<View, String> mSharedElements = new LinkedHashMap<>();

        Extras(Map<View, String> sharedElements) {
            mSharedElements.putAll(sharedElements);
        }

        @NonNull
        public Map<View, String> getSharedElements() {
            return Collections.unmodifiableMap(mSharedElements);
        }

        public static final class Builder {
            private final LinkedHashMap<View, String> mSharedElements = new LinkedHashMap<>();

            @NonNull
            public StickyFragmentNavigator.Extras.Builder addSharedElements(@NonNull Map<View, String> sharedElements) {
                for (Map.Entry<View, String> sharedElement : sharedElements.entrySet()) {
                    View view = sharedElement.getKey();
                    String name = sharedElement.getValue();
                    if (view != null && name != null) {
                        addSharedElement(view, name);
                    }
                }
                return this;
            }

            @NonNull
            public StickyFragmentNavigator.Extras.Builder addSharedElement(@NonNull View sharedElement, @NonNull String name) {
                mSharedElements.put(sharedElement, name);
                return this;
            }

            @NonNull
            public StickyFragmentNavigator.Extras build() {
                return new StickyFragmentNavigator.Extras(mSharedElements);
            }
        }
    }
}

步骤2:使用自定义标签

现在打开你的navigation.xml文件,并将与底部导航相关的fragment标签重命名为你之前在@Navigator.Name()中给出的任何名称。

<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/mobile_navigation"
    app:startDestination="@+id/navigation_home">

    <sticky_fragment
        android:id="@+id/navigation_home"
        android:name="com.example.bottomnavigationlogic.ui.home.HomeFragment"
        android:label="@string/title_home"
        tools:layout="@layout/fragment_home" />

</navigation>

步骤三:添加全局动作

全局动作是一种从应用程序中的任何位置导航到目标的方法。您可以使用可视编辑器或直接使用xml添加全局动作。在每个片段上设置以下设置以设置全局动作:

  • 目标:自身
  • popUpTo:自身
  • singleTop:true / 已选中

enter image description here enter image description here

在添加全局操作后,您的navigation.xml应该看起来像这样

<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/mobile_navigation"
    app:startDestination="@+id/navigation_home">

    <sticky_fragment
        android:id="@+id/navigation_home"
        android:name="com.example.bottomnavigationlogic.ui.home.HomeFragment"
        android:label="@string/title_home"
        tools:layout="@layout/fragment_home" />

    <sticky_fragment
        android:id="@+id/navigation_images"
        android:name="com.example.bottomnavigationlogic.ui.images.ImagesFragment"
        android:label="@string/title_images"
        tools:layout="@layout/fragment_images" />

    <sticky_fragment
        android:id="@+id/navigation_videos"
        android:name="com.example.bottomnavigationlogic.ui.videos.VideosFragment"
        android:label="@string/title_videos"
        tools:layout="@layout/fragment_videos" />
    <sticky_fragment
        android:id="@+id/navigation_songs"
        android:name="com.example.bottomnavigationlogic.ui.songs.SongsFragment"
        android:label="@string/title_songs"
        tools:layout="@layout/fragment_songs" />
    <sticky_fragment
        android:id="@+id/navigation_notifications"
        android:name="com.example.bottomnavigationlogic.ui.notifications.NotificationsFragment"
        android:label="@string/title_notifications"
        tools:layout="@layout/fragment_notifications" />
    <action
        android:id="@+id/action_global_navigation_home"
        app:destination="@id/navigation_home"
        app:launchSingleTop="true"
        app:popUpTo="@id/navigation_home" />
    <action
        android:id="@+id/action_global_navigation_notifications"
        app:destination="@id/navigation_notifications"
        app:launchSingleTop="true"
        app:popUpTo="@id/navigation_notifications" />
    <action
        android:id="@+id/action_global_navigation_songs"
        app:destination="@id/navigation_songs"
        app:launchSingleTop="true"
        app:popUpTo="@id/navigation_songs" />
    <action
        android:id="@+id/action_global_navigation_videos"
        app:destination="@id/navigation_videos"
        app:launchSingleTop="true"
        app:popUpTo="@id/navigation_videos" />
</navigation>

步骤 4:使用全局操作

当您编写了{{某些代码}}时,

 NavigationUI.setupWithNavController (bottomNavigationView, navHostFragment.getNavController ());

然后在setupWithNavController()内部,NavigationUI使用bottomNavigationView.setOnNavigationItemSelectedListener()根据被点击的菜单项的ID导航到正确的片段。它的默认行为就像我之前提到的那样。我们将添加自己的实现,并使用全局操作来实现我们期望的返回按键行为。

以下是您在MainActivity中简单完成此操作的方法:

 bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
                int id = menuItem.getItemId();
                if (menuItem.isChecked()) return false;

                switch (id)
                {
                    case R.id.navigation_home :
                        navController.navigate(R.id.action_global_navigation_home);
                        break;
                    case R.id.navigation_images :
                        navController.navigate(R.id.action_global_navigation_images);
                        break;
                    case R.id.navigation_videos :
                        navController.navigate(R.id.action_global_navigation_videos);
                        break;
                    case R.id.navigation_songs :
                        navController.navigate(R.id.action_global_navigation_songs);
                        break;
                    case R.id.navigation_notifications :
                        navController.navigate(R.id.action_global_navigation_notifications);
                        break;
                }
                return true;

            }
        });

最终步骤5:将自定义导航器添加到NavController中

按照以下方式在MainActivity中添加您的导航器。确保传递NavHostFragmentchildFragmentManager

navController.getNavigatorProvider().addNavigator(new StickyFragmentNavigator(this, navHostFragment.getChildFragmentManager(),R.id.nav_host_fragment));

同时,使用setGraph()方法将导航图添加到NavController中,如下所示。

以下是经过步骤4步骤5后我的MainActivity的样子。

public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        BottomNavigationView navView = findViewById(R.id.nav_view);
       
        AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
                R.id.navigation_home, R.id.navigation_images, R.id.navigation_videos,R.id.navigation_songs,R.id.navigation_notifications)
                .build();
        NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
        
        final NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        navController.getNavigatorProvider().addNavigator(new StickyFragmentNavigator(this, navHostFragment.getChildFragmentManager(),R.id.nav_host_fragment));
        navController.setGraph(R.navigation.mobile_navigation);
        
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
        NavigationUI.setupWithNavController(navView,navController);
        
        navView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
                int id = menuItem.getItemId();
                if (menuItem.isChecked()) return false;

                switch (id)
                {
                    case R.id.navigation_home :
                        navController.navigate(R.id.action_global_navigation_home);
                        break;
                    case R.id.navigation_images :
                        navController.navigate(R.id.action_global_navigation_images);
                        break;
                    case R.id.navigation_videos :
                        navController.navigate(R.id.action_global_navigation_videos);
                        break;
                    case R.id.navigation_songs :
                        navController.navigate(R.id.action_global_navigation_songs);
                        break;
                    case R.id.navigation_notifications :
                        navController.navigate(R.id.action_global_navigation_notifications);
                        break;
                }
                return true;

            }
        });

    }

}

希望这能有所帮助。

谢谢,实际上我想保存在我的一个片段中点击的按钮,感谢你的帮助,兄弟。 - Kyle Mutta
我已经阅读了你的新问题,它与之前的问题相同。你可以更新这个问题,使其更具体,而不是创建一个新帖子。但无论如何,当我有空时,我会稍后发布一个适当的解决方案。 - Syed Ahmed Jamil
2
@KyleMutta 很抱歉让你等了一个多星期。我实际上卡在寻找一种需要最少代码量且不使用导航组件库的解决方案上。但最终我还是找到了一个。请阅读更新后的解决方案。它有点长,但值得一读。希望能对你有所帮助。 - Syed Ahmed Jamil
2
这应该不难。 - aligur
这难道不会导致内存膨胀,因为所有的片段都必须保留在内存中吗? - Sarah Multitasker
显示剩余6条评论

1
我不确定这是否是您要找的答案,但如果您关心管理状态,现代管理状态的方法是使用称为视图模型的东西。视图模型是MVVM架构的组成部分。它们的目的是持有和公开数据以便于您的片段/活动显示。使用导航架构,如果您将与每个片段相关的数据适当地存储在此视图模型中,则状态将保留在视图模型中。
话虽如此,我个人建议还应了解MVVM架构以及特定的视图模型。否则,保留状态的暴力方法是在片段中使用savedInstance状态,并手动保存和恢复重要数据。
链接: - Android View Model Component - Android architecture guide

0

我认为你可能需要防止在点击底部导航视图项两次时重新创建片段。在NavigationUI.setupWithNavController(bottomNavigationView, navHostFragment.getNavController());之后,加入bottomNavigationView.setOnNavigationItemReselectedListener { /*Nothing to ignore reselection*/}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接