点击返回按钮时更改BottomNavigationView图标

3
在我的布局底部,我有一个带有三个片段的 BottomNavigationView 。如果我点击返回按钮,片段会切换但是底部图标不会改变。我该如何解决这个问题?

addToBackStack()有效。也许您有一些建议可以使代码更简洁。

在活动或片段中拥有片段标签是好的做法吗?
public class MainActivity extends AppCompatActivity {

    private FragmentManager mFragmentManager;
    private BottomNavigationView mBottomNavigationView;
    private static final String HOME_FRAGMENT = "homeFragment";
    private static final String SEARCH_FRAGMENT = "searchFragment";
    private static final String SHARE_FRAGMENT = "shareFragment";
    private boolean isFirstFragment;
    private long mBackPressedTime;

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

            init();
            setBottomNavigationView();
        }

        private void init() {
            mBottomNavigationView = findViewById(R.id.bottomNavigationView);

            mFragmentManager = getSupportFragmentManager();
            mBackPressedTime = 0;
        }

        private void setBottomNavigationView() {
            setFragment(HomeFragment.newInstance(), HOME_FRAGMENT);
            isFirstFragment = true;
            mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
                @Override
                public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                    switch (item.getItemId()) {
                        case R.id.ic_home:
                            setFragment(HomeFragment.newInstance(), HOME_FRAGMENT);
                            return true;
                        case R.id.ic_search:
                            setFragment(SearchFragment.newInstance(), SEARCH_FRAGMENT);
                            return true;
                        case R.id.ic_circle:
                            setFragment(ShareFragment.newInstance(), SHARE_FRAGMENT);
                            return true;

                        default:
                            return false;
                    }
                }
            });
        }

        private void setFragment(Fragment fragment, String tag) {
            FragmentTransaction transaction = mFragmentManager.beginTransaction();
            transaction.replace(R.id.container, fragment, tag);
            if (isFirstFragment) {
                transaction.addToBackStack(tag);
            }
            transaction.commit();
        }

        @Override
        public void onBackPressed() {
            final long currentTimeMillis = System.currentTimeMillis();
            if (mFragmentManager.getBackStackEntryCount() > 0) {
                mFragmentManager.popBackStack();
            } else if (currentTimeMillis - mBackPressedTime > 2000) {
                mBackPressedTime = currentTimeMillis;
                Toast.makeText(this, getString(R.string.reach_homescreen), Toast.LENGTH_SHORT).show();
            } else {
                super.onBackPressed();
            }
        }
    }
4个回答

5

@Hans Baum,尝试使用以下代码而不是将您的第一个片段添加到返回堆栈中:

    @Override
    public void onBackPressed() {
        if(mBottomNavigationView.getSelectedItemId () != R.id.ic_home)
        {
            mBottomNavigationView.setSelectedItemId(R.id.ic_home);
        }
        else
        {
            super.onBackPressed();
        }     
    }

如果您在主页片段中,则此代码将退出您的活动,否则如果您在任何其他片段中,则会转到主页片段。因此,无需添加addToBackStack()。因此,您的serFragment()方法应该是这样的,
  private void setFragment(Fragment fragment, String tag) {
        FragmentTransaction transaction = mFragmentManager.beginTransaction();
        transaction.replace(R.id.container, fragment, tag);
        transaction.commit();
    }

希望能帮到你。 请注意,在你的代码中,你从未将false分配给isFirstFragment,因此我假设所有片段都被添加到了后退栈中,这会消耗大量内存。
更新: 由于您的要求不同,您想要像Instagram一样的选项卡,我希望这个实现能帮助到您。
    Deque<Integer> mStack = new ArrayDeque<>();
    boolean isBackPressed  = false;

    private void setBottomNavigationView() {
        mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.ic_home:
                         if(!isBackPressed) {
                           pushFragmentIntoStack(R.id.ic_home);
                       }
                        isBackPressed = false
                        setFragment(HomeFragment.newInstance(), HOME_FRAGMENT);
                        return true;
                    case R.id.ic_search:
                        if(!isBackPressed) {
                           pushFragmentIntoStack(R.id.ic_search);
                       }
                        isBackPressed = false
                        setFragment(SearchFragment.newInstance(), SEARCH_FRAGMENT);
                        return true;
                    case R.id.ic_circle:
                        if(!isBackPressed) {
                           pushFragmentIntoStack(R.id.ic_circle);
                       }
                        isBackPressed = false
                        setFragment(ShareFragment.newInstance(), SHARE_FRAGMENT);
                        return true;

                    default:
                        return false;
                }
            }
        });
        mBottomNavigationView.setOnNavigationItemReselectedListener(new 
              BottomNavigationView.OnNavigationItemReselectedListener() {
            @Override
            public void onNavigationItemReselected(@NonNull MenuItem item) {

            }
        });
     mBottomNavigationView.setSelectedItemId(R.id.ic_home);
     pushFragmentIntoStack(R.id.ic_home);
    }

    private void pushFragmentIntoStack(int id)
    {
        if(mStack.size() < 3)
        {
            mStack.push(id);
        }
        else
        {
            mStack.removeLast();
            mStack.push(id);
        }
    }

    private void setFragment(Fragment fragment, String tag) {
        FragmentTransaction transaction = mFragmentManager.beginTransaction();
        transaction.replace(R.id.container, fragment, tag);
        transaction.commit();
    }

    @Override
    public void onBackPressed() {
        if(mStack.size() > 1)
        {
            isBackPressed = true;
            mStack.pop();
            mBottomNavigationView.setSelectedItemId(mStack.peek());
        }
        else 
        {
            super.onBackPressed();
        }
    }

我使用了双端队列来存储标签被点击的顺序,因为有3个标签,所以队列大小为3。在后退键按下时,它将弹出堆栈并跳转到该选项卡。如果没有项目在堆栈中,则会退出活动。

重要提示: 在这种情况下不要将片段添加到返回堆栈中,因为随着选项卡的切换,返回堆栈计数将不断增加,并可能导致内存溢出异常。

对于问题2: 关于您提出的Fragment标签问题,将标签保存在Fragment中是一个好习惯。由于Fragment可以在任何活动中重复使用,而不是将标签添加到每个使用它的活动中,最好在Fragment本身中添加标签。


假设你只想将主页片段添加到backStack中,那么你应该这样做:case R.id.ic_home: setFragment(HomeFragment.newInstance(), HOME_FRAGMENT); isFirstFragment = false; return true; - Velmurugan V
但是如果你遵循提出的答案,你将永远不需要这个 isFirstFragment 变量本身。只需尝试一下 :) - Velmurugan V
如果出现了 oops,那么情况就有点棘手了,我的答案就不适用了。如果你期望的行为是每当我点击返回时都回到主页然后退出,那么它可以工作。 - Velmurugan V
我想要在backStack中的每个片段。返回按钮应该调用我所在的最后一个片段。 - Hans Baum
@HansBaum 我更新了我的回答,请看看它是否能帮助到您。我使用了双端队列(deque)来存储按下的标签页(tab)的顺序,在后退按钮按下时回到该标签页。如果deque中没有标签页,则退出该活动。 - Velmurugan V
显示剩余11条评论

0

试试这个:

@Override
public void onBackPressed() {
    if(navigation_bottom.getSelectedItemId () != R.id.action_home)
    {
        FragmentTransaction transaction = 
        ((HomeActivity)this).getSupportFragmentManager().beginTransaction();
        transaction.replace(R.id.frameLayout_home, new HomeFragment());
        transaction.commit();
        navigation_bottom.setSelectedItemId(R.id.action_home);
    }
    else
    {
        HomeActivity.this.finishAffinity();
    }     
}

0

你可以在片段事务中将片段名称添加到后退栈中

        getFragmentManager()
            .beginTransaction()
            .replace(R.id.frame, YourCurrentFragment.newInstance())
            .addToBackStack(YourFragment.class.getName())
            .commit();

然后,您可以添加 onBackstackChangedListener 来获取当前选择的片段。

fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            FragmentManager.BackStackEntry bse = fragmentManager.
                    getBackStackEntryAt(fragmentManager.getBackStackEntryCount() -1);
            //bse will be the backstack entry for current fragment
            if (bse.getName().equals(YourFragment.class.getName())) {
                navigationView.getMenu().getItem(0).setChecked(true);
            } else if (bse.getName().equals(YourSecondFragment.class.getName())) {
                navigationView.getMenu().getItem(1).setChecked(true);
            }
        }
    });

0

这是一个旧问题。

如果您正在从未来阅读此内容,并使用导航组件,则这是一种声明性的方式(自动)在页面更改时更新底部图标。

基于此文档 使用 NavigationUI 更新 UI 组件。 除非 Google 再次进行任何更改

首先将目的地与菜单项绑定

这意味着,只需将 android:id 属性的值提供给 MenuItem,与您的 <navigation> 的子项片段相同

在菜单/your_menu_file.xml 中

<menu>
  <item
    android:id="@+id/navigation__fragment_home"
    android:title="@string/example_label_nav_home"/>
    
  <item
    android:id="@+id/navigation__fragment_about"
    android:title="@string/example_label_nav_about"/>
</menu>

在导航/your_navigation_file.xml中

<navigation>
  <fragment
    android:id="@+id/navigation__fragment_home"
    tools:layout="@layout/fragment_page_home"/>
    
  <fragment
    android:id="@+id/navigation__fragment_about"
    tools:layout="@layout/fragment_page_about"/>
</navigation>

其次,获取NavHostFragment以检索NavController根据此文档
NavHostFragment navHostFragment =
        (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.your_FragmentContainerView);
NavController navController = navHostFragment.getNavController();

最好将navHostFragmentnavController放置并保存到活动类中,以便您可以重复使用它。
最后,根据此文档调用setupWithNavController()来设置您的BottomNavigationView(这也适用于顶部应用栏)。
BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);
NavigationUI.setupWithNavController(bottomNav, navController);

现在,实际上您不必监听BottomNavigationView的任何点击事件和返回按钮事件。

BottomNavigationView会自动更改所选项目,包括导航。


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