如何复制YouTube的应用导航逻辑

时间:2017-08-07 18:56:38

标签: android android-fragments fragment-backstack

我想在我的应用导航逻辑中实现,就像在Youtube应用中一样。 (BottomNavigationView +片段管理)。我想要这个,因为这些片段很重,所以我希望它们被懒惰初始化然后存储在backstack中,我觉得YouTube就是这样做的。我已经实现了BottomNagivationView,但我在片段管理方面遇到了问题。

我的代码:

bottomNavigationView.setOnTabSelectedListener { position, _ -> 
    setFragment(OnlinePageFragment.Page.values()[position])
}

其中Pages是枚举

enum class Page(index: Int, val klass: Class<*>) {
        ONE(0, OnePageFragment::class.java),
        TWO(1, TwoPageFragment::class.java),
        THREE(2, ThreePageFragment::class.java)
    }

这是我的setFragment函数

fun setFragment(page: OnlinePageFragment.Page) {
    var fragment: Fragment? = supportFragmentManager.findFragmentByTag(page.klass.name)
    val tag = page.klass.name

    if (fragment == null)
        fragment = OnlinePageFragment.newInstance(page, null)

    val ft = supportFragmentManager.beginTransaction()
    with(ft) {
        replace(R.id.fragmentContainer, fragment, tag)
        addToBackStack(tag)
        commit()
    }

}

override fun onBackPressed() {
    if (supportFragmentManager.backStackEntryCount == 1) finish()
    else super.onBackPressed()
}

它有效,但不如YouTube应用程序好。 YouTube应用程序有一些神奇的行为,即每个片段只保留一个事务,而我的应用程序允许创建“无限”的事务堆栈。您对YouTube应用中的工作原理有什么想法吗?

3 个答案:

答案 0 :(得分:1)

无需使用视图寻呼机即可进行管理。 我已经实施了,请检查一下。 https://github.com/sandeshsk/BackStackFragmentRedirectsToHome

如果有任何问题,请更新。

这是分配片段的方法

public void addFragment(FragmentManager fragmentManager,
                               Fragment fragment,
                               int containerId,boolean isFromHome){

    fragmentManager.popBackStack(null,FragmentManager.POP_BACK_STACK_INCLUSIVE);

    FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
    if(isFromHome){
        fragmentTransaction.replace(containerId,fragment);
    }else{
        fragmentTransaction.add(new HomeFragment(),"Home");
        fragmentTransaction.addToBackStack("Home");
    }
    fragmentTransaction.replace(containerId,fragment).commit();

}

这是您的导航项目侦听器

 private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
        = new BottomNavigationView.OnNavigationItemSelectedListener() {

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.navigation_home:
               if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
                    addFragment(getSupportFragmentManager(), new HomeFragment(), R.id.frame, true);
                }else{
                    getSupportFragmentManager().popBackStack();
                }
                return true;
            case R.id.navigation_dashboard:
                addFragment(getSupportFragmentManager(),new DashboardFragment(),R.id.frame,false);
                return true;
            case R.id.navigation_notifications:
                addFragment(getSupportFragmentManager(),new NotificationFragment(),R.id.frame,false);
                return true;
            case R.id.navigation_setting:
                addFragment(getSupportFragmentManager(),new SettingFragment(),R.id.frame,false);
                return true;
        }
        return false;
    }
};

onBackPressed方法

 @Override
public void onBackPressed() {
    if(getSupportFragmentManager().getBackStackEntryCount()>0){
        navigation.setSelectedItemId(R.id.navigation_home);
    }else {
        super.onBackPressed();
    }
}

答案 1 :(得分:0)

我想这里最好的方法是使用ViewPager存储根片段。导航栏将更改ViewPager的页面。

根片段将内容片段存储在其ChildFragmentManagers中,并实现内容片段之间的导航逻辑。

此外,您还需要实现从活动到根片段的委托背压事件的逻辑,以在其后台堆栈中执行导航。

UPD#1
确保ViewPager中的根片段未被销毁,请使用setOffscreenPageLimit()

UPD#2 如果您不想使用ViewPager,则可以使用此answer中的代码来管理根片段。

答案 2 :(得分:0)

由于san's answer按预期显示了片段,但是每次更改片段时,都有一些bug两次在HomeFragment上调用onCreateView。另外,每次更改页面时,更改页面都会实例化新片段。
因此,我拿起code并修改了setPage,它的工作效果很好。

方法如下:

private void setPage(Page page) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    String tag = page.name();

    FragmentTransaction transaction = fragmentManager.beginTransaction();

    // detach everything
    for (Fragment fragment : fragmentManager.getFragments()) {
        transaction.detach(fragment);
    }

    // Retrieve fragment instance, if it was already created
    Fragment fragment = fragmentManager.findFragmentByTag(tag);
    if (fragment == null) { // If not, crate new instance and add it
        try {
            fragment = (Fragment) page.clazz.newInstance();
            transaction.add(R.id.frame, fragment, tag);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    } else { // otherwise just attach it
        transaction.attach(fragment);
    }
    transaction.commit();
}

onBackPressed实现

@Override
public void onBackPressed() {
    if (!getSupportFragmentManager().findFragmentByTag(Page.HOME.name()).isDetached()) {
        super.onBackPressed();
    } else {
        navigation.setSelectedItemId(R.id.navigation_home);
    }
}
相关问题