Меню настроек Android во фрагменте
Я пытаюсь добавить пункт в меню параметров из группы фрагментов.
Я создал новый MenuFragment
Класс и расширен это для фрагментов, в которые я хочу включить пункт меню. Вот код:
public class MenuFragment extends Fragment {
MenuItem fav;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
fav = menu.add("add");
fav.setIcon(R.drawable.btn_star_big_off);
}
}
По какой-то причине onCreateOptionsMenu
Похоже, не для запуска.
25 ответов
Вызовите супер метод:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO Add your menu entries here
super.onCreateOptionsMenu(menu, inflater);
}
Поместите операторы журнала в код, чтобы увидеть, не вызывается ли метод или не изменяется ли меню вашим кодом.
Также убедитесь, что вы звоните SetHasOptionsMenu
в onCreate
уведомить фрагмент о том, что он должен участвовать в обработке меню опций.
У меня была та же проблема, но я думаю, что лучше подвести итог и представить последний шаг, чтобы она заработала:
Добавьте метод setHasOptionsMenu(true) в ваш фрагмент
onCreate(Bundle savedInstanceState)
метод.Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)
(если вы хотите сделать что-то другое в меню вашего фрагмента) иonOptionsItemSelected(MenuItem item)
методы в вашем фрагменте.Внутри вашего
onOptionsItemSelected(MenuItem item)
Метод Activity, убедитесь, что вы возвращаете false, когда действие пункта меню будет реализовано вonOptionsItemSelected(MenuItem item)
Фрагмент метода.
Пример:
Деятельность
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getSupportMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.activity_menu_item:
// Do Activity menu item stuff here
return true;
case R.id.fragment_menu_item:
// Not implemented here
return false;
default:
break;
}
return false;
}
Фрагмент
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
....
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Do something that differs the Activity's menu here
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.activity_menu_item:
// Not implemented here
return false;
case R.id.fragment_menu_item:
// Do Fragment menu item stuff here
return true;
default:
break;
}
return false;
}
Если вы найдете onCreateOptionsMenu(Menu menu, MenuInflater inflater)
метод не вызывается, убедитесь, что вы вызываете следующее из фрагмента onCreate(Bundle savedInstanceState)
метод:
setHasOptionsMenu(true)
В моем случае мне нужно было меню, чтобы обновить webview
внутри определенного фрагмента, для этого я использовал:
Фрагмент:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO Add your menu entries here
inflater.inflate(R.menu.menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.exit:
System.exit(1);
break;
case R.id.refresh:
webView.reload();
break;
}
return true;
}
Файл menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/exit" android:title="Exit" android:icon="@drawable/ic_action_cancel" />
<item android:id="@+id/refresh" android:title="Refresh" android:icon="@drawable/ic_action_refresh" />
</menu>
TL;DR
Использовать android.support.v7.widget.Toolbar
и просто сделай:
toolbar.inflateMenu(R.menu.my_menu)
toolbar.setOnMenuItemClickListener {
onOptionsItemSelected(it)
}
Автономная панель инструментов
Большинство предлагаемых решений, таких как setHasOptionsMenu(true)
работают только тогда, когда родительская активность имеет панель инструментов в своем макете и объявляет ее через setSupportActionBar()
, Затем фрагменты могут участвовать в меню этого точного ActionBar:
Fragment.onCreateOptionsMenu (): инициализировать содержимое стандартного меню параметров узла Fragment.
Если вам нужна отдельная панель инструментов и меню для одного конкретного фрагмента, вы можете сделать следующее:
menu_custom_fragment.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_save"
android:title="SAVE" />
</menu>
custom_fragment.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
...
CustomFragment.kt
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(layout.custom_fragment, container, false)
val toolbar = view.findViewById<Toolbar>(R.id.toolbar)
toolbar.inflateMenu(R.menu.menu_custom_fragment)
toolbar.setOnMenuItemClickListener {
onOptionsItemSelected(it)
}
return view
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_save -> {
// TODO: User clicked the save button
true
}
else -> super.onOptionsItemSelected(item)
}
}
Да, это так просто. Вам даже не нужно переопределять onCreate()
или же onCreateOptionsMenu()
,
PS: это работает только с android.support.v4.app.Fragment
а также android.support.v7.widget.Toolbar
(также обязательно используйте AppCompatActivity
и AppCompat
тема в вашем styles.xml
).
В menu.xml
Вы должны добавить все пункты меню. Затем вы можете скрыть элементы, которые вы не хотите видеть при начальной загрузке.
menu.xml
<item
android:id="@+id/action_newItem"
android:icon="@drawable/action_newItem"
android:showAsAction="never"
android:visible="false"
android:title="@string/action_newItem"/>
добавлять setHasOptionsMenu(true)
в методе onCreate() для вызова пунктов меню в вашем классе Fragment.
FragmentClass.java
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
Вам не нужно переопределять onCreateOptionsMenu
снова в вашем классе Fragment. Пункты меню можно изменить (добавить / удалить) путем переопределения onPrepareOptionsMenu
метод доступен во фрагменте.
@Override
public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.action_newItem).setVisible(true);
super.onPrepareOptionsMenu(menu);
}
Существует новый способ сделать это, так какandroidx.activity:activity:1.4.0
Вы должны использовать API MenuProvider .
Он используется следующим образом:
Вместо того, чтобы звонитьsuper.setHasOptionMenu
и осуществлениеonCreateOptionsMenu
ты должен позвонитьaddMenuProvider
вonViewCreated
.
Пример:
class ExampleFragment : Fragment(R.layout.fragment_example) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// The usage of an interface lets you inject your own implementation
val menuHost: MenuHost = requireActivity()
// Add menu items without using the Fragment Menu APIs
// Note how we can tie the MenuProvider to the viewLifecycleOwner
// and an optional Lifecycle.State (here, RESUMED) to indicate when
// the menu should be visible
menuHost.addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
// Add menu items here
menuInflater.inflate(R.menu.example_menu, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
// Handle the menu selection
return true
}
}, viewLifecycleOwner)
}
источник: журнал изменений активности
В моем случае вот шаги.
Шаг 1
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Here notify the fragment that it should participate in options menu handling.
setHasOptionsMenu(true);
}
Шаг 2
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// First clear current all the menu items
menu.clear();
// Add the new menu items
inflater.inflate(R.menu.post_stuff, menu);
super.onCreateOptionsMenu(menu, inflater);
}
Шаг 3
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.post_stuff:
Log.d(TAG, "Will post the photo to server");
return true;
case R.id.cancel_post:
Log.d(TAG, "Will cancel post the photo");
return true;
default:
break;
}
return super.onOptionsItemSelected(item);
}
Вам нужно использовать menu.clear(), прежде чем раздувать меню.
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
а также
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
Если вы хотите добавить свое меню на заказ
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_custom, menu);
}
У меня была такая же проблема, мои фрагменты были страницами ViewPager. Причина, по которой это происходило, в том, что при создании экземпляра FragmentPagerAdapter я использовал дочерний диспетчер фрагментов, а не диспетчер фрагментов поддержки активности.
Ваш код в порядке. Только супер отсутствовал в методе:
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO add your menu :
inflater.inflate(R.menu.my_menu, menu);
//TODO call super
super.onCreateOptionsMenu(menu, inflater);
}
Файл меню:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/play"
android:titleCondensed="Speak"
android:showAsAction="always"
android:title="Speak"
android:icon="@drawable/ic_play">
</item>
<item
android:id="@+id/pause"
android:titleCondensed="Stop"
android:title="Stop"
android:showAsAction="always"
android:icon="@drawable/ic_pause">
</item>
</menu>
Код деятельности:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.speak_menu_history, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.play:
Toast.makeText(getApplicationContext(), "speaking....", Toast.LENGTH_LONG).show();
return false;
case R.id.pause:
Toast.makeText(getApplicationContext(), "stopping....", Toast.LENGTH_LONG).show();
return false;
default:
break;
}
return false;
}
Код фрагмента:
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.play:
text = page.getText().toString();
speakOut(text);
// Do Activity menu item stuff here
return true;
case R.id.pause:
speakOf();
// Not implemented here
return true;
default:
break;
}
return false;
}
Теперь, в 2022 году, Google устарел setHasOptionsMenu , и вы должны использоватьMenuProvider
вместо. На своем опыте я получилNoSuchMethodException
на каком-то андроиде 11, 12 когда пользовалсяsetHasOptionsMenu
метод.
Этот метод больше не требуется при использовании MenuProvider для предоставления меню вашей активности, который заменяет onCreateOptionsMenu в качестве рекомендуемого способа обеспечения последовательного, необязательно учитывающего жизненный цикл и модульного способа обработки создания меню и выбора элементов.
Вот как вы добавляете меню в свой банкомат активности/фрагмента:
/**
* Using the addMenuProvider() API directly in your Activity
**/
class ExampleActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Add menu items without overriding methods in the Activity
addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
// Add menu items here
menuInflater.inflate(R.menu.example_menu, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
// Handle the menu selection
return true
}
})
}
}
/**
* Using the addMenuProvider() API in a Fragment
**/
class ExampleFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// The usage of an interface lets you inject your own implementation
val menuHost: MenuHost = requireActivity()
// Add menu items without using the Fragment Menu APIs
// Note how we can tie the MenuProvider to the viewLifecycleOwner
// and an optional Lifecycle.State (here, RESUMED) to indicate when
// the menu should be visible
menuHost.addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
// Add menu items here
menuInflater.inflate(R.menu.example_menu, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
// Handle the menu selection
return true
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
Вот справочная ссылка .
Ах, для ленивых, как я, вы также должны добавить это:
dependencies {
val activity_version = "1.5.1"
// Java language implementation
implementation("androidx.activity:activity:$activity_version")
// Kotlin
implementation("androidx.activity:activity-ktx:$activity_version")
}
Я сходил с ума, потому что ни один из ответов здесь не работал для меня.
Чтобы показать меню мне пришлось позвонить: setSupportActionBar(toolbar)
Готово!
Примечание: если ваш toolbar
представление не находится в том же макете активности, который вы не можете использовать вышеупомянутый вызов непосредственно из вашего класса деятельности, в этом случае вам нужно получить это действие из вашего класса фрагмента, а затем вызвать setSupportActionBar(toolbar)
, Помните: ваш класс активности должен расширять AppCompatActivity.
Надеюсь, что этот ответ поможет вам.
Набор
setHasMenuOptions(true)
работает, если в приложении есть тема с
Actionbar
такие как
Theme.MaterialComponents.DayNight.DarkActionBar
или Activity имеет собственную панель инструментов, в противном случае
onCreateOptionsMenu
во фрагменте не вызывается.
Если вы хотите использовать отдельную панель инструментов, вам нужно либо получить активность, либо установить панель инструментов в качестве панели действий поддержки с помощью
(requireActivity() as? MainActivity)?.setSupportActionBar(toolbar)
который позволяет вызывать ваш фрагмент onCreateOptionsMenu.
Другой альтернативой является расширение собственного меню панели инструментов с помощью
toolbar.inflateMenu(R.menu.YOUR_MENU)
и слушатель элемента с
toolbar.setOnMenuItemClickListener {
// do something
true
}
Я бы добавил к этому одну вещь и причину, по которой она не работала для меня.
Это похоже на ответ Napster.
Убедитесь, что хостинг вашего фрагмента расширяется
AppCompatActivity
неFragmentActivity
!public class MainActivity extends AppCompatActivity { }
Из Справочной документации Google для FragmentActivity:
Примечание. Если вы хотите реализовать действие, которое включает в себя панель действий, вам следует вместо этого использовать класс ActionBarActivity, который является подклассом этого класса, поэтому он позволяет использовать API-интерфейсы фрагментов на уровне API 7 и выше.
Чтобы обновить ответ Napster -
ActionBarActivity
сейчас не рекомендуется использоватьAppCompatActivity
вместо.Когда используешь
AppCompatActivity
также убедитесь, что вы установилиTheme.AppCompat
или похожая тема " (Google Doc).
Замечания: android.support.v7.app.AppCompatActivity
является подклассом android.support.v4.app.FragmentActivity
класс (см. справочный документ AppCompatActivity).
Моя проблема была немного другой. Я все сделал правильно. Но я унаследовал неправильный класс для действия, в котором размещен фрагмент.
Так что, чтобы быть ясно, если вы переопределяете onCreateOptionsMenu(Menu menu, MenuInflater inflater)
во фрагменте убедитесь, что ваш класс активности, в котором находится этот фрагмент, наследует android.support.v7.app.ActionBarActivity
(в случае, если вы хотите поддерживать ниже уровня API 11).
Я унаследовал android.support.v4.app.FragmentActivity
поддерживать уровень API ниже 11.
Настройка меню параметров после создания фрагмента работала хорошо для меня.
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
}
В папке вашего меню создайте XML-файл.menu и добавьте этот XML-файл.
<item
android:id="@+id/action_search"
android:icon="@android:drawable/ic_menu_search"
android:title="@string/action_search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always|collapseActionView" />
В вашем фрагменте класса overide этот метод и
implement SearchView.OnQueryTextListener in your fragment class
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setHasOptionsMenu(true);
}
Теперь просто настройте свой XML-файл меню в классе фрагмента.
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_main, menu);
final MenuItem item = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView)
MenuItemCompat.getActionView(item);
MenuItemCompat.setOnActionExpandListener(item,
new MenuItemCompat.OnActionExpandListener() {
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// Do something when collapsed
return true; // Return true to collapse action view
}
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
// Do something when expanded
return true; // Return true to expand action view
}
});
}
Если ни один из приведенных выше вариантов не работает для вас, попробуйте это в своем фрагменте:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
....
toolBar = rootView.findViewById(R.id.import_contacts_toolbar)
toolBar?.title = "Your title"
toolBar?.subtitle = "yor subtitile"
contactsActivity().setSupportActionBar(toolBar)
toolBar?.inflateMenu(R.menu.import_contacts_menu)
...
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.1 -> {
return true
}
R.id.2 -> {
return true
}
}
return false
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
menu.clear()
inflater.inflate(R.menu.import_contacts_menu, menu)
super.onCreateOptionsMenu(menu, inflater)
val search = menu.findItem(R.id.action_search)
val searchView = search.actionView as SearchView
searchView.requestFocus()
val txtSearch = searchView.findViewById<View>(androidx.appcompat.R.id.search_src_text) as EditText
txtSearch.hint = "Search..."
txtSearch.setHintTextColor(Color.WHITE);
txtSearch.setTextColor(Color.WHITE)
try {
val f: Field = TextView::class.java.getDeclaredField("mCursorDrawableRes")
f.setAccessible(true)
f.set(txtSearch, R.drawable.search_edit_text_cursor)
} catch (ignored: Exception) {
Log.d(TAG, "failed to expose cursor drawable $ignored")
}
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String): Boolean {
return true
}
})
searchView.setOnCloseListener {
}
}
В моем случае у меня был пункт меню поиска, который всегда отображался. Это xml для него:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
app:showAsAction="always"
app:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="Search"/>
<item android:id="@+id/1"
android:title="1">
</item>
<item android:id="@+id/2"
android:title="2">
</item>
</menu>
Если все вышеперечисленное не работает, вам нужно отладить и убедиться, что функция onCreateOptionsMenu была вызвана (поместив журнал отладки или записи...)
Если он не запущен, возможно, ваша тема Android не поддерживает панель действий. Откройте AndroidManifest.xml и установите значение для android:theme
с панелью действий поддержки темы:
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat">
Вот как это сделать с пользовательской панелью инструментов во фрагменте. Он похож на метод mbo , но с заменой устаревшего метода.
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
this.rootView = inflater.inflate(R.layout.fragment_study_list, container, false) as DrawerLayout
// Set the menu
val toolbar = this.rootView.findViewById<Toolbar>(R.id.studylist_toolbar)
toolbar.addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
// Add menu items here
menuInflater.inflate(R.menu.studylist_menu, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
// Handle the menu selection
Log.d("CONJUU","Menu clicked")
return true
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
Это то, что я сделал, чтобы скрыть и показать ВСЕ параметры меню, когда фрагмент загружен и уничтожен соответственно. Это избавляет от риска указывать на
null
для
R.id.your_menu_item
и позволил мне повторно использовать фрагмент в другом месте.
lateinit var optionsMenu: Menu
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
menu.iterator().forEach {
it.isVisible = false
}
optionsMenu = menu
super.onCreateOptionsMenu(menu, inflater)
}
override fun onDestroyView() {
optionsMenu.iterator().forEach {
it.isVisible = true
}
super.onDestroyView()
}
В вашем методе onCreate добавьте setHasOptionMenu()
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
Затем переопределите свой onCreateOptionsMenu
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add("Menu item")
.setIcon(android.R.drawable.ic_delete)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}