【问题标题】:How to implement proper back navigation for the Android BottomNavigation如何为 Android BottomNavigation 实现正确的后退导航
【发布时间】:2017-10-03 16:20:44
【问题描述】:

我花了一整天的时间寻找答案,但没有找到适合这个问题的任何解决方案。

我正在寻找一种方法来创建类似于 Instagram 或 PlayKiosk 应用程序的底部导航用法。片段应该只被添加到后台堆栈一次。当按下后退按钮时,我希望应用程序跳回到访问过的最后一个 Fragment,并且 BottomNavigation 的按钮以适应该 Fragment。

目前我使用以下代码:

//BottomNavigationListener
private BottomNavigationView.OnNavigationItemSelectedListener buttonNavigationItemSelectedListener
        = new BottomNavigationView.OnNavigationItemSelectedListener() {

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        switch (item.getItemId()) {
            case R.id.navigation_game:
                currentFragment = GameFragment.newInstance();
                fragmentManager.beginTransaction()
                        .addToBackStack(TAG_FRAGMENT_GAME)
                        .replace(R.id.content_frame, currentFragment, TAG_FRAGMENT_GAME)
                        .commit();
                break;
            case R.id.navigation_tournament:
                currentFragment = TournamentFragment.newInstance();
                fragmentManager.beginTransaction()
                        .addToBackStack(TAG_FRAGMENT_TOURNAMENT)
                        .replace(R.id.content_frame, currentFragment, TAG_FRAGMENT_TOURNAMENT)
                        .commit();
                break;
            case R.id.navigation_history:
                break;
        }
        return true;
    }

};

但这导致了一个问题,即我可以按几次底部导航按钮,每次点击都会实例化一个新的片段。此外,BottomNavigation 按钮也没有根据 Fragments 设置。

我找到了这个答案,但没有成功Prevent The Same Fragment From Stacking More Than Once ( addToBackStack)

【问题讨论】:

  • 你可以处理 onBackPressed 方法吗?在打开时保留最后一个片段,当您按下返回按钮时,只需替换您访问的最后一个片段。

标签: android android-fragments


【解决方案1】:

您可以使用以下结构来拥有与 Instagram 相同的工作逻辑。

首先创建一个自定义的有限唯一队列类。它只保存最后 N 个项目,其中 N 是传递给其构造函数的限制数(或最大项目数)。此外,这种类型的队列只保留一个类的一个实例。如果 A 类的一个实例已经在队列中,而另一个 A 类的实例要添加到队列中,则先删除前一个实例,然后再插入新的对象。

public class LimitedUniqueQueue<E> extends LinkedList<E> {

    private int limit;

    public LimitedUniqueQueue(int limit) {
        this.limit = limit;
    }

    @Override
    public boolean add(E o) {
        // For uniqueness
        for (int i = 0; i < super.size(); i++) {
            E item = super.get(i);
            if (item.getClass() == o.getClass()) {
                super.remove(i);
                break;
            }
        }
        boolean added = super.add(o);
        // For size limit
        while (added && size() > limit) {
            super.remove();
        }
        return added;
    }
}

然后按如下方式更新您的活动:

public class MainActivity extends AppCompatActivity {

    private BottomNavigationView navigation;
    private LimitedUniqueQueue<Fragment> queue;
    ...
    private BottomNavigationView.OnNavigationItemSelectedListener onNavigationItemSelectedListener
                = new BottomNavigationView.OnNavigationItemSelectedListener() {

        Fragment gameFragment;
        Fragment tournamentFragment;
        Fragment profileFragment;

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
            switch (item.getItemId()) {
                case R.id.navigation_game:
                    if (gameFragment == null) {
                        gameFragment = new GameFragment();
                    }
                    transaction.replace(R.id.content, gameFragment).commit();
                    queue.add(gameFragment);
                    return true;
                case R.id.navigation_tournament:
                    if (tournamentFragment == null) {
                        tournamentFragment = new TournamentFragment();
                    }
                    transaction.replace(R.id.content, tournamentFragment).commit();
                    queue.add(tournamentFragment);
                    return true;
                case R.id.navigation_profile:
                    if (profileFragment == null) {
                        profileFragment = new ProfileFragment();
                    }
                    transaction.replace(R.id.content, profileFragment).commit();
                    queue.add(profileFragment);
                    return true;
            }
            return false;
        }

    };

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

        navigation = (BottomNavigationView) findViewById(R.id.navigation);
        queue = new LimitedUniqueQueue<>(navigation.getMenu().size());

        navigation.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener);
        navigation.setSelectedItemId(R.id.navigation_game);
        ...
    }

    @Override
    public void onBackPressed() {
        if (queue.size() > 1) {
            queue.removeLast();
            Fragment previousFragment = queue.getLast();
            if (previousFragment instanceof GameFragment) {
                navigation.setSelectedItemId(R.id.navigation_game);
            } else if (previousFragment instanceof TournamentFragment) {
                navigation.setSelectedItemId(R.id.navigation_tournament);
            } else if (previousFragment instanceof ProfileFragment) {
                navigation.setSelectedItemId(R.id.navigation_profile);
            }
        } else {
            super.onBackPressed();
        }
    }

    ...
}

【讨论】:

  • 刚刚测试过,对我来说效果很好。非常感谢。我所做的唯一添加是在 onCreate 中将新的 GameFragment 添加到队列中,使其在开始时已经在队列中,并作为我按下后退按钮时的默认后备。
  • 不客气。我在onCreate() 方法中调用navigation.setSelectedItemId(R.id.navigation_game),以便在启动时显示GameFragment 并将其隐式添加到onNavigationItemSelected() 中的queue。无论如何,我很高兴它成功了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-10
  • 2023-01-25
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多