后按后更新导航抽屉的选定状态

Posted

技术标签:

【中文标题】后按后更新导航抽屉的选定状态【英文标题】:Update selected state of navigation drawer after back press 【发布时间】:2015-03-05 15:41:22 【问题描述】:

按下后处理导航抽屉的选定状态的正确方法是什么?

我有一个导航抽屉,其中包含 n 个条目(在列表视图中),例如 android Studio 中的 SDK 示例。 当我单击导航抽屉条目时,我希望将它们添加到后台堆栈中,以便我可以移回它们。

在 onNavigationDrawerItemSelected(int pos) 我有

        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        if (position == 0) 
            transaction.replace(R.id.container, new FragmentA());
         else if (position == 1) 
            transaction.replace(R.id.container, new FragmentB());
         else 
            transaction.replace(R.id.container, new FragmentC());
        
        transaction.addToBackStack(null);
        transaction.commit();

当我单击抽屉中的第二个条目时,B 被选中并替换 A。如果我随后单击后退按钮,片段 A 会再次按原样显示,但 B 在导航抽屉中仍处于选中状态。

如何在按下返回后更新抽屉的选择状态?

不知何故,我需要调用 mDrawerListView.setItemChecked(position, true);或 NavigationDrawerFragment.selectItem(int position)。但是到哪个位置呢?我怎么记住它?

使用 onBackPressed 拦截?

@Override
    public void onBackPressed() 

但是我怎么知道哪个片段再次处于活动状态?以及对应的位置。

有一些我看不到的简单解决方案吗?似乎将返回与抽屉导航结合使用并更新选择状态是一种标准模式。

【问题讨论】:

【参考方案1】:

此模式在“正确的后退导航”文档的Implement Back Navigation for Fragments 部分中进行了描述。

如果您的应用程序更新其他用户界面元素以反映 您的片段的当前状态,例如操作栏,请记住 提交事务时更新 UI。你应该更新 您的用户界面在返回堆栈更改后除了何时 你提交交易。 您可以在 FragmentTransaction 通过设置一个 FragmentManager.OnBackStackChangedListener

getSupportFragmentManager().addOnBackStackChangedListener(
    new FragmentManager.OnBackStackChangedListener() 
        public void onBackStackChanged() 
            // Update your UI here.
        
    );

这将是刷新导航抽屉中当前选项的合适位置。

【讨论】:

这仅回答何时更新的问题。我也可以使用 onBackPressed()。主要问题是应该在抽屉中突出显示哪个位置?我可能需要在 onNavigationDrawerItemSelected 中获取旧的、现在“活动”的片段和映射片段-> 位置反转。也许使用 setTargetFragment 将位置存储为请求代码? @spatialist 是的,这会起作用,然后在 Fragment 的onCreateView() 中通知抽屉。尽管如此,我仍然建议使用监听器而不是onBackPressed()。例如,按 Back 可能实际上不会更改当前片段(例如关闭搜索视图)。【参考方案2】:

今天我也有同样的挣扎。我通过在每个片段和我唯一的 MainActivity 中实现一个接口 FragmentToActivity 来解决它。

界面:

public interface FragmentToActivity 
    public void SetNavState(int id);

片段:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) 
    ((FragmentToActivity) getActivity()).SetNavState(R.id.nav_myitemid); //just add this line

MainActivity:

@Override
public void SetNavState(int id) 
    NavigationView nav = (NavigationView) findViewById(R.id.nav_view);
    nav.setCheckedItem(id);

如果你不知道R.id.nav_myitemid 来自哪里,这里是我的activity_main_drawer.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">    
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_myitemid"
            android:title="@string/home" />
    </group>
</menu>

【讨论】:

太棒了!但是我有一个问题,为什么你使用接口函数而不是普通的公共函数? @KhanStan99 他需要回调,setCheckedItem 只能从父活动而不是 Fragment 完成, 又好又干净!【参考方案3】:

我在试图找出解决这个问题的方法时遇到了很多麻烦,我最终使用的解决方案在我看来并不“好”,但它确实有效。

在我的应用程序中,当我将片段添加到后台堆栈时,我最终在导航抽屉中添加了片段位置。这样我就可以用我的 onBackStackChanged() 中的位置调用 setItemChecked。所以基本上我所做的是:

@Override
public void onBackStackChanged() 
    FragmentManager fm = getFragmentManager();
    String name = fm.getBackStackEntryAt(fm.getBackStackEntryCount()-1).getName();
    mDrawerList.setItemChecked(Integer.parseInt(name),true);


public class DrawerItemClickListener implements android.widget.AdapterView.OnItemClickListener 
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) 
        selectItem(position);
    


private void selectItem(int position) 
    Fragment fragment;

    switch (position) 
        case 0: // Home
            fragment = new ContentFragment();
            break;
        case 1: // Another
           fragment = new AnotherFragment();
            break;
        default: // Logout
        
            finish();

            Intent intent = new Intent(this, StartupActivity.class);
            startActivity(intent);

            return;
        
    

    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.beginTransaction()
            .replace(R.id.content_frame, fragment)
            .addToBackStack(Integer.toString(position))
            .commit();

    mDrawerList.setItemChecked(position, true);
    setTitle(mNavigationItems[position]);
    mDrawerLayout.closeDrawer(mDrawerList);

【讨论】:

哇,这太棒了。谢谢! 非常感谢 :) 不得不花足够的时间以丑陋的方式,直到找到你的答案。 :)【参考方案4】:

以@matiash 的出色回答为基础(如果您 +1 我的回答,请务必 +1 他/她)

首先,我们假设我们在Activity

getFragmentManager().addOnBackStackChangedListener(
    new FragmentManager.OnBackStackChangedListener() 
        public void onBackStackChanged() 
            // Update your UI here.
        
    );

您可以通过以下方式找到当前片段:

(来自https://***.com/a/24589081/878159)

Fragment f = getFragmentManager().findFragmentById(R.id.frag_container);
if (f instanceof CustomFragmentClass) 
    // do something with f
    f.doSomething();

这应该让你更近一步。然后这样的事情会让你参考你的导航抽屉:

nav = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.nav);

然后取决于你如何设置你的抽屉片段代码:

nav.updateSelected(position);

updateSelected(int) 方法将在您的 NavigationDrawerFragment 类中,可能看起来像这样:

public void updateSelected(int position) 
    mCurrentSelectedPosition = position;
    if (mDrawerListView != null) 
        mDrawerListView.setItemChecked(position, true);
    

注意:有很多方法可以做到这一点,但这应该可以帮助您入门。

【讨论】:

【参考方案5】:

其实很简单。您已经在 Drawer 布局中拥有导航视图。对于每个按下的项目,它都会打开相应的片段,对吗? 现在转到每个片段的onCreateView 并执行此操作...

NavigationView navigationView = getActivity().findViewById(R.id.navigation_view);
        navigationView.setCheckedItem(R.id.this_fragment's_item);

另外,在您处理项目点击的主要活动中,使用 if 语句,这样

if (navigation_view.getMenu().findItem(R.id.account_nav).isChecked())   
  drawer_layout.closeDrawer(GravityCompat.START);

以上避免了在其自身之上创建片段的多个实例。

【讨论】:

这是最好的解决方案【参考方案6】:

这已经很晚了,但是对于那些使用导航菜单而不是列表的人来说,我是这样做的

@SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) 
        // Handle navigation view item clicks here.
       int position=0;
       Fragment fragment;
        int id = item.getItemId();
        if (id == R.id.nav_home) 
            fragment=new HomeFragment();
            position=0;

         else if (id == R.id.nav_profile) 

           fragment=new ProfileFragment();
            position=1;
          else if (id == R.id.nav_settings) 
             fragment=new SettingsFragment();
            position=2;
        

          getSupportFragmentManager()
            .beginTransaction()
            .add(R.id.relative_container, fragment)
            .addToBackStack(""+position)
            .commit();

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    

并为 backstack 添加监听器

 getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() 
        @Override
        public void onBackStackChanged() 

             FragmentManager fm = getSupportFragmentManager();
             String name = fm.getBackStackEntryAt(fm.getBackStackEntryCount()-1).getName();
               if(!TextUtils.isEmpty(name))
             selectNavigationDrawerItem(Integer.parseInt(name));

        
    );

selectNavigationDrawerItem 方法

 private void selectNavigationDrawerItem(int position)

               if(position==0)
                   navigationView.setCheckedItem(R.id.nav_home);
               
               else if(position==1)
                   navigationView.setCheckedItem(R.id.nav_profile);
               
               else if(position==2)
                   navigationView.setCheckedItem(R.id.nav_settings);   
              

     

【讨论】:

【参考方案7】:

这是 kotlin 中的一个解决方案。

在你想要被抽屉选中并突出显示的片段中,你只需这样做:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
    super.onViewCreated(view, savedInstanceState)

    val navView: NavigationView = activity!!.findViewById(R.id.nav_view)
    navView.setCheckedItem(R.id.name_of_this_fragment)

    ...

【讨论】:

以上是关于后按后更新导航抽屉的选定状态的主要内容,如果未能解决你的问题,请参考以下文章

Vue 使用 vuex 从另一个组件控制和更新状态

Flutter:在 Drawer 中更改 body 时保留状态

牛轧糖状态栏上的导航抽屉?

在导航抽屉片段中保存视图页面片段的状态

Android - 为带有导航抽屉和应用栏的应用完全隐藏状态和导航栏

Android Kitkat 4.4版本状态栏下使用导航抽屉时状态栏颜色显示为白色不透明