12

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

我有一个带有 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() {}

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

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

4

7 回答 7

8

此模式在“正确的后退导航”文档的“片段的实现后退导航”部分中进行了描述。

如果您的应用程序更新其他用户界面元素以反映片段的当前状态,例如操作栏,请记住在提交事务时更新 UI。除了提交事务之外,您还应该在返回堆栈更改后更新您的用户界面。您可以通过设置 a 来监听 a 何时 FragmentTransactionFragmentManager.OnBackStackChangedListener恢复:

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

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

于 2015-01-07T17:48:32.220 回答
7

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

界面:

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
}

主要活动:

@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>
于 2017-02-03T14:52:31.330 回答
5

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

在我的应用程序中,当我将片段添加到后台堆栈时,我最终在导航抽屉中添加了片段位置。这样我就可以用我的 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);
}
于 2015-03-27T13:13:18.150 回答
3

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

首先,我们假设我们在Activity

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

您可以使用以下内容找到当前片段:

(来自https://stackoverflow.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);
    }
}

注意:有很多方法可以做到这一点,但这应该让你开始。

于 2015-05-27T21:41:37.877 回答
1

其实很简单。您已经在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);

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

于 2021-02-24T16:05:50.190 回答
0

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

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

     }
于 2017-09-04T07:21:15.263 回答
0

这是 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)

    ...
}
于 2020-05-17T22:45:55.323 回答