通过AS创建一个新的"Navigation Drawer Activity"项目,希望通过预构建的界面缩减代码开发时间,该功能希望能在选择不同的navigation后有不同的事件(比如密码验证)
@Override
protected void onCreate(Bundle savedInstanceState) {
...
DrawerLayout drawer = binding.drawerLayout;
NavigationView navigationView = binding.navView;
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
Log.d(TAG, "onNavigationItemSelected: item = " + item.getTitle());
return false;
}
});
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_recognize, R.id.nav_register, R.id.nav_admin)
.setOpenableLayout(drawer)
.build();
navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
....
}
通过上述方法无法给navigationview 设置选择监听,选择其他navigation后,没有log打印
由于与构建的项目使用了如下代码:
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
在代码NavigationUI.setupWithNavController方法中有默认设置navigationView 的 NavigationItemSelectedListener监听:
androidx.navigation.ui.NavigationUI.java
public static void setupWithNavController(@NonNull final NavigationView navigationView,
@NonNull final NavController navController) {
// 给navigationView 设置监听方法
navigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
// 选择了一个Item后,跳转到相对应的fragment中
boolean handled = onNavDestinationSelected(item, navController);
if (handled) {
ViewParent parent = navigationView.getParent();
if (parent instanceof Openable) {
((Openable) parent).close(); // 关闭navigationView侧边栏
} else {
BottomSheetBehavior bottomSheetBehavior =
findBottomSheetBehavior(navigationView);
if (bottomSheetBehavior != null) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
}
}
return handled;
}
});
final WeakReference weakReference = new WeakReference<>(navigationView);
// 此方法监听切换fragment事件,保证切换后,对应的item可以被选中
navController.addOnDestinationChangedListener(
new NavController.OnDestinationChangedListener() {
@Override
public void onDestinationChanged(@NonNull NavController controller,
@NonNull NavDestination destination, @Nullable Bundle arguments) {
NavigationView view = weakReference.get();
if (view == null) {
navController.removeOnDestinationChangedListener(this);
return;
}
Menu menu = view.getMenu();
for (int h = 0, size = menu.size(); h < size; h++) {
MenuItem item = menu.getItem(h);
item.setChecked(matchDestination(destination, item.getItemId()));
}
}
});
}
可以看出,我们设置NavigationItemSelectedListener后,setupWithNavController()方法又设置了一次,导致我们设置的监听被替换了。
所以可以有如下方法进行修改:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
DrawerLayout drawer = binding.drawerLayout;
NavigationView navigationView = binding.navView;
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_recognize, R.id.nav_register, R.id.nav_admin)
.setOpenableLayout(drawer)
.build();
navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
// 设置选择监听
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
Log.d(TAG, "onNavigationItemSelected: item = " + item.getTitle());
return false;
}
});
....
}
修改设置监听的顺序为setupWithNavController()方法之后,可以正常打印出我们的log,同时出现了一个新的问题:
setupWithNavController()方法中的listener不生效了,这个listener中当我们选中其他navigation后,会切换到相应的界面,没有了这个监听,导致点击navigation后没有任何效果。
AdminFragment.java
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DBOpenHelper helper = DBOpenHelper.getInstance(getActivity());
boolean adminRegisted = helper.adminRegisted();
if (adminRegisted) {
startVerify();
} else {
startAdminRegiste(false);
}
}
private void startVerify() {
Intent intent = new Intent();
intent.setClassName("com.mobiletek.facerecognition","com.mobiletek.facerecognition.ui.admin.AdminVerifyActivity");
startActivityForResult(intent,VERIFY_CODE);
}
当我们点击Admin navigation后,跳转到AdminFragment界面,在onCreate()方法中跳转到验证界面
当前方法会出现新问题,在我们点击navigation后,会先切换到adminfragment界面,然后在启动验证界面,导致屏幕会闪烁一下(在性能差的设备上尤其明显,可能会显示adminfragment界面一两秒后才会显示验证界面)
protected void onCreate(Bundle savedInstanceState) {
...
DrawerLayout drawer = binding.drawerLayout;
NavigationView navigationView = binding.navView;
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow)
.setOpenableLayout(drawer)
.build();
navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
setupWithNavController(navigationView, navController);
}
// 将NavigationUI.java中的方法cp到当前位置,修改为我们需要的功能
public void setupWithNavController(@NonNull final NavigationView navigationView,
@NonNull final NavController navController) {
navigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
Log.d(TAG, "onNavigationItemSelected: " + navigationView.getCheckedItem().getTitle());
MenuItem checkedItem = navigationView.getCheckedItem();
boolean handled = false;
if (checkedItem != null && checkedItem.getItemId() == item.getItemId()) {
handled = true;
} else {
// 首先保存我们需要跳转的界面,待验证通过后再跳转
needToItem = item;
if (item.getItemId() == R.id.nav_gallery){
Intent intent = new Intent();
intent.setClassName("com.almalence.myapplication","com.almalence.myapplication.MainActivity2");
mContext.startActivityForResult(intent, VERIFY_CODE);
} else {
// 切换到需要显示的界面
handled = NavigationUI.onNavDestinationSelected(item, navController);
}
}
ViewParent parent = navigationView.getParent();
if (parent instanceof Openable) {
((Openable) parent).close();
}
return handled;
}
});
final WeakReference weakReference = new WeakReference<>(navigationView);
navController.addOnDestinationChangedListener(
new NavController.OnDestinationChangedListener() {
@Override
public void onDestinationChanged(@NonNull NavController controller,
@NonNull NavDestination destination, @Nullable Bundle arguments) {
NavigationView view = weakReference.get();
if (view == null) {
navController.removeOnDestinationChangedListener(this);
return;
}
Menu menu = view.getMenu();
for (int h = 0, size = menu.size(); h < size; h++) {
MenuItem item = menu.getItem(h);
item.setChecked(matchDestination(destination, item.getItemId()));
}
}
});
}
static boolean matchDestination(@NonNull NavDestination destination,
@IdRes int destId) {
NavDestination currentDestination = destination;
while (currentDestination.getId() != destId && currentDestination.getParent() != null) {
currentDestination = currentDestination.getParent();
}
return currentDestination.getId() == destId;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case VERIFY_CODE:
if (resultCode == RESULT_OK)
// 如果验证通过,跳转到之前选择的界面
NavigationUI.onNavDestinationSelected(needToItem, navController);
break;
}
}
此方法暂时没有其他问题,也没有闪屏的问题出现