工具栏 - 只需一个活动即可从抽屉切换到后退按钮

时间:2015-02-15 21:39:46

标签: android android-fragments navigation

我一直在寻找如何在抽屉打开/关闭图标(从汉堡包到箭头)到简单的向后箭头之间切换的方法。我的应用程序目前只有一个Activity在几个片段之间切换。有一次,我想在一个主要片段(即抽屉中的一个片段)之间转换为分层次在前一个片段下的片段(即“添加新片段”)。在这个新片段中,我想让工具栏显示后退按钮而不是抽屉按钮。

我一直在四处寻找并尝试不同的解决方案。以下是最值得注意的:

目前,我正在考虑一种创建自定义图标的漫长而艰巨的方法,我隐藏并显示(并隐藏/显示本机抽屉图标)。但是,有没有更好的方法在抽屉和后退按钮之间切换?

作为一个侧面相关的问题,我一直在查看Material Design文档,并且一些示例在左上角有一个X.与实现抽屉vs后退/向上按钮相比,实现起来有多么不同?

感谢〜

修改

我可以弄清楚如何替换图标,但我如何获得点击事件?

到目前为止,这是我最好的领导:

我现在尝试过:

  • 必要时禁用DrawerToggle(即mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);
  • 在我的NavigationDrawerFragment,我的Activity中添加了onOptionsItemSelected中的日志,以及我正在测试的当我item.getItemId() == android.R.id.home为真时运行的DialogFragment。这些日志语句都没有结束

为了更好的上下文,我现在有一个全屏片段,在菜单中添加一个“保存”按钮,并将抽屉图标更改为“X”。片段可以获取保存菜单事件,但是当点击X时,甚至活动和抽屉都无法获得。

EDIT2:

根据要求,这里有一些代码。请注意,这一切都来自this Github repo,我正在积极研究(注意我在这里或那里有一些无用的功能来进行快速测试)。

ActivityMain

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Add the toolbar
    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    if (mToolbar != null) {
        setSupportActionBar(mToolbar);
    }

    // Initialize the drawer
    mNavigationDrawerFragment = (NavigationDrawerFragment)
            getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);

    // Set up the drawer
    mNavigationDrawerFragment.setUp(
            R.id.navigation_drawer,
            (DrawerLayout) findViewById(R.id.drawer_layout),
            mToolbar);

    // TODO: Check if this helps to catch the main toolbar button click
    getSupportActionBar().setDisplayShowHomeEnabled(true);

    // Get the titles for the Toolbar
    mTitles = getResources().getStringArray(R.array.drawer_items);

    mDrawerPosition = -1;
    if (savedInstanceState == null) {
        // If there was no saved position, then the default, starting position should be used
        forceChangeItemSelected(0);
    }
    else {
        // Otherwise, get the saved position from the bundle
        int position = savedInstanceState.getInt(KEY_DRAWERPOS);
        mNavigationDrawerFragment.setSelectedItem(position);
        // Title needs to be re-set
        getSupportActionBar().setTitle(mTitles[position]);
    }

    // If I include the below bit, then the DrawerToggle doesn't function
        // I don't know how to switch it back and forth
    mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(LOG_TAG, "Navigation was clicked");

        }
    });
}

@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.
    Log.d(LOG_TAG, "Activity responding to menu click...");
    if(item.getItemId() == android.R.id.home) Log.d(LOG_TAG, "Activity got it....");

    // If the fragment is supposed to handle things, then let it
    if(mIsFragmentHandlingMenus) return false;

    int id = item.getItemId();
    if(id == R.id.save) {
        // This isn't implemented! If chosen, then there's a bug!
        Log.e(LOG_TAG, "onOptionsItemSelected: Save was selected!");
    }

    return super.onOptionsItemSelected(item);
}

@Override
public void fragmentHandlingMenus(boolean isFragmentHandlingMenus) {
    // Simply store the setting
    mIsFragmentHandlingMenus = isFragmentHandlingMenus;

    // Toggle the drawer as necessary
    mNavigationDrawerFragment.toggleDrawerUse(!isFragmentHandlingMenus);
}

NavigationDrawerFragment

public void toggleDrawerUse(boolean useDrawer) {
    // Enable/Disable the icon being used by the drawer
    mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);

    // TODO: Enable/Disable the drawer even being able to open/close
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Log.d(LOGTAG, "Drawer responding to menu click...");
    if(item.getItemId() == android.R.id.home) Log.d(LOGTAG, "Drawer got it....");
    if (mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

GoalAdderFragment

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    // Allow this fragment to handle toolbar menu items
    setHasOptionsMenu(true);

    // Set up the toolbar
    ((ActionBarActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    ((ActionBarActivity) getActivity()).getSupportActionBar().setHomeAsUpIndicator(android.R.drawable.ic_menu_close_clear_cancel);
    ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(getResources().getString(R.string.title_addgoal));
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    // Cache the Activity as the frag handler if necessary
    if(mFragHandler == null)
        mFragHandler = (TransactionHandler.FragmentTransactionHandler) getActivity();
    // Tell the Activity to let fragments handle the menu events
    mFragHandler.fragmentHandlingMenus(true);
}

@Override
public void onDetach() {
    super.onDetach();

    // Tell the Activity that it can now handle menu events once again
    mFragHandler.fragmentHandlingMenus(false);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.save_menu, menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Log.d(LOGTAG, "Item id: " + item.getItemId() + " | Save id: " + R.id.save);
    Toast.makeText(getActivity(), "Fragment activated!", Toast.LENGTH_SHORT).show();

    switch (item.getItemId()) {
        case R.id.save:
            return true;
        case android.R.id.home:
            return true;
        default:
            break;
    }

    return false;
}

解决方案:

这是我最终解决的最终解决方案,借助下面接受的答案:

NavigationDrawerFragment

private View.OnClickListener mOriginalListener;

public void setUp(int fragmentId, DrawerLayout drawerLayout, Toolbar toolbar) {
     /* Rest of setting up code */

     // Save the default listener after setting everything else up
     mOriginalListener = mDrawerToggle.getToolbarNavigationClickListener();
}

// Tells the toolbar+drawer to switch to the up button or switch back to the normal drawer
public void toggleDrawerUse(boolean useDrawer) {
    // Enable/Disable the icon being used by the drawer
    mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);

    // Switch between the listeners as necessary
    if(useDrawer)
        mDrawerToggle.setToolbarNavigationClickListener(mOriginalListener);
    else
        mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getActivity(), "Custom listener", Toast.LENGTH_SHORT).show();
            }
        });
}

6 个答案:

答案 0 :(得分:44)

将此代码放入onCreate()的{​​{1}}。对我来说效果很好。甚至使用Activity及更高版本。

compileSdk 23

答案 1 :(得分:27)

它甚至可以用于最新的 API 24

在您的活动中onCreate()执行此操作:

final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
final DrawerLayout drawer = (DrawerLayout) view.findViewById(R.id.drawer_layout);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

final ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, 
    R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();

final View.OnClickListener originalToolbarListener = toggle.getToolbarNavigationClickListener();

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
        if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
            toggle.setDrawerIndicatorEnabled(false);
            toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    getSupportFragmentManager().popBackStack();
                }
            });
        } else {
            toggle.setDrawerIndicatorEnabled(true);
            toggle.setToolbarNavigationClickListener(originalToolbarListener);
        }
    }
});

答案 2 :(得分:12)

这可能不是你想听到的,但即使从概念的角度来看,我也会去寻找新的活动而不是片段。

您的主要活动与抽屉严格关联,因此加载新片段而无法访问抽屉对我没有任何意义(但如果您愿意,请随意等待其他答案)。一项新的活动可以解决这两个问题,因为它没有抽屉,可能是主要的孩子。

你的旁边问题看起来也很明显。 A"添加新"活动可以很好地融入"全屏对话框#34;指南中的视觉模式。参见:

http://www.google.com/design/spec/components/dialogs.html#dialogs-full-screen-dialogs

此模式在右上角有一个" save",正按钮和一个X.从概念上讲,X按钮是取消/中止一个进程,而不是导航一些backstack。这意味着你在不让任何行动发生的情况下解雇某些事情。这非常适合您想要做的事情。

从设计的角度来看,它可以通过新的Activity轻松制作,可以保持在其他人之上。此外,如果碎片的点基本上能够在平板电脑和更大的屏幕中同时代表两个或更多 - 再次 - 我不会对我左边的旧片段和#34;添加新&&感到高兴#34;片段在右边。

相反 - 在平板电脑上 - 我会按指南的建议进行浮动对话。

http://www.google.com/design/spec/components/dialogs.html#dialogs-confirmation-dialogs

所以全屏活动带有用于手机的X按钮,以及用于平板电脑的浮动对话框(底部带有按钮)。对我而言,这是最准则一致的方法。


我建议阅读整个链接。关于< - 和X之间的差异,

  

X与向上箭头不同,向上箭头在持续保存视图状态或应用程序具有草稿或自动保存功能时使用。例如,在“设置”中使用向上箭头,因为所有更改都会立即提交。

  

触摸this Settings example中的X会丢弃所有更改。仅在触摸保存时才会保存更改。

答案 3 :(得分:4)

@matusalem的答案非常好。我只需要添加一点 - 小心,因为也可以通过从屏幕左侧滑入来打开抽屉。对于一些人来说,这可能是理想的,但对我来说,我禁用了抽屉,因为除了我的主要片段之外,它在任何片段中都没有意义。这里的滑动很容易被禁用 - Navigation drawer - disable swipe

这可能属于对答案的评论,但我没有足够的声誉。道歉。

答案 4 :(得分:0)

在更改片段时,我在同一活动中切换汉堡包菜单和后退箭头时遇到了同样的问题。这是我的工作解决方案,希望对某人有所帮助。

您的活动中的监听器:

private View.OnClickListener toolbarMenuListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //will be called only if toggle.setDrawerIndicatorEnabled(false); !
            Log.v(tag,"toggle onClick:"+v.getId()+" android.R.id.home:"+android.R.id.home);
            onBackPressed();
        }
    };

代码onCreate()类似于:

...
...
setSupportActionBar(toolbar);
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();

//set listener so you know when back on arrow is pressed
toggle.setToolbarNavigationClickListener(toolbarMenuListener);
...
...

您对评论感兴趣的部分(返回的类是我的一些类,可以设置为无效):

/**
     * Method to set up action bar drawer.
     * @param enableBackDrawerIcon set true if want to show drawer back arrow,
     *                             false to show hamburger menu.
     * @param title shown next to drawer icon
     */
    public BaseMenusActivity drawerSetupToggle(boolean enableBackDrawerIcon, String title) {
        //NOTE: order of methods call is important!
        // If you change order order of setDrawerIndicatorEnabled and setDisplayHomeAsUpEnabled
        // method calls it won't work, weird bugs will happen (like no icon at all)
        if(enableBackDrawerIcon){
            Log.v(tag,"show drawer back icon");
            //hides hamburger menu and enables View.OnClickListener to be called
            toggle.setDrawerIndicatorEnabled(false);
            //show back arrow
            if(getSupportActionBar()!=null)
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        } else {
            Log.v(tag,"show hamburger menu");
            //hide back arrow
            if(getSupportActionBar()!=null)
                getSupportActionBar().setDisplayHomeAsUpEnabled(false);
            //shows hamburger menu and prevents View.OnClickListener to be called
            toggle.setDrawerIndicatorEnabled(true);
        }

        setTitle(title);
        return this;
    }

注意:被调用方法的顺序很重要!如果可以像这样写两行但是将不工作(至少对我而言)会更好:

toggle.setDrawerIndicatorEnabled(!enableBackDrawerIcon);
     getSupportActionBar().setDisplayHomeAsUpEnabled(enableBackDrawerIcon);

如果您对方法调用的顺序感兴趣,请查看这些方法的实现。

答案 5 :(得分:0)

//This if block makes the menu back button to respond to clicks
    //The onOptionsItemSelected fun for whatever reason was not capturing back menu clicks
    if (toolbar != null) {
       /* toggle = ActionBarDrawerToggle(
                this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
        toggle.syncState()
        drawer_layout.setDrawerListener(toggle)*/
        supportFragmentManager.addOnBackStackChangedListener(object : FragmentManager.OnBackStackChangedListener {
            override fun onBackStackChanged() {
                if (supportFragmentManager.backStackEntryCount > 0) {
                    supportActionBar?.setDisplayHomeAsUpEnabled(true) // show back button
                    toolbar.setNavigationOnClickListener(object : View.OnClickListener {
                        override fun onClick(v: View) {
                            onBackPressed()
                        }
                    })
                } else {
                    //show hamburger
                    supportActionBar?.setDisplayHomeAsUpEnabled(false)
                    toggle.syncState()
                    toolbar.setNavigationOnClickListener(object : View.OnClickListener {
                        override fun onClick(v: View) {
                            drawer_layout.openDrawer(GravityCompat.START)
                        }
                    })
                }
            }
        })

    }
  

您需要注释掉“ toggle = ActionBarDrawerToggle(                   这个,drawer_layout,工具栏,R.string.navigation_drawer_open,R.string.navigation_drawer_close)           toggle.syncState()           如果您在Android Studio中使用自动生成的Navigation布局,则抽屉式布局.setDrawerListener(toggle)“(4-7行),否则后菜单按钮的行为将不固定。这就是我所做的,并且对我来说效果很好。希望这对某人有帮助