简体   繁体   English

片段中的导航抽屉后退按钮

[英]Navigation Drawer back button in fragments

I started creating of app which use one activity (Navigation Drawer) and many fragments.我开始创建使用一个活动(导航抽屉)和许多片段的应用程序。 But I unable to use toolbar back button to navigate back from fragments.但我无法使用工具栏后退按钮从片段导航回来。 Hardware back button works perfectly.硬件后退按钮完美运行。 I know that I need to override onOptionsItemSelected , catch android.R.id.home , check if there are something in back stack and than pop it.我知道我需要覆盖onOptionsItemSelected ,捕获android.R.id.home ,检查后堆栈中是否有东西然后弹出它。 After changing fragment, "burger" button changes to "back arrow", but when I click on it onOptionsItemSelected never fired, just opens the NavigationDrawer menu.更改片段后,“汉堡”按钮更改为“后退箭头”,但是当我单击它时onOptionsItemSelected从未触发,只需打开 NavigationDrawer 菜单。

Here the code from activity:这是来自活动的代码:

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

}

And how I change (replace) fragments from HomeFragment :以及我如何从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();

setNavigationOnClick() on the toolbar after setSupportActionBar(toolbar) : setNavigationOnClick()上的toolbarsetSupportActionBar(toolbar)

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

I have made a small app for reference我做了一个小应用程序供参考

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

}

SecondFragment第二个片段

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

}

MainActivity主要活动

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>

I had the same issue.我有同样的问题。

I finally solved by adding this in the onCreate method.我最终通过在 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);

            }
        }
    });

Hope it helps希望它有帮助

Add below code in onCreate() method that's work for me在对我有用的 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();
            }
        }
    });

Simplest solution for Kotlin Developers Kotlin 开发人员最简单的解决方案

Just add this in your root activity where fragments resist只需将其添加到片段抵抗的根活动中

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

Here, whenever setDisplayHomeAsUpEnabled is set true , I am showing back button.在这里,每当setDisplayHomeAsUpEnabled设置为true ,我都会显示后退按钮。 and on cliking it, I am calling super.onBackPressed() which is similar to what your back button does!在点击它时,我正在调用super.onBackPressed()这类似于您的后退按钮的作用!

The thing that solved me the issue was this:解决我这个问题的事情是这样的:

I used the code that came automatically with the NavigationDrawerActivity template, and it was something like this:我使用了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);

And I struggled a lot with many tricks and hacks, and nothing was working for me.我在许多技巧和窍门上挣扎了很多,但没有任何效果对我有用。

At some point, I have noticed that the fragments that I want to enable the back button are " being considered as top level destinations ", exactly what the comment says in the code above.在某些时候,我注意到我想要启用后退按钮的片段“被视为顶级目的地”,这正是上面代码中的注释所说的。 Then I removed those sub-fragments and BOOM that was it, with not a single hack or line of code needed, this is provided out of the box , I just needed to pay attention to it.然后我删除了那些子片段和 BOOM 就是这样,不需要一个 hack 或一行代码,这是开箱即用的,我只需要注意它。

So in my case, I changed to the below code:所以就我而言,我改为以下代码:

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

: Just removed R.id.nav_y, R.id.nav_z in the AppBarConfiguration : 刚刚删除了R.id.nav_y, R.id.nav_z , AppBarConfiguration

I struggled with this for a while.我为此挣扎了一段时间。 I got it to work by:我通过以下方式让它工作:

  1. setting my the drawer layout in MainActivity.kt:在 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. Include the toolbar layout in each of the fragments that I need the Toolbar:在我需要工具栏的每个片段中包含工具栏布局:
    <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. Add the following code to the home fragment of the drawer, the one from which other fragments are called using the drawer.将以下代码添加到抽屉的主片段中,使用抽屉从中调用其他片段。 This gets the hamburguer icon to work, onside the onViewCreated method:这使汉堡包图标在 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. Add the following code in the kotlin code for the fragments that are after the home fragment, this will get the back arrow:在 kotlin 代码中为 home 片段之后的片段添加以下代码,这将得到后退箭头:
      val navController = findNavController()
        val appBarConfiguration = AppBarConfiguration(navController.graph)

        view.findViewById<Toolbar>(R.id.toolbar)
            .setupWithNavController(navController, appBarConfiguration)
  1. Edit the navigation graph to make sure the fragments are connected correctly编辑导航图以确保片段正确连接

Hope this helps everyone struggling with this :)希望这可以帮助每个为此苦苦挣扎的人:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 带碎片和后退按钮的抽屉 - Drawer with Fragments & back Button 应用程序退出导航抽屉内的片段内的后退按钮 - App exits on presing back button inside fragments within navigation drawer 导航抽屉,处理后退按钮转到上一个片段? - Navigation drawer, handling the back button to go to previous fragments? 导航抽屉后退按钮 - Navigation Drawer back button 带有片段的后退按钮导航 - back button navigation with fragments 导航抽屉中的后退按钮支持 - Support back button in navigation drawer 按下后退按钮时,应用程序将关闭,同时在单个活动中使用导航抽屉中的片段 - On pressing back button app closes while using fragments in navigation drawer in single activity 在导航抽屉中具有多个片段的活动中按后退按钮时退出应用程序 - Exit app when press Back button in activity with multiple fragments in Navigation Drawer 向工具栏添加后退按钮,用于打开导航抽屉的Home Fragment以外的所有碎片 - Add back button to Toolbar for all Fragments other than Home Fragment which opens Navigation Drawer 在带有导航抽屉的嵌套片段中在操作栏中实现向后导航? - Implementing back navigation in action bar in nested fragments with navigation drawer?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM