Как закрыть навигацию DrawerLayout onBackPressed во фрагменте элементов навигации
Я создал проект Androidx с шаблоном навигации. Теперь я хочу, чтобы закрыть BoxLayout onBackPressed()
во фрагменте. А также я хочу установить OnClickListener
для элементов меню ящика в мое пользовательское меню, и я не могу переопределить onBackPressed()
во фрагменте.
Я хочу знать, как мне этого добиться?
Вот мой ящик:
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:id="@+id/drawerLayoutHome"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="350dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@color/colorPrimaryDark"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:itemIconTint="@color/white"
app:itemTextColor="@color/white"
app:menu="@menu/activity_main_drawer"/>
</androidx.drawerlayout.widget.DrawerLayout>
Я хочу добиться этого:
override fun onBackPressed() {
if (drawerLayoutHome.isDrawerOpen(GravityCompat.END)) {
drawerLayoutHome.closeDrawer(GravityCompat.END)
} else {
super.onBackPressed()
}
}
Но это для Activity
, как этого добиться во Fragment
?
Ответы
Ответ 1
Здравствуйте, вы можете сделать это, включив свой фрагмент в действие и переопределив onbackpress с помощью кода kotlin.
Это ваша функция обратного нажатия, и в ней вы можете получить свой навигационный хост, а затем вы можете проверить свой текущий фрагмент или фрагмент, в котором вы хотите открыть ящик.
override fun onBackPressed() {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragmentHomeHost)
val completeFragmentList = navHostFragment?.childFragmentManager?.fragments
if (completeFragmentList != null) {
when {
completeFragmentList[0] is HomeFragment -> {
val home = (completeFragmentList[0] as HomeFragment)
}
else -> super.onBackPressed()
}
}
if (drawerLayoutHome.isDrawerOpen(GravityCompat.START)) {
drawerLayoutHome.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
А вот мотыгой вы можете получить выбор элемента слушателя
override fun onNavigationItemSelected(item: MenuItem): Boolean {
drawerLayoutHome.closeDrawer(GravityCompat.START)
return true
}
Ответ 2
Вы можете сделать это внутри Fragment
, внедрив OnBackStackChangedListener
в свой ящик.
Затем свяжите ваш ящик с ActionBarDrawerToggle
и переопределите onBackStackChanged()
и синхронизируйте переключение вашего ящика.
Вот полный фрагмент:
public class MainActivity extends AppCompatActivity implements FragmentManager.OnBackStackChangedListener {
private DrawerLayout drawer;
private ActionBarDrawerToggle toggle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Bind listener here
getSupportFragmentManager().addOnBackStackChangedListener(this);
drawer = findViewById(R.id.drawer_layout);
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
toggle.setToolbarNavigationClickListener(view -> onBackPressed());
drawer.addDrawerListener(toggle);
toggle.syncState();
}
//Override fragment backstack lister method
@Override
public void onBackStackChanged() {
//toggle.setDrawerIndicatorEnabled(getSupportFragmentManager().getBackStackEntryCount() == 0);
//getSupportActionBar().setDisplayHomeAsUpEnabled(getSupportFragmentManager().getBackStackEntryCount() > 0);
toggle.syncState();
}
@Override
public void onBackPressed() {
drawer = findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START))
drawer.closeDrawer(GravityCompat.START);
else {
//super.onBackPressed();
}
}
}
Я надеюсь, что это поможет вам.
Ответ 3
Короткий ответ
Невозможно перехватить onBackPressed
вызов onBackPressed
внутри Fragment
без добавления дополнительного кода внутри хостинга Activity
. Это просто не реализовано в Android SDK
. Просто остановись.
Оригинальный ответ
В классе Fragment
нет такой вещи, как onBackPressed
вызов onBackPressed
. Но вы можете реализовать это самостоятельно:
Создать интерфейс:
interface FragmentOnBackPressedListener {
fun onBackPressed()
}
Сейчас в вашей деятельности:
override fun onBackPressed() {
// find top fragment
val currentFragment = supportFragmentManager
.findFragmentById(R.id.your_nav_host_fragment_id)?.let {
it.childFragmentManager.fragments.getOrNull(0)
}
// check if it implements FragmentOnBackPressedListener
if (currentFragment is FragmentOnBackPressedListener ) {
// if it does, transfer flow to the fragment
return currentFragment.onBackPressed()
}
// if it doesn't, apply default behaviour
super.onBackPressed()
}
Тогда в вашем фрагменте:
class ExampleFragment : Fragment(), FragmentOnBackPressedListener {
override fun onBackPressed() {
// close your drawer layout here if visible
// Also: to close fragment, invoke
// findNavController().navigateUp() or findNavController().popBackStack()
}
}
Ответ 4
Я предпочел реализовать эту иерархию представлений в основной деятельности, чтобы решить точно такую же проблему, с которой вы столкнулись. В конце концов я написал обходной путь для сокрытия нижнего листа внутри фрагмента. С небольшой адаптацией это также работает для вашего сценария с макетом ящика.
Вы можете просто добавить пустой элемент в дочерний стек вашего фрагмента. Оттуда фреймворк автоматически обрабатывает обратные события:
private val listener = OnBackStackChangedListener {
drawerLayoutHome.takeUnless { backStackCollapse }
?.closeDrawer(END)
}
private val callback = object : SimpleDrawerListener() {
override fun onDrawerOpened(
drawerView: View
) = collapse(true)
override fun onDrawerClosed(
drawerView: View
) = collapse()
}
private val name = "DrawerLayout.backStackCollapse"
private val backStackCollapse
get() = with(childFragmentManager) {
for (i in 0 until backStackEntryCount)
if (getBackStackEntryAt(i).name == name)
[email protected] true
false
}
private fun collapse(
opened: Boolean = false
) {
childFragmentManager.apply {
removeOnBackStackChangedListener(listener)
if (opened) {
addOnBackStackChangedListener(listener)
if (backStackCollapse) return
return transaction(allowStateLoss = true) {
addToBackStack(name)
}
}
if (isStateSaved) return
popBackStack(name, POP_BACK_STACK_INCLUSIVE)
}
}
override fun onViewCreated(
view: View,
savedInstanceState: Bundle?
) {
drawerLayoutHome.addDrawerListener(callback)
collapse()
}
override fun onDestroyView() {
collapse()
super.onDestroyView()
}
В зависимости от того, как вы используете навигацию, может потребоваться предварительно вызвать collapse()
.
Ответ 5
Вы не можете использовать OnbackPressed непосредственно во Fragment, но вы можете взять Basefragment, где вам нужно взять экземпляр действия и создать метод, как показано ниже, вы можете изменить его в соответствии с вашими требованиями, вам нужно расширить Basefragment в каждом фрагменте, где вы хотите эту функцию, Надеюсь, что это поможет вам:)
fun setupToolBarWithBackArrow(toolbar: Toolbar, Title: String?) {
val actionBar: ActionBar?
(mActivity as BaseActivity).setSupportActionBar(toolbar)
actionBar = (mActivity as BaseActivity).getSupportActionBar()
if (actionBar != null) {
actionBar!!.setDisplayHomeAsUpEnabled(true)
actionBar.setDisplayShowTitleEnabled(false)
actionBar.setHomeAsUpIndicator(R.drawable.ic_vector_back)
}
toolbar.setNavigationOnClickListener { view -> mActivity.onBackPressed() }
title = toolbar.findViewById<TextView>(R.id.tvTitle)
title.setText(Title ?: "")
}
Ответ 6
У меня была похожая проблема.
Итак, я добавил это в onBackPressed с моим файлом Activity.
FragmentManager manager = getSupportFragmentManager();
if (manager.getBackStackEntryCount() > 0) {
while (manager.getBackStackEntryCount() > 0) {
manager.popBackStackImmediate();
}
Ответ 7
В Kotlin просто переопределите этот метод в своей деятельности,
override fun onBackPressed() {
if (drawer_layout.isDrawerOpen(GravityCompat.START))
drawer_layout.closeDrawer(GravityCompat.START)
else
super.onBackPressed()
}