【问题标题】:Change BottomNavigationView Icons on Back Button clicked单击后退按钮时更改 BottomNavigationView 图标
【发布时间】:2020-09-14 02:13:54
【问题描述】:

在我的布局底部,我有一个 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();
            }
        }
    }

【问题讨论】:

    标签: java android xml


    【解决方案1】:

    @Hans Baum 不要将您的第一个片段添加到后端堆栈,请尝试此代码,

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

    如果您在 Home Fragment 中,此代码将退出您的活动,否则如果您在任何其他 Fragment 中,它将转到 Home Fragment。 所以不需要 addToBackStack() 。所以你的 serFragment() 方法应该是,

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

    希望对您有所帮助。 请注意,在您的代码中,您从未将 false 分配给 isFirstFragment,因此我假设所有片段都添加到非常消耗内存的 backstack。

    更新: 由于您的要求不同。因为您希望 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();
            }
        }
    

    我使用 deque 来存储单击选项卡的顺序因为有 3 个选项卡,所以 deque 大小为 3 。在后按时它将弹出堆栈并转到该选项卡。如果堆栈中没有项目,它将退出活动。

    重要提示: 在这种情况下不要将片段添加到 backstack,因为当我切换选项卡时,backstack 计数会不断增加,甚至可能导致 MemoryOutOfBound 异常。

    至问题 2: 来到您的 Fragment 标签问题,最好将标签保存在 fragment 中。由于片段可以在任何活动中重复使用,而不是在每个使用它的活动中添加标签,所以如果我们将它添加到片段本身中会很好。

    【讨论】:

    • 我应该在哪里分配 isFirstFragment = false; ?
    • 我想要像 Instagram 应用一样的后退按钮。它应该查看堆栈顶部并切换到该片段
    • 我认为只将主片段添加到 backStack 是您的意图,您应该这样做case R.id.ic_home: setFragment(HomeFragment.newInstance(), HOME_FRAGMENT); isFirstFragment = false return true;
    • 但是,如果您遵循建议的答案,您将永远不需要这个 isFirstFragment 变量本身。试试吧 :)
    • 哎呀,然后它变得有点棘手,我的 ans 将无法正常工作。如果您想要的行为是每当我单击返回时它必须回家然后退出,它就可以工作。
    【解决方案2】:

    试试这个:

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

    【讨论】:

      【解决方案3】:

      您可以在分片交易中添加分片名称在backstack中

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

      然后可以添加onBackstackChangedListener来获取当前选中的fragment

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

      【讨论】:

        猜你喜欢
        • 2022-10-14
        • 2014-02-16
        • 1970-01-01
        • 2017-01-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-09
        相关资源
        最近更新 更多