【问题标题】:Navigation Drawer back button in fragments片段中的导航抽屉后退按钮
【发布时间】:2018-01-10 10:57:36
【问题描述】:

我开始创建使用一个活动(导航抽屉)和许多片段的应用程序。 但我无法使用工具栏后退按钮从片段中导航回来。硬件后退按钮完美运行。我知道我需要覆盖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()
  • onCreateOptionsMenu 已被覆盖并返回 true。

标签: android android-fragments navigation-drawer


【解决方案1】:

setNavigationOnClick() 在toolbar 之后setSupportActionBar(toolbar) 上:

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

我做了一个小app供参考

第一个片段

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

activity_main

<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 我已经为参考创建了一个示例,并且它工作得很好。检查编辑的答案。
  • 感谢您的回复。我通过阅读ActionBarDrawerToggle 的一些文档解决了一个问题。我们应该使用没有Toolbar参数的构造函数:public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, int openDrawerContentDescRes, int closeDrawerContentDescRes)
【解决方案2】:

我有同样的问题。

我终于通过在 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);

            }
        }
    });

希望对你有帮助

【讨论】:

    【解决方案3】:

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

    只需将其添加到您的根活动中,片段会抵抗

    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(),这与您的后退按钮的作用类似!

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        解决我的问题是这样的:

        我使用了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 就是它,不需要一个 hack 或一行代码,这是开箱即用的,我只需要注意它。 p>

        所以在我的情况下,我改为以下代码:

        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,然后为非顶级目的地出现后退箭头。
        【解决方案6】:

        我为此苦苦挣扎了一段时间。我让它工作了:

        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)
        
        1. 在我需要工具栏的每个片段中包含工具栏布局:
            <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 代码中为 home 片段之后的片段添加以下代码,这将获得返回箭头:
              val navController = findNavController()
                val appBarConfiguration = AppBarConfiguration(navController.graph)
        
                view.findViewById<Toolbar>(R.id.toolbar)
                    .setupWithNavController(navController, appBarConfiguration)
        
        1. 在目标片段的 onCreateView 方法中添加以下代码:
              activity?.onBackPressedDispatcher?.addCallback(
                    viewLifecycleOwner, object : OnBackPressedCallback(true) {
                        override fun handleOnBackPressed() {
                 
                            findNavController().navigate(
                                [back fragment directions] )
                        }
                    })
        

        希望这可以帮助每个为此苦苦挣扎的人:)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-04-11
          • 1970-01-01
          • 2020-12-20
          • 2014-02-01
          • 2015-07-01
          • 1970-01-01
          • 2023-03-29
          相关资源
          最近更新 更多