后按后更新导航抽屉的选定状态
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)
...
【讨论】:
以上是关于后按后更新导航抽屉的选定状态的主要内容,如果未能解决你的问题,请参考以下文章
Flutter:在 Drawer 中更改 body 时保留状态