Ответ 1
Кажется, исправлено в SupportLibrary 25.1.0:) Изменить: Кажется, исправлено, что состояние выбора сохраняется при вращении экрана.
Я использую новый android.support.design.widget.BottomNavigationView
из библиотеки поддержки.
Как я могу установить текущий выбор из кода? Я понял, что выбор меняется на первый элемент после поворота экрана. Конечно, это также помогло бы, если бы кто-нибудь мог сказать мне, как "сохранить" текущее состояние BottomNavigationView
в функции onPause
и как восстановить его в onResume
.
Спасибо!
Кажется, исправлено в SupportLibrary 25.1.0:) Изменить: Кажется, исправлено, что состояние выбора сохраняется при вращении экрана.
Из API 25.3.0 был введен метод setSelectedItemId(int id)
который позволяет пометить элемент как выбранный, как если бы он был нажат.
Из документов:
Установите идентификатор выбранного пункта меню. Это ведет себя так же, как нажатие на элемент.
Пример кода:
BottomNavigationView bottomNavigationView;
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(myNavigationItemListener);
bottomNavigationView.setSelectedItemId(R.id.my_menu_item_id);
ВАЖНЫЙ
Вы ДОЛЖНЫ уже добавить все элементы в меню и установить прослушиватель перед вызовом setSelectedItemId, чтобы BottomNavigationView мог выполнить соответствующее поведение кода. Если вы вызовете setSelectedItemId перед добавлением пунктов меню и настройкой слушателя, ничего не произойдет.
Чтобы программно щелкнуть элемент BottomNavigationBar, который вам нужно использовать:
View view = bottomNavigationView.findViewById(R.id.menu_action_item);
view.performClick();
Это правильно упорядочивает все элементы с их метками.
Для тех, кто все еще использует SupportLibrary <25.3.0
Я не уверен, является ли это полным ответом на этот вопрос, но моя проблема была очень похожа - мне пришлось обработать нажатие кнопки back
и перенести пользователя на предыдущую вкладку, где он находился. Так что, может быть, мое решение кому-нибудь пригодится:
private void updateNavigationBarState(int actionId){
Menu menu = bottomNavigationView.getMenu();
for (int i = 0, size = menu.size(); i < size; i++) {
MenuItem item = menu.getItem(i);
item.setChecked(item.getItemId() == actionId);
}
}
Пожалуйста, имейте в виду, что если пользователь BottomNavigationView
другую вкладку навигации, BottomNavigationView
не очистит выбранный в данный момент элемент, поэтому вам нужно вызвать этот метод в вашем onNavigationItemSelected
после обработки действия навигации:
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.some_id_1:
// process action
break;
case R.id.some_id_2:
// process action
break;
...
default:
return false;
}
updateNavigationBarState(item.getItemId());
return true;
}
Что касается сохранения состояния экземпляра, я думаю, что вы можете поиграть с тем же action id
в виде навигации и найти подходящее решение.
bottomNavigationView.setSelectedItemId(R.id.action_item1);
где action_item1
- идентификатор пункта меню.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bottomNavigationView.setOnNavigationItemSelectedListener(this);
Menu menu = bottomNavigationView.getMenu();
this.onNavigationItemSelected(menu.findItem(R.id.action_favorites));
}
Теперь возможно, чтобы версия 25.3.0 вызывала setSelectedItemId()
\o/
Добавьте android:enabled="true"
в элементы BottomNavigationMenu.
А затем установите bottomNavigationView.setOnNavigationItemSelectedListener(mListener)
и установите его как выбранный, выполнив bottomNavigationView.selectedItemId = R.id.your_menu_id
Это, вероятно, будет добавлено в ближайшие обновления. Но тем временем для достижения этого вы можете использовать отражение.
Создайте пользовательский вид, простирающийся от BottomNavigationView и получив доступ к некоторым его полям.
public class SelectableBottomNavigationView extends BottomNavigationView {
public SelectableBottomNavigationView(Context context) {
super(context);
}
public SelectableBottomNavigationView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SelectableBottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setSelected(int index) {
try {
Field f = BottomNavigationView.class.getDeclaredField("mMenuView");
f.setAccessible(true);
BottomNavigationMenuView menuView = (BottomNavigationMenuView) f.get(this);
try {
Method method = menuView.getClass().getDeclaredMethod("activateNewButton", Integer.TYPE);
method.setAccessible(true);
method.invoke(menuView, index);
} catch (SecurityException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
И затем используйте его в вашем файле макета xml.
<com.your.app.SelectableBottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemBackground="@color/primary"
app:itemIconTint="@drawable/nav_item_color_state"
app:itemTextColor="@drawable/nav_item_color_state"
app:menu="@menu/bottom_navigation_menu"/>
Выше API 25 вы можете использовать setSelectedItemId (menu_item_id), но в API 25 вы должны сделать иначе, пользовательское меню, чтобы получить дескриптор, а затем установитьChecked на Проверяемый конкретный элемент
Я не хочу изменять код. Если это так, я рекомендовал вам попробовать BottomNavigationViewEx.
Вам просто нужно заменить вызов методом setCurrentItem(index);
и getCurrentItem()
.
Просто добавив другой способ выполнить выбор программно - это, вероятно, было намерением в первую очередь или, возможно, это было добавлено позже.
Menu bottomNavigationMenu = myBottomNavigationMenu.getMenu();
bottomNavigationMenu.performIdentifierAction(selected_menu_item_id, 0);
performIdentifierAction
принимает идентификатор элемента Menu
и флаг.
Дополнительную информацию см. в документации .
Я сделал ошибку Google в том, что нет надежного способа выбрать страницу в BottomNavigationView: https://code.google.com/p/android/issues/detail?id=233697 p >
У NavigationView, похоже, была аналогичная проблема, которую они исправили добавлением нового метода setCheckedItem().
Вы можете попробовать метод executeClick:
View view = bottomNavigationView.findViewById(R.id.YOUR_ACTION);
view.performClick();
private void setSelectedItem(int actionId) {
Menu menu = viewBottom.getMenu();
for (int i = 0, size = menu.size(); i < size; i++) {
MenuItem menuItem = menu.getItem(i);
((MenuItemImpl) menuItem).setExclusiveCheckable(false);
menuItem.setChecked(menuItem.getItemId() == actionId);
((MenuItemImpl) menuItem).setExclusiveCheckable(true);
}
}
Единственный минус решения использует MenuItemImpl
, который является "внутренним" для библиотеки (хотя и общедоступным).
Просто поместите это в свой XML файл, где у вас есть bottomnavigationView.
'<android.support.design.widget.BottomNavigationView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:itemIconSize="15dp"/>'
ЕСЛИ ВАМ НУЖНО ДИНАМИЧЕСКИ ПРОЙТИ ФРАГМЕНТНЫЕ АРГУМЕНТЫ, ДЕЛАЙТЕ ЭТО
Здесь есть множество (в основном повторяющихся или устаревших) ответов, но ни один из них не удовлетворяет очень распространенной необходимости: динамически передавать различные аргументы фрагменту, загруженному во вкладку.
Вы не можете динамически передавать различные аргументы загруженному фрагменту, используя setSelectedItemId(R.id.second_tab)
, который в итоге вызывает статический OnNavigationItemSelectedListener
. Чтобы преодолеть это ограничение, я сделал это в моем MainActivity
, который содержит вкладки:
fun loadArticleTab(articleId: String) {
bottomNavigationView.menu.findItem(R.id.tab_article).isChecked = true // use setChecked() in Java
supportFragmentManager
.beginTransaction()
.replace(R.id.main_fragment_container, ArticleFragment.newInstance(articleId))
.commit()
}
Метод ArticleFragment.newInstance()
реализован как обычно:
private const val ARG_ARTICLE_ID = "ARG_ARTICLE_ID"
class ArticleFragment : Fragment() {
companion object {
/**
* @return An [ArticleFragment] that shows the article with the given ID.
*/
fun newInstance(articleId: String): ArticleFragment {
val args = Bundle()
args.putString(ARG_ARTICLE_ID, day)
val fragment = ArticleFragment()
fragment.arguments = args
return fragment
}
}
}
Отражение - плохая идея.
Направляйтесь в gist. Существует метод, который выполняет выбор, но также вызывает обратный вызов:
@CallSuper
public void setSelectedItem(int position) {
if (position >= getMenu().size() || position < 0) return;
View menuItemView = getMenuItemView(position);
if (menuItemView == null) return;
MenuItemImpl itemData = ((MenuView.ItemView) menuItemView).getItemData();
itemData.setChecked(true);
boolean previousHapticFeedbackEnabled = menuItemView.isHapticFeedbackEnabled();
menuItemView.setSoundEffectsEnabled(false);
menuItemView.setHapticFeedbackEnabled(false); //avoid hearing click sounds, disable haptic and restore settings later of that view
menuItemView.performClick();
menuItemView.setHapticFeedbackEnabled(previousHapticFeedbackEnabled);
menuItemView.setSoundEffectsEnabled(true);
mLastSelection = position;
}