使用Android中的工具栏实现正确的后退导航和主页按钮处理

Aji*_*ana 19 android android-fragments navigation-drawer android-navigationview

我在同一个活动中使用单个活动和多个片段(附加截图)来提供无缝导航.但在实现最新的工具栏和导航视图后,似乎很难处理导航和主页按钮.我遇到以下问题.

  • 在左上角管理汉堡/后退按钮.将图标和功能切换到菜单和返回导航.
    • 页面标题 - 每当推送和弹出片段时更改页面标题.

我已经尝试了一些事情,比如重写onBackPressed(),setHomeAsUpIndicator,手动弹出片段.早些时候我使用ActionBarDrawer切换来处理这个问题,但现在却以某种方式失败了.我检查了谷歌样本,他们似乎在大多数地方使用单独的活动.

任何人都可以指导我如何实现适当的后退导航来处理内部片段和页面标题中的NavigationView,Back按钮?我正在使用AppCompatActivity,android.app.Fragment,NavigationViewToolbar.

片段1  - >片段2  - >片段3

ade*_*ede 16

用你Activity和你的某种责任分工说明要容易得多Fragment.

活动和片段的责任划分 问题1:管理左上角的汉堡/后退按钮.将图标和功能切换到菜单和返回导航.

从图示中,解决方案应该由the封装Activity,看起来像这样:

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {

    private ActionBarDrawerToggle mDrawerToggle;
    private DrawerLayout mDrawer;
    private ActionBar mActionBar;

    private boolean mToolBarNavigationListenerIsRegistered = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        mActionBar = getSupportActionBar();

        mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        mDrawer.addDrawerListener(mDrawerToggle);
        mDrawerToggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        // On orientation change savedInstanceState will not be null.
        // Use this to show hamburger or up icon based on fragment back stack.
        if(savedInstanceState != null){
            resolveUpButtonWithFragmentStack();
        } else {
            // You probably want to add your ListFragment here.
        }
    }

    @Override
    public void onBackPressed() {

        if (mDrawer.isDrawerOpen(GravityCompat.START)) {
            mDrawer.closeDrawer(GravityCompat.START);

        } else {
            int backStackCount = getSupportFragmentManager().getBackStackEntryCount();

            if (backStackCount >= 1) {
                getSupportFragmentManager().popBackStack();
                // Change to hamburger icon if at bottom of stack
                if(backStackCount == 1){
                    showUpButton(false);
                }
            } else {
                super.onBackPressed();
            }
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;

        } else if (id == android.R.id.home) {
            // Home/Up logic handled by onBackPressed implementation
            onBackPressed();
        }

        return super.onOptionsItemSelected(item);
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        // Navigation drawer item selection logic goes here

        mDrawer.closeDrawer(GravityCompat.START);
        return true;
    }

    private void replaceFragment() {
        /**
        * Your fragment replacement logic goes here
        * e.g.
        * FragmentTransaction ft = getFragmentManager().beginTransaction();
        * String tag = "MyFragment";
        * ft.replace(R.id.content, MyFragment.newInstance(tag), tag).addToBackStack(null).commit();
        */

        // The part that changes the hamburger icon to the up icon
        showUpButton(true);
    }

    private void resolveUpButtonWithFragmentStack() {
        showUpButton(getSupportFragmentManager().getBackStackEntryCount() > 0);
    }

    private void showUpButton(boolean show) {
        // 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(show) {
            // Remove hamburger
            mDrawerToggle.setDrawerIndicatorEnabled(false);
            // Show back button
            mActionBar.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) {
                        onBackPressed();
                    }
                });

                mToolBarNavigationListenerIsRegistered = true;
            }

        } else {
            // Remove back button
            mActionBar.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.
    }
}
Run Code Online (Sandbox Code Playgroud)

问题2:页面标题 - 每当推送和弹出片段时更改页面标题.

从本质上讲,这可以在onStart每个中处理,Fragment即ListFragment,DetailsFragment和CommentsFragment看起来像这样:

@Override
public void onStart() {
    super.onStart();
    // where mText is the title you want on your toolbar/actionBar
    getActivity().setTitle(mText);
}
Run Code Online (Sandbox Code Playgroud)

也许值得拥有setRetainInstance(true)onCreate你的碎片也是如此.


shr*_*der 5

TL;博士

观看:https: //youtu.be/ANpBWIT3vlU

克隆这个:https: //github.com/shredderskelton/androidtemplate.

这是一个非常常见的问题,我通过创建一种模板项目来克服这个问题,每当我开始一个新的Android项目时我都会使用它.我们的想法是尽可能多地抽象处理后退按钮的逻辑,"汉堡包"指示器和片段管理到可重用的类:

首先创建一个BaseActivity和BaseFragment类.这是您尽可能多地使用可重用代码的地方.

让我们从您的BaseActivity开始

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    fragmentManager = getSupportFragmentManager();
    fragmentHandler = new AddFragmentHandler(fragmentManager);
    fragmentManager.addOnBackStackChangedListener(backStackListener);
}
Run Code Online (Sandbox Code Playgroud)

FragmentManager是拥有后台堆栈的关键,因此您需要从此处侦听后台堆栈的更改.AddFramentHandler是我编写的一个小类,可以更容易地从Fragments中添加Fragments.稍后会详细介绍.

@Override
public void onBackPressed() {
    if (sendBackPressToDrawer()) {
        //the drawer consumed the backpress
        return;
    }

    if (sendBackPressToFragmentOnTop()) {
        // fragment on top consumed the back press
        return;
    }

    //let the android system handle the back press, usually by popping the fragment
    super.onBackPressed();

    //close the activity if back is pressed on the root fragment
    if (fragmentManager.getBackStackEntryCount() == 0) {
        finish();
    }
}
Run Code Online (Sandbox Code Playgroud)

onBackPressed是大多数魔法发生的地方.你注意到方法的纯文本格式..我是一个巨大的清洁代码粉丝 - 如果你需要写评论,你的代码不干净.基本上你需要真正有一个中心位置,当你不确定为什么没有按照你期望的方式按下按钮时你可以跑到哪里.这个方法就是那个地方.

private void syncDrawerToggleState() {
    ActionBarDrawerToggle drawerToggle = getDrawerToggle();
    if (getDrawerToggle() == null) {
        return;
    }
    if (fragmentManager.getBackStackEntryCount() > 1) {
        drawerToggle.setDrawerIndicatorEnabled(false);
        drawerToggle.setToolbarNavigationClickListener(navigationBackPressListener); //pop backstack
    } else {
        drawerToggle.setDrawerIndicatorEnabled(true);
        drawerToggle.setToolbarNavigationClickListener(drawerToggle.getToolbarNavigationClickListener()); //open nav menu drawer
    }
}
Run Code Online (Sandbox Code Playgroud)

这是BaseActivity的另一个关键部分.基本上,此方法检查您是否位于根片段并相应地设置指示符.请注意,它会根据后端堆栈中的碎片数来更改侦听器.

然后是BaseFragment:

@Override
public void onResume() {
    super.onResume();
    getActivity().setTitle(getTitle());
}

protected abstract String getTitle();
Run Code Online (Sandbox Code Playgroud)

上面的代码显示了片段如何处理标题.