从android中的片段管理工具栏的导航和后退按钮
Posted
技术标签:
【中文标题】从android中的片段管理工具栏的导航和后退按钮【英文标题】:Manage toolbar's navigation and back button from fragment in android 【发布时间】:2015-06-16 00:05:05 【问题描述】:我的所有片段都通过ActionBarActivity
(mainActivity)控制,在mainActivity内部实现了DrawerLayout
,所有子片段都通过drawerLayout的列表项点击推送。我面临的问题是通过drawerLayout推送片段后,我想将抽屉图标更改为ToolBar
的后退图标,以便用户可以导航到上一个片段并在同一片段内处理android.R.id.home
的回调或在 mainActivity 内。
我使用的代码是:
MainActivity.java
public class MainActivity extends ActionBarActivity
private DrawerLayout layoutDrawer;
private ActionBarDrawerToggle drawerToggler;
private Stack<Fragment> stack;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
stack = new Stack<Fragment>();
layoutDrawer = (DrawerLayout) findViewById(R.id.layout_drawer);
drawerToggler = new ActionBarDrawerToggle(this, layoutDrawer, toolbar,
R.string.app_name, R.string.app_name);
layoutDrawer.setDrawerListener(drawerToggler);
setUpDrawerList();
pushFragment(new FirstFragment(), true);
Session.setContext(getApplicationContext());
@Override
public boolean onCreateOptionsMenu(Menu menu)
getMenuInflater().inflate(R.menu.main, menu);
return true;
@Override
public boolean onOptionsItemSelected(MenuItem item)
if (drawerToggler.isDrawerIndicatorEnabled()
&& drawerToggler.onOptionsItemSelected(item))
return true;
switch (item.getItemId())
case android.R.id.home:
Toast.makeText(this, "Back from activity", Toast.LENGTH_SHORT)
.show();
return true;
return super.onOptionsItemSelected(item);
@Override
protected void onPostCreate(Bundle savedInstanceState)
super.onPostCreate(savedInstanceState);
drawerToggler.syncState();
@Override
public void onBackPressed()
popFragment();
private void setUpDrawerList()
ListView listView = (ListView) findViewById(R.id.list_drawer);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
Arrays.asList(new String[] "First Fragment",
"Second Fragment" ));
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener()
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
layoutDrawer.closeDrawers();
drawerToggler.setDrawerIndicatorEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
pushFragment(getFragment(position), true);
);
private Fragment getFragment(int pos)
switch (pos)
case 0:
return new FirstFragment();
case 1:
return new SecondFragment();
return null;
public void pushFragment(Fragment fragment, boolean add)
FragmentTransaction transation = getSupportFragmentManager()
.beginTransaction();
if (add)
stack.push(fragment);
transation.replace(R.id.layout_content, fragment);
transation.commit();
public void popFragment()
if (!stack.isEmpty())
Fragment fragment = stack.elementAt(stack.size() - 2);
stack.pop();
pushFragment(fragment, false);
else
super.onBackPressed();
drawerToggler.setDrawerIndicatorEnabled(stack.size() == 1);
public void clearBackStack()
stack.clear();
FirstFragment.java
public class FirstFragment extends Fragment
@Override
public void onActivityCreated(Bundle savedInstanceState)
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
return inflater.inflate(R.layout.fragment_first, container, false);
@Override
public void onResume()
super.onResume();
ActionBar actionBar = ((ActionBarActivity)getActivity()).getSupportActionBar();
actionBar.setTitle("First Fragment");
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
super.onCreateOptionsMenu(menu, inflater);
menu.clear();
inflater.inflate(R.menu.fragment_menu, menu);
@Override
public boolean onOptionsItemSelected(MenuItem item)
switch(item.getItemId())
case android.R.id.home:
Toast.makeText(getActivity(), "Back from fragment", Toast.LENGTH_SHORT).show();
getActivity().onBackPressed();
return true;
return super.onOptionsItemSelected(item);
从上面的代码我无法得到android.R.id.home
的回调并且每次设置主页按钮都不起作用actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
任何帮助将不胜感激。
谢谢
【问题讨论】:
github.com/tekinarslan/AndroidMaterialDesignToolbar 在这个你可以得到你想要的同样的行为 【参考方案1】:在你的 xml 中添加一个工具栏
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_
android:layout_
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
<TextView
android:layout_
android:layout_
android:text="Fragment title"/>
</android.support.v7.widget.Toolbar>
然后在 Fragment 中的 onCreateView 方法中:
Toolbar toolbar = view.findViewById(R.id.toolbar);
toolbar.setNavigationIcon(R.drawable.ic_back_button);
toolbar.setNavigationOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
getActivity().onBackPressed();
);
【讨论】:
我需要getActivity()
,因为我的片段是如何设置的。
fragment中的toolbar如何覆盖activity的toolbar?当我测试这段代码时,片段的工具栏显示在活动的工具栏下方。
如果您启用了setHasOptionsMenu(true);
,那么` setNavigationOnClickListener ` 将不起作用,您必须覆盖onOptionsItemSelected
以获取详细信息,请查看@Amandeep Rohila 答案
还有什么办法吗?
这仍然不起作用。奇怪它是如何获得支持的【参考方案2】:
您必须管理主 Activity 上的后退按钮按下操作,因为您的主 Activity 是片段的容器。
首先,将所有片段添加到 transaction.addToBackStack(null),现在导航返回按钮调用将在主要活动中进行。希望下面的代码对你有所帮助...
@Override
public boolean onOptionsItemSelected(MenuItem item)
switch (item.getItemId())
case android.R.id.home:
onBackPressed();
return super.onOptionsItemSelected(item);
你也可以使用
Fragment fragment =fragmentManager.findFragmentByTag(Constant.TAG);
if(fragment!=null)
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.remove(fragment).commit();
并根据片段名称更改标题,您可以使用以下代码:
activity.getSupportActionBar().setTitle("Keyword Report Detail");
【讨论】:
请注意,您需要在onBackPressed()
之后调用 return true
才能使其正常工作。如果您不这样做,onBackPressed()
将无效,因为调用将被 super.onOptionsItemSelected(item)
消耗【参考方案3】:
我有很多解决方案,但没有一个能完美运行。我在我的项目中使用了各种可用的解决方案,如下所示。请在初始化工具栏和抽屉布局的类中使用此代码。
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
@Override
public void onBackStackChanged()
if (getSupportFragmentManager().getBackStackEntryCount() > 0)
drawerFragment.mDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);// show back button
toolbar.setNavigationOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
onBackPressed();
);
else
//show hamburger
drawerFragment.mDrawerToggle.setDrawerIndicatorEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
drawerFragment.mDrawerToggle.syncState();
toolbar.setNavigationOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
drawerFragment.mDrawerLayout.openDrawer(GravityCompat.START);
);
);
【讨论】:
在尝试了几个解决方案后,它终于对我有用了。 这很好用,我在 show hamburguer else (drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); 的末尾添加了一行,在 (if (getSupportFragmentManager().getBackStackEntryCount( ) > 0) ) 即 (drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);) 所以当显示返回箭头时,用户无法通过拖动打开抽屉 这太不清楚了...drawerFragment
是什么,mDrawerToggle
是什么?糟糕答案的令人震惊的例子
@GeorgySavatkov,这是问题的答案,而不是教程。请不要从教程的角度看。
这对我有用。我有一个实现 AppCompatCallback 的 FragmentActivity。我删除了导航抽屉和汉堡图标部分,因为我不需要它,但这很好用。【参考方案4】:
可能是最干净的解决方案:
abstract class NavigationChildFragment : Fragment()
abstract fun onCreateChildView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View?
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View?
val activity = activity as? MainActivity
activity?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
setHasOptionsMenu(true)
return onCreateChildView(inflater, container, savedInstanceState)
override fun onDestroyView()
val activity = activity as? MainActivity
activity?.supportActionBar?.setDisplayHomeAsUpEnabled(false)
setHasOptionsMenu(false)
super.onDestroyView()
override fun onOptionsItemSelected(item: MenuItem): Boolean
val activity = activity as? MainActivity
return when (item.itemId)
android.R.id.home ->
activity?.onBackPressed()
true
else -> super.onOptionsItemSelected(item)
只需使用这个类作为所有应该支持导航的 Fragment 的父类。
【讨论】:
为了支持导航,我们还需要做些什么? 只需继承NavigationChildFragment
。【参考方案5】:
您可以在片段内部使用工具栏,它很容易处理。首先将 Toolbar 添加到 Fragment 的布局中
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:layout_
android:layout_
android:fitsSystemWindows="true"
android:minHeight="?attr/actionBarSize"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:background="?attr/colorPrimaryDark">
</android.support.v7.widget.Toolbar>
在片段的 onCreateView 方法中,您可以像这样处理工具栏。
Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
toolbar.setTitle("Title");
toolbar.setNavigationIcon(R.drawable.ic_arrow_back);
IT 会将工具栏、标题和后退箭头导航设置为工具栏。您可以将任何图标设置为 setNavigationIcon 方法。
如果您需要在单击工具栏导航图标时触发任何事件,您可以使用它。
toolbar.setNavigationOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
//handle any click event
);
如果您的活动有导航抽屉,您可能需要在单击导航返回按钮时打开它。你可以这样打开那个抽屉。
toolbar.setNavigationOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
DrawerLayout drawer = (DrawerLayout) getActivity().findViewById(R.id.drawer_layout);
drawer.openDrawer(Gravity.START);
);
完整代码在这里
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
//inflate the layout to the fragement
view = inflater.inflate(R.layout.layout_user,container,false);
//initialize the toolbar
Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
toolbar.setTitle("Title");
toolbar.setNavigationIcon(R.drawable.ic_arrow_back);
toolbar.setNavigationOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
//open navigation drawer when click navigation back button
DrawerLayout drawer = (DrawerLayout) getActivity().findViewById(R.id.drawer_layout);
drawer.openDrawer(Gravity.START);
);
return view;
【讨论】:
你的R.drawable.ic_arrow_back
是一个自定义drawable,你知道Android使用的“官方”箭头是否可以抓取?【参考方案6】:
首先添加导航返回按钮
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
然后,在您的 HostActivity 中添加 Method。
@Override
public boolean onOptionsItemSelected(MenuItem item)
if (item.getItemId()==android.R.id.home)
super.onBackPressed();
Toast.makeText(this, "OnBAckPressed Works", Toast.LENGTH_SHORT).show();
return super.onOptionsItemSelected(item);
试试这个,绝对有效。
【讨论】:
好主意,但要注意这是用于操作栏而不是工具栏【参考方案7】:(科特林) 在托管片段的活动中:
override fun onOptionsItemSelected(item: MenuItem): Boolean
when (item.itemId)
android.R.id.home ->
onBackPressed()
return true
return super.onOptionsItemSelected(item)
我发现当我向项目添加片段时,它们默认显示操作栏主页按钮,要删除/禁用它,请将其放入 onViewCreated() (如果未显示,请使用 true 启用它):
val actionBar = this.requireActivity().actionBar
actionBar?.setDisplayHomeAsUpEnabled(false)
【讨论】:
【参考方案8】:如果您正在使用 androidx 片段并希望在单击返回主页按钮时返回 MainActivity,请使用以下代码。
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
super.onViewCreated(view, savedInstanceState);
setHasOptionsMenu(true);
....
@Override
public boolean onOptionsItemSelected(MenuItem item)
if (item.getItemId() == android.R.id.home)
requireActivity().onBackPressed();
return super.onOptionsItemSelected(item);
【讨论】:
这不起作用【参考方案9】:我找到的最简单的解决方案是简单地将其放入您的片段中:
androidx.appcompat.widget.Toolbar toolbar = getActivity().findViewById(R.id.toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
NavController navController = Navigation.findNavController(getActivity(),
R.id.nav_host_fragment);
navController.navigate(R.id.action_position_to_destination);
);
我个人想转到另一个页面,但是您当然可以将 onClick 方法中的 2 行替换为您要执行的操作。
【讨论】:
这与导航库无关 你可以使用 navController.navigateUp()【参考方案10】:Kotlin 中可以处理 标题栏后按动作 以及 导航栏后按动作 并且可以从所有片段中调用的通用方法是:
fun configureToolbarBackPress(
customToolBar: Toolbar,
parentVw: View,
activity: Activity,
title: String,
targetResId: Int
)
customToolBar.setNavigationIcon(R.drawable.ic_arrow_back)
customToolBar.title = title
customToolBar.setNavigationOnClickListener
parentVw.findNavController().navigate(targetResId)
(activity as DashboardActivityNew).onBackPressedDispatcher.addCallback(
object : OnBackPressedCallback(true)
override fun handleOnBackPressed()
parentVw.findNavController().navigate(targetResId)
)
现在从任何片段中调用此方法 onViewCreated(),如下所示:
AppUtils.configureToolbarBackPress(
customToolbar as Toolbar,
view,
requireActivity(),
getString(R.string.title),
R.id.mainFragment
)
在 fragment.xml 中包含工具栏,如下所示:
<include
android:id="@+id/customToolbar"
layout="@layout/dashboard_toolbar" />
dashboard_toolbar.xml的布局如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:layout_
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:background="@color/colorPrimary"/>
【讨论】:
所以基本上你在每个片段中添加工具栏。对吗? 没错,每个片段都有一个自定义工具栏! 糟糕的编程习惯!【参考方案11】:OnToolBar左侧有一个导航图标
Toolbar toolbar = (Toolbar) findViewById(R.id.tool_bar);
toolbar.setTitle(getResources().getString(R.string.title_activity_select_event));
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
通过在左侧导航图标出现并在导航图标上单击它调用父活动。
在清单中,我们可以通知系统有关父活动的信息。
<activity
android:name=".CategoryCloudSelectActivity"
android:parentActivityName=".EventSelectionActivity"
android:screenOrientation="portrait" />
【讨论】:
以上是关于从android中的片段管理工具栏的导航和后退按钮的主要内容,如果未能解决你的问题,请参考以下文章
后退按钮关闭应用程序而不是转到上一个片段 android 导航组件