导航抽屉,如何处理返回按钮以返回先前的片段?

34

我正在使用内置导航抽屉来运行我的应用程序。我无法完全弄清楚如何处理返回按钮。当按下返回按钮时,我希望它再次加载第一个片段。片段1。

因此,当应用程序启动时,您会看到Fragment1启动。然后他们可以点击Fragment 2-5以转到其他页面。在所有这些页面中,我希望返回按钮将用户带回Fragment1。用户唯一能够通过返回按钮退出应用程序的地方是Fragment1。

由于所有内容都由FragmentActivity处理,因此我尝试在那里更改返回按钮。但是,我一直收到强制关闭错误:

(01-11 14:09:33.114: E/AndroidRuntime(8292): android.view.InflateException: Binary XML file line #7: Error inflating class fragment)
这是我目前为止的进展: 我已经确保像这样将片段添加到后退栈中:
fm.beginTransaction().replace(R.id.main, newFragment).addToBackStack("fragBack").commit();

返回按钮:

@Override
public void onBackPressed() {
    if (getSupportFragmentManager().findFragmentByTag("fragBack") != null) {
        
    }
    else {
        super.onBackPressed();
        return;
    }
    if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
        Toast.makeText(getApplicationContext(), "Test", Toast.LENGTH_LONG).show();
        Fragment frag = getSupportFragmentManager().findFragmentByTag("fragBack");
        FragmentTransaction transac = getSupportFragmentManager().beginTransaction().remove(frag);
                transac.commit();
    }
    
}

有人知道我需要做什么吗?我需要在每个片段中调用onBackPressed(如果可能的话),而不是控制抽屉的FragmentActivity吗?在我以前的应用程序中,无论用户在哪个片段上,返回按钮都可以关闭应用程序,但我现在制作的这个应用程序中,我希望返回按钮返回到Fragment1。

非常感谢您的帮助,谢谢。

onItemClick

@Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            
            Fragment newFragment = new MapsPage();
            FragmentManager fm = getSupportFragmentManager();
            switch(i) {
            case 0:
                newFragment = new Fragment2();
                break;
            case 1:
                newFragment = new Fragment3();
                break;
            case 2:
                newFragment = new Fragment4();
                break;
            case 3:
                newFragment = new Fragment5();
                break;
            }
            fm.beginTransaction().add(R.id.main, newFragment).addToBackStack("fragback").commit();
            drawerLayout.closeDrawer(rl);
        }

真的吗?在我所有的导航抽屉中,返回按钮总是退出应用程序。无论用户选择进入哪个片段。我的布局有自定义插件,但它是标准布局,我不认为这是我的问题,但我会发布它。 - RED_
1
你需要在提交 FragmentTransaction 之前使用方法 addToBackStack()(这是你已经做的)。你不需要再做任何事情,就可以通过返回按钮回到上一个片段。 - Dreagen
还有,Vipul是正确的,你得到的错误是与你的布局文件有关的问题。 - Dreagen
是的,只需在提交之前将 Fragment1 添加到后退堆栈中,无论用户位于哪个片段,按下返回按钮都将带他回到 Fragment1。还要确保不将其他片段添加到后退堆栈中。 - Adnan Mulla
@Dreagen 我刚刚注释掉了我的onBackPressed方法,但是当我按下返回按钮时,出现了强制关闭错误。和我问题中的异常一样。 - RED_
显示剩余4条评论
6个回答

37

不要:

fm.beginTransaction().replace(R.id.main, newFragment).addToBackStack("fragBack").commit();

电话:

fm.beginTransaction().add(R.id.main, newFragment).addToBackStack("fragBack").commit();

addToBackStack函数与add一起使用。

replace函数会删除先前的片段,并放置新的片段,因此在您的后退栈中始终只有一个片段。因此,请使用add函数来保留先前的片段。

要始终从任何片段到达片段1,请尝试执行以下操作:

getFragmentManager().popBackStack();
fm.beginTransaction().add(R.id.main, newFragment).addToBackStack("fragBack").commit();

这将从后退栈中移除最后一个事务并添加一个新的事务。 请尝试。


谢谢。这样可以让后退栈正常工作。您知道如何使其始终返回到Fragment1吗?这样他们可以转到Fragment2,然后是3,但按下返回按钮将带他们回到Fragment1。如果需要,我可以发布我的完整onItemClick。 - RED_
1
这只是一个猜测 - 但如果您在其他片段之间切换时不调用addToBackStack()方法(仅在离开fragment1时调用它),那么可能会出现这种情况。 - Dreagen
这听起来很合理。我已经发布了我的onItemClick以获得更好的想法。 - RED_
尝试在添加之前每次调用getFragmentManager().popBackStack()。 - vipul mittal
成功了!非常感谢。也要感谢@Dreagen 。我很喜欢完全不需要使用onBackPressed。我在每个case:里面放置了popBackStack,看到您的编辑后,那也可能会起作用。+1并标记为答案。 - RED_
显示剩余3条评论

6

尽管这个问题有点老,但我想报告我的发现,以便其他可能遇到同样问题的人。对于我来说,采用被接受答案中建议的方法会使图层重叠,很快就无法阅读。下面的代码(改编自被接受答案)避免了重叠,但仍将屏幕添加到后退栈。

fragmentManager.beginTransaction().replace(R.id.container, fragment).addToBackStack("fragBack").commit();

3

在某些情况下,您必须使用replace,那么您就无法使用addtobackstack(),因此您可以在MainActivity中使用此代码。 在此代码中,当您按下返回键时,您始终会进入第一个片段(我称其为HomeFragment),当您处于HomeFragment时,它会询问两次是否要退出应用程序。

 private Boolean exit = false;
@Override
    public void onBackPressed() {
        if (exit) {
            super.onBackPressed();
            return;
        }

    try {
        FragmentManager fragmentManager = getSupportFragmentManager();
        Fragment fragment = fragmentManager.findFragmentByTag("HOME");
        if (fragment != null) {
            if (fragment.isVisible()) {
                this.exit = true;
                Toast.makeText(this, "Press Back again to Exit", Toast.LENGTH_SHORT).show();
            }
        }
        else {
            fragment = HomeFragment.class.newInstance();
            getFragmentManager().popBackStack();
            fragmentManager.beginTransaction().replace(R.id.flContent, fragment, "HOME").commit();
        }
    } catch (Exception e) {

    }
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            exit = false;
        }
    }, 2000);
}

1
 - @Override
       public void onBackPressed() {
           DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
           int backstack = getSupportFragmentManager().getBackStackEntryCount();
           if (drawer.isDrawerOpen(GravityCompat.START)) {
               drawer.closeDrawer(GravityCompat.START);
           } else if (backstack > 0) {
              for (int i = 0; i < backstack; i++) {
                   getSupportFragmentManager().popBackStackImmediate();
            }
       } else {
           this.finish();
        }
    }

为了改进您的答案,也许您可以添加一些关于您解决方案的描述文本。 - Kalamarico

0

我有4个片段附加到底部导航活动中,最近遇到了这个问题,并成功解决如下:我的活动代码

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;

import android.os.Bundle;
import android.view.MenuItem;

import com.google.android.material.bottomnavigation.BottomNavigationView;

public class BottomNavigationActivity extends AppCompatActivity
{
    NavController navController;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bottom_navigation);


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

        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new FragmentHome()).commit();


    }

    public boolean onSupportNavigateUp() {
        return navController.navigateUp();
    }

    private BottomNavigationView.OnNavigationItemSelectedListener navListener= new
            BottomNavigationView.OnNavigationItemSelectedListener()
            {
                @Override
                public boolean onNavigationItemSelected(@NonNull MenuItem item)
                {

                    Fragment selectedFragment = null ;


                    switch (item.getItemId())
                    {

                        case R.id.nav_home:
                            selectedFragment = new FragmentHome();

                            break;

                        case R.id.nav_search:
                            BottomNavigationActivity.this.getSupportFragmentManager().beginTransaction()
                                    .addToBackStack(null)
                                    .commit();
                            selectedFragment=new FragmentSearch();
                            break;

                        case R.id.nav_cart:
                            BottomNavigationActivity.this.getSupportFragmentManager().beginTransaction()
                                    .addToBackStack(null)
                                    .commit();
                            selectedFragment=new FragmentCart();
                            break;

                        case R.id.nav_user:
                            BottomNavigationActivity.this.getSupportFragmentManager().beginTransaction()
                                    .addToBackStack(null)
                                    .commit();
                                selectedFragment= new FragmentAccount();
                                        break;


                    }

                    getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
                            selectedFragment).commit();

                    return true;

                }
            };


    @Override
    public void onBackPressed()
    {
        int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
        if (backStackEntryCount == 0) {
            super.onBackPressed();
        } else
            {
            goHome();
        }
    }

    public void goHome()
    {
        //Following code will set the icon of the bottom navigation to active
        final BottomNavigationView mBottomNav = findViewById(R.id.bottom_navigation);
        MenuItem homeItem = mBottomNav.getMenu().getItem(0);
        mBottomNav.setSelectedItemId(homeItem.getItemId());
        getSupportFragmentManager().popBackStackImmediate();

        //To delete all entries from back stack immediately one by one.
        int backStackEntry = getSupportFragmentManager().getBackStackEntryCount();
        for (int i = 0; i < backStackEntry; i++) {
            getSupportFragmentManager().popBackStackImmediate();
        }
        //To navigate to the Home Fragment
        final FragmentHome homeFragment = new FragmentHome();
        FragmentTransaction myFragmentTransaction = getSupportFragmentManager().beginTransaction();
        myFragmentTransaction.replace(R.id.fragment_container, homeFragment, "HomeFrag Tag");
        myFragmentTransaction.commit();
    }
}

-1
我建议通过在一开始就正确管理事务来避免完全覆盖 onBackPressed()。这将有助于避免在后续实现疯狂逻辑。
首先,我们需要设置一个私有类变量,以便启用初始化:
private boolean popNext = false;

以下代码允许我们通过将其放在堆栈上来设置初始后退功能。之后每次,当popNext设置为true时,我们弹出初始事务并推入新事务。因此,我们正在用1>Y事务替换1>X事务。
额外嵌入的if语句处理选择初始项的问题,因为我们不想从1>1开始。如果是我们的初始情况,我们只需关闭抽屉。其他情况需要像返回按钮一样操作,但我们需要记住将其设置为返回到初始状态!
if(popNext){
    if(i == INITIAL_POSITION){
        onBackPressed();
        mDrawerLayout.closeDrawer(mDrawerList);
        popNext = false;
        return;
    }
    getFragmentManager().popBackStackImmediate();
}
else{
    if(i == INITIAL_POSITION){
        mDrawerLayout.closeDrawer(mDrawerList);
        return;
    }
    popNext=true;
}
getFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit();

注意:我的代码使用的是getFragmentManager()而不是getSupportFragmentManager(),这是一种原生函数,我相信从Honeycomb开始就有了。


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