Setting Custom ActionBar Title from Fragment

172,714

Solution 1

===Update October, 30, 2019===

Since we have new components such as ViewModel and LiveData, we can have a different/easier way to update Activity Title from Fragment by using ViewModel and Live Data

Activity

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
    })
} }

MainFragment

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")
} }

And MainModelView:

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

And then you can update the Activity title from Fragment using viewModel.updateActionBarTitle("Custom Title From Fragment")

===Update April, 10, 2015===

You should use listener to update your action bar title

Fragment:

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

And Activity:

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

Read more here: https://developer.android.com/training/basics/fragments/communicating.html

Solution 2

What you're doing is correct. Fragments don't have access to the ActionBar APIs, so you have to call getActivity. Unless your Fragment is a static inner class, in which case you should create a WeakReference to the parent and call Activity.getActionBar from there.

To set the title for your ActionBar, while using a custom layout, in your Fragment you'll need to call getActivity().setTitle(YOUR_TITLE).

The reason you call setTitle is because you're calling getTitle as the title of your ActionBar. getTitle returns the title for that Activity.

If you don't want to get call getTitle, then you'll need to create a public method that sets the text of your TextView in the Activity that hosts the Fragment.

In your Activity:

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

In your Fragment:

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

Docs:

Activity.getTitle

Activity.setTitle

Also, you don't need to call this.whatever in the code you provided, just a tip.

Solution 3

Google examples tend to use this within the fragments.

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

The fragment will belong to an ActionBarActivity and that is where the reference to the actionbar is. This is cleaner because the fragment doesn't need to know exactly what activity it is, it only needs to belong to an activity that implements ActionBarActivity. This makes the fragment more flexible and can be added to multiple activities like they are meant to.

Now, all you need to do in the fragment is.

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

This works well if you have a base fragment that your fragments inherit from instead of the normal fragment class.

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

Then in your fragment.

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

Solution 4

I don't think that the accepted answer is a perfect answer for it. Since all the activities that use

Toolbar

are extended using

AppCompatActivity

, the fragments called from it can use the below mentioned code for changing the title.

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

Solution 5

Setting Activity’s title from a Fragment messes up responsibility levels. Fragment is contained within an Activity, so this is the Activity, which should set its own title according to the type of the Fragment for example.

Suppose you have an interface:

interface TopLevelFragment
{
    String getTitle();
}

The Fragments which can influence the Activity’s title then implement this interface. While in the hosting activity you write:

@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());
}

In this manner Activity is always in control what title to use, even if several TopLevelFragments are combined, which is quite possible on a tablet.

Share:
172,714
TheLettuceMaster
Author by

TheLettuceMaster

Android (Java) Developer for fun, and Professional Full Stack Developer using python, ruby, JavaScript, SQL and php.

Updated on August 13, 2021

Comments

  • TheLettuceMaster
    TheLettuceMaster over 2 years

    In my Main FragmentActivity, I setup my custom ActionBar title like this:

        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);
    

    This works perfect. However, once I open other Fragments, I want the title to change. I am not sure how to access the Main Activity to do this? In the past, I did this:

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

    Can someone advise on the proper method?

    XML:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent" >
    
        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="5dp"
            android:ellipsize="end"
            android:maxLines="1"
            android:text=""
            android:textColor="#fff"
            android:textSize="25sp" />
    
    </RelativeLayout>
    
  • TheLettuceMaster
    TheLettuceMaster about 11 years
    I try and I can't get it to work. For what its worth, I posted my XML and also removed "this". No changes... And no, its not a static inner class.
  • adneal
    adneal about 11 years
    Wait, what doesn't work? In your OP you don't mention anything not working, you only mention wanting to know the "proper" method to set the title in the ActionBar from a Fragment. Also, I wasn't offering removing this.whatever as a solution to anything, that was only a code formatting tip.
  • adneal
    adneal about 11 years
    I think I understand what you're having trouble with and edited my answer accordingly.
  • luckyreed76
    luckyreed76 over 10 years
    It worked for me! I just used getActionBar().setTitle(title) instead in the activity. Thanks
  • Priya Narayanan
    Priya Narayanan over 9 years
    This answer is to change the title of the Activity, the question is about the ActionBar's title instead.
  • Ammar ali
    Ammar ali about 9 years
    Thankx perfect answer
  • Amir
    Amir about 9 years
    Thanks. very nice solution , by this way you can also pass custom view to your action bar.
  • Jose Manuel Abarca Rodríguez
    Jose Manuel Abarca Rodríguez almost 9 years
    @KickingLettuce, I struggle with this answer for a while until I made it work (that's why I upvoted it, as well as your question) : in my navigation-drawer main activity ( menu_act ) I made global "mTitle", and, in a fragment, first I assign a title to "mTitle" ( ( (menu_act) getActivity() ).mTitle = "new_title" ), and immediately I do ( (menu_act) getActivity() ).restoreActionBar();. Inside "restoreActionBar()" I have "actionBar.setTitle( mTitle );".
  • baybora.oren
    baybora.oren about 8 years
    for me, Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); ((TextView)toolbar.getChildAt(0)).setText(title);
  • dstrube
    dstrube almost 8 years
    Exactly what I was looking for- worked better for me than the chosen answer. Thanks!
  • Akhil Dad
    Akhil Dad over 7 years
    This should be the accepted answer because all other approaches are making fragment tied to particular activity, which is not a scalable design, also this is what android framework also recommends
  • Francis Rodrigues
    Francis Rodrigues about 7 years
    Thanks @Anisetti! That's just what I did in Fragment class.
  • fr4gus
    fr4gus over 6 years
    With recent updates on Android SDK, instead of ActionBarActivity, you may want to downcast to AppCompatActivity in case you are using v7 backwards compatibility library (which you probably should).
  • winklerrr
    winklerrr over 6 years
    This should be the accepted answer! It correctly shows how it should be done: If a fragment wants to communicate with its hosting activity, the communication should always happen through an interface. The fragment provides the interface and the activity implements it! If multiple fragments have to communicate the same way with an activity, write an abstract fragment which provides the interface and then let every fragment inherit from this "base fragment".
  • winklerrr
    winklerrr over 6 years
    By the way: The onAttach(Activity) method used in this answer is deprecated by this time. Check inside the onAttach(Context) method wether the passed host activity implements the required interface (retrieve host activity via context.getActivity()) .
  • winklerrr
    winklerrr over 6 years
    This answer works, but according to the Android docs it's bad practice. If a fragment wants to communicate with its host activity, let the activity implement an interface provided by the fragment. The fragment can then call the method through the implemented interface. See Frank Nguyen answer. His answer is correct.
  • AXSM
    AXSM over 6 years
    This approach have a problem, what if you have many fragments, each one sets a different title on the actionbar. I think it works good if you always replace with new fragment instances. But what if you just add fragments, instead of replacing? the title wont change when you press back, for my case It wont work. I think there should be a better way to achieve that.
  • TheLettuceMaster
    TheLettuceMaster over 6 years
    After many years I changed this to the accepted answer because as we know, interfaces are the way fragments and activities need to communicate.
  • Paixols
    Paixols over 5 years
    Excellent! Using the toolbar_title overrides the fragment title if using Android Navigation Components.
  • me_
    me_ over 5 years
    that's wonderful if you have access to the title directly from the activity... but if the title is a dynamically set string in the fragment there needs to be communication between the fragment and the activity
  • Simon
    Simon over 5 years
    This does not work when the Toolbar of our activity has a TextView that has the role of the title
  • Joe Lapp
    Joe Lapp almost 5 years
    This was my problem -- where I was setting the title. However, ((MainActivity) getActivity()).setTitle("title") sufficed. In Kotlin, my fragment does activity!!.title = "title.
  • Sunil Chaudhary
    Sunil Chaudhary almost 4 years
    waht about FragmentActivity i need to use this class
  • Farid
    Farid over 2 years
    @Simon Because it has nothing to do with setTitle(), it is custom implementation and you have to handle it yourself
  • Dimitri Williams
    Dimitri Williams about 2 years
    Works perfectly even now. Thx!