У меня есть вопрос. Я использую компоненты архитектуры навигации Android, используя нижний навигационный вид с одиночной активностью. Как сделать так, чтобы фрагмент открывался только один раз? Даже если кнопка, вызывающая этот фрагмент, нажимается несколько раз? Как сделать так, чтобы фрагмент добавлялся в задний стек только один раз? Я создал тестовый проект, чтобы попробовать
xml navigation_test
<?xml version = "1.0" encoding = "utf-8"?>
<navigation xmlns:android = "http://schemas.android.com/apk/res/android"
xmlns:app = "http://schemas.android.com/apk/res-auto"
xmlns:tools = "http://schemas.android.com/tools"
android:id = "@+id/test_nav"
app:startDestination = "@id/firstFragment">
<fragment
android:id = "@+id/firstFragment"
android:name = "ru.artem_nr.navigation_test.FirstFragment"
android:label = "fragment_first"
tools:layout = "@layout/fragment_first" >
<action
android:id = "@+id/action_firstFragment_to_secondFrag"
app:destination = "@id/navigation"
app:launchSingleTop = "true"
app:popUpTo = "@+id/secondFrag"
app:popUpToInclusive = "false" />
</fragment>
<navigation android:id = "@+id/navigation"
app:startDestination = "@id/secondFrag">
<fragment
android:id = "@+id/secondFrag"
android:name = "ru.artem_nr.navigation_test.SecondFrag"
android:label = "fragment_second"
tools:layout = "@layout/fragment_second" />
</navigation>
</navigation>
основная деятельность
открытый класс MainActivity расширяет AppCompatActivity реализует NavigationView.OnNavigationItemSelectedListener {
public Toolbar toolbar;
public DrawerLayout drawerLayout;
public NavController navController;
public NavigationView navigationView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupNavigation();
}
private void setupNavigation() {
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
drawerLayout = findViewById(R.id.drawer_layout);
navigationView = findViewById(R.id.nav_view);
navController = Navigation.findNavController(this, R.id.garden_nav_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);
NavigationUI.setupWithNavController(navigationView, navController);
navigationView.setNavigationItemSelectedListener((NavigationView.OnNavigationItemSelectedListener) this);
}
@Override
public boolean onSupportNavigateUp() {
return (boolean) NavigationUI.navigateUp(navController, drawerLayout);
}
@Override
public void onBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
menuItem.setChecked(true);
drawerLayout.closeDrawers();
int id = menuItem.getItemId();
switch (id) {
case R.id.nav_home:
navController.navigate(R.id.navigation);
break;
}
return true;
}
}
осколки - просто болванка
неважно, какую навигацию я использую. В моем реальном проекте нижний навигационный вид, но я создал тестовый проект, как я уже сказал. В макете ящика та же проблема. Да, вы правы, я пытаюсь справиться с этим. Если пользователь повторно выбирает какой-либо фрагмент, он возвращается в стек. Как это исправить?
Вам нужно объявить фрагмент как "SingleTop".
Для этого есть опция в меню в правой части окна редактора навигационного графика под опциями анимации.
В качестве альтернативы вы можете установить его программно через класс NavOptions.Builder, используя метод setLaunchSingleTop(true), как подробно описано в ссылке ниже:
Таким образом, ваш метод выбора должен выглядеть так:
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem){
NavOptions.Builder builder = new NavOptions.Builder()
.setLaunchSingleTop(true);
NavOptions options = builder.build();
navController.navigate(item.getItemId(), null, options);
Вы имеете в виду, что пытались сделать это программно с помощью NavOptions.Builder? Если вы поделитесь своим кодом, возможно, мы сможем обнаружить проблему для вас.
нет, я сделал это в интерфейсе. Я создал тестовый проект, чтобы попробовать
попробовать программно?
да, спасибо за ответ, но это работает не во всех ситуациях
вы можете установитьLaunchSingleTop() только для действий, а не для фрагментов, поэтому вы не можете «объявить фрагмент как SingleTop», хотя я бы хотел, чтобы вы могли!
Согласно Эта проблема, BottomNavigationView предлагает OnNavigationItemReselectedListener, который имеет приоритет над OnNavigationItemSelectedListener, установленным NavigationUI:
bottomNavigationView.setOnNavigationItemReselectedListener(
new BottomNavigationView.OnNavigationReselectedListener() {
@Override
public void onNavigationItemReselected(MenuItem item) {
// By doing nothing, we ignore reselection events
}
});
NavigationView не предлагает тот же интерфейс, что и это другая проблема, поэтому вам нужно скопировать поведение по умолчанию (обратите внимание, что он вызывает NavigationUI.onNavDestinationSelected(), а не просто вызывает navigate() напрямую, в отличие от вашего кода) и добавляете собственную проверку для isChecked() для защиты от повторного выбора.
так что, на данный момент у платформы нет решения этой проблемы по умолчанию? если я правильно понимаю
@АртемАртемов - правильно, каждый компонент полностью независим друг от друга.
Я пытался использовать NavOptions.Builder для просмотра навигации бота. Это помогает при многократных кликах по одному фрагменту, но не помогает в ситуации, когда я много раз кликаю по разным иконкам. Создает большой бэк-стек, теперь я вижу один путь - создать isChecked(), как вы сказали, но это не очень хорошее решение в большом проекте, потому что нам нужно создать метод isChecked() для каждого фрагмента.
Вы не должны устанавливать OnNavigationItemSelectedListener в нижней части навигации — NavigationUI.setupWithNavController() по умолчанию делает все правильно.
@ianhanniballake У меня похожие проблемы с глобальными действиями при использовании drawerlayout. У меня есть 3 пункта назначения верхнего уровня (по одному для каждого элемента ящика). Они просто накладываются друг на друга. Использование launchSingleTop ничего не делает. Кроме того, анимация входа/выхода не работает для глобальных действий. Это ошибка? Известная проблема? ВАИ? Если нет, то куда сообщить?
В вашем вопросе говорится о нижней навигации, но в вашем коде используется
NavigationView. Что именно вы пытаетесь использовать? Пытаетесь обработать случай, когда пользователь повторно выбирает элемент в меню, который уже выбран?