【问题标题】:Manage toolbar's navigation and back button from fragment in android从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);

任何帮助将不胜感激。

谢谢

【问题讨论】:

标签: android android-actionbar navigation-drawer android-fragmentactivity android-toolbar


【解决方案1】:

在你的 xml 中添加一个工具栏

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        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_height="wrap_content"
    android:layout_width="match_parent"
    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_width="match_parent"
        android:layout_height="@dimen/abc_action_bar_default_height_material"
        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" />
    

    【讨论】:

      猜你喜欢
      • 2016-08-20
      • 2018-01-23
      • 1970-01-01
      • 2023-03-09
      • 2020-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多