在 Android BottomNavigationView 中设置选定项

我正在使用来自支持库的新 android.support.design.widget.BottomNavigationView。 如何从代码中设置当前选定内容?我意识到,在旋转屏幕之后,选择内容会变回第一个项目。当然,如果有人能告诉我,如何在 onPause函数中“保存”BottomNavigationView的当前状态,以及如何在 onResume中恢复它,也会有所帮助。

谢谢!

169283 次浏览

这可能会在即将到来的更新中添加。但同时,为了实现这一点,您可以使用反射。

创建一个从 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"/>

你不想修改你的代码。 如果是这样,我建议您尝试 下方导航视图

您只需要替换方法 setCurrentItem(index);getCurrentItem()的调用。

Click here to view the image

反省是个坏主意

有一个方法执行选择,但也调用回调:

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


}

只是添加另一种方法来以编程方式执行选择——这可能是最初的意图,也可能是后来添加的。

Menu bottomNavigationMenu = myBottomNavigationMenu.getMenu();
bottomNavigationMenu.performIdentifierAction(selected_menu_item_id, 0);

performIdentifierAction接受一个 Menu项 ID 和一个标志。

有关更多信息,请参见 文件

对于那些仍在使用 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不会清除当前选中的项目,所以你需要在导航操作处理后在 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导航视图,并找到合适的解决方案。

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


bottomNavigationView.setOnNavigationItemSelectedListener(this);
Menu menu = bottomNavigationView.getMenu();
this.onNavigationItemSelected(menu.findItem(R.id.action_favorites));
}

似乎在 SupportLibrary 25.1.0: 中已修复 编辑: 似乎是固定的,选择的状态是保存的,当旋转屏幕。

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,它是库的“内部”(尽管是公共的)。

要以编程方式单击 BottomNavigationBar 项,您需要使用:

View view = bottomNavigationView.findViewById(R.id.menu_action_item);
view.performClick();

这样可以正确地安排所有项目及其标签。

我曾经给 Google 写过一个错误,因为在 BottomNavigationView: https://code.google.com/p/android/issues/detail?id=233697上没有可靠的方法来选择页面

NavigationView 显然也有类似的问题,他们通过添加一个新的 setCheckedItem ()方法解决了这个问题。

现在可以从25.3.0版本调用 setSelectedItemId() o/

从 API 25.3.0开始,我们引入了 setSelectedItemId(int id)方法,它可以让你像点击一样标记一个选中的项目。

来自文件:

设置选定的菜单项 ID。这与点击一个项目的行为相同。

代码示例:

BottomNavigationView bottomNavigationView;
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(myNavigationItemListener);
bottomNavigationView.setSelectedItemId(R.id.my_menu_item_id);

很重要

在调用 setSelectedItemId 之前,必须的已经将所有项目添加到菜单中(以防您以编程方式执行此操作) ,并设置了监听器(我相信您希望在调用此方法时监听器中的代码运行)。如果在添加菜单项和设置侦听器之前调用 setSelectedItemId,则不会发生任何事情。

在 API 25之上,您可以使用 setSelectedItemId (menu _ item _ id) 但在 API 25下,你必须采取不同的做法, 用户菜单获取句柄,然后设置检查到检查的具体项目

android:enabled="true"添加到 BottomNavigationMenu 项。

然后设置 bottomNavigationView.setOnNavigationItemSelectedListener(mListener) 通过做 bottomNavigationView.selectedItemId = R.id.your_menu_id设置它为选择

您可以尝试 PerformClick 方法:

View view = bottomNavigationView.findViewById(R.id.YOUR_ACTION);
view.performClick();

剪辑

从 API 25.3.0开始,我们引入了 setSelectedItemId(int id)方法,它可以让你像点击一样标记一个选中的项目。

bottomNavigationView.setSelectedItemId(R.id.action_item1);

其中 action_item1是菜单项 ID。

如果需要动态传递片段参数,请这样做

这里有很多(大多是重复的或过时的)答案,但没有一个能满足一个非常普遍的需求: 动态地将不同的参数传递给加载到选项卡中的片段

不能使用 setSelectedItemId(R.id.second_tab)动态地将不同的参数传递给已加载的片段,因为 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
}
}


}

使用

        bottomNavigationView.getMenu().getItem(POSITION).setChecked(true);

希望这个能帮上忙

//Setting default selected menu item and fragment
bottomNavigationView.setSelectedItemId(R.id.action_home);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, new HomeFragment()).commit();

它更多的是确定与相应的底部导航菜单项同时加载的默认片段。 您可以在 OnResume 回调中包含相同的内容

使用此选项可以通过菜单 ID 设置选定的底部导航菜单项

MenuItem item = mBottomNavView.getMenu().findItem(menu_id);
item.setChecked(true);

这个方法对我有用。

private fun selectBottomNavigationViewMenuItem(bottomNavigationView : BottomNavigationView,@IdRes menuItemId: Int) {
bottomNavigationView.setOnNavigationItemSelectedListener(null)
bottomNavigationView.selectedItemId = menuItemId
bottomNavigationView.setOnNavigationItemSelectedListener(this)
}

例子

 override fun onBackPressed() {
replaceFragment(HomeFragment())
selectBottomNavigationViewMenuItem(navView, R.id.navigation_home)
}






private fun replaceFragment(fragment: Fragment) {
val transaction: FragmentTransaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.frame_container, fragment)
transaction.commit()
}
navigationView.getMenu().findItem(R.id.navigation_id).setChecked(true);

要更改 Tab,这个代码可以工作!

    activity?.supportFragmentManager?.beginTransaction().also { fragmentTransaction ->
fragmentTransaction?.replace(R.id.base_frame, YourFragment())?.commit()
}


val bottomNavigationView: BottomNavigationView = activity?.findViewById(R.id.bottomNavigationView) as BottomNavigationView
bottomNavigationView.menu.findItem(R.id.navigation_item).isChecked = true