如何在导航控件片段中关闭导航 DrawerLayout onBackPressed

Posted

技术标签:

【中文标题】如何在导航控件片段中关闭导航 DrawerLayout onBackPressed【英文标题】:How to close navigation DrawerLayout onBackPressed in Navigation Controls Fragment 【发布时间】:2019-09-19 12:47:49 【问题描述】:

我创建了一个具有 Navigation 模式的 Androidx 项目。现在我想在片段中关闭 drawerLayout 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_
    android:layout_>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_
        android:layout_
        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】:

您好,您可以通过让您的片段处于活动状态并使用 kotlin 代码覆盖 onbackpress 来做到这一点

这是您的后台功能,您可以在其中获取导航主机,然后您可以检查当前的片段或要在其中打开抽屉的片段。

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】:

您可以通过在抽屉活动中实现 OnBackStackChangedListenerFragment 中执行此操作。 然后,将您的抽屉与ActionBarDrawerToggle 链接并覆盖onBackStackChanged() 并同步您的抽屉切换。

这是完整的sn-p:

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();
        
    

希望对你有帮助。

【讨论】:

我的抽屉在我的活动中 我在活动中实现了背压。但我要求直接在片段中处理它不想在活动中编写代码 这里有两种不同的方法,onBackPressed() 用于活动,onBackStackChanged() 用于片段,FragmentManager.OnBackStackChangedListener 在 activty 上,因此当片段可见并在那时触发onBackStackChanged() 时返回动作。在onBackStackChanged() 内部,我们同步了抽屉标签toggle.syncState(),所以如果抽屉打开了,那么抽屉就会关闭。 嘿,这个答案需要用 kotlin 语言解释,而你是用 java 提供的 根据 android doc,java 和 kotlin 一起工作非常顺利,另一件事是您可以在 android studio 中轻松地将任何 java 代码转换为 kotlin。【参考方案3】:

简答

如果不在托管Activity 内添加额外代码,就无法拦截Fragment 内的onBackPressed 回调。它根本没有在Android SDK 中实现。停下来。

原答案

Fragment 类中没有 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()
  


【讨论】:

不,谢谢,但我实际上已经这样做了,但我问这可能吗?要做到这一点 ?直接在片段中而不写在活动中 @ArbazPirwani 那么恐怕答案是不可能【参考方案4】:

在 Kotlin 中,只需在您的活动中重写此方法,

override fun onBackPressed() 
    if (drawer_layout.isDrawerOpen(GravityCompat.START))
        drawer_layout.closeDrawer(GravityCompat.START)
    else
        super.onBackPressed()

【讨论】:

你说得对,我已经这样做了,但我希望它在片段中 片段总是包含一个基础活动。您可以在该活动中使用您的导航抽屉....... 在片段中使用它有什么好处?例如:如果您在第一个片段中使用它,并且通过导航抽屉中的任何项目导航到另一个片段,它将导航到另一个片段,并且您必须创建导航抽屉到该片段以及再次导航到任何其他片段......很快...... 我的导航抽屉只能在一个片段中使用 然后,尝试在片段中创建自定义工具栏,并将您的活动主题设置为 noActionBar。希望它有效....【参考方案5】:

我也遇到过类似的问题。

所以,我在我的 Activity 文件的 onBackPressed 中添加了这个。

FragmentManager manager = getSupportFragmentManager();
 if (manager.getBackStackEntryCount() > 0) 
                while (manager.getBackStackEntryCount() > 0) 
                    manager.popBackStackImmediate();
                

【讨论】:

【参考方案6】:

您可以使用OnBackPressedDispatcher 从片段内控制onBackPress()

class SampleFragment: Fragment() 
 
    override fun onCreate(savedInstanceState: Bundle?) 
            super.onCreate(savedInstanceState)
        
        requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true)
            override fun handleOnBackPressed() 
                if (drawer.isDrawerOpen(GravityCompat.START)) 
                    drawer.closeDrawer(GravityCompat.START)
                 else
                    isEnabled = false
                    requireActivity().onBackPressed() 
                
            )
        
 

【讨论】:

以上是关于如何在导航控件片段中关闭导航 DrawerLayout onBackPressed的主要内容,如果未能解决你的问题,请参考以下文章

如何在导航控制器中关闭视图控制器,而不关闭整个堆栈

不能在 Swift 中关闭导航控制器

单击抽屉的任何项目而不在android中关闭时更改导航抽屉内的视图和内容

如何在 VS 2013 中关闭 Razor 突出显示?

如何在机器人框架中关闭浏览器插件警报

在导航控制器上添加片段而不是替换片段?