Android FragmentTabHost - Not fully baked yet?

前端 未结 4 444
南笙
南笙 2020-12-10 02:21

I wanted to see if anyone has had success with customization of tabs using FragmentTabHost that comes with the new Android API level 17.

I was excited to be able to

4条回答
  •  春和景丽
    2020-12-10 02:44

    I'd like to mention some more issues with FragmentTabHost. I'm using a ViewPager where each page (View) contains a FragmenTabHost and I had to overcome several problems:

    1) FragmentTabHost assumes that it's the only FragmentTabHost in its parent FragmentManager (2nd argument to FragmentTabHost.setup()). This causes the rest of the problems...

    2) the "tags" you provide when calling addTab() are passed straight through to the FragmentManager, so if you just use hardcoded tags for all your pages (a perfectly reasonable thing to do) your first page will create tab fragments while every other page will reuse those tabs. Yes, page 2 controls page 1...

    Solution is to generate unique tag names. I appended the page number to the hardcoded strings:

    public Object instantiateItem( ViewGroup container, int position )
    {
        ...
        tabHost.addTab( tabHost.newTabSpec( "tab1_" + position ) ...);
        tabHost.addTab( tabHost.newTabSpec( "tab2_" + position ) ...);
        tabHost.addTab( tabHost.newTabSpec( "tab3_" + position ) ...);
        ...
    }
    

    3) All tab fragments get placed in a container identified only by "view id" (the 3rd argument to FragmentTabHost.setup()). This means that when the FragmentManager resolves the viewId to a View, it always finds the first instance (from the first page). All your other pages are ignored.

    Solution to this is to assign unique ids to your "tab content" views, for example:

    public Object instantiateItem( ViewGroup container, int position )
    {
        View view = m_inflater.inflate(R.layout.page, null);
    
        View tabContent = view.findViewById(R.id.realtabcontent);
        tabContent.setId(m_nextViewId);
        m_nextViewId++;
    
        MyFragmentTabHost tabHost = (MyFragmentTabHost) view.findViewById(android.R.id.tabhost);
        tabHost.setup(m_activity, m_activity.getSupportFragmentManager(), tabContent.getId());
        ...
    }
    

    4) It doesn't remove tab fragments when destroyed. While the ViewPager destroys unused Views as you swipe, the FragmentTabHosts contained within those views "leak" the tab fragments. When the ViewPager re-instantiates a previously seen page (using previously used tags), FragmentTabHost will notice that the fragments for those tabs already exist and simply reattach them. This blows up because the fragments point to views that have been destroyed by the ViewPager.

    The solution is to remove fragments when FragmentTabHost is destroyed. You'll want to add this code to onDetachedFromWindow() in your local copy of FragmentTabHost.java

    class MyFragmentTabHost
    {
        ...
    
        protected void onDetachedFromWindow()
        {
            super.onDetachedFromWindow();
            mAttached = false;
    
            boolean removeFragments = false;
            if( mContext instanceof Activity )
            {
                Activity activity = (Activity)mContext;
                removeFragments = !activity.isDestroyed();
            }
    
            if( removeFragments )
            {
                FragmentTransaction ft = null;
                for (int i = 0; i < mTabs.size(); i++)
                {
                    TabInfo tab = mTabs.get(i);
                    if (tab.fragment != null)
                    {
                        if (ft == null)
                        {
                            ft = mFragmentManager.beginTransaction();
                        }
                        ft.remove(tab.fragment);
                    }
                }
    
                if (ft != null)
                {
                    ft.commit();
                    mFragmentManager.executePendingTransactions();
                }
            }
        }
    

    You could probably also work around these issues by using a FragmentPagerAdapter or FragmentStatePagerAdapter (makes Fragments) instead of a standard PagerAdapter (makes Views). Then you'd call FragmentTabHost.setup( ... fragment.getChildFragmentManager() ... ).

提交回复
热议问题