如何在 Android 打开 DrawerLayout 时编辑导航菜单项标题
How to edit a navigation menu item title while the DrawerLayout is open on Android
在 Android 上,当在 DrawerLayout
中创建 NavigationView
时,可以通过 1) 获取 [= 的句柄来修改个人 MenuItems
的头衔17=] 主要 activity:
// Get a handle for the navigation view.
NavigationView navigationView = findViewById(R.id.navigationview);
// Get handles for the navigation menu items.
final Menu navigationMenu = navigationView.getMenu();
final MenuItem navigationFirstMenuItem = navigationMenu.getItem(0);
final MenuItem navigationSecondMenuItem = navigationMenu.getItem(1);
final MenuItem navigationThirdMenuItem = navigationMenu.getItem(2);
和 2) 使用 DrawerListener
:
打开抽屉时修改菜单项的标题
// The drawer listener is used to update the navigation menu.
drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
@Override
public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
}
@Override
public void onDrawerOpened(@NonNull View drawerView) {
}
@Override
public void onDrawerClosed(@NonNull View drawerView) {
}
@Override
public void onDrawerStateChanged(int newState) {
if ((newState == DrawerLayout.STATE_SETTLING) || (newState == DrawerLayout.STATE_DRAGGING)) { // A drawer is opening or closing.
// Update the title of the menu items.
navigationFirstMenuItem.setTitle(getString(R.string.newString));
navigationSecondMenuItem.setEnabled(false);
navigationThirdMenuItem.setTitle(getString(R.string.title) + " - " + counterInt);
}
}
});
但是,一旦抽屉打开,尝试在不同线程上更改主菜单 activity 运行 的一部分的菜单项之一的标题会导致以下错误,这会使应用程序崩溃:
W/System.err: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
例如,尝试从 WebViewClient.shouldInterceptRequest()
内部更新导航菜单项可能会产生此错误,如下所示:
// Get a handle for the WebView.
WebView webView = findViewById(R.id.webview);
// Set a WebViewClient.
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url){
navigationFirstMenuItem.setTitle(getString(R.string.newString));
navigationSecondMenuItem.setEnabled(false);
navigationThirdMenuItem.setTitle(getString(R.string.title) + " - " + counterInt);
}
}
问题是如何更新菜单项,例如,有一个在其中一个标题中递增的计数器,同时导航抽屉打开并且需要更新菜单项的事件是运行 在不同的线程上。
正如@MikeM. 所建议的,问题的解决方案是使用 Activity.runOnUiThread()
从创建它的同一线程更新导航。具体来说,以下代码对我有用。
// Get a handle for the activity. This is used to update the menu item titles while the navigation menu is open.
Activity activity = this;
// Get a handle for the WebView.
WebView webView = findViewById(R.id.webview);
// Set a WebViewClient.
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url){
// Update the menu items' titles from the UI thread.
activity.runOnUiThread(() -> {
navigationFirstMenuItem.setTitle(getString(R.string.newString));
navigationSecondMenuItem.setEnabled(false);
navigationThirdMenuItem.setTitle(getString(R.string.title) + " - " + counterInt);
});
}
}
理解这个问题的关键是导航视图是由UI线程创建的。事后看来这似乎很明显,但错误消息并没有具体说明这一点,而且这种行为最初让我相信有一些专用的 DrawerLayout
线程只能通过 DrawerLayout
侦听器访问.
在 Android 上,当在 DrawerLayout
中创建 NavigationView
时,可以通过 1) 获取 [= 的句柄来修改个人 MenuItems
的头衔17=] 主要 activity:
// Get a handle for the navigation view.
NavigationView navigationView = findViewById(R.id.navigationview);
// Get handles for the navigation menu items.
final Menu navigationMenu = navigationView.getMenu();
final MenuItem navigationFirstMenuItem = navigationMenu.getItem(0);
final MenuItem navigationSecondMenuItem = navigationMenu.getItem(1);
final MenuItem navigationThirdMenuItem = navigationMenu.getItem(2);
和 2) 使用 DrawerListener
:
// The drawer listener is used to update the navigation menu.
drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
@Override
public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
}
@Override
public void onDrawerOpened(@NonNull View drawerView) {
}
@Override
public void onDrawerClosed(@NonNull View drawerView) {
}
@Override
public void onDrawerStateChanged(int newState) {
if ((newState == DrawerLayout.STATE_SETTLING) || (newState == DrawerLayout.STATE_DRAGGING)) { // A drawer is opening or closing.
// Update the title of the menu items.
navigationFirstMenuItem.setTitle(getString(R.string.newString));
navigationSecondMenuItem.setEnabled(false);
navigationThirdMenuItem.setTitle(getString(R.string.title) + " - " + counterInt);
}
}
});
但是,一旦抽屉打开,尝试在不同线程上更改主菜单 activity 运行 的一部分的菜单项之一的标题会导致以下错误,这会使应用程序崩溃:
W/System.err: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
例如,尝试从 WebViewClient.shouldInterceptRequest()
内部更新导航菜单项可能会产生此错误,如下所示:
// Get a handle for the WebView.
WebView webView = findViewById(R.id.webview);
// Set a WebViewClient.
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url){
navigationFirstMenuItem.setTitle(getString(R.string.newString));
navigationSecondMenuItem.setEnabled(false);
navigationThirdMenuItem.setTitle(getString(R.string.title) + " - " + counterInt);
}
}
问题是如何更新菜单项,例如,有一个在其中一个标题中递增的计数器,同时导航抽屉打开并且需要更新菜单项的事件是运行 在不同的线程上。
正如@MikeM. 所建议的,问题的解决方案是使用 Activity.runOnUiThread()
从创建它的同一线程更新导航。具体来说,以下代码对我有用。
// Get a handle for the activity. This is used to update the menu item titles while the navigation menu is open.
Activity activity = this;
// Get a handle for the WebView.
WebView webView = findViewById(R.id.webview);
// Set a WebViewClient.
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url){
// Update the menu items' titles from the UI thread.
activity.runOnUiThread(() -> {
navigationFirstMenuItem.setTitle(getString(R.string.newString));
navigationSecondMenuItem.setEnabled(false);
navigationThirdMenuItem.setTitle(getString(R.string.title) + " - " + counterInt);
});
}
}
理解这个问题的关键是导航视图是由UI线程创建的。事后看来这似乎很明显,但错误消息并没有具体说明这一点,而且这种行为最初让我相信有一些专用的 DrawerLayout
线程只能通过 DrawerLayout
侦听器访问.