177

在使用 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

在我修改的片段中onCreateonOptionsItemSelected如下所示:

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每当按下<时,它都会从活动中调用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;
        … 
    }
4

12 回答 12

83

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 有关3 行指示器行为的其他提示,请参阅在 Android 开发人员上创建导航抽屉。

于 2013-11-13T12:39:18.907 回答
30

您已经写过,要实现较低级别的片段,您将替换现有的片段,而不是在新活动中实现较低级别的片段。

我认为您将不得不手动实现后退功能:当用户按下后您有弹出堆栈的代码(例如在Activity::onBackPressed覆盖中)。因此,无论您在哪里执行此操作,都可以反转setDrawerIndicatorEnabled.

于 2013-06-29T00:39:26.873 回答
14

我用了下一个东西:

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);
                }
            }
        });
于 2015-11-23T09:44:38.790 回答
12

如果您的向上操作栏按钮不起作用,请不要忘记添加侦听器:

// Navigation back icon listener
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onBackPressed();
        }
});

我在使用主页按钮实现抽屉导航时遇到了一些麻烦,除了操作按钮之外,一切正常。

于 2016-01-14T21:52:31.633 回答
10

尝试根据 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);
    }
}
于 2013-08-02T02:33:37.577 回答
6

跟进

@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,这在我的情况下是必需的,否则我有时会出现向上插入符号的奇怪行为。

于 2013-10-02T08:02:14.863 回答
2

这个答案很有效,但有一点问题。没有显式调用,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);
        }

    }
于 2015-03-13T05:49:44.530 回答
2

我为托管活动创建了一个界面来更新汉堡菜单的视图状态。对于顶级片段,我将切换设置为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);
    }

}
于 2014-09-05T01:20:42.480 回答
1

如果您查看 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 以再次启用导航列表图标。

谢谢,小猫

于 2015-10-07T12:17:15.297 回答
1

你可以看看这个小例子! https://github.com/oskarko/NavDrawerExample

于 2016-09-26T15:56:21.333 回答
1

逻辑很清楚。如果片段返回堆栈已清除,则显示返回按钮。如果片段堆栈不清晰,则显示材质汉堡包动画。

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;
}
于 2015-09-07T12:58:25.277 回答
0

IMO,在riwnodennyk 或 Tom 的解决方案中使用 onNavigateUp()(如此处所示)更清洁,似乎效果更好。只需将 onOptionsItemSelected 代码替换为:

@Override
public boolean onSupportNavigateUp() {
    if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
        // handle up navigation
        return true;
    } else {
        return super.onSupportNavigateUp();
    }
}
于 2014-09-15T12:41:13.380 回答