【问题标题】:How to copy YouTube's app navigation logic如何复制 YouTube 的应用导航逻辑
【发布时间】:2017-08-07 18:56:38
【问题描述】:

我想在我的应用中实现导航逻辑,就像在 Youtube 应用中一样。 (底部导航视图+片段管理)。我想要这个,因为这些片段很重,所以我希望它们被惰性初始化,然后存储在 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 应用中的工作原理吗?

【问题讨论】:

    标签: android android-fragments fragment-backstack


    【解决方案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();
        }
    }
    

    【讨论】:

    • 我接受了这个答案,因为它按预期显示了片段。但是有问题。每次我切换片段时 HomeFragment 都会创建两次。
    【解决方案2】:

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

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

    您还需要实现从活动到根片段的委派回压事件的逻辑,以在其回栈中执行导航。

    UPD #1
    确保根片段没有在 ViewPager 中被破坏,使用setOffscreenPageLimit()

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

    【讨论】:

    • ViewPager 是我目前的实现,我想改变它,因为它太重,一次加载 3 个片段。
    • @StachuBarański 如果在开始时加载 3 个片段太重,则在第一次加载时仅加载第一个片段,如果用户滑动查看其他片段,则会填充数据。
    • @hardartcore 我不能这样做,因为 ViewPager 需要加载所有 3 个片段,因此它们在滑动过程中是可见的。
    • @StachuBarański你可以做到,只要听setUserVisibleHint(boolean b),当b为真时触发内容加载(且未加载或加载,或在某些其他情况下)
    • 好的,很高兴知道 :) 无论如何,我选择了管理根片段,它工作得很好(片段只创建一次,onCreateView 只调用一次,一切都是惰性初始化的)。查看接受的答案。
    【解决方案3】:

    由于san's answer 按预期显示片段,但存在一些错误,每次更改片段时都会在 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);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多