使用片段后端堆栈处理ActionBar标题?

Chr*_*Orr 73 android android-fragments actionbarsherlock android-actionbar

我有一个Activity加载到的地方ListFragment,在点击时,它向下钻取一个级别,显示一个新类型ListFragment,替换原来的(使用showFragment下面的方法).这是放在后面的堆栈上.

开始时,活动会在操作栏中显示默认标题(即根据应用程序自动设置android:label).

显示层次结构中下一级别的列表时,单击的项目名称应成为操作栏的标题.

但是,按下时Back,我希望恢复原始默认标题.这不是什么FragmentTransaction知道的,所以标题不会恢复.

我模糊地读过FragmentBreadCrumbs,但这似乎需要使用自定义视图.我正在使用ActionBarSherlock,并且更愿意没有我自己的自定义标题视图.

这样做的最佳方式是什么?是否有可能没有大量的样板代码并且必须跟踪沿途显示的标题?


protected void showFragment(Fragment f) {
  FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
  ft.replace(R.id.fragment_container, f);
  ft.addToBackStack(null);
  ft.commit();
}
Run Code Online (Sandbox Code Playgroud)

War*_*zit 120

在每个片段和每个活动中,我都会像这样更改标题.这样,活动标题将始终是正确的:

@Override
public void onResume() {
    super.onResume();
    // Set title
    getActivity().getActionBar()
        .setTitle(R.string.thetitle);
}
Run Code Online (Sandbox Code Playgroud)

在某些情况下,onResume不会在片段内部调用.在某些情况下,我们可以使用:

public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if(isVisibleToUser) {
        // Set title
        getActivity().getActionBar()
            .setTitle(R.string.thetitle);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • -1.这种方法不正确!根据Android设计指南,当使用导航抽屉切换片段时,操作栏标题应仅在导航抽屉onClosed回调中更改.在导航抽屉关闭之前,您的方法将错误地更改标题. (9认同)
  • 这对我不起作用所以我把它改成了`getActivity().setTitle(R.string.thetitle);`然后它才有效. (7认同)
  • @zyamys如果你有更好的建议,我很乐意提出建议.这是我当时能想到的最佳答案. (6认同)
  • 这只有在FragmentTransaction期间替换片段时才有效.如果添加片段而不是替换怎么办? (4认同)
  • 我没有必要强制转换或获取操作栏(即我只是调用`getActivity().setTitle(...)`),但这是一种合理的方法.谢谢. (2认同)
  • 此解决方案可能很难应用于将两个片段加载到同一布局中的情况.如果它们都设置了标题,那么哪一个是正确的?或者其中一个只设置标题但另一个不会?这将导致难以阅读和理解代码.在这种情况下,唯一可行的解​​决方案是在使用FragmentManager添加/替换片段时设置标题,并将Activity保持为知道正在加载什么以及必须设置标题的主控制器. (2认同)
  • @WarpZit,启动时将调用正确的“ B”的OnResume,但问题与反向堆栈有关。当您从A-> B转到时,您更改标题,但是当您返回到A时,如问题“ A”中的onResume不会被调用以再次将标题更改为原始标题 (2认同)

Mac*_*ski 27

由于最初的答案很老,这也可能有所帮助.正如文档所述,人们可能希望注册一个listener来监听托管中的后台堆栈更改Activity:

getSupportFragmentManager().addOnBackStackChangedListener(
        new FragmentManager.OnBackStackChangedListener() {
            public void onBackStackChanged() {
                // Update your UI here.
            }
        });
Run Code Online (Sandbox Code Playgroud)

然后,找出回调方法的情况下,并设置一个适当的标题,而无需访问ActionBarFragment.

这是一个更优雅的解决方案,因为它Fragment不需要知道ActionBar存在,Activity并且通常是管理backstack的地方,因此在那里处理它似乎更合适.Fragment应该始终只考虑自己的内容,而不是周围的环境.

有关该文档主题的更多信息.

  • 一段时间后,我得出结论,两种解决方案都很好,它只取决于你的用例,有时使用onResume和有时是片段管理器监听器会更可行.总而言之,了解您的工具总是很好,因此您可以充分满足您的需求. (2认同)
  • @Maciej Pigulski然后呢?我们对此回调中的上下文一无所知. (2认同)
  • @caBBAlainB,上下文被托管在Activity的FragmentManager中,所以你必须确定标题对经理进行一些繁琐的检查 - 因此它并不好.今天我不记得我在这里的推理是什么.我认为这个想法主要来自于在这个答案中链接的Android文档中的内容(听众片段上方的段落).今天我宁愿去使用接受的答案. (2认同)

Mea*_*man 11

让控制活动完成所有工作如下:

监听backstack事件(在onCreate()活动中):

// Change the title back when the fragment is changed
    getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            Fragment fragment = getFragment();
            setTitleFromFragment(fragment);
        }
    });
Run Code Online (Sandbox Code Playgroud)

从容器中获取当前片段:

/**
 * Returns the currently displayed fragment.
 * @return
 *      Fragment or null.
 */
private Fragment getFragment() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.container);
    return fragment;
}
Run Code Online (Sandbox Code Playgroud)

在内容视图中设置片段:

private void setFragment(Fragment fragment, boolean addToBackStack) {
    // Set the activity title
    setTitleFromFragment(fragment);
    .
    .
    .
}
Run Code Online (Sandbox Code Playgroud)

  • 这听起来"从设计/分离关注/可重用性的角度来看可能更糟糕"?请用有效的参数进行备份吗?您将活动设为"控制器"并根据其内容设置自己的标题.如果我想在多个地方使用片段,或者有多个片段的活动,并且他们都试图设置标题,该怎么办? (3认同)

Jem*_*rov 7

Warpzit是对的.这也解决了设备方向改变时的标题问题.此外,如果您使用支持v7操作栏,您可以从片段获取操作栏,如下所示:

@Override
public void onResume() {
    super.onResume();
    ((ActionBarActivity)getActivity()).getSupportActionBar().setTitle("Home");
}
Run Code Online (Sandbox Code Playgroud)


Lee*_*ell 6

最好让操作系统尽可能多地完成工作.假设每个片段都使用.addToBackStack("title")正确命名,那么你可以覆盖onBackPressed这样的东西来实现所需的行为:

// this example uses the AppCompat support library
// and works for dynamic fragment titles
@Override
public void onBackPressed() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    int count = fragmentManager.getBackStackEntryCount();
    if (count <= 1) {
        finish();
    }
    else {
        String title = fragmentManager.getBackStackEntryAt(count-2).getName();
        if (count == 2) {
            // here I am using a NavigationDrawer and open it when transitioning to the initial fragment
            // a second back-press will result in finish() being called above.
            mDrawerLayout.openDrawer(mNavigationDrawerFragment.getView());
        }
        super.onBackPressed();
        Log.v(TAG, "onBackPressed - title="+title);
        getSupportActionBar().setTitle(title);
    }
}
Run Code Online (Sandbox Code Playgroud)


Gnz*_*zlt 5

我使用与Lee方法类似的解决方案,但onBackStackChanged()改为使用method。

首先,在将事务添加到后台堆栈时设置片段名称。

getSupportFragmentManager().beginTransaction()
                .replace(R.id.frame_content, fragment)
                .addToBackStack(fragmentTitle)
                .commit();
Run Code Online (Sandbox Code Playgroud)

然后,我将覆盖该onBackStackChanged()方法,并setTitle()使用最后一个Backstack条目名称进行调用。

@Override
public void onBackStackChanged() {
    int lastBackStackEntryCount = getSupportFragmentManager().getBackStackEntryCount() - 1;
    FragmentManager.BackStackEntry lastBackStackEntry =
            getSupportFragmentManager().getBackStackEntryAt(lastBackStackEntryCount);

    setTitle(lastBackStackEntry.getName());
}
Run Code Online (Sandbox Code Playgroud)

  • 使用Backstack条目名称的问题是它不能处理配置更改。解决此问题的一种有趣而正确的方法是使用setBreadCrumbTitle(int res)保存标题资源ID,您可以在onBackStackChanged中使用它来设置标题。 (3认同)