Android - 将ActionBar返回按钮切换为导航按钮

63
我有以下问题:
我知道如何设置工具栏以显示返回按钮图标,而不是汉堡菜单图标。
从这里: enter image description here 到这里: enter image description here 使用: getSupportActionBar().setDisplayHomeAsUpEnabled(true); 现在,我想要相反的操作,我想从返回按钮图标改为汉堡菜单图标:
从这里: enter image description here 到这里: enter image description here 我该怎么做?
更新:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setSupportActionBar(mToolbar);
    getSupportActionBar().setDisplayShowTitleEnabled(false);
}

private void enableViews(boolean enable) {
    if(enable) {
        // Enables back button icon
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    } else {
        // TODO: Enables burger icon
    }
}

也许这个链接可以帮到你:https://dev59.com/A14c5IYBdhLWcg3wT41O#28072236 - Omid Heshmatinia
13个回答

137
如果我假设您在布局中使用了android.support.v4.widget.DrawerLayout,那么这种方法可能适用于您; 我只在API 21上进行了测试,但考虑到它主要使用支持库,它应该可以在更低或更高的目标上工作(著名的最后一句话)。
import android.support.v7.app.ActionBarDrawerToggle
import android.support.v4.widget.DrawerLayout

    ActionBarDrawerToggle mDrawerToggle;
    DrawerLayout drawerLayout;
    private boolean mToolBarNavigationListenerIsRegistered = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setSupportActionBar(mToolbar);
        getSupportActionBar().setDisplayShowTitleEnabled(false);
        // Get DrawerLayout ref from layout
        drawerLayout = (DrawerLayout)findViewById(R.id.drawer);
        // Initialize ActionBarDrawerToggle, which will control toggle of hamburger.
        // You set the values of R.string.open and R.string.close accordingly.
        // Also, you can implement drawer toggle listener if you want.
        mDrawerToggle = new ActionBarDrawerToggle (this, drawerLayout, mToolbar, R.string.open, R.string.close);
        // Setting the actionbarToggle to drawer layout
        drawerLayout.addDrawerListener(mDrawerToggle);
        // Calling sync state is necessary to show your hamburger icon...
        // or so I hear. Doesn't hurt including it even if you find it works
        // without it on your test device(s)
        mDrawerToggle.syncState();
    }

    /**
     * To be semantically or contextually correct, maybe change the name
     * and signature of this function to something like:
     *
     * private void showBackButton(boolean show)
     * Just a suggestion.
     */
     private void enableViews(boolean enable) {

        // To keep states of ActionBar and ActionBarDrawerToggle synchronized,
        // when you enable on one, you disable on the other.
        // And as you may notice, the order for this operation is disable first, then enable - VERY VERY IMPORTANT.
        if(enable) {
            //You may not want to open the drawer on swipe from the left in this case  
            drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
            // Remove hamburger
            mDrawerToggle.setDrawerIndicatorEnabled(false);
            // Show back button
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            // when DrawerToggle is disabled i.e. setDrawerIndicatorEnabled(false), navigation icon
            // clicks are disabled i.e. the UP button will not work.
            // We need to add a listener, as in below, so DrawerToggle will forward
            // click events to this listener.
            if(!mToolBarNavigationListenerIsRegistered) {
                mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        // Doesn't have to be onBackPressed
                        onBackPressed();
                    }
                });

                mToolBarNavigationListenerIsRegistered = true;
            }

        } else {
            //You must regain the power of swipe for the drawer. 
            drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);

            // Remove back button
            getSupportActionBar().setDisplayHomeAsUpEnabled(false);
            // Show hamburger 
            mDrawerToggle.setDrawerIndicatorEnabled(true);
            // Remove the/any drawer toggle listener
            mDrawerToggle.setToolbarNavigationClickListener(null);
            mToolBarNavigationListenerIsRegistered = false;
        }

        // So, one may think "Hmm why not simplify to:
        // .....
        // getSupportActionBar().setDisplayHomeAsUpEnabled(enable);
        // mDrawer.setDrawerIndicatorEnabled(!enable);
        // ......
        // To re-iterate, the order in which you enable and disable views IS important #dontSimplify.
    }


该解决方案使用 ActionBarDrawerToggle.setDrawerIndicatorEnabled 来切换汉堡菜单图标的可见性,使用 ActionBar.setDisplayHomeAsUpEnabled 来显示“上一页” 按钮的可见性,基本上利用它们各自的 drawable 资源。
其他假设:
  • 您的 Activity 主题继承自 Theme.AppCompat.Light.NoActionBar

3
太棒了,@Ade.Akinyede! 这正是我在寻找的。你满足了我所有的奖励要求,给出了良好的解释和示例,还做了很好的分析和建议。你赢得了这个奖励。 - Antonio
1
很高兴听到这个好消息 :) - ade.akinyede
2
你救了我的一天!好的解决方案!我使用碎片在我的应用程序中构建它,而且它工作得很好!干得好!+1 - Trancer
1
@ThânHoàng 我想象中在你的活动中,你有抽屉布局(drawerLayout)和工具栏(toolbar)。 - ade.akinyede
1
这个答案非常全面。谢谢您先生! - hernanemartinez
显示剩余14条评论

7

在这种情况下,前几个解决方案都不起作用:

  • 一个 Activity 和多个 Fragments
  • 一个 Fragment(SettingsFragment)应该显示返回图标而不是汉堡菜单
  • 使用 com.google.android.material.appbar.AppBarLayout、androidx.appcompat.widget.Toolbar 和 ActionBarDrawerToggle

我在 Activity 的 onCreate() 方法中调用了这个方法:

private fun initBackStackChangeListener() {
    supportFragmentManager.addOnBackStackChangedListener {
        val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)

        if (fragment is SettingsFragment) {
            menuDrawerToggle?.isDrawerIndicatorEnabled = false
            drawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
            menuDrawerToggle?.setToolbarNavigationClickListener { onBackPressed() }
            supportActionBar?.setDisplayHomeAsUpEnabled(true)
        } else {
            supportActionBar?.setDisplayHomeAsUpEnabled(false)
            drawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
            menuDrawerToggle?.isDrawerIndicatorEnabled = true
            menuDrawerToggle?.toolbarNavigationClickListener = null
            menuDrawerToggle?.syncState()
        }
    }
}

menuDrawerToggle 就是这样的:

menuDrawerToggle = ActionBarDrawerToggle(
        this, drawer_layout, toolbar,
        R.string.navigation_drawer_open,
        R.string.navigation_drawer_close
    ).apply {
        drawer_layout.addDrawerListener(this)
        this.syncState()
    }

非常好用。也许可以帮到其他人。


5

我在Google I/O 2017 Android App中找到了灵活的解决方案。

public Toolbar getToolbar() {
    if (mToolbar == null) {
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        if (mToolbar != null) {
            setSupportActionBar(mToolbar);
            mToolbar.setNavigationContentDescription(R.string.navdrawer_description_a11y);
            mToolbarTitle = (TextView) mToolbar.findViewById(R.id.toolbar_title);
            if (mToolbarTitle != null) {
                int titleId = getNavigationTitleId();
                if (titleId != 0) {
                    mToolbarTitle.setText(titleId);
                }
            }

            // We use our own toolbar title, so hide the default one
            getSupportActionBar().setDisplayShowTitleEnabled(false);
        }
    }
    return mToolbar;
}

/**
 * @param clickListener The {@link android.view.View.OnClickListener} for the navigation icon of
 *                      the toolbar.
 */
protected void setToolbarAsUp(View.OnClickListener clickListener) {
    // Initialise the toolbar
    getToolbar();
    if (mToolbar != null) {
        mToolbar.setNavigationIcon(R.drawable.ic_up);
        mToolbar.setNavigationContentDescription(R.string.close_and_go_back);
        mToolbar.setNavigationOnClickListener(clickListener);
    }
}

所以使用方法非常简单。
setToolbarAsUp(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // onBackPressed();
        // or navigate to parent or some other intent
    }
});

5
我认为所选答案过于hacky。我尝试实现它,但在这个过程中我意识到ActionBarDrawerToggle实际上没有什么好的用处(也许这就是为什么它从有关导航抽屉的官方Android教程中被删除的原因):当你想要协调导航抽屉和操作栏时,它并不能使你的生活更轻松。
问题在于你只有一个主页“按钮”,而且它有两个不同的功能——当你在主屏幕上时,它会打开抽屉,而当你在应用程序中进一步向下时,它会返回上级。将工具栏作为参数传递给ActionBarDrawerToggle构造函数,将菜单图标添加到其中,并在单击事件上调用openDrawer。现在,如果你想切换到up事件,你必须关闭这个特殊的图标,并重新启用操作栏的固有返回功能... 这仍然是一团糟。
所以如果ActionBarDrawerToggle对你没有帮助(也许有人会找到一种方法使它有用),那么为什么要首先使用它呢?以下是如何在没有它的情况下完成它的方法:
boolean homeShouldOpenDrawer; // flag for onOptionsItemSelected

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // if you're using NoActionBar theme
    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    ActionBar actionbar = getSupportActionBar();

    // enables the home button with a <-
    actionbar.setDisplayHomeAsUpEnabled(true);

    // replaces the <- with the menu (hamburger) icon 
    // (ic_menu should be in every empty project, and can be easily added)
    actionbar.setHomeAsUpIndicator(R.drawable.ic_menu);

    // I assume your first fragment/state should be main screen, i.e. home = opens drawer
    homeShouldOpenDrawer = true;
    ...
}

private void enableViews(boolean enable) {
    if(enable) {
        // Enables back button icon
        // passing null or 0 brings back the <- icon
        getSupportActionBar().setHomeAsUpIndicator(null);
        homeShouldOpenDrawer = false;
    } else {
        // Enables burger icon
        getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_menu);
        homeShouldOpenDrawer = true;
    }

}

// this is called whenever a selection is made from the action bar
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            if (homeShouldOpenDrawer) {
                drawerLayout.openDrawer(GravityCompat.START);
            } else {
                onBackPressed();
            }
    }

    return super.onOptionsItemSelected(item);
}

2
首先,在您的帖子中删除名称呼叫和负面态度。这是反社区的。作为开发人员,应该清楚生态系统变化的速度有多快,解决方案也会随之改变。 - ade.akinyede
1
没有冒犯你的意图。我花了两天时间试图将你的答案与教程协调一致,这是我得出的结论。我也可能错了,也许有更好的方法来做到这一点。 - Maverick Meerkat
此外,我不认为这是你的“错”,在操作栏和导航抽屉之间实现良好连接所需的生态系统中肯定缺少某些东西。 - Maverick Meerkat

3

您可以使用以下方法更改操作栏按钮:

        getSupportActionBar().setHomeAsUpIndicator(R.drawable.back_button);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

1
感谢您的回答,@Martín Huergo。我希望不提供可绘制图标,除非它是本机Android可绘制图标。 - Antonio

1

我一直在尝试在我的应用程序中使用这些示例,但似乎没有一个有效。我正在使用片段,并且其中一些必须显示后退选项而不是主页。以下是我的实现(使用 Kotlin):

override fun onResume() {
    super.onResume()
    var drawerLayout: DrawerLayout = activity.findViewById(R.id.drawer_layout)
    drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
    var actionBar = (activity as MainActivity).supportActionBar
    actionBar!!.setDisplayHomeAsUpEnabled(true)
    var  toggle= (activity as MainActivity).drawerToggle
    toggle.isDrawerIndicatorEnabled = false
    toggle.setToolbarNavigationClickListener { v ->  activity.onBackPressed() }
}

override fun onStop() {
    super.onStop()
    var drawerLayout: DrawerLayout = activity.findViewById(R.id.drawer_layout)
    drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
    var actionBar = (activity as MainActivity).supportActionBar
    actionBar!!.setDisplayHomeAsUpEnabled(false)
    var  toggle= (activity as MainActivity).drawerToggle
    toggle.setToolbarNavigationClickListener {null}
    toggle.syncState()
}

注意: 这些是片段内覆盖的onResume和onStop方法。

备注: 只有在调用toggle.syncState()方法时,汉堡包图标才会显示出来。我花了将近24小时来弄清楚为什么主页图标没有显示。

希望我的帖子能帮助到某些人。


toggle.syncState()非常重要...谢谢你的提示! - Smitty-Werben-Jager-Manjenson

0

-> 如果您在HomeActivity和InitialFragment上有一个抽屉,您必须显示DrawerToggle,并且在内部片段之后,您不想显示抽屉,而是要显示返回按钮并更改所有片段的标题。

  • 在您的活动中将actionbartoggle公开。

  • 在主页片段中编写此代码。

    @Override
    
    public void onResume() 
    
    {
        super.onResume();
    
        ((HomeActivity)getActivity()).getSupportActionBar().setTitle("主页");
    
        ((HomeActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
    
        ((HomeActivity)getActivity()).actionBarDrawerToggle.setDrawerIndicatorEnabled(true);}    
    
  • 在其他片段中编写此代码:

         @Override
        public void onResume() 
    {     super.onResume();
            ((HomeActivity)getActivity()).getSupportActionBar().setTitle("我的账户");
            ((HomeActivity)getActivity()).actionBarDrawerToggle.setDrawerIndicatorEnabled(false);
            ((HomeActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }
    
  • 在您的主要活动中,在onBackPressed中编写以下内容:

    @Override
        public void onBackPressed() 
        {
            if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
    
                getSupportFragmentManager().popBackStackImmediate();
    
            } else {
    
                super.onBackPressed();
    
            }
        }
    

0

使用这个

getSupportActionBar().setDisplayShowHomeEnabled(true);

0
 final Toolbar toolbar = (Toolbar) findViewById(R.id.actionbar);
      toolbar.setTitle(Html.fromHtml("<font color=#ffffff>" +     getString(R.string.print_s) + "</font>"));
      toolbar.setNavigationIcon(getResources().getDrawable(R.drawable.menu_icon));
     toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
             DetailActivity.this.finish();
        }
    });
    toolbar.inflateMenu(R.menu.fav);
    toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener()     {
        @Override
        public boolean onMenuItemClick(MenuItem item) {
              item.setIcon(R.drawable.back_icon)
              return true;
            }
            return false;
        }
    });

谢谢你的回答,@Dinesh。你有看过我的悬赏描述吗? - Antonio

0
尝试将以下style AppTheme代码添加到您的activity's theme/style.xml中,它将使您的hamburger icon变成具有animationback icon条件:如果您正在使用带有NavigationDrawerAppCompatActivity/ActionBarActivity的汉堡包图标。
<style name="AppTheme" parent="Theme.AppCompat.Light">
            <item name="windowActionBar">false</item>
            <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
        </style>


<style name="DrawerArrowStyle" parent="Widget.AppCompat.DrawerArrowToggle">
    <item name="spinBars">true</item>
    <item name="color">@android:color/white</item>
</style>

希望有所帮助!或者你只需要使用drawable进行操作。

请查看 链接


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接