在使用 Navigation Drawer 时,Android 开发人员建议在 ActionBar 中“只有那些在 Navigation Drawer 中表示的屏幕实际上应该具有 Navigation Drawer 图像”,并且“所有其他屏幕都具有传统的 up carat”。
有关详细信息,请参见此处:http://youtu.be/F5COhlbpIbY
我正在使用一个活动来控制多个级别的片段,并且可以让导航抽屉图像在所有级别上显示和运行。
创建较低级别的片段时,我可以调用 ActionBarDrawerToggle
setDrawerIndicatorEnabled(false)
来隐藏导航抽屉图像并显示向上插入符号
LowerLevelFragment lowFrag = new LowerLevelFragment();
//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout,
lowFrag, "lowerFrag").addToBackStack(null).commit();
我遇到的问题是,当我导航回顶层片段时,Up 克拉仍然显示,而不是原始的 Navigation Drawer 图像。关于如何“刷新”顶级片段上的 ActionBar 以重新显示 Navigation Drawer 图像的任何建议?
解决方案
汤姆的建议对我有用。这是我所做的:
主要活动
此活动控制应用程序中的所有片段。
在准备新片段以替换其他片段时,我将 DrawerToggle setDrawerIndicatorEnabled(false)
设置为:
LowerLevelFragment lowFrag = new LowerLevelFragment();
//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout,
lowFrag).addToBackStack(null).commit();
接下来,在覆盖 onBackPressed
时,我通过将 DrawerToggle 设置为 setDrawerIndicatorEnabled(true)
来恢复上述内容,如下所示:
@Override
public void onBackPressed() {
super.onBackPressed();
// turn on the Navigation Drawer image;
// this is called in the LowerLevelFragments
setDrawerIndicatorEnabled(true)
}
在 LowerLevelFragments
在我这样修改 onCreate
和 onOptionsItemSelected
的片段中:
在 onCreate
中添加了 setHasOptionsMenu(true)
以启用配置选项菜单。还要设置 setDisplayHomeAsUpEnabled(true)
以启用操作栏中的 <:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// needed to indicate that the fragment would
// like to add items to the Options Menu
setHasOptionsMenu(true);
// update the actionbar to show the up carat/affordance
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}
然后在 onOptionsItemSelected
中,每当按下 < 时,它都会从 Activity 调用 onBackPressed()
以在层次结构中向上移动一级并显示导航抽屉图像:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Get item selected and deal with it
switch (item.getItemId()) {
case android.R.id.home:
//called when the up affordance/carat in actionbar is pressed
getActivity().onBackPressed();
return true;
…
}
.replace(R.id.frag_layout
。如果这是另一个层次结构级别,我希望您将其.add
放到后台堆栈中。
theDrawerToggle.setDrawerIndicatorEnabled(false);
?我认为它是在 Main Activity 类文件中声明的。我找不到引用这个的方法。有什么提示吗?
ToolbarWidgetWrapper
中的 setDisplayOptions()
方法(内部 android.support.v7.internal.widget 包)在第二次输入同一片段时不会重新创建图标。当其他人也偶然发现这个问题时,将其留在这里。
1-2-3 很简单。
如果你想达到:
1) Drawer Indicator - 当 Back Stack 中没有碎片或 Drawer 已打开时
2) 箭头 - 当一些 Fragments 在 Back Stack 中时
private FragmentManager.OnBackStackChangedListener
mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
syncActionBarArrowState();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mDrawerToggle = new ActionBarDrawerToggle(
this,
mDrawerLayout,
R.drawable.ic_navigation_drawer,
0,
0
) {
public void onDrawerClosed(View view) {
syncActionBarArrowState();
}
public void onDrawerOpened(View drawerView) {
mDrawerToggle.setDrawerIndicatorEnabled(true);
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
getSupportFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener);
}
@Override
protected void onDestroy() {
getSupportFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener);
super.onDestroy();
}
private void syncActionBarArrowState() {
int backStackEntryCount =
getSupportFragmentManager().getBackStackEntryCount();
mDrawerToggle.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}
3) 两个指标根据它们的形状起作用
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mDrawerToggle.isDrawerIndicatorEnabled() &&
mDrawerToggle.onOptionsItemSelected(item)) {
return true;
} else if (item.getItemId() == android.R.id.home &&
getSupportFragmentManager().popBackStackImmediate()) {
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
PS 请参阅 Creating a Navigation Drawer on Android Developers,了解有关 3 线指示器行为的其他提示。
您已经写过,要实现较低级别的片段,您将替换现有的片段,而不是在新活动中实现较低级别的片段。
我认为您必须手动实现后退功能:当用户按下后您有弹出堆栈的代码(例如在 Activity::onBackPressed
覆盖中)。因此,无论您在哪里执行此操作,都可以反转 setDrawerIndicatorEnabled
。
我用了下一个东西:
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
if(getSupportFragmentManager().getBackStackEntryCount() > 0){
mDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
else {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
mDrawerToggle.setDrawerIndicatorEnabled(true);
}
}
});
drawerToggle.setToolbarNavigationClickListener(
单击箭头时会调用此侦听器
如果您的向上操作栏按钮不起作用,请不要忘记添加侦听器:
// Navigation back icon listener
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
我在使用主页按钮实现抽屉导航时遇到了一些麻烦,除了操作按钮之外,一切正常。
尝试根据 DrawerToggle 的状态处理 MainActivity 中的 Home 项选择。这样您就不必为每个片段添加相同的代码。
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Only handle with DrawerToggle if the drawer indicator is enabled.
if (mDrawerToggle.isDrawerIndicatorEnabled() &&
mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
// Handle action buttons
switch (item.getItemId()) {
// Handle home button in non-drawer mode
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
onBackPressed()
中处理后台堆栈,因为我希望两者具有相同的行为。
跟进
@dzeikei 给出的解决方案很简洁,但可以扩展,当使用片段时,当后台堆栈为空时自动处理设置抽屉指示器。
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Only handle with DrawerToggle if the drawer indicator is enabled.
if (mDrawerToggle.isDrawerIndicatorEnabled() &&
mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
// Handle action buttons
switch (item.getItemId()) {
// Handle home button in non-drawer mode
case android.R.id.home:
// Use getSupportFragmentManager() to support older devices
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.popBackStack();
// Make sure transactions are finished before reading backstack count
fragmentManager.executePendingTransactions();
if (fragmentManager.getBackStackEntryCount() < 1){
mDrawerToggle.setDrawerIndicatorEnabled(true);
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
编辑
对于@JJD 的问题。
片段在活动中保存/管理。上述代码在该活动中编写一次,但仅处理 onOptionsItemSelected
的向上插入符号。
在我的一个应用程序中,我还需要处理按下后退按钮时向上插入符号的行为。这可以通过覆盖 onBackPressed
来处理。
@Override
public void onBackPressed() {
// Use getSupportFragmentManager() to support older devices
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.executePendingTransactions();
if (fragmentManager.getBackStackEntryCount() < 1){
super.onBackPressed();
} else {
fragmentManager.executePendingTransactions();
fragmentManager.popBackStack();
fragmentManager.executePendingTransactions();
if (fragmentManager.getBackStackEntryCount() < 1){
mDrawerToggle.setDrawerIndicatorEnabled(true);
}
}
};
请注意 onOptionsItemSelected
和 onBackPressed
之间的代码重复,这可以通过创建一个方法并在两个地方调用该方法来避免。
另请注意,我再添加两次 executePendingTransactions
,在我的情况下这是必需的,否则我有时会出现向上插入符号的奇怪行为。
NavigationDrawerFragment
类中维护 mDrawerToggle
。为了让它工作,我还需要切换主页按钮/指示器的状态 - 请参阅:NavigationDrawerFragment#toggleDrawerIndicator
。此外,我不确定您是否需要在 onOptionsItemSelected
中进行初始检查:我取消了它的注释。 - Simplified example
onOptionsItemSelected
中的初始检查是正确的。这确保了导航抽屉仍然在顶级层次结构中打开。但是,我将代码移到了 NavigationDrawerFragment#onOptionsItemSelected
。这有助于我不将 mDrawerToggle
暴露给 MainActivity
。
我为托管活动创建了一个界面来更新汉堡菜单的视图状态。对于顶级片段,我将切换设置为 true
,对于我想要显示向上 < 的片段箭头 我将切换设置为 false
。
public class SomeFragment extends Fragment {
public interface OnFragmentInteractionListener {
public void showDrawerToggle(boolean showDrawerToggle);
}
private OnFragmentInteractionListener mListener;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
this.mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener");
}
}
@Override
public void onResume() {
super.onResume();
mListener.showDrawerToggle(false);
}
}
然后在我的活动中......
public class MainActivity extends Activity implements SomeFragment.OnFragmentInteractionListener {
private ActionBarDrawerToggle mDrawerToggle;
public void showDrawerToggle(boolean showDrawerIndicator) {
mDrawerToggle.setDrawerIndicatorEnabled(showDrawerIndicator);
}
}
此 answer 工作正常,但出现了一点问题。 getSupportActionBar().setDisplayHomeAsUpEnabled(false)
没有被显式调用,即使后台堆栈中没有项目,它也会导致抽屉图标被隐藏,因此更改 setActionBarArrowDependingOnFragmentsBackStack()
方法对我有用。
private void setActionBarArrowDependingOnFragmentsBackStack() {
int backStackEntryCount = getSupportFragmentManager()
.getBackStackEntryCount();
// If there are no items in the back stack
if (backStackEntryCount == 0) {
// Please make sure that UP CARAT is Hidden otherwise Drawer icon
// wont display
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
// Display the Drawer Icon
mDrawerToggle.setDrawerIndicatorEnabled(true);
} else {
// Show the Up carat
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Hide the Drawer Icon
mDrawerToggle.setDrawerIndicatorEnabled(false);
}
}
逻辑很清楚。如果片段返回堆栈已清除,则显示返回按钮。如果片段堆栈不清晰,则显示材质汉堡包动画。
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
syncActionBarArrowState();
}
}
);
private void syncActionBarArrowState() {
int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
mNavigationDrawerFragment.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}
//add these in Your NavigationDrawer fragment class
public void setDrawerIndicatorEnabled(boolean flag){
ActionBar actionBar = getActionBar();
if (!flag) {
mDrawerToggle.setDrawerIndicatorEnabled(false);
actionBar.setDisplayHomeAsUpEnabled(true);
mDrawerToggle.setHomeAsUpIndicator(getColoredArrow());
} else {
mDrawerToggle.setDrawerIndicatorEnabled(true);
}
mDrawerToggle.syncState();
getActivity().supportInvalidateOptionsMenu();
}
//download back button from this(https://www.google.com/design/icons/) website and add to your project
private Drawable getColoredArrow() {
Drawable arrowDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_arrow_back_black_24dp);
Drawable wrapped = DrawableCompat.wrap(arrowDrawable);
if (arrowDrawable != null && wrapped != null) {
// This should avoid tinting all the arrows
arrowDrawable.mutate();
DrawableCompat.setTint(wrapped, Color.GRAY);
}
return wrapped;
}
如果您查看 GMAIL 应用程序并来这里搜索 carret/affordance 图标..
我会要求你这样做,以上答案都不清楚。我能够修改接受的答案。
NavigationDrawer --> Listview 包含子片段。
子片段将像这样列出
firstFragment == 位置 0 ---> 这将有子片段 --> 片段
第二个片段
第三个片段等等....
在 firstFragment 中,您还有其他片段。
在 DrawerActivity 上调用它
getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
if (getFragmentManager().getBackStackEntryCount() > 0) {
mDrawerToggle.setDrawerIndicatorEnabled(false);
} else {
mDrawerToggle.setDrawerIndicatorEnabled(true);
}
}
});
并在片段中
setHasOptionsMenu(true);
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Get item selected and deal with it
switch (item.getItemId()) {
case android.R.id.home:
//called when the up affordance/carat in actionbar is pressed
activity.onBackPressed();
return true;
}
return false;
}
在 OnBackPressed Drawer 活动方法上,将抽屉切换设置为 true 以再次启用导航列表图标。
谢谢,小猫
IMO,在 riwnodennyk 或 Tom 的解决方案中使用 onNavigateUp()(如 here 所示)更清洁,似乎效果更好。只需将 onOptionsItemSelected 代码替换为:
@Override
public boolean onSupportNavigateUp() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
// handle up navigation
return true;
} else {
return super.onSupportNavigateUp();
}
}
不定期副业成功案例分享
setActionBarArrowDependingOnFragmentsBackStack()
...多么长的名字:P