从 Fragment 设置自定义 ActionBar 标题

Posted

技术标签:

【中文标题】从 Fragment 设置自定义 ActionBar 标题【英文标题】:Setting Custom ActionBar Title from Fragment 【发布时间】:2013-03-11 18:03:38 【问题描述】:

在我的 Main FragmentActivity 中,我设置了我的自定义 ActionBar 标题,如下所示:

    LayoutInflater inflator = (LayoutInflater) this
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View v = inflator.inflate(R.layout.custom_titlebar, null);

    TextView tv = (TextView) v.findViewById(R.id.title);
    Typeface tf = Typeface.createFromAsset(this.getAssets(),
            "fonts/capsuula.ttf");
    tv.setTypeface(tf);
    tv.setText(this.getTitle());

    actionBar.setCustomView(v);

这很完美。但是,一旦我打开其他Fragments,我希望更改标题。我不确定如何访问 Main Activity 来执行此操作?过去,我是这样做的:

((MainFragmentActivity) getActivity()).getSupportActionBar().setTitle(
            catTitle);

有人可以建议正确的方法吗?

XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:background="@android:color/transparent" >

    <TextView
        android:id="@+id/title"
        android:layout_
        android:layout_
        android:layout_centerVertical="true"
        android:layout_marginLeft="5dp"
        android:ellipsize="end"
        android:maxLines="1"
        android:text=""
        android:textColor="#fff"
        android:textSize="25sp" />

</RelativeLayout>

【问题讨论】:

***.com/a/28279341/1651286 应该是公认的答案,因为它具有可扩展性。因为所有其他方法都将片段绑定到特定的活动,这不是一个可扩展的设计,如果同一个片段必须用于多个活动怎么办,这也是 android 框架也推荐的。 看这个链接***.com/a/46705242/1770868 【参考方案1】:

你所做的是正确的。 Fragments 无权访问 ActionBar API,因此您必须调用 getActivity。除非您的 Fragment 是静态内部类,在这种情况下,您应该为父级创建一个 WeakReference 并从那里调用 Activity.getActionBar

要为您的ActionBar 设置标题,同时使用自定义布局,您需要在您的Fragment 中调用getActivity().setTitle(YOUR_TITLE)

之所以调用setTitle,是因为您调用getTitle 作为ActionBar 的标题。 getTitle 返回该 Activity 的标题。

如果您不想调用getTitle,那么您需要创建一个公共方法,将TextView 的文本设置在承载ActivityActivity 中。

在您的活动中

public void setActionBarTitle(String title)
    YOUR_CUSTOM_ACTION_BAR_TITLE.setText(title);

在您的片段中

((MainFragmentActivity) getActivity()).setActionBarTitle(YOUR_TITLE);

文档:

Activity.getTitle

Activity.setTitle

另外,您无需在提供的代码中调用this.whatever,只是一个提示。

【讨论】:

我尝试过,但无法正常工作。为了它的价值,我发布了我的XML 并删除了“this”。没有变化...不,它不是 static 内部类。 等等,什么不起作用?在您的 OP 中,您没有提及任何不起作用,您只提到想知道从FragmentActionBar 中设置标题的“正确”方法。另外,我并没有提供删除 this.whatever 作为解决任何问题的方法,这只是一个代码格式提示。 我想我了解您遇到的问题并相应地编辑了我的答案。 感谢完美答案 @KickingLettuce,我在这个答案上挣扎了一段时间,直到我让它起作用(这就是我赞成它的原因,以及你的问题):在我的导航抽屉主要活动中(menu_act)我创建了全局“mTitle”,并且在一个片段中,首先我为“mTitle”(( (menu_act) getActivity() ).mTitle = "new_title")分配了一个标题,然后立即执行( (menu_act) getActivity() ).restoreActionBar();。在“restoreActionBar()”里面我有“actionBar.setTitle(mTitle);”。【参考方案2】:

===2019 年 10 月 30 日更新===

由于我们有了 ViewModelLiveData 等新组件,我们可以通过使用 ViewModel 和 Live Data 以不同/更简单的方式从 Fragment 更新 Activity Title p>

活动

class MainActivity : AppCompatActivity() 
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_activity)
    if (savedInstanceState == null) 
        supportFragmentManager.beginTransaction()
            .replace(R.id.container, MainFragment.newInstance())
            .commitNow()
    
    viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
    viewModel.title.observe(this, Observer 
        supportActionBar?.title = it
    )
 

主片段

class MainFragment : Fragment() 
companion object 
    fun newInstance() = MainFragment()

private lateinit var viewModel: MainViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View 
    return inflater.inflate(R.layout.main_fragment, container, false)

override fun onActivityCreated(savedInstanceState: Bundle?) 
    super.onActivityCreated(savedInstanceState)
    activity?.run 
        viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
     ?: throw Throwable("invalid activity")
    viewModel.updateActionBarTitle("Custom Title From Fragment")
 

还有 MainModelView:

class MainViewModel : ViewModel() 
private val _title = MutableLiveData<String>()
val title: LiveData<String>
get() = _title
fun updateActionBarTitle(title: String) = _title.postValue(title) 

然后您可以使用 viewModel.updateActionBarTitle("Custom Title From Fragment") 从 Fragment 更新 Activity 标题

===2015 年 4 月 10 日更新===

您应该使用监听器来更新您的操作栏标题

片段:

public class UpdateActionBarTitleFragment extends Fragment 
    private OnFragmentInteractionListener mListener;

    public UpdateActionBarTitleFragment() 
    

    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        if (getArguments() != null) 
        
    

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) 
        if (mListener != null) 
            mListener.onFragmentInteraction("Custom Title");
        
        return inflater.inflate(R.layout.fragment_update_action_bar_title2, container, false);
    

    @Override
    public void onAttach(Activity activity) 
        super.onAttach(activity);
        try 
            mListener = (OnFragmentInteractionListener) activity;
         catch (ClassCastException e) 
            throw new ClassCastException(activity.toString()
                    + " must implement OnFragmentInteractionListener");
        
    

    @Override
    public void onDetach() 
        super.onDetach();
        mListener = null;
    

    public interface OnFragmentInteractionListener 
        public void onFragmentInteraction(String title);
    

和活动:

public class UpdateActionBarTitleActivity extends ActionBarActivity implements UpdateActionBarTitleFragment.OnFragmentInteractionListener 

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

    @Override
    public void onFragmentInteraction(String title) 
        getSupportActionBar().setTitle(title);
    

在此处阅读更多信息:https://developer.android.com/training/basics/fragments/communicating.html

【讨论】:

它对我有用!我只是在活动中使用了 getActionBar().setTitle(title) 。谢谢 正是我想要的——对我来说比选择的答案更好。谢谢! 这应该是公认的答案!它正确地显示了它应该如何完成:如果一个片段想要与它的宿主活动通信,通信应该总是通过一个接口发生。片段提供接口,活动实现它!如果多个片段必须以相同的方式与一个活动进行通信,请编写一个提供接口的抽象片段,然后让每个片段都继承自这个“基础片段”。 顺便说一句:这个答案中使用的 onAttach(Activity) 方法此时已弃用。检查onAttach(Context) 方法内部传递的主机活动是否实现了所需的接口(通过context.getActivity() 检索主机活动)。 多年后我将其更改为公认的答案,因为我们知道,接口是片段和活动需要通信的方式。【参考方案3】:

Google 示例倾向于在片段中使用它。

private ActionBar getActionBar() 
    return ((ActionBarActivity) getActivity()).getSupportActionBar();

片段将属于一个 ActionBarActivity 并且是对 actionbar 的引用所在的位置。这样更简洁,因为片段不需要确切知道它是什么活动,它只需要属于实现了 ActionBarActivity 的活动。这使得片段更加灵活,并且可以添加到多个活动中,就像它们应该做的那样。

现在,您需要在片段中做的就是。

getActionBar().setTitle("Your Title");

如果您有一个片段继承自基础片段而不是普通片段类,则此方法效果很好。

public abstract class BaseFragment extends Fragment 
    public ActionBar getActionBar() 
        return ((ActionBarActivity) getActivity()).getSupportActionBar();
    

然后在你的片段中。

public class YourFragment extends BaseFragment 
    @Override
    public void onActivityCreated(Bundle savedInstanceState) 
        super.onActivityCreated(savedInstanceState);
        getActionBar().setTitle("Your Title");
    

【讨论】:

谢谢。非常好的解决方案,通过这种方式,您还可以将自定义视图传递给您的操作栏。 随着 Android SDK 的最新更新,而不是 ActionBarActivity,您可能希望向下转换为 AppCompatActivity,以防您使用 v7 向后兼容库(您可能应该这样做)。【参考方案4】:

我不认为接受的答案是完美的答案。由于所有使用

的活动

工具栏

使用

扩展

AppCompatActivity

,从中调用的片段可以使用下面提到的代码来更改标题。

((AppCompatActivity) context).getSupportActionBar().setTitle("Your Title");

【讨论】:

即使现在也能完美运行。谢谢!【参考方案5】:

Fragment 设置Activity 的标题会混淆责任级别。 Fragment包含在Activity中,所以这是Activity,它应该根据Fragment的类型设置自己的标题。

假设你有一个接口:

interface TopLevelFragment

    String getTitle();

Fragments 可以影响Activity 的标题然后实现这个接口。在您编写的托管活动中:

@Override
protected void onCreate(Bundle savedInstanceState)

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    FragmentManager fm = getFragmentManager();
    fm.beginTransaction().add(0, new LoginFragment(), "login").commit();


@Override
public void onAttachFragment(Fragment fragment)

    super.onAttachFragment(fragment);

    if (fragment instanceof TopLevelFragment)
        setTitle(((TopLevelFragment) fragment).getTitle());

以这种方式,Activity 始终可以控制要使用的标题,即使组合了多个 TopLevelFragments,这在平板电脑上是完全可能的。

【讨论】:

【参考方案6】:

一个简单的 Kotlin 示例

将此适应您的片段类:

    /**
     * A simple [Fragment] subclass.
     *
     * Updates the action bar title when onResume() is called on the fragment,
     * which is called every time you navigate to the fragment
     *
     */

    class MyFragment : Fragment() 

        override fun onResume() 
            super.onResume()
            (requireActivity() as MyMainActivity).supportActionBar?.title = "My Fragment!"
        


    

【讨论】:

【参考方案7】:

在我们可以这样使用的片段中,它对我来说很好用。

getActivity().getActionBar().setTitle("YOUR TITLE");

【讨论】:

当我们activity的Toolbar有一个TextView具有title的作用时,这不起作用 @Simon 因为和setTitle()没有关系,是自定义实现,需要自己处理【参考方案8】:

以防万一您遇到代码问题,请尝试将 getSupportActionBar().setTitle(title) 放在片段的 onResume() 中,而不是 onCreateView(...)

MainActivity.java 中:

public void setActionBarTitle(String title) 
    getSupportActionBar().setTitle(title);

在片段中:

 @Override
 public void onResume()
     super.onResume();
     ((MainActivity) getActivity()).setActionBarTitle("Your Title");
 

【讨论】:

这是我的问题——在哪里我设置了标题。但是,((MainActivity) getActivity()).setTitle("title") 就足够了。在 Kotlin 中,我的片段是 activity!!.title = "title【参考方案9】:

使用以下内容:

getActivity().setTitle("YOUR_TITLE");

【讨论】:

这个答案是改变Activity的标题,问题是关于ActionBar的标题。 谢谢@Anisetti!这正是我在 Fragment 课上所做的。【参考方案10】:

这是我在使用 NavigationDrawer 时从片段设置 ActionBar 标题的解决方案。 该解决方案使用接口,因此片段不需要直接引用父 Activity:

1) 创建接口:

public interface ActionBarTitleSetter 
    public void setTitle(String title); 

2)在Fragment的onAttach中,将activity转换为Interface类型并调用SetActivityTitle方法:

@Override 
public void onAttach(Activity activity)  
    super.onAttach(activity);
    ((ActionBarTitleSetter) activity).setTitle(getString(R.string.title_bubbles_map)); 

3) 在activity中,实现ActionBarTitleSetter接口:

@Override 
public void setTitle(String title)  
    mTitle = title; 

【讨论】:

这应该是公认的答案,因为所有其他方法都使片段与特定活动相关联,这不是可扩展的设计,这也是 android 框架也推荐的【参考方案11】:

更改标题的最佳事件onCreateOptionsMenu

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) 
    View view = inflater.inflate(R.layout.general, container,  
    setHasOptionsMenu(true); // <-Add this line
    return view;


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

    // If use specific menu
    menu.clear();
    inflater.inflate(R.menu.path_list_menu, menu);
    // If use specific menu

    ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle("Your Fragment");
    super.onCreateOptionsMenu(menu, inflater);

【讨论】:

【参考方案12】:

如果您有一个包含许多片段的主要活动,通常使用navigationDrawer。你的片段有一系列标题,当你按下,让它们改变,把它放在保存片段的主要活动中

@Override
     public void onBackPressed() 

    int T=getSupportFragmentManager().getBackStackEntryCount();
    if(T==1)  finish();

    if(T>1) 
        int tr = Integer.parseInt(getSupportFragmentManager().getBackStackEntryAt(T-2).getName());
        setTitle(navMenuTitles[tr]);  super.onBackPressed();
      


这假设你给每个片段一个标签,通常在你将片段添加到navigationDrawer列表时的某个地方,根据在列表上按下的位置。所以那个位置就是我在标签上捕获的:

    fragmentManager.beginTransaction().
replace(R.id.frame_container, fragment).addToBackStack(position).commit();

现在,navMenuTitles 是您在 onCreate 上加载的内容

 // load slide menu items
        navMenuTitles = getResources().getStringArray(R.array.nav_drawer_items);

数组xml是strings.xml上的数组类型字符串资源

 <!-- Nav Drawer Menu Items -->
    <string-array name="nav_drawer_items">
        <item>Title one</item>
        <item>Title Two</item>
    </string-array>

【讨论】:

【参考方案13】:

将您的答案保存在 String[] 对象中,并在 MainActivity 中将其设置为 OnTabChange() 如下www

String[] object = "Fragment1","Fragment2","Fragment3";

public void OnTabChange(String tabId)

int pos =mTabHost.getCurrentTab();     //To get tab position
 actionbar.setTitle(object.get(pos));



//Setting in View Pager
public void onPageSelected(int arg0) 
    mTabHost.setCurrentTab(arg0);
actionbar.setTitle(object.get(pos));

【讨论】:

【参考方案14】:

你这样铸造方法的缺点

((MainFragmentActivity) getActivity()).getSupportActionBar().setTitle(
        catTitle);

是您的片段在 MainActivityFragment 之外不再可重用。如果您不打算在该活动之外使用它,那么没有问题。更好的方法是根据活动有条件地设置标题。所以在你的片段中,你会写:

if (getActivity() instanceof ActionBarActivity) 
    ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle("Some Title");

【讨论】:

【参考方案15】:

如果您使用的是 google 提供的 android studio 1.4 stable 模板,那么您必须在 onNavigationItemSelected methode 中编写以下代码 您的相关片段在其中调用 if 条件。

 setTitle("YOUR FRAGMENT TITLE");

【讨论】:

如果您可以直接从活动中访问标题,那就太好了......但是如果标题是片段中动态设置的字符串,则片段和活动之间需要进行通信【参考方案16】:

我得到了一个非常简单的解决方案来设置 ActionBar Title 片段或任何活动都不会让人头疼。

只需修改定义工具栏的xml如下:

<android.support.design.widget.AppBarLayout
        android:layout_
        android:layout_
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_
            android:layout_
            android:background="@color/colorPrimaryDark"
            app:popupTheme="@style/AppTheme.PopupOverlay" >

            <TextView
                style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
                android:id="@+id/toolbar_title"
                android:layout_
                android:layout_
                android:text="@string/app_name"
                />
            </android.support.v7.widget.Toolbar>

    </android.support.design.widget.AppBarLayout> 

1) 如果您想在片段中设置操作栏,那么:

Toolbar toolbar = findViewById(R.id.toolbar);
TextView toolbarTitle = (TextView) toolbar.findViewById(R.id.toolbar_title);

要在任何地方使用它,您可以在活动中定义一个方法

public void setActionBarTitle(String title) 
        toolbarTitle.setText(title);
    

要在活动中调用此方法,只需调用它即可。

setActionBarTitle("Your Title")

要从活动的片段中调用此方法,只需调用它即可。

((MyActivity)getActivity()).setActionBarTitle("Your Title");

【讨论】:

太棒了!如果使用 Android 导航组件,则使用 toolbar_title 会覆盖片段标题。【参考方案17】:

只是为了添加到选定的答案,您可能还想在主要活动中添加第二种方法。所以你最终会在你的主要活动中使用以下方法:

public void setActionBarTitle(String title) 
    getSupportActionBar().setTitle(title);


public void setActionBarTitle(int resourceId) 
    setActionBarTitle(getResources().getString(resourceId);

这将允许您从 String 变量以及资源 ID(例如 strings.xml 文件中的R.id.this_is_a_string)设置标题。这也将更像getSupportActionBar().setTitle() 的工作方式,因为它允许您传入资源 ID。

【讨论】:

【参考方案18】:

如上所述,有很多方法。您也可以在onNavigationDrawerSelected()DrawerActivity 中执行此操作

public void setTitle(final String title)
    ((TextView)findViewById(R.id.toolbar_title)).setText(title);


@Override
public void onNavigationDrawerItemSelected(int position) 
    // update the main content by replacing fragments
    fragment = null;
    String title = null;
    switch(position)

    case 0:
        fragment = new HomeFragment();
        title = "Home";
        break;
    case 1:
        fragment = new ProfileFragment();
        title = ("Find Work");
        break;
    ...
    
    if (fragment != null)

        FragmentManager fragmentManager = getFragmentManager();
        fragmentManager
        .beginTransaction()
        .replace(R.id.container,
                fragment).commit();

        //The key is this line
        if (title != null && findViewById(R.id.toolbar_title)!= null ) setTitle(title);
    

【讨论】:

【参考方案19】:

至少对我来说,在运行时更改标签标题有一个简单的答案(经过大量挖掘):

TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); tabLayout.getTabAt(MyTabPos).setText("我的新文本");

【讨论】:

【参考方案20】:

如果您使用ViewPager(如我的情况),您可以使用:

getSupportActionBar().setTitle(YOURE_TAB_BAR.getTabAt(position).getText());

在您的VIEW_PAGER.addOnPageChangeListeneronPageSelected 方法中

【讨论】:

【参考方案21】:

在您的 MainActivity 中,onCreate:

getSupportActionBar().setDisplayHomeAsUpEnabled(true);

并在onResume下的片段活动中:

getActivity().setTitle(R.string.app_name_science);

可选:- 如果它们显示空引用警告

Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);


Objects.requireNonNull(getActivity()).setTitle(R.string.app_name_science);

【讨论】:

【参考方案22】:

基于 Ashish 的 Java 回答解决方案

private void setUI()
    mainToolbar = findViewById(R.id.mainToolbar);
    setSupportActionBar(mainToolbar);
    DrawerLayout drawer = findViewById(R.id.drawer_layout);
    NavigationView navigationView = findViewById(R.id.nav_view);
    mAppBarConfiguration = new AppBarConfiguration.Builder(
            R.id.nav_home, R.id.nav_messaging, R.id.nav_me)
            .setDrawerLayout(drawer)
            .build();

    navController = Navigation.findNavController(this, R.id.nav_host_fragment);
    navController.addOnDestinationChangedListener(navDestinationChange);
    NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
    NavigationUI.setupWithNavController(navigationView, navController);


private NavController.OnDestinationChangedListener navDestinationChange = new NavController.OnDestinationChangedListener()
    @Override
    public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) 
        if(R.id.nav_home==destination.getId())
            destination.setLabel("News");
        
    
;

【讨论】:

以上是关于从 Fragment 设置自定义 ActionBar 标题的主要内容,如果未能解决你的问题,请参考以下文章

Android Date Picker Fragment 不能设置为自定义日期

Android的Fragment的自定义转场动画

Android Fragment 切换动画设置

如何从适配器 startActivityForResult 以将结果返回给 Fragment

Fragment中不能使用自定义带参构造函数

android 怎么在自定义控件中获取控件所在fragment的实例