Как закрыть навигацию 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()
}