Android 在选定的底部导航视图项上重新创建的片段

Android 在选定的底部导航视图项上重新创建的片段,android,android-fragments,android-support-library,bottomnavigationview,Android,Android Fragments,Android Support Library,Bottomnavigationview,下面是我选择的底部导航视图项目的代码 bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { Fragment fragment = null;

下面是我选择的底部导航视图项目的代码

bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {  
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    Fragment fragment = null;
    switch (item.getItemId()) {
        case R.id.action_one:
            // Switch to page one
            fragment = FragmentA.newInstance();
            break;
        case R.id.action_two:
            // Switch to page two
            fragment = FragmentB.newInstance();
            break;
        case R.id.action_three:
            // Switch to page three
            fragment = FragmentC.newInstance();
            break;
    }
    getSupportFragmentManager().beginTransaction().replace(R.id.container,fragment,"TAG").commit();
    return true;
}
});
现在我的问题是每次重新创建片段时,我不希望每次尝试添加addToBackStack(null)时都重新创建片段,但在这种情况下,按“后退”按钮会不断从堆栈中弹出我不想要的片段

是否有任何方法可以在选定的底部导航栏上显示片段,而无需重新创建片段

请尝试以下操作:

bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
                @Override
                public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                    Fragment fragment = null;
                    Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.container);
                    switch (item.getItemId()) {
                        case R.id.action_one:
                            // Switch to page one
                            if (!(currentFragment instanceof FragmentA)) {
                                fragment = FragmentA.newInstance();
                            }
                            break;
                        case R.id.action_two:
                            // Switch to page two
                            if (!(currentFragment instanceof FragmentB)) {
                                fragment = FragmentB.newInstance();
                            }
                            break;
                        case R.id.action_three:
                            // Switch to page three
                            if (!(currentFragment instanceof FragmentC)) {
                                fragment = FragmentC.newInstance();
                            }
                            break;
                    }
                    getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment, "TAG").commit();
                    return true;
                }
            });

这将在您的
容器中获取当前片段,如果您再次单击此片段,则不会重新添加该片段。

setOnNavigationItemReselectedListener将是一个更好的解决方案,使用支持库v26,您可以这样做

FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

Fragment curFrag = mFragmentManager.getPrimaryNavigationFragment();
if (curFrag != null) {
    fragmentTransaction.detach(curFrag);
}

Fragment fragment = mFragmentManager.findFragmentByTag(tag);
if (fragment == null) {
    fragment = new YourFragment();
    fragmentTransaction.add(container.getId(), fragment, tag);
} else {
    fragmentTransaction.attach(fragment);
}

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

使用
更换
时要小心。即使提供内存中已经存在的片段,
replace
也会重新启动片段的生命周期。为了避免重新启动,事务对象的方法包括
add
show
hide
,这些方法可用于在不重新启动的情况下显示正确的片段

private fun switchFragment(selectedTabIndex: Int) {
    val previousTabIndex = this.currentTabIndex
    this.currentTabIndex = selectedTabIndex

    val transaction = supportFragmentManager.beginTransaction()
    val tag = fragments[this.currentTabIndex].tag

    // if the fragment has not yet been added to the container, add it first
    if (supportFragmentManager.findFragmentByTag(tag) == null) {
        transaction.add(R.id.container, fragments[this.currentTabIndex], tag)
    }

    transaction.hide(fragments[previousTabIndex])
    transaction.show(fragments[this.currentTabIndex])
    transaction.commit()
}

如下所示使用setOnNavigationItemReselectedListener:

private BottomNavigationView.OnNavigationItemReselectedListener onNavigationItemReselectedListener
            = new BottomNavigationView.OnNavigationItemReselectedListener() {

        @Override
        public void onNavigationItemReselected(@NonNull MenuItem item) {
            Toast.makeText(MainActivity.this, "Reselected", Toast.LENGTH_SHORT).show();
        }
    };
并使用以下命令调用它:

navigation.setOnNavigationItemReselectedListener(onNavigationItemReselectedListener);

在正确的导航中涉及到几个测试用例,我正在粘贴我的代码,并检查所有测试用例

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        switch (item.getItemId()) {
            case R.id.dashboard:
                Fragment fragment;
                fragment = fragmentManager.findFragmentByTag(DashboardFragment.TAG);

                if (fragment == null) {
                    fragment = new DashboardFragment();
                    fragmentTransaction.add(R.id.frame, fragment, DashboardFragment.TAG);
                } else {
                    fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
                    fragmentTransaction.attach(fragment);
                }
                fragmentTransaction.setPrimaryNavigationFragment(fragment);
                fragmentTransaction.commitNow();
                return true;
            case R.id.expenses:
                fragment = fragmentManager.findFragmentByTag(ExpenseFragment.TAG);
                if (fragment == null) {
                    fragment = new ExpenseFragment();
                    fragmentTransaction.add(R.id.frame, fragment, ExpenseFragment.TAG);
                } else {
                    fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
                    fragmentTransaction.attach(fragment);
                }
                fragmentTransaction.setPrimaryNavigationFragment(fragment);
                fragmentTransaction.commitNow();
                return true;
            case R.id.vehicle_parts:
                Bundle bundle = new Bundle();
                bundle.putInt("odometer", vehicle.getOdometer());
                bundle.putString("vehicle_id", vehicle.get_id());
                fragment = fragmentManager.findFragmentByTag(PartsFragment.TAG);
                if (fragment == null) {
                    fragment = new PartsFragment();
                    fragment.setArguments(bundle);
                    fragmentTransaction.add(R.id.frame, fragment, PartsFragment.TAG);

                } else {
                    fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
                    fragmentTransaction.attach(fragment);
                }
                fragmentTransaction.setPrimaryNavigationFragment(fragment);
                fragmentTransaction.commitNow();
                return true;
            case R.id.blog:
                fragment = fragmentManager.findFragmentByTag(BlogFragment.TAG);

                if (fragment == null) {
                    fragment = new BlogFragment();
                    fragmentTransaction.add(R.id.frame, fragment, BlogFragment.TAG);

                } else {
                    fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
                    fragmentTransaction.attach(fragment);
                }
                fragmentTransaction.setPrimaryNavigationFragment(fragment);
                fragmentTransaction.commitNow();
                return true;
        }
        return false;

我遇到了同样的问题,最后我找到了解决方案,你可以试试这段代码。这是我的工作

import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MenuItem;
import android.widget.FrameLayout;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
public BottomNavigationView bv;
public home_fragment home;
public account_fragment afrag;
public other_fragment other;
public FrameLayout fr;
android.support.v4.app.Fragment current;
//public FragmentTransaction frt;
    public static int temp=0;
    final Fragment fragment11 = new account_fragment();
    final Fragment fragment22 = new home_fragment();
    final Fragment fragment33 = new other_fragment();
    final FragmentManager fm = getSupportFragmentManager();
    Fragment active = fragment11;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bv=(BottomNavigationView) findViewById(R.id.navigationView);


        fm.beginTransaction().add(R.id.main_frame, fragment33, "3").hide(fragment33).commit();
        fm.beginTransaction().add(R.id.main_frame, fragment22, "2").hide(fragment22).commit();
        fm.beginTransaction().add(R.id.main_frame,fragment11, "1").commit();
bv.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {

            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.account:
                        fm.beginTransaction().hide(active).show(fragment11).commit();
                        active = fragment11;
                        return true;

                    case R.id.home1:
                        fm.beginTransaction().hide(active).show(fragment22).commit();
                        active = fragment22;
                        return true;

                    case R.id.other:
                        fm.beginTransaction().hide(active).show(fragment33).commit();
                        active = fragment33;
                        return true;
                }
                return false;
            }
        });
      bv.setOnNavigationItemReselectedListener(new BottomNavigationView.OnNavigationItemReselectedListener() {
          @Override
          public void onNavigationItemReselected(@NonNull MenuItem item) {
              Toast.makeText(MainActivity.this, "Reselected", Toast.LENGTH_SHORT).show();

          }
      });


    }

}

我改进了@Viven的答案,并与Kotlin一起编写了它。我的版本仅第一次实例化片段,隐藏/显示。我是科特林的新人,所以告诉我我是否可以改进

我们需要持有一个id来标记地图:

private val fragmentTags = hashMapOf(
        R.id.action_home to "home_fragment",
        R.id.action_profile to "profile_fragment"
)
侦听器代码:

bottomNavigation.run {
    setOnNavigationItemSelectedListener { menuItem ->
        supportFragmentManager.beginTransaction()
                .let { transaction ->
                    // hide current fragment
                    supportFragmentManager.primaryNavigationFragment?.let {
                        // if selected fragment's tag is same, do nothing.
                        if (it.tag == fragmentTags[menuItem.itemId]) {
                            return@setOnNavigationItemSelectedListener true
                        }

                        transaction.hide(it)
                    }

                    var fragment  = supportFragmentManager.findFragmentByTag(fragmentTags[menuItem.itemId])

                    if (fragment == null) {
                        // instantiate fragment for the first time
                        fragment = when(menuItem.itemId) {
                            R.id.action_home -> HomeFragment()
                            R.id.action_profile -> ProfileFragment()
                            else -> null
                        }?.also {
                            // and add it 
                            transaction.add(R.id.frame, it, fragmentTags[menuItem.itemId])
                        }
                    } else {
                        // if it's found show it 
                        transaction.show(fragment)
                    }


                    transaction
                            .setPrimaryNavigationFragment(fragment)
                            .setReorderingAllowed(true)
                }.commitNowAllowingStateLoss()

        return@setOnNavigationItemSelectedListener true
    }

    //bottomNavigation things
    itemIconTintList = null
    selectedItemId = R.id.action_home
}

我通过添加一个
ViewPager
解决了这个问题,我将所有导航片段都委托给了它。它的适配器(
FragmentPagerAdapter
)在用户浏览
BotoomNavigationView
时不会重新创建片段实例

要实现这一点,您必须完成5个简单的步骤:

  • 在布局中添加一个
    ViewPager
  • 实现其适配器:

    class YourNavigationViewPagerAdapter(fm: FragmentManager,
                                         private val param1: Int,
                                         private val param2: Int)
    
        : FragmentPagerAdapter(fm) {
    
        override fun getItem(p0: Int) = when(p0) {
            0 -> NavigationFragment1.newInstance(param1, param2)
            1 -> NavigationFragment2.newInstance(param1, param2)
            2 -> NavigationFragment3.newInstance(param1, param2)
            else -> null
        }
    
        override fun getCount() = 3
    }
    
  • 不要忘记设置新适配器:

    yourViewPager.adapter = YourNavigationViewPagerAdapter(supportFragmentManager, param1, param2)
    
  • 将导航项SelectedListener的
    设置为
    底部导航视图
    ,如下所示:

    yourBottomNavigationView.setOnNavigationItemSelectedListener {
    
        when(it.itemId) {
    
            R.id.yourFirstFragmentMenuItem -> {
                yourViewPager.currentItem = 0
                true
            }
    
            R.id.yourSecondFragmentMenuItem -> {
                yourViewPager.currentItem = 1
                true
            }
    
            R.id.yourThirdFragmentMenuItem -> {
                yourViewPager.currentItem = 2
                true
            }
    
    
            else -> false
        }
    }
    
    yourViewPager.addOnPageChangeListener(object : 
    ViewPager.OnPageChangeListener {
        override fun onPageScrollStateChanged(p0: Int) {
    
        }
    
        override fun onPageScrolled(p0: Int, p1: Float, p2: Int) {
    
        }
    
        override fun onPageSelected(p0: Int) {
            yourBottomNavigationView.menu.getItem(p0).isChecked = true
        }
    
    })
    
  • OnPageChangeListener
    设置为您的
    ViewPager
    ,如下所示:

    yourBottomNavigationView.setOnNavigationItemSelectedListener {
    
        when(it.itemId) {
    
            R.id.yourFirstFragmentMenuItem -> {
                yourViewPager.currentItem = 0
                true
            }
    
            R.id.yourSecondFragmentMenuItem -> {
                yourViewPager.currentItem = 1
                true
            }
    
            R.id.yourThirdFragmentMenuItem -> {
                yourViewPager.currentItem = 2
                true
            }
    
    
            else -> false
        }
    }
    
    yourViewPager.addOnPageChangeListener(object : 
    ViewPager.OnPageChangeListener {
        override fun onPageScrollStateChanged(p0: Int) {
    
        }
    
        override fun onPageScrolled(p0: Int, p1: Float, p2: Int) {
    
        }
    
        override fun onPageSelected(p0: Int) {
            yourBottomNavigationView.menu.getItem(p0).isChecked = true
        }
    
    })
    
  • 享受:)


  • 这似乎对我很有效。我使用show或hide来维护片段状态,而不是附加和分离

        public void changeFragment(Fragment fragment, String tagFragmentName) {
    
            FragmentManager mFragmentManager = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
    
            Fragment currentFragment = mFragmentManager.getPrimaryNavigationFragment();
            if (currentFragment != null) {
                fragmentTransaction.hide(currentFragment);
            }
    
            Fragment fragmentTemp = mFragmentManager.findFragmentByTag(tagFragmentName);
            if (fragmentTemp == null) {
                fragmentTemp = fragment;
                fragmentTransaction.add(R.id.frame_layout, fragmentTemp, tagFragmentName);
            } else {
                fragmentTransaction.show(fragmentTemp);
            }
    
            fragmentTransaction.setPrimaryNavigationFragment(fragmentTemp);
            fragmentTransaction.setReorderingAllowed(true);
            fragmentTransaction.commitNowAllowingStateLoss();
        }
    
    这就是我使用它的方式

         private void initViews() {
            BottomNavigationView bottomNavigationView = findViewById(R.id.navigation);
            bottomNavigationView.setOnNavigationItemSelectedListener
                    (new BottomNavigationView.OnNavigationItemSelectedListener() {
                        @Override
                        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                            Fragment selectedFragment = null;
                            switch (item.getItemId()) {
                                case R.id.explore:
                                    changeFragment(new ExploreFragment(), ExploreFragment.class
                                            .getSimpleName());
                                    toggleViews(true, "");
                                    break;
                                case R.id.favorite:
                                    changeFragment(new FavoriteFragment(), FavoriteFragment.class
                                            .getSimpleName());
                                    toggleViews(false, "Favorites");
                                    break;
                                case R.id.venue:
                                    changeFragment(new VenueFragment(), VenueFragment.class.getSimpleName());
                                    toggleViews(false, "Venues");
                                    break;
                                case R.id.profile:
                                    changeFragment(new ProfileFragment(), ProfileFragment.class
                                            .getSimpleName());
                                    toggleViews(false, "Profile");
                                    break;
                            }
                            return true;
                        }
                    });
    
            //Manually displaying the first fragment - one time only
            changeFragment(new ExploreFragment(), ExploreFragment.class
                    .getSimpleName());
    
        }
    

    我用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()
    }
    

    使用向导库轻松处理导航


    这是我找到的最好的方法

    private void replace_fragment(Fragment fragment) {
    
            String tag = fragment.getClass().getSimpleName();
            FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
    
            Fragment curFrag = getSupportFragmentManager().getPrimaryNavigationFragment();
            Fragment cacheFrag = getSupportFragmentManager().findFragmentByTag(tag);
    
            if (curFrag != null)
                tr.hide(curFrag);
    
            if (cacheFrag == null) {
                tr.add(R.id.main_frame, fragment, tag);
            } else {
                tr.show(cacheFrag);
                fragment = cacheFrag;
            }
    
            tr.setPrimaryNavigationFragment(fragment);
            tr.commit();
    
        }
    

    当再次单击当前选定的导航按钮时,此解决方案将停止重新创建片段。但是,每次用户单击不同的导航按钮时,都会创建一个新片段

    只需添加此行,以避免从
    BottomNavigationView

     bottomNavigation.setOnNavigationItemReselectedListener(new BottomNavigationView.OnNavigationItemReselectedListener() {
                @Override
                public void onNavigationItemReselected(@NonNull MenuItem item) {
                 // do nothing here   
                }
            });
    
    或使用lambda:

    bottomNavigation.setOnNavigationItemReselectedListener(item -> { });
    

    如何使用BottomNavigationView停止重新创建已可见的片段

    第一步--

    现在是第二步---


    没有必要这样做。实际上,您真正需要的是在特定的fragmet中保存视图

     private   View view;
    if(view == null){
        view = inflater.inflate(R.layout.call_fragment_edited, container, false);
    
    }
    

    因此,每当您创建新片段时,您都会看到当前状态

    为了避免这种情况,我必须创建一个映射:
    map
    ,每个片段都有一个静态id。我在
    活动的
    onCreate
    中创建所有片段。但不推荐这样做。您可以在
    backstack
    中添加带有唯一标记的片段,如
    fragment.class.getSimpleName()
    ,然后使用
    findFragmentByTag
    找到它并再次提交。请确保将每个
    片段
    添加到
    backstack
    Hi Raphael,正如我所说的,我不想将其添加到backstack,因为它会干扰back press实现,因为您可以在
    活动
    中覆盖
    onBackPressed()
    。注释
    super.onBackPressed()
    否,但如果用户执行操作,除了这些片段之外,还有一些片段将添加到堆栈中。我只是不想同时将与底部导航视图关联的片段添加到Backback中不想重新创建它们不点击底部导航栏项目查看它不会再次创建片段,再次单击back按钮时我可以重定向到主页…请帮助我Sir您可以
    覆盖
    功能
    on在主
    活动
    上反按
    。在您的代码中,如果加载了第一个片段,则在单击第二个片段后,再单击第二个片段后,如果用户单击第一个片段,则也是重新创建。@MiteshVanaliya是的,如果您不想每次都重新创建片段,请存储变量,或者将所有片段放在
    ViewPager
    中,并更改当前项。@Raphael Teyssandier当前片段返回null有关完整示例,请查看此git repo。如果要在返回和删除时保持状态/列表位置等,您认为这是处理三个目的地之间导航的最佳方法吗向前地使用这种方法有什么注意事项吗?可能的内存泄漏或任何需要注意的事项?我对生命周期不是百分之百的了解,是的,这样做会更好吗?或者,我认为恢复实例状态才是我真正想知道的!它似乎没有在记忆中保存碎片;它只是以某种方式使页面片段看起来分层(第1页在第2页下面查看)。使用
    show
    hide
    而不是
    attach
    detach
    效果更好。它不会重新启动片段的生命周期您在这里使用了稍微令人困惑的措辞,只是为了澄清:替换不会在片段添加到UI时重新启动片段的生命周期-它会结束当前附加的片段,然后添加新的片段,我们只能假设该片段在删除或删除时已被销毁
        private boolean loadFragment(Fragment fragment, String argument,MenuItem item) {
      
        if (fragment != null) {
           
            transaction = fragmentManager.beginTransaction();
            transaction.addToBackStack( argument );
            transaction.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_FADE );
            transaction.replace( R.id.frame_container, fragment,                         "demofragment").commitAllowingStateLoss();
    
         lastSelectedItemId= item.getItemId();
        
            return true;
        }
        return false;
    }
    
     private   View view;
    if(view == null){
        view = inflater.inflate(R.layout.call_fragment_edited, container, false);
    
    }