Встановлення власного заголовка ActionBar із фрагмента


90

У своєму 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_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 framework.
Akhil Dad

Подивіться це посилання stackoverflow.com/a/46705242/1770868
Ahmad Агазаде

Відповіді:


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

А потім ви можете оновити заголовок Activity з Fragment за допомогою 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

1
Саме те, що я шукав, для мене працювало краще, ніж обрана відповідь. Дякую!
dstrube

1
Це має бути прийнятою відповіддю! Це правильно показує, як це слід робити: Якщо фрагмент хоче спілкуватися зі своєю хостинговою діяльністю, спілкування завжди має відбуватися через інтерфейс. Фрагмент надає інтерфейс, а діяльність реалізує його! Якщо кілька фрагментів повинні взаємодіяти однаково з діяльністю, напишіть абстрактний фрагмент, який надає інтерфейс, а потім нехай кожен фрагмент успадковується від цього "базового фрагмента".
winklerrr

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

2
Через багато років я змінив це на прийняту відповідь, оскільки, як ми знаємо, інтерфейси - це спосіб обміну фрагментами та діяльністю.
TheLettuceMaster

150

Те, що ви робите, є правильним. Fragmentsне мають доступу до ActionBarAPI, тому вам доведеться зателефонувати getActivity. Якщо ви Fragmentне є статичним внутрішнім класом, у цьому випадку вам слід створити a 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 ви не згадуєте, що щось не працює, ви лише згадуєте, що хочете знати "правильний" метод, щоб встановити заголовок у заголовку ActionBarз Fragment. Крім того, я не пропонував видалення this.whateverяк рішення будь-чого, це була лише порада щодо форматування коду.
adneal

Здається, я розумію, з чим у вас виникають проблеми, і відповідно відредагував мою відповідь.
adneal

Thanksx ідеальна відповідь
Ammar ali

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

Ті Fragment, які можуть вплинути на 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(...)ie

У 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. Це рішення використовує інтерфейс, тому фрагменти не потребують безпосереднього посилання на батьківську активність:

1) Створіть інтерфейс:

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

2) У фрагменті onAttach передайте активність типу Interface і викликайте метод 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; 
}

Це має бути прийнятою відповіддю, оскільки всі інші підходи роблять фрагмент, прив’язаний до певної діяльності, що не є масштабованим дизайном, також це те, що рекомендує андроїд-фреймворк
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 відповідають або мають вищу версію у вашому проекті:

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

Якщо у вас є основна діяльність із великою кількістю фрагментів, які ви вводите, як правило, використовуючи навігатор І у вас є масив заголовків для ваших фрагментів, коли ви натискаєте назад, щоб вони змінилися, поставте це в основну дію, яка містить фрагменти

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

}

Це передбачає, що для кожного фрагмента ви додаєте йому тег, як правило, десь, коли ви додаєте фрагменти до списку NaviDrawer, відповідно до позиції, натиснутої в списку. Отже, ця позиція - це те, що я фіксую на тезі:

    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

Збережіть свою відповідь у об’єкті 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методі, в якому ваш пов'язаний фрагмент викликає умову.

 setTitle("YOUR FRAGMENT TITLE");

це чудово, якщо у вас є доступ до заголовка безпосередньо з активності ... але якщо заголовок - це динамічно встановлений рядок у фрагменті, між фрагментом та діяльністю має бути зв’язок
me_

1

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

Просто змініть 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");

Відмінно! Використання панелі інструментів_заголовок замінює заголовок фрагмента, якщо використовується компоненти навігації Android.
Paixols

0

Просто щоб додати до вибраної відповіді, ви можете також додати другий метод до своєї основної діяльності. Отже, у вашій основній діяльності ви отримаєте наступні методи:

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

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

Це дозволить вам встановити заголовок як зі змінної String, так і з ідентифікатором ресурсу, наприклад 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.