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

时间:2017-02-21 09:06:09

标签: android android-fragments android-support-library bottomnavigationview

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

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)时都要重新创建片段,但是这种情况下按下后退按钮会不断地从堆栈中弹出片段,我不会我想要。

有没有办法在选定的底部导航栏上显示片段而无需重新创建片段

16 个答案:

答案 0 :(得分:19)

使用支持库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();

答案 1 :(得分:7)

使用replace时要小心。即使提供已存在于内存中的片段,replace也会重新启动片段的生命周期。为避免重新启动,事务对象的方法包括addshowhide,可用于显示正确的片段而无需重新启动它。

private fun switchFragment(index: Int) {
    val transaction = supportFragmentManager.beginTransaction()
    val tag = fragments[index].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[index], tag)
    }

    transaction.hide(fragments[navigationBar.currentTabPosition])
    transaction.show(fragments[index])
    transaction.commit()
}

答案 2 :(得分:7)

只需添加此行即可避免从Fragment重新创建BottomNavigationView

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

答案 3 :(得分:5)

试试这个:

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;
                }
            });

这将获得container中的当前片段,如果再次单击此片段,则不会重新添加片段。

答案 4 :(得分:5)

我遇到了同样的问题,最后我找到了解决方案,您可以尝试下面的代码。这对我有用。

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();

          }
      });


    }

}

答案 5 :(得分:4)

像这样使用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);

答案 6 :(得分:4)

这对我来说似乎很好。而不是附加和分离,我使用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());

    }

答案 7 :(得分:3)

setOnNavigationItemReselectedListener将是一个更好的解决方案

答案 8 :(得分:1)

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

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

  1. 在您的布局中添加ViewPager
  2. 实现其适配器:

    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
    }
    
  3. 不要忘记设置新适配器:

    yourViewPager.adapter = YourNavigationViewPagerAdapter(supportFragmentManager, param1, param2)
    
  4. 为您的OnNavigationItemSelectedListener设置BottomNavigationView,如下所示:

    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
        }
    }
    
  5. 为您的OnPageChangeListener设置ViewPager,如下所示:

    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
        }
    
    })
    
  6. 享受:)

答案 9 :(得分:0)

正确导航涉及多个测试用例,我在检查所有测试用例时粘贴了我的代码。

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;

答案 10 :(得分:0)

我改进了@Viven的答案,并用Kotlin编写了。我的版本仅第一次实例化片段,隐藏/显示。我是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
}

答案 11 :(得分:0)

我在Kotlin中编写了一个Extension函数。

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

答案 12 :(得分:0)

使用Cicerone库轻松处理导航。

https://github.com/terrakok/Cicerone

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
    switch (menuItem.getItemId()) {
        case R.id.save: {
            router.replaceScreen("your fragment1");
            menuItem.setChecked(true);
            break;
        }
        case R.id.search_lessons: {
            router.replaceScreen("your fragment2");
            menuItem.setChecked(true);
            break;
        }
        case R.id.profile_student: {
            router.replaceScreen("your fragment3");
            menuItem.setChecked(true);
            break;
        }

    }
    return false;
}

答案 13 :(得分:0)

我发现的最好方法。

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();

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

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {

            switch (item.getItemId()) {
                case R.id.nav_posts:
                    replace_fragment(new PostsFragment());
                    return true;
                case R.id.nav_stores:
                    replace_fragment(new StoresFragment());
                    return true;
                case R.id.nav_chats:
                    replace_fragment(new DiscussionsFragment());
                    return true;
                case R.id.nav_account:
                    replace_fragment(new ProfileFragment());
                    return true;
            }
            return false;
        }
    };

答案 14 :(得分:0)

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

第1步-

     @Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    Fragment fragment = null;
    String valuestring;      
    **if (item.getItemId() == lastSelectedItemId) { // added this
        Log.e( "both id is same","LULL" );
        return true;
    }**
    switch (item.getItemId()) {
       case R.id.navigation_category:
      SetActioBarText( getString( R.string.label_actionbar_categories ) );
            fragment = new CategoryFragment();
            valuestring = "category";
            break;
        case R.id.navigation_favourite:
     SetActioBarText( getString( R.string.label_actionbar_favourites ) );
            fragment = new FavouriteFragment();
            valuestring = "favourites";
            break;
        default:
            throw new IllegalStateException( "Unexpected value: " +                  menuItem.getItemId() );
    }
    return loadFragment( fragment, valuestring ,menuItem);
}

现在步骤2 ---

    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;
}

答案 15 :(得分:0)

无需执行此答案。实际上,您真正需要的是将视图保存在特定的片段中。

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

}

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