BottomNavigationView - 如何避免重新创建片段并重用它们

Posted

技术标签:

【中文标题】BottomNavigationView - 如何避免重新创建片段并重用它们【英文标题】:BottomNavigationView - How to avoid recreation of Fragments and reuse them 【发布时间】:2017-12-21 04:37:24 【问题描述】:

我想在我的项目中制作一个底部导航栏。每个视图都有它自己的片段。问题是,每次我单击按钮以将视图更改为例如从最近浏览到收藏夹时,它都会创建具有全新状态的新片段(例如滚动位置,文本更改了我的片段包含的任何内容)。我知道在官方 android 文档中写到底部导航栏应该重置任务状态,但我认为这对用户来说太不舒服了。 我希望有一种类似的功能,例如 instagram,您可以从提要更改为探索并返回到提供图像缓存的滚动位置,所有内容都保持保存。我几乎尝试了所有方法来解决这个问题,唯一有效的方法是根据情况设置可见性 GONE 和设置可见性 VISIBLE,但我知道这不是正确的方法,应该有更好的方法,我不是在手动谈论保存所需的实例。我几乎遵循了所有关于底部导航片段的教程,但有趣的是,没有人有兴趣在不每次调用 new 的情况下使用它。

FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, FirstFragment.newInstance());
fragmentTransaction.commit();

bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() 
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) 
        Fragment fragment = null;
        switch (item.getItemId()) 
            case R.id.menu_dialer:
                fragment = FirstFragment.newInstance();
                break;
            case R.id.menu_email:
                fragment = SecondFragment.newInstance();
                break;
            case R.id.menu_map:
                fragment = ThirdFragment.newInstance();
                break;
        
        if (fragment != null) 
            FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            fragmentTransaction.replace(R.id.frameLayout, fragment);
            fragmentTransaction.commit();
        
        return true;
    
);

我也尝试了 onAttach 和 Deattach 解决方案,但仍然没有成功。

VIDEO LINK : new i tried Nino Handler version it only works when i tap on the same fragment button

可能与我使用金丝雀版本或我的 gradle 依赖项有问题有关?

新的更新:

public class MainActivity extends AppCompatActivity 

    private TextView mTextMessage;


    private static final String TAG_FRAGMENT_ONE = "fragment_one";
    private static final String TAG_FRAGMENT_TWO = "fragment_two";

    private FragmentManager fragmentManager;
    private Fragment currentFragment;

    String TAG = "babken";
    private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
            = new BottomNavigationView.OnNavigationItemSelectedListener() 

        Fragment fragment = null;
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) 
            switch (item.getItemId()) 
                case R.id.navigation_home:
                   fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
                    if (fragment == null) 
                        fragment = FragmentFirst.newInstance();
                    
                    replaceFragment(fragment, TAG_FRAGMENT_ONE);

                    break;
                case R.id.navigation_dashboard:

                     fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_TWO);
                    if (fragment == null) 
                        fragment = FragmentSecond.newInstance();
                    
                    replaceFragment(fragment, TAG_FRAGMENT_TWO);

                    break;
            
            return true;

        
    ;

    private void replaceFragment(@NonNull Fragment fragment, @NonNull String tag) 
        if (!fragment.equals(currentFragment)) 
            fragmentManager
                    .beginTransaction()
                    .replace(R.id.armen, fragment, tag)
                    .commit();
            currentFragment = fragment;
        
    
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        fragmentManager = getSupportFragmentManager();
        Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
        if (fragment == null) 
            fragment = FragmentFirst.newInstance();
        
        replaceFragment(fragment, TAG_FRAGMENT_ONE);
        BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
    


【问题讨论】:

【参考方案1】:

我有类似的问题,但这段代码解决了我的问题。

public class MainActivity extends AppCompatActivity 

    boolean doubleBackToExitPressedOnce = false;
    final Fragment fragment1 = new HomeFragment();
    final Fragment fragment2 = new DashboardFragment();
    final Fragment fragment3 = new NotificationsFragment();
    final FragmentManager fm = getSupportFragmentManager();
    Fragment active = fragment1;
    BottomNavigationView navigation;

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        navigation = (BottomNavigationView) findViewById(R.id.navigation);
        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
        setFragment(fragment1, "1", 0);
    


    private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
            = new BottomNavigationView.OnNavigationItemSelectedListener() 

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) 
            switch (item.getItemId()) 
                case R.id.navigation_home:
                    setFragment(fragment1, "1", 0);
                    return true;
                case R.id.navigation_dashboard:
                    setFragment(fragment2, "2", 1);
                    return true;
                case R.id.navigation_notifications:
                    setFragment(fragment3, "3", 2);
                    return true;
            
            return false;
        
    ;

    public void setFragment(Fragment fragment, String tag, int position) 
        if (fragment.isAdded()) 
            fm.beginTransaction().hide(active).show(fragment).commit();
         else 
            fm.beginTransaction().add(R.id.main_container, fragment, tag).commit();
        
        navigation.getMenu().getItem(position).setChecked(true);
        active = fragment;
    

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return super.onCreateOptionsMenu(menu);
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        int id = item.getItemId();

        if (id == R.id.action_settings) 
            startActivity(new Intent(MainActivity.this, SettingsActivity.class));
            return true;
        

        return super.onOptionsItemSelected(item);
    

    @Override
    public void onBackPressed() 
        if (active == fragment1) 
            if (doubleBackToExitPressedOnce) 
                super.onBackPressed();
                return;
            
            this.doubleBackToExitPressedOnce = true;
            Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();
         else 
            setFragment(fragment1, "1", 0);
        
    

【讨论】:

你可能犯了一个小错误......如果代码是为了避免重新创建新实例,那么这一点。 这是一个有趣的方法。感激不尽 隐藏和显示是否调用了任何生命周期方法。?在显示片段任何想法时如何为回收站视图设置动画。 这个答案是错误。如果不首先尝试使用findFragmentByTag 获取它,您不能假设创建一个片段的新实例。这将在进程死亡后中断。 @RahulPawar 如果您不想在导航时重新创建片段,则应先添加它们,在 savedInstanceState != null 时使用 findFragmentByTag 获取它们,否则附加/分离(或显示/隐藏) 它们在您导航时。我之前写了一个示例,您可以在这里看到:github.com/Zhuinden/BottomNavChildFragmentExample/blob/…【参考方案2】:

我不会全局保留片段实例。 而是在创建片段时向片段添加标签

getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.container, new PlaceholderFragment(), TAG_PLACEHOLDER)
            .commit();

然后你总是可以像这样检索它:

Fragment fragment = getSupportFragmentManager().findFragmentByTag(TAG_PLACEHOLDER);
    if (fragment == null) 
        fragment = new PlaceholderFragment();
    
    getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.container, fragment, TAG_PLACEHOLDER)
            .commit();

更新:我更新了我的答案并提供了一个完整的解决方案:

private static final String TAG_FRAGMENT_ONE = "fragment_one";
private static final String TAG_FRAGMENT_TWO = "fragment_two";
private static final String TAG_FRAGMENT_THREE = "fragment_three";

private FragmentManager fragmentManager;
private Fragment currentFragment;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    // instantiate the fragment manager
    fragmentManager = getSupportFragmentManager();

    Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
    if (fragment == null) 
        fragment = FirstFragment.newInstance();
    
    replaceFragment(fragment, TAG_FRAGMENT_ONE);

    bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
    bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() 
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) 
            Fragment fragment = null;
            switch (item.getItemId()) 
                case R.id.menu_dialer:
                    // I'm aware that this code can be optimized by a method which accepts a class definition and returns the proper fragment
                    Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
                    if (fragment == null) 
                        fragment = FirstFragment.newInstance();
                    
                    replaceFragment(fragment, TAG_FRAGMENT_ONE);
                    break;
                case R.id.menu_email:
                    Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_TWO);
                    if (fragment == null) 
                        fragment = SecondFragment.newInstance();
                    
                    replaceFragment(fragment, TAG_FRAGMENT_TWO);
                    break;
                case R.id.menu_map:
                    Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_THREE);
                    if (fragment == null) 
                        fragment = ThirdFragment.newInstance();
                    
                    replaceFragment(fragment, TAG_FRAGMENT_THREE);
                    break;
            
            return true;
        
    );


private void replaceFragment(@NonNull Fragment fragment, @NonNull String tag) 
    if (!fragment.equals(currentFragment)) 
        fragmentManager
            .beginTransaction()
            .replace(R.id.frameLayout, fragment, tag)
            .commit();
        currentFragment = fragment;
    

附加信息:如果您想确保片段状态不会改变,并且如果您还希望能够滑动片段,您应该考虑使用ViewPager 和FragmentStatePagerAdapter 并更改当前片段在每次点击事件的适配器中

【讨论】:

我试过了,我放在问题底部的链接检查一下 你使用 3 个不同的标签吗? 我如何向您发送个人信息 to.show you my.code ? 我更新了问题,它再次给出了相同的输出。我不知道是什么问题。您正在使用此代码并保留所有片段状态?你是什​​么安卓工作室版本?我不认为这是问题,但正如你所看到的,我也在做同样的事情,但它对我不起作用 尝试在你的片段中添加 setRetainInstance(true)【参考方案3】:

我为 FragmentManager 类写了一个 Kotlin 扩展函数

fun FragmentManager.switch(containerId: Int, newFrag: Fragment, tag: String) 

    var current = findFragmentByTag(tag)
    beginTransaction()
        .apply 

            //Hide the current fragment
            primaryNavigationFragment?.let  hide(it) 

            //Check if current fragment exists in fragmentManager
            if (current == null) 
                current = newFrag
                add(containerId, current!!, tag)
             else 
                show(current!!)
            
        
        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
        .setPrimaryNavigationFragment(current)
        .setReorderingAllowed(true)
        .commitNowAllowingStateLoss()

这可以由supportFragmentManager.swtich(R.id.container,newFrag,newFrag.TAG)onNavigationItemSelected调用

【讨论】:

【参考方案4】:

之前的所有答案都使用fragmentTransaction.replace(...)。这将通过销毁它来替换当前片段(这会导致问题)。因此,所有这些解决方案实际上都不起作用。

这是我能找到的最接近此问题的解决方案:

private void selectContentFragment(Fragment fragmentToSelect)

    FragmentTransaction fragmentTransaction = this.getSupportFragmentManager().beginTransaction();

    if (this.getSupportFragmentManager().getFragments().contains(fragmentToSelect)) 
        // Iterate through all cached fragments.
        for (Fragment cachedFragment : this.getSupportFragmentManager().getFragments()) 
            if (cachedFragment != fragmentToSelect) 
                // Hide the fragments that are not the one being selected.
                fragmentTransaction.hide(cachedFragment);
            
        
        // Show the fragment that we want to be selected.
        fragmentTransaction.show(fragmentToSelect);
     else 
        // The fragment to be selected does not (yet) exist in the fragment manager, add it.
        fragmentTransaction.add(R.id.fragment_container, fragmentToSelect);
    

    fragmentTransaction.commit();

要完成这项工作,您应该在 Activity 中跟踪数组(或单独的变量)中的片段。我在 SparseArray 中预先实例化了所有片段以供参考。

【讨论】:

我试过了,它可以工作,但有一个小问题。【参考方案5】:

您可以使用 attach() 和 detach() 方法:

private FirstFragment firstFragment = FirstFragment.newInstance();
private SecondFragment secondFragment= SecondFragment.newInstance();
private ThirdFragment thirdFragment = ThirdFragment.newInstance();

navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() 
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) 
            switch (item.getItemId()) 
                case R.id.menu_dialer:
                    changeFragment(firstFragment, "firstFragment");
                    return true;
                case R.id.menu_email:
                    changeFragment(secondFragment, "secondFragment");
                    return true;
                case R.id.menu_map:
                    changeFragment(thirdFragment, "thirdFragment");
                    return true;
            
            return false;
        
    );

public void changeFragment(Fragment fragment, String tagFragmentName) 

    FragmentManager mFragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

    Fragment currentFragment = mFragmentManager.getPrimaryNavigationFragment();
    if (currentFragment != null) 
        fragmentTransaction.detach(currentFragment);
    

    Fragment fragmentTemp = mFragmentManager.findFragmentByTag(tagFragmentName);
    if (fragmentTemp == null) 
        fragmentTemp = fragment;
        fragmentTransaction.add(R.id.content, fragmentTemp, tagFragmentName);
     else 
        fragmentTransaction.attach(fragmentTemp);
    

    fragmentTransaction.setPrimaryNavigationFragment(fragmentTemp);
    fragmentTransaction.setReorderingAllowed(true);
    fragmentTransaction.commitNowAllowingStateLoss();

【讨论】:

【参考方案6】:

所以我已经为此研究了很长时间,并发现无法重用具有自动保存状态的片段,您必须手动保存所需的状态,然后在创建新片段时检索它们,但是滚动呢定位它太难了,甚至在某些情况下也无法保存滚动视图位置状态(例如回收站视图)。因此,当我单击片段变得可见的按钮时,我使用了称为可见性的概念,其他片段会自动隐藏。

【讨论】:

嗨,我也有同样的问题..你能给我一些关于解决方案或代码的细节吗? 您自己的问题是否有效?我也有同样的问题。 @ArmenHovhannisian @ArmenHovhannisian If someone need more detailed explanation about VISIBILITY.VISIBLE and VISIBILITY.GONE comment i will go through details你能提供更多细节吗? 这没有提供实际代码作为参考。不应该被接受的答案 我认为@Bukunmi 的答案应该被接受而不是这个。【参考方案7】:

怎么样。 您在类中声明片段

Fragment firstFragment,secondFragment,thirdFragment;

然后在 switch-case 中你可以这样编码:

switch (item.getItemId()) 
    case R.id.menu_dialer:
        if(firstFragment != null) 
            fragment = firstFragment;
        else
            fragment = FirstFragment.newInstance();
        
        break;
    case R.id.menu_email:
        // the same ...
        break;
    case R.id.menu_map:
        //the same ...
        break;

【讨论】:

我试过了,但让我再检查一次,也许我错过了什么。 每次创建新片段时【参考方案8】:

Thomas 的回答几乎帮助了我,但有一个问题是,每当我第一次打开新片段时,它们会重叠,但一旦我通过按菜单按钮再次打开它们,它们就不会重叠。

于是我修改了他的代码,用下面的代码得到了解决方案:

 private fun selectContentFragment(fragmentToSelect: Fragment) 
        val fragmentTransaction = fragmentManager?.beginTransaction()
        if (fragmentManager?.fragments?.contains(fragmentToSelect)!!) 
            // Show the fragment that we want to be selected.
            fragmentTransaction?.show(fragmentToSelect)
         else 
            // The fragment to be selected does not (yet) exist in the fragment manager, add it.
            fragmentTransaction?.add(R.id.container, fragmentToSelect)
        
        // Iterate through all cached fragments.
        for (cachedFragment in fragmentManager?.fragments!!) 
            if (cachedFragment !== fragmentToSelect) 
                // Hide the fragments that are not the one being selected.
                // Uncomment following line and change the name of the fragment if your host isn't an activity and a fragment otherwise whole view will get hidden.
                // if (!cachedFragment.toString().contains("HomeContainerFragment"))

                fragmentTransaction?.hide(cachedFragment)
            
        
        fragmentTransaction?.commit()
    

确保您不是每次都传递片段的新实例。

这会起作用:

selectContentFragment(
                    when (item.itemId) 
                        R.id.home -> frag1
                        R.id.photoGallery -> frag2
                        else -> Home()
                    
            )

其中frag1frag2 是定义为的全局变量:

 val frag1 = Home()
 val frag2 = PhotoGallery()

这不起作用:

selectContentFragment(
                    when (item.itemId) 
                        R.id.home -> Home()
                        R.id.photoGallery -> PhotoGallery()
                        else -> Home()
                    
            )

这浪费了我几个小时。希望对其他人有所帮助!

【讨论】:

这个方法如何处理backstack?【参考方案9】:

**这段代码对我很有帮助。就像优酷一样。 **

private Deque<Integer> fragmentIds = new ArrayDeque<>(3);
int itemId;
private HomeFragment homeFragment = new HomeFragment();
private FavouriteFragment favouriteFragment = new FavouriteFragment();
private NearmeFragment nearmeFragment = new NearmeFragment();
BottomNavigationView bottomNavigationView;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    fragmentIds.push(R.id.action_home);
    showTabWithoutAddingToBackStack(homeFragment);
    bottomNavigationView = findViewById(R.id.bottom_navigation);
    bottomNavigationView.setOnNavigationItemSelectedListener(onNavigationItemClicked);


private BottomNavigationView.OnNavigationItemSelectedListener onNavigationItemClicked = new BottomNavigationView.OnNavigationItemSelectedListener() 
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) 
        itemId= item.getItemId();
        if(fragmentIds.contains(itemId))
            fragmentIds.remove(itemId);
        
        fragmentIds.push(itemId);
        showTabWithoutAddingToBackStack(getFragment(item.getItemId()));
        return true;
    
;

private Fragment getFragment(int fragmentId) 
    switch (fragmentId) 
        case R.id.action_home:
            return homeFragment;
        case R.id.action_favorites:
            return favouriteFragment;
        case R.id.action_nearme:
            return nearmeFragment;
    
    return homeFragment;


private void showTabWithoutAddingToBackStack(Fragment fragment) 
    getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName()).commit();


@Override
public void onBackPressed() 
    if(fragmentIds.getLast() != R.id.action_home)
            fragmentIds.addLast(R.id.action_home);
    
    fragmentIds.pop();
    bottomNavigationView.getMenu().getItem(fragmentIds.size()-1).setChecked(true);
    if (!fragmentIds.isEmpty()) 
        showTabWithoutAddingToBackStack(getFragment(fragmentIds.peek()));
     else 
        finish();
    

【讨论】:

【参考方案10】:

对于 Kotlin 用户可以帮助此代码:

首先创建一个FragmentManager扩展类

fun FragmentManager.replace(containerId: Int, fragment: Fragment, tag: String) 
var current = findFragmentByTag(tag)
beginTransaction()
    .apply 
        //Hide the current fragment
        primaryNavigationFragment?.let  hide(it) 

        //Check if current fragment exists in fragmentManager
        if (current == null) 
            current = fragment
            add(containerId, current!!, tag)
         else 
            show(current!!)
        
    
    .setTransition(FragmentTransaction.TRANSIT_ENTER_MASK)
    .setPrimaryNavigationFragment(current)
    .setReorderingAllowed(true)
    .commitNowAllowingStateLoss()

现在只需调用你的

 onNavigationItemSelected



supportFragmentManager.replace(R.id.fragment_id,YourFragmentClass,yourFragment.TAG) 

【讨论】:

【参考方案11】:

我遇到了同样的问题,但我通过创建多个片段主机解决了这个问题,然后我在片段主机中添加了片段。当底部导航 itemSelected 时,我只是使所需的主机 Fragment 可见,而其他主机 FragmentsHost 的可见性消失了。

这种方法可能是错误的,但它对我来说非常有效。 而且我们不必手动处理片段的状态,只需要处理活动的回压即可。 但是这个方法不会暂停片段。

而且我不知道如何处理片段的暂停和恢复,所以请回复我。

也许这会对你有所帮助。

这是我的 fragmentHosts 主页活动:

<androidx.fragment.app.FragmentContainerView
        android:id="@+id/homeFragmentHost"
        android:layout_
        android:layout_
        app:layout_constraintBottom_toTopOf="@+id/bottom_NavBar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:visibility="gone"/>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/libraryFragmentHost"
        android:layout_
        android:layout_
        app:layout_constraintBottom_toTopOf="@+id/bottom_NavBar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:visibility="gone"/>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/myStuffFragmentHost"
        android:layout_
        android:layout_
        app:layout_constraintBottom_toTopOf="@+id/bottom_NavBar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:visibility="gone"/>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/moreFragmentHost"
        android:layout_
        android:layout_
        app:layout_constraintBottom_toTopOf="@+id/bottom_NavBar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:visibility="gone"/>

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_NavBar"
        android:layout_
        android:layout_
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/app_bottom_nav_menu"
        tools:ignore="BottomAppBar" />

Home Activity onCreate:

supportFragmentManager.beginTransaction().replace(R.id.homeFragmentHost,HomeFragment()).commitNow()
    supportFragmentManager.beginTransaction().replace(R.id.libraryFragmentHost,LibraryFragment()).commitNow()
    supportFragmentManager.beginTransaction().replace(R.id.myStuffFragmentHost,MyStuffFragment()).commitNow()
    supportFragmentManager.beginTransaction().replace(R.id.moreFragmentHost,MoreFragment()).commitNow()
    homeFragmentHost.visibility = View.VISIBLE

底部导航项选择的侦听器

    override fun onNavigationItemSelected(item: MenuItem): Boolean 
    return when(item.itemId)
        R.id.homeFragment -> loadFragmentHost(homeFragmentHost)
        R.id.libraryFragment -> loadFragmentHost(libraryFragmentHost)
        R.id.myStuffFragment -> loadFragmentHost(myStuffFragmentHost)
        R.id.moreFragment -> loadFragmentHost(moreFragmentHost)
        else -> false
    

loadFragmentHost 函数

    private fun loadFragmentHost(view:FragmentContainerView): Boolean 
    val list = arrayListOf(homeFragmentHost,libraryFragmentHost,myStuffFragmentHost,moreFragmentHost)
    list.remove(view)
    view.visibility = View.VISIBLE
    list.forEach 
        it.visibility = View.GONE
    
    return true

【讨论】:

【参考方案12】:
public class MainActivity extends AppCompatActivity 

    boolean doubleBackToExitPressedOnce = false;
    final Fragment fragment1 = new HomeFragment();
    final Fragment fragment2 = new DashboardFragment();
    final Fragment fragment3 = new NotificationsFragment();
    final FragmentManager fm = getSupportFragmentManager();
    Fragment active = fragment1;
    BottomNavigationView navigation;

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        navigation = (BottomNavigationView) findViewById(R.id.navigation);
        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
        setFragment(fragment1, "1", 0);
    


    private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
            = new BottomNavigationView.OnNavigationItemSelectedListener() 

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) 
            switch (item.getItemId()) 
                case R.id.navigation_home:
                    setFragment(fragment1, "1", 0);
                    return true;
                case R.id.navigation_dashboard:
                    setFragment(fragment2, "2", 1);
                    return true;
                case R.id.navigation_notifications:
                    setFragment(fragment3, "3", 2);
                    return true;
            
            return false;
        
    ;

    public void setFragment(Fragment fragment, String tag, int position) 
        if (fragment.isAdded()) 
            fm.beginTransaction().hide(active).show(fragment).commit();
         else 
            fm.beginTransaction().add(R.id.main_container, fragment, tag).commit();
        
        navigation.getMenu().getItem(position).setChecked(true);
        active = fragment;
    

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return super.onCreateOptionsMenu(menu);
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        int id = item.getItemId();

        if (id == R.id.action_settings) 
            startActivity(new Intent(MainActivity.this, SettingsActivity.class));
            return true;
        

        return super.onOptionsItemSelected(item);
    

    @Override
    public void onBackPressed() 
        if (active == fragment1) 
            if (doubleBackToExitPressedOnce) 
                super.onBackPressed();
                return;
            
            this.doubleBackToExitPressedOnce = true;
            Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();
         else 
            setFragment(fragment1, "1", 0);
        
    

    维护底部工作表片段Reusable BackPress 维护 双击退出

【讨论】:

【参考方案13】:
public class MainActivity extends AppCompatActivity 
    BottomNavigationView bottomNavigationView;
    Toaster toaster;
    private final Fragment androidFragment = new AndroidFragment();
    private final Fragment settingsFragment = new SettingsFragment();
    Fragment active;    
    String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) 

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    toaster = new Toaster(this);
    bottomNavigationView = findViewById(R.id.bottom_navigation);
    renderFragment(androidFragment, "android");
    active = androidFragment;
    bottomNavigationView.setOnItemSelectedListener(
            item -> 
                Log.e(TAG, "onCreate: " + active );
                if(item.getItemId() == R.id.action_android)
                    renderFragment(androidFragment, "android");
                    return true;
                
                else if(item.getItemId() == R.id.action_settings)
                    renderFragment(settingsFragment, "settings");
                    return true;
                
                return false;
            
    );


private void renderFragment(Fragment fragment, String tag)
    FragmentManager fragmentManager = getSupportFragmentManager();
    if(fragment.isAdded())
        fragmentManager.beginTransaction().hide(active).show(fragment)
                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).commit();
    
    else 
        if(active != null)
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container_view, fragment, tag)
                    .hide(active)
                    .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).commit();
        
        else 
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container_view, fragment, tag)
                    .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).commit();
        
    
    active = fragment;


这对我有用,伙计们。

【讨论】:

【参考方案14】:

将三个片段创建为类的成员并重用它们。

public class MainActivity extends AppCompatActivity 
    private final Fragment mFirstFragment = new FirstFragment();
    private final Fragment mSecondFragment = new SecondFragment();
    private final Fragment mThirdFragment = new ThirdFragment();

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        ...... 
        ......
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.frameLayout, mFirstFragment);
        fragmentTransaction.commit();

        bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() 
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) 
                if (bottomNavigationView.getSelectedItemId() != item.getItemId()) 
                    switch (item.getItemId()) 
                        R.id.menu_dialer:
                            replaceFragment(mFirstFragment);
                            break;
                        case R.id.menu_email:
                            replaceFragment(mSecondFragment);
                            break;
                        case R.id.menu_map:
                            replaceFragment(mThirdFragment);
                            break;
                    
                
                return true;
            
        );
    

    private void replaceFragment(Fragment fragment) 
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.frameLayout, fragment);
        fragmentTransaction.commit();
    


【讨论】:

甚至没有尝试很明显它会创建新的片段。 可以,但只有一次。 但是为什么每次点击只有一次它会转到获取片段并再次创建新片段的方法。 你错了。片段创建一次,每次调用replaceFragment 时都会重复使用。您可以自己尝试并确认。 输出与先前答案的情况相同,链接位于问题的底部。

以上是关于BottomNavigationView - 如何避免重新创建片段并重用它们的主要内容,如果未能解决你的问题,请参考以下文章

如何在 BottomNavigationView 的片段上打开搜索界面?

如何禁用 BottomNavigationView 移位模式?

如何在Android的BottomNavigationView中设置填充项

如何将 BottomNavigationView 与 NavigationView 一起对齐到 Activity 的底部?

Android关于BottomNavigationView效果实现指南

Android关于BottomNavigationView效果实现指南