在片段中的导航抽屉返回按钮

11

我开始创建一个使用一个活动(Navigation Drawer)和多个片段的应用程序。 但是我无法使用工具栏返回按钮从片段中导航回来。硬件返回按钮完美地工作。我知道我需要覆盖onOptionsItemSelected,捕获android.R.id.home,检查后退堆栈是否有内容,然后弹出它。更改片段后,“汉堡包”按钮会变成“返回箭头”,但当我单击它时,onOptionsItemSelected从未触发,只打开 NavigationDrawer 菜单。

这里是活动页面的代码:

public class NavDrawerActivity extends AppCompatActivity implements ... {

    NavigationView navigationView;
    BottomNavigationView bottomNavigationView;
    ActionBarDrawerToggle toggle;
    FragmentManager fragmentManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_nav_drawer);

        fragmentManager = getSupportFragmentManager();

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);

        toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);

        drawer.addDrawerListener(toggle);

        toggle.syncState();

        // Set back button
        fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
            @Override
            public void onBackStackChanged() {
                if (fragmentManager.getBackStackEntryCount() > 0) {
                    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
                } else {
                    getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                    toggle.syncState();
                }
            }
        });

        // Load default fragment
        changeFragment(new HomeFragment(), false);

        navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        View headerLayout = navigationView.getHeaderView(0);

        bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
        bottomNavigationView.setOnNavigationItemSelectedListener(this);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                Toast.makeText(this, "Back pressed", Toast.LENGTH_SHORT)
                        .show();
                onBackPressed();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);

        if (drawer.isDrawerOpen(GravityCompat.START))
            drawer.closeDrawer(GravityCompat.START);

        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else if (fragmentManager.getBackStackEntryCount() > 0) {
            fragmentManager.popBackStack();
        } else {
            super.onBackPressed();
        }
    }

    private void changeFragment(Fragment fm, boolean addToBackStack)
    {
        FragmentTransaction ft = fragmentManager.beginTransaction();
        ft.replace(R.id.frame_layout_content, fm);
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        if (addToBackStack) ft.addToBackStack(null);
        ft.commit();
    }

}

我如何替换来自HomeFragment的片段:

IndexDetailFragment newFragment = new IndexDetailFragment();
Bundle args = new Bundle();

args.putString(IndexDetailFragment.ARG_INDEX_ID, id);

newFragment.setArguments(args);

FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
transaction.replace(R.id.frame_layout_content, newFragment);
transaction.addToBackStack(null);

transaction.commit();

这只是一个猜测,你可能需要覆盖onCreateOptionsMenu() - terencey
onCreateOptionsMenu已经被重写并返回true。 - Alex
6个回答

12

在调用setSupportActionBar(toolbar)之后,对toolbar调用setNavigationOnClick()

setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                Toast.makeText(getActivity(), "Back clicked!",     
                Toast.LENGTH_SHORT).show();
            }
        });

我制作了一个小应用程序供参考

FirstFragment

 public class FirstFragment extends Fragment {


public FirstFragment() {
    // Required empty public constructor
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_first, container, false);
}

}

第二片段

public class SecondFragment extends Fragment {


public SecondFragment() {
    // Required empty public constructor
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_second, container, false);
}

}

主活动

public class MainActivity extends AppCompatActivity {
private Toolbar mToolbar;
private ActionBarDrawerToggle drawerToggle;
private DrawerLayout mDrawerLayout;
private String TAG = "MainActivity";
private FragmentManager mFragmentManager;
private NavigationView mNavigationView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    if (mToolbar != null) {
        setSupportActionBar(mToolbar);
    }
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer);
    drawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) {
        @Override
        public void onDrawerOpened(View drawerView) {
            super.onDrawerOpened(drawerView);
        }

        @Override
        public void onDrawerClosed(View drawerView) {
            super.onDrawerClosed(drawerView);
        }
    };
    mDrawerLayout.addDrawerListener(drawerToggle);
    mNavigationView = findViewById(R.id.navigation);
    mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            switch (item.getItemId()) {
                case R.id.first:
                    changeFragment(new FirstFragment(), true);
                    return true;
                case R.id.second:
                    changeFragment(new SecondFragment(), true);
                    return true;
            }
            return false;
        }
    });
    mFragmentManager = getSupportFragmentManager();
    changeFragment(new FirstFragment(), true);
}

@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    if (drawerToggle != null) {
        drawerToggle.syncState();
    }
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == android.R.id.home) {
        Log.i(TAG, "onOptionsItemSelected: Home Button Clicked");
        if (mDrawerLayout.isDrawerOpen(Gravity.START)) {
            mDrawerLayout.closeDrawer(Gravity.START);
        } else {
            mDrawerLayout.openDrawer(Gravity.START);
        }
    }
    return super.onOptionsItemSelected(item);
}

@Override
public void onBackPressed() {
    if (mDrawerLayout.isDrawerOpen(Gravity.START)) {
        mDrawerLayout.closeDrawer(Gravity.START);
    }
    if (mDrawerLayout.isDrawerOpen(Gravity.START)) {
        mDrawerLayout.closeDrawer(Gravity.START);
    } else if (mFragmentManager.getBackStackEntryCount() > 0) {
        mFragmentManager.popBackStack();
    } else {
        super.onBackPressed();
    }
}

private void changeFragment(Fragment fragment, boolean needToAddBackstack) {
    FragmentTransaction mFragmentTransaction = mFragmentManager.beginTransaction();
    mFragmentTransaction.replace(R.id.FRAME_CONTENT, fragment);
    mFragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    if (needToAddBackstack)
        mFragmentTransaction.addToBackStack(null);
    mFragmentTransaction.commit();
}
}

主活动

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">
            

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/abc_action_bar_default_height_material"
        android:background="@color/colorPrimaryDark"
        />

    <FrameLayout
        android:id="@+id/FRAME_CONTENT"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="@dimen/abc_action_bar_default_height_material" />
</FrameLayout>

<android.support.design.widget.NavigationView
    android:id="@+id/navigation"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:layout_marginTop="@dimen/abc_action_bar_default_height_material"
    app:menu="@menu/drawermenu" />
  </android.support.v4.widget.DrawerLayout>

@Alex,我已经创建了一个样例供参考,它运行得非常完美。请查看编辑后的答案。 - Arvind
1
感谢您的回复。我通过阅读ActionBarDrawerToggle的一些文档解决了一个问题。我们应该使用没有Toolbar参数的构造函数:public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, int openDrawerContentDescRes, int closeDrawerContentDescRes) - Alex

4
我曾经遇到过同样的问题。 最终我通过在onCreate方法中添加以下代码解决了问题。
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if(getSupportFragmentManager().getBackStackEntryCount() == 0){
                drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
                menuToggle.setDrawerIndicatorEnabled(true);
            }else{
                drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
                menuToggle.setDrawerIndicatorEnabled(false);

            }
        }
    });

希望您能从中受益。

1

Kotlin 开发人员的最简解决方案

只需在根 Activity 中添加此代码片段,即可让所有 Fragment 生效。

if (supportFragmentManager.backStackEntryCount > 0) {
            supportActionBar!!.setDisplayHomeAsUpEnabled(true)
            toolbar.setNavigationOnClickListener {
                if (supportFragmentManager.backStackEntryCount > 0) {
                    super.onBackPressed()
                } else {
                    supportActionBar!!.setDisplayHomeAsUpEnabled(false)
                    drawerLayout.addDrawerListener(toggle)
                    toggle.syncState()
                    drawerLayout.openDrawer(GravityCompat.START)
                }
            }
        } else {
            supportActionBar!!.setDisplayHomeAsUpEnabled(false)
            drawerLayout.addDrawerListener(toggle)
            toggle.syncState()
        }

在这里,每当 setDisplayHomeAsUpEnabled 被设置为 true 时,我会显示返回按钮。并且在点击它时,我会调用 super.onBackPressed(),这与您的返回按钮所做的类似!

0
解决我的问题的东西是这个:
我使用了自带的NavigationDrawerActivity模板代码,大致上是这样的:
    NavigationView navigationView = _binding.navView;
    // Passing each menu ID as a set of Ids because each menu should be considered as top level destinations:
    _AppBarConfiguration = new AppBarConfiguration.Builder(R.id.nav_x, R.id.nav_y, R.id.nav_z)
            .setOpenableLayout(_binding.drawerLayout)
            .build();
    _navController = Navigation.findNavController(this, R.id.nav_host_content_main);
    NavigationUI.setupActionBarWithNavController(this, _navController, _AppBarConfiguration);
    NavigationUI.setupWithNavController(navigationView, _navController);

我曾经尝试了很多技巧和黑科技,但都没有起作用。

后来我发现,我想要启用返回按钮的片段被“视为顶级目标”,就像上面代码中的注释所说的那样。然后我删除了这些子片段,BOOM,问题解决了,不需要任何黑科技或代码,这是开箱即用的功能,我只需要注意它。

因此,在我的情况下,我改成了以下代码:

NavigationView navigationView = _binding.navView;
// Passing each menu ID as a set of Ids because each menu should be considered as top level destinations:
_AppBarConfiguration = new AppBarConfiguration.Builder(R.id.nav_x)
        .setOpenableLayout(_binding.drawerLayout)
        .build();
_navController = Navigation.findNavController(this, R.id.nav_host_content_main);
NavigationUI.setupActionBarWithNavController(this, _navController, _AppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, _navController);

:只需在AppBarConfiguration中删除R.id.nav_y,R.id.nav_z即可。


我遇到了类似的问题,但我的应用程序架构是模块化的。我的功能模块提供它们的导航图,并将其导航图的ID作为顶级目标传递。当我在某些功能模块的导航图中进行更深入的导航时,我从未看到返回按钮。我所做的是给出第一个片段的ID而不是导航图的ID,然后对于非顶级目标,返回箭头就会出现。 - Shibakaneki

0

我曾经为此苦苦挣扎。最终通过以下步骤解决了问题:

  1. 在MainActivity.kt中设置我的抽屉布局:
    val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
         val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment

    val navController = navHostFragment.navController
    findViewById<NavigationView>(R.id.nav_view).setupWithNavController(navController)
        appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)

在我需要工具栏的每个片段中包含工具栏布局:
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            />

</com.google.android.material.appbar.AppBarLayout>
</LinearLayout>
  1. 将以下代码添加到抽屉的主页片段中,该片段通过抽屉调用其他片段。这将使汉堡图标在onViewCreated方法中工作:
    var myToolbar = requireActivity()
            .findViewById<androidx.appcompat.widget.Toolbar>(R.id.toolbar)
        myToolbar.inflateMenu(R.menu.menu)
    
     val drawerLayout = requireActivity().findViewById<DrawerLayout>(R.id.drawer_layout)
        val toggle = ActionBarDrawerToggle(
            activity, drawerLayout, myToolbar,
            R.string.navigation_drawer_open, R.string.navigation_drawer_close
        )
        toggle.setDrawerIndicatorEnabled(true)
        drawerLayout.addDrawerListener(toggle)
        toggle.syncState()
    
    
    
  2. 在后续片段的Kotlin代码中添加以下代码,这将获得返回箭头:
      val navController = findNavController()
        val appBarConfiguration = AppBarConfiguration(navController.graph)

        view.findViewById<Toolbar>(R.id.toolbar)
            .setupWithNavController(navController, appBarConfiguration)

在目标片段中的onCreateView方法中添加以下代码:
      activity?.onBackPressedDispatcher?.addCallback(
            viewLifecycleOwner, object : OnBackPressedCallback(true) {
                override fun handleOnBackPressed() {
         
                    findNavController().navigate(
                        [back fragment directions] )
                }
            })

希望这能帮助所有遇到困难的人 :)

0
在onCreate()方法中添加以下代码 这对我有用
    getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);
                toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this, " Back Pressed ", Toast.LENGTH_SHORT).show();
                    }
                });
            } else {
                getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                drawerToggle = new ActionBarDrawerToggle(MainActivity.this, drawerLayout, toolbar,
                        R.string.app_name, R.string.app_name);
                drawerLayout.addDrawerListener(drawerToggle);
                drawerToggle.syncState();
            }
        }
    });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接