Установка настраиваемого заголовка ActionBar из фрагмента


90

В своем главном 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, я хочу, чтобы название изменилось. Я не знаю, как для этого получить доступ к Главному 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_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>

stackoverflow.com/a/28279341/1651286 должен быть принятым ответом из-за его масштабируемости. поскольку все другие подходы делают фрагмент привязанным к определенной активности, что не является масштабируемым дизайном, что, если один и тот же фрагмент должен использоваться с несколькими действиями, это также то, что также рекомендует фреймворк Android.
Akhil Dad

Посмотрите эту ссылку stackoverflow.com/a/46705242/1770868
Ахмад Агазаде

Ответы:


98

=== Обновление 30 октября 2019 г. ===

Поскольку у нас есть новые компоненты, такие как ViewModel и LiveData , у нас может быть другой / более простой способ обновления заголовка действия из фрагмента с помощью ViewModel и Live Data.

Деятельность

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

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

=== Обновление от 10 апреля 2015 г. ===

Вы должны использовать слушателя для обновления заголовка панели действий

Фрагмент:

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


1
У меня это сработало! Я просто использовал getActionBar (). SetTitle (title) вместо этого в действии. Спасибо
luckyreed76 02

1
То, что я искал, сработало лучше, чем выбранный ответ. Благодаря!
dstrube

1
Это должен быть принятый ответ! Он правильно показывает, как это должно быть сделано: если фрагмент хочет взаимодействовать со своим хостингом, связь всегда должна происходить через интерфейс. Фрагмент предоставляет интерфейс, а действие его реализует! Если несколько фрагментов должны одинаково взаимодействовать с действием, напишите абстрактный фрагмент, который предоставляет интерфейс, а затем позвольте каждому фрагменту унаследовать от этого «базового фрагмента».
winklerrr

3
Между прочим: метод onAttach (Activity), используемый в этом ответе, к настоящему времени устарел. Проверьте внутри onAttach(Context)метода, реализует ли переданная активность хоста требуемый интерфейс (получение активности хоста через context.getActivity()).
winklerrr

2
Спустя много лет я изменил это на принятый ответ, потому что, как мы знаем, интерфейсы - это способ взаимодействия фрагментов и действий.
TheLettuceMaster

150

То, что вы делаете, правильно. Fragmentsу вас нет доступа к ActionBarAPI, поэтому вам нужно позвонить getActivity. Если ваш Fragmentвнутренний класс не является статическим, в этом случае вы должны создать WeakReferenceродительский класс и вызвать Activity. getActionBarоттуда.

Чтобы установить заголовок для вашего ActionBar, при использовании настраиваемого макета, Fragmentвам необходимо позвонить getActivity().setTitle(YOUR_TITLE).

Причина, по которой вы звоните, setTitleзаключается в том, что вы звоните getTitleв качестве названия вашего ActionBar. getTitleвозвращает заголовок для этого Activity.

Если вы не хотите получать вызов getTitle, вам необходимо создать общедоступный метод, который устанавливает текст вашего TextViewв том, Activityчто содержит Fragment.

В вашей деятельности :

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

В вашем фрагменте :

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

Документы:

Activity.getTitle

Activity.setTitle

Кроме того, вам не нужно вызывать this.whateverкод, который вы предоставили, просто подсказку.


Я пытаюсь, но не могу заставить его работать. Как бы то ни было, я разместил свое XMLи также удалил «это». Никаких изменений ... И нет, это не staticвнутренний класс.
TheLettuceMaster

Подождите, что не работает? В своем OP вы не упоминаете, что что-то не работает, вы только упоминаете, что хотите знать «правильный» метод для установки заголовка в ActionBarfrom a Fragment. Кроме того, я не предлагал удаление this.whateverкак решение чего-либо, это был всего лишь совет по форматированию кода.
adneal

Думаю, я понимаю, с чем у вас проблемы, и соответствующим образом отредактировал свой ответ.
adneal

Спасибо, идеальный ответ
Аммар Али

@KickingLettuce, я какое-то время борюсь с этим ответом, пока не заставил его работать (вот почему я проголосовал за него, а также за ваш вопрос): в моем основном menu_actдействии панели навигации ( ) я сделал глобальный "mTitle", и в фрагмент, сначала я присваиваю заголовок «mTitle» ( ( (menu_act) getActivity() ).mTitle = "new_title"), и сразу же делаю это ( (menu_act) getActivity() ).restoreActionBar();. Внутри «restoreActionBar ()» у меня есть «actionBar.setTitle (mTitle);».
Хосе Мануэль Абарка Родригес

29

В примерах Google это обычно используется во фрагментах.

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

Фрагмент будет принадлежать ActionBarActivity, и именно здесь находится ссылка на панель действий. Это проще, потому что фрагменту не нужно точно знать, что это за действие, ему нужно только принадлежать действию, которое реализует 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 (что, вероятно, вам следует).
fr4gus

8

Постановка Activityтитула из Fragmentразряда перепутанных уровней ответственности. 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, всегда можно контролировать, какой заголовок использовать, даже если несколько слов TopLevelFragmentобъединены, что вполне возможно на планшете.


6

Я не думаю, что принятый ответ - идеальный ответ. Поскольку все действия, которые используют

Панель инструментов

расширены с использованием

AppCompatActivity

, вызываемые из него фрагменты могут использовать указанный ниже код для изменения заголовка.

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

5

Во фрагменте мы можем использовать вот так: у меня он работает нормально.

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

Это не работает, если на панели инструментов нашей активности есть TextView, который играет роль заголовка
Саймон

4

На всякий случай, если у вас возникли проблемы с кодом, попробуйте поместить 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")хватило. В Котлине есть мой фрагмент activity!!.title = "title.
Джо Лапп


2

Вот мое решение для установки заголовка ActionBar из фрагментов при использовании NavigationDrawer. В этом решении используется интерфейс, поэтому фрагментам не нужно напрямую ссылаться на родительский Activity:

1) Создайте интерфейс:

public interface ActionBarTitleSetter {
    public void setTitle(String title); 
}

2) Во фрагменте onAttach активность к типу интерфейса и вызовите метод SetActivityTitle:

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

3) В действии реализуем интерфейс ActionBarTitleSetter :

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

Это должен быть принятый ответ, потому что все другие подходы делают фрагмент привязанным к определенной деятельности, что не является масштабируемым дизайном, также это то, что также рекомендует фреймворк Android
Akhil Dad

2

Лучшее мероприятие для смены названия 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);
}

2

Простой пример Котлина

Предполагая, что эти gradle deps совпадают или имеют более высокую версию в вашем проекте:

kotlin_version = '1.3.41'
nav_version_ktx = '2.0.0'

Адаптируйте это к своим классам фрагментов:

    /**
     * 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() {
    private lateinit var mainActivity: MainActivity

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

       //[...]

        mainActivity = this.activity as MainActivity

       //[...]
    }

    override fun onResume() {
        super.onResume()
        mainActivity.supportActionBar?.title = "My Fragment!"
    }


    }

1

Если у вас есть основное действие с большим количеством вставленных фрагментов, обычно с помощью 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>

1

Сохраните ur Answer в объекте String [] и установите его OnTabChange () в MainActivity, как показано ниже.

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

1

Обратная сторона вашего подхода к кастингу

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

заключается в том, что ваш фрагмент больше не может использоваться повторно за пределами MainActivityFragment. Если вы не планируете использовать его за пределами этой деятельности, тогда нет проблем. Лучше было бы условно задать заголовок в зависимости от активности. Итак, внутри вашего фрагмента вы должны написать:

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

1

Если вы используете стабильный шаблон android studio 1.4, предоставленный google, вам нужно было написать следующий код в onNavigationItemSelectedметоде, в котором связанный фрагмент вызывает условие if.

 setTitle("YOUR FRAGMENT TITLE");

это замечательно, если у вас есть доступ к заголовку непосредственно из действия ... но если заголовок представляет собой динамически устанавливаемую строку во фрагменте, должна быть связь между фрагментом и действием
me_

1

Я получаю очень простое решение для установки ActionBar Title в фрагменте или любом действии без какой-либо головной боли.

Просто измените xml, в котором определена панель инструментов, как показано ниже:

<android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/colorPrimaryDark"
            app:popupTheme="@style/AppTheme.PopupOverlay" >

            <TextView
                style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
                android:id="@+id/toolbar_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                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");

Превосходно! Использование toolbar_title переопределяет заголовок фрагмента при использовании компонентов навигации Android.
Paixols

0

Чтобы добавить к выбранному ответу, вы также можете добавить второй метод к своему основному действию. Таким образом, вы получите следующие методы в своей основной деятельности:

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

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

Это позволит вам установить заголовок как из строковой переменной, так и из идентификатора ресурса, например R.id.this_is_a_stringиз вашего файла strings.xml. Это также будет работать немного больше, чем как getSupportActionBar().setTitle()работает, поскольку позволяет вам передавать идентификатор ресурса.


0

Как указано выше, существует множество способов. Вы также можете сделать это 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);
    }
}

0

По крайней мере, для меня был простой ответ (после долгих поисков) на изменение заголовка вкладки во время выполнения:

TabLayout tabLayout = (TabLayout) findViewById (R.id.tabs); tabLayout.getTabAt (MyTabPos) .setText («Мой новый текст»);


0

Если вы используете ViewPager(как в моем случае), вы можете использовать:

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

в onPageSelectedметоде вашегоVIEW_PAGER.addOnPageChangeListener


0

В вашем 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);
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.