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

时间:2012-11-20 11:30:50

标签: android android-fragments android-actionbar actionbarsherlock

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

开头,活动会在操作栏中显示默认标题(即根据应用程序的FragmentTransaction自动设置)。

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

然而,当按 Back 时,我希望恢复原始默认标题。这不是FragmentBreadCrumbs所知道的,因此标题不会被恢复。

我模糊地阅读了protected void showFragment(Fragment f) { FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.replace(R.id.fragment_container, f); ft.addToBackStack(null); ft.commit(); } ,但这似乎需要使用自定义视图。我正在使用ActionBarSherlock,并且更愿意没有我自己的自定义标题视图。

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


{{1}}

11 个答案:

答案 0 :(得分:119)

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

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

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

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

答案 1 :(得分:26)

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

getSupportFragmentManager().addOnBackStackChangedListener(
        new FragmentManager.OnBackStackChangedListener() {
            public void onBackStackChanged() {
                // Update your UI here.
            }
        });

然后,识别回调方法中的情况并设置正确的标题,而无需访问ActionBar中的Fragment

这是一个更优雅的解决方案,因为Fragment不必了解ActionBar存在,Activity通常是管理后台堆栈的地方,因此需要处理它似乎更合适。 Fragment在任何时候都应该只考虑自己的内容,而不是周围环境。

有关documentation

中主题的更多信息

答案 2 :(得分:10)

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

监听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);
        }
    });

从容器中获取当前片段:

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

在内容视图中设置片段:

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

答案 3 :(得分:7)

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

@Override
public void onResume() {
    super.onResume();
    ((ActionBarActivity)getActivity()).getSupportActionBar().setTitle("Home");
}

答案 4 :(得分: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);
    }
}

答案 5 :(得分:5)

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

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

getSupportFragmentManager().beginTransaction()
                .replace(R.id.frame_content, fragment)
                .addToBackStack(fragmentTitle)
                .commit();

然后我覆盖onBackStackChanged()方法,并使用最后一个backstack条目名称调用setTitle()

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

    setTitle(lastBackStackEntry.getName());
}

答案 6 :(得分:4)

使用碎片方法:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)

在每个Fragment外观上调用它,但onResume不是。

答案 7 :(得分:1)

最好的方法是使用android提供的接口OnBackStackChangedListener方法onBackStackChanged()。

假设我们有一个导航抽屉,其中有4个选项供用户导航。在那种情况下,我们将有4个片段。让我们先看看代码,然后我将解释它的工作原理。

    private int mPreviousBackStackCount = 0;
    private String[] title_name = {"Frag1","Frag2","Frag3","Frag4"};
    Stack<String> mFragPositionTitleDisplayed;

    public class MainActivity extends ActionBarActivity implements FragmentManager.OnBackStackChangedListener
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ....
    ....
    ....
    getSupportFragmentManager().addOnBackStackChangedListener(this);
    mFragPositionTitleDisplayed = new Stack<>();
}

public void displayFragment() {
    Fragment fragment = null;
    String title = getResources().getString(R.string.app_name);
    switch (position) {
        case 0:
            fragment = new Fragment1();
            title = title_name[position];
            break;
        case 1:
            fragment = new Fragment2();
            title = title_name[position];
            break;
        case 2:
            fragment = new Fragment3();
            title = title_name[position];
            break;
        case 3:
            fragment = new Fragment4();
            title = title_name[position];
            break;
        default:
            break;
    }
    if (fragment != null) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction()
                .replace(R.id.container_body, fragment)
                .addToBackStack(null)
                .commit();
        getSupportActionBar().setTitle(title);
    }
}

@Override
public void onBackStackChanged() {
    int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
    if(mPreviousBackStackCount >= backStackEntryCount) {
        mFragPositionTitleDisplayed.pop();
        if (backStackEntryCount == 0)
            getSupportActionBar().setTitle(R.string.app_name);
        else if (backStackEntryCount > 0) {
            getSupportActionBar().setTitle(mFragPositionTitleDisplayed.peek());
        }
        mPreviousBackStackCount--;
    }
    else{
        mFragPositionTitleDisplayed.push(title_name[position]);
        mPreviousBackStackCount++;
    }

}   

在显示的代码中,我们有displayFragment()方法。在这里,我根据从导航抽屉中选择的选项显示片段。变量位置对应于从导航抽屉中的ListView或RecyclerView单击的项目的位置。我使用getSupportActionBar.setTitle(title)相应地设置了操作栏标题,其中标题存储了相应的标题名称。

每当我们从导航抽屉中单击该项目时,将显示一个片段,具体取决于单击该用户的项目。但是在后端,这个片段被添加到backstack,而onBackStachChanged()方法被命中。我所做的是我创建了一个变量mPreviousBackStackCount并将其初始化为0.我还创建了一个额外的堆栈,它将存储操作栏标题名称。每当我向backstack添加一个新片段时,我都会将相应的标题名称添加到我创建的堆栈中。在对面,每当我按下后退按钮onBackStackChanged()被调用,我从我的堆栈中弹出最后一个标题名称,并将标题设置为堆栈的peek()方法派生的名称。

示例:

让我们说我们的android backstack是空的:

按导航抽屉中的选择1: 调用onBackStachChanged()并将Fragment 1添加到android backstack,将backStackEntryCount设置为1并将Frag1推送到我的堆栈,mFragPositionTitleDisplayed的大小变为1。

按导航抽屉中的选择2: 调用onBackStachChanged()并将Fragment 2添加到android backstack中,backStackEntryCount设置为2,Frag2被推送到我的堆栈,mFragPositionTitleDisplayed的大小变为2。

现在我们在android堆栈和堆栈中都有2个元素。按下后退按钮onBackStackChanged()被调用,backStackEntryCount的值为1.代码进入if部分并从我的堆栈中弹出最后一个条目。因此,android backstack只有1个片段 - “Fragment 1”,我的堆栈只有1个标题 - “Frag1”。现在我只是从我的堆栈中偷看()标题并将操作栏设置为该标题。

请记住:要设置动作蝙蝠标题,请使用peek()而不是pop()否则当您打开2个以上的碎片并尝试通过按后退按钮返回时,您的应用程序将崩溃。

答案 8 :(得分:1)

你用onKeyDown解决了问题! 我有一个布尔 mainisopen = true&lt; - MainFragment是可见的 其他片段mainisopen = false

这是我的代码:

public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK && mainisopen == false) {
        mainisopen = true;
        HomeFrag fragment = new HomeFrag();
        FragmentTransaction fragmentTransaction =
                getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.fragmet_cont, fragment);
        fragmentTransaction.commit();
        navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.getMenu().findItem(R.id.nav_home).setChecked(true);
        navigationView.setNavigationItemSelectedListener(this);
        this.setTitle("Digi - Home"); //Here set the Title back
        return true;
    } else {
        if (keyCode == KeyEvent.KEYCODE_BACK && mainisopen == true) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage("Wollen sie die App schliessen!");
            builder.setCancelable(true);

            builder.setPositiveButton("Ja!", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    System.exit(1);
                }
            });

            builder.setNegativeButton("Nein!", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    Toast.makeText(getApplicationContext(), "Applikation wird fortgesetzt", Toast.LENGTH_SHORT).show();
                }
            });

            AlertDialog dialog = builder.create();
            dialog.show();

            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

}

答案 9 :(得分:0)

here所述,我的解决方案是将此代码添加到MainActivity onCreate method():并更改操作栏标题

FragmentManager fragmentManager=getSupportFragmentManager();
fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
        Fragment currentFragment = fragmentManager.findFragmentById(R.id.My_Container_1_ID);
        currentFragment.onResume();
    }
});

并在片段的onResume()方法中更改操作栏标题

@Override
public void onResume() {
    super.onResume();
    AppCompatActivity activity = (AppCompatActivity) getActivity();
    ActionBar actionBar = activity.getSupportActionBar();
    if(actionBar!=null) {
        actionBar.setTitle("Fragment Title");
        actionBar.setSubtitle("Subtitle");
    }

}

答案 10 :(得分:-3)

要在背面更新操作栏标题。只需简单地把

<强> getActivity.setTitle( “标题”)

在onCreateView方法中。