美文网首页
FragmentTabHost实现App主界面

FragmentTabHost实现App主界面

作者: A_Coder | 来源:发表于2016-11-09 19:49 被阅读0次

    • Android重写FragmentTabHost源码来实现状态保存
      FragmentTabHost在切换Fragment的时候是使用detach()和attach()来实现Fragment的隐藏和显示的,这样的话每次切换肯定要重新加载布局,除了使用detach()和attach(),我们还可以使用show()和hide()来实现显示和隐藏,这样可以保存状态,方案出来了就是修改FragmentTabHost源码将切换Fragment的方式detach()和attach()改为hide()和show()。
    /* 
     * Copyright (C) 2012 The Android Open Source Project 
     * 
     * Licensed under the Apache License, Version 2.0 (the "License"); 
     * you may not use this file except in compliance with the License. 
     * You may obtain a copy of the License at 
     * 
     *      http://www.apache.org/licenses/LICENSE-2.0 
     * 
     * Unless required by applicable law or agreed to in writing, software 
     * distributed under the License is distributed on an "AS IS" BASIS, 
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
     * See the License for the specific language governing permissions and 
     * limitations under the License. 
     */  
      
    import java.util.ArrayList;  
      
    import android.content.Context;  
    import android.content.res.TypedArray;  
    import android.os.Bundle;  
    import android.os.Parcel;  
    import android.os.Parcelable;  
    import android.support.v4.app.Fragment;  
    import android.support.v4.app.FragmentManager;  
    import android.support.v4.app.FragmentTransaction;  
    import android.util.AttributeSet;  
    import android.view.View;  
    import android.view.ViewGroup;  
    import android.widget.FrameLayout;  
    import android.widget.LinearLayout;  
    import android.widget.TabHost;  
    import android.widget.TabWidget;  
      
    /** 
     * Special TabHost that allows the use of {@link Fragment} objects for its tab 
     * content. When placing this in a view hierarchy, after inflating the hierarchy 
     * you must call {@link #setup(Context, FragmentManager, int)} to complete the 
     * initialization of the tab host. 
     *  
     * <p> 
     * Here is a simple example of using a FragmentTabHost in an Activity: 
     *  
     * {@sample 
     * development/samples/Support4Demos/src/com/example/android/supportv4/app/ 
     * FragmentTabs.java complete} 
     *  
     * <p> 
     * This can also be used inside of a fragment through fragment nesting: 
     *  
     * {@sample 
     * development/samples/Support4Demos/src/com/example/android/supportv4/app/ 
     * FragmentTabsFragmentSupport.java complete} 
     */  
    public class FragmentTabHost extends TabHost implements  
            TabHost.OnTabChangeListener {  
        private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();  
        private FrameLayout mRealTabContent;  
        private Context mContext;  
        private FragmentManager mFragmentManager;  
        private int mContainerId;  
        private TabHost.OnTabChangeListener mOnTabChangeListener;  
        private TabInfo mLastTab;  
        private boolean mAttached;  
      
        static final class TabInfo {  
            private final String tag;  
            private final Class<?> clss;  
            private final Bundle args;  
            private Fragment fragment;  
      
            TabInfo(String _tag, Class<?> _class, Bundle _args) {  
                tag = _tag;  
                clss = _class;  
                args = _args;  
            }  
        }  
      
        static class DummyTabFactory implements TabHost.TabContentFactory {  
            private final Context mContext;  
      
            public DummyTabFactory(Context context) {  
                mContext = context;  
            }  
      
            @Override  
            public View createTabContent(String tag) {  
                View v = new View(mContext);  
                v.setMinimumWidth(0);  
                v.setMinimumHeight(0);  
                return v;  
            }  
        }  
      
        static class SavedState extends BaseSavedState {  
            String curTab;  
      
            SavedState(Parcelable superState) {  
                super(superState);  
            }  
      
            private SavedState(Parcel in) {  
                super(in);  
                curTab = in.readString();  
            }  
      
            @Override  
            public void writeToParcel(Parcel out, int flags) {  
                super.writeToParcel(out, flags);  
                out.writeString(curTab);  
            }  
      
            @Override  
            public String toString() {  
                return "FragmentTabHost.SavedState{"  
                        + Integer.toHexString(System.identityHashCode(this))  
                        + " curTab=" + curTab + "}";  
            }  
      
            public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {  
                public SavedState createFromParcel(Parcel in) {  
                    return new SavedState(in);  
                }  
      
                public SavedState[] newArray(int size) {  
                    return new SavedState[size];  
                }  
            };  
        }  
      
        public FragmentTabHost(Context context) {  
            // Note that we call through to the version that takes an AttributeSet,  
            // because the simple Context construct can result in a broken object!  
            super(context, null);  
            initFragmentTabHost(context, null);  
        }  
      
        public FragmentTabHost(Context context, AttributeSet attrs) {  
            super(context, attrs);  
            initFragmentTabHost(context, attrs);  
        }  
      
        private void initFragmentTabHost(Context context, AttributeSet attrs) {  
            TypedArray a = context.obtainStyledAttributes(attrs,  
                    new int[] { android.R.attr.inflatedId }, 0, 0);  
            mContainerId = a.getResourceId(0, 0);  
            a.recycle();  
      
            super.setOnTabChangedListener(this);  
        }  
      
        private void ensureHierarchy(Context context) {  
            // If owner hasn't made its own view hierarchy, then as a convenience  
            // we will construct a standard one here.  
            if (findViewById(android.R.id.tabs) == null) {  
                LinearLayout ll = new LinearLayout(context);  
                ll.setOrientation(LinearLayout.VERTICAL);  
                addView(ll, new FrameLayout.LayoutParams(  
                        ViewGroup.LayoutParams.MATCH_PARENT,  
                        ViewGroup.LayoutParams.MATCH_PARENT));  
      
                TabWidget tw = new TabWidget(context);  
                tw.setId(android.R.id.tabs);  
                tw.setOrientation(TabWidget.HORIZONTAL);  
                ll.addView(tw, new LinearLayout.LayoutParams(  
                        ViewGroup.LayoutParams.MATCH_PARENT,  
                        ViewGroup.LayoutParams.WRAP_CONTENT, 0));  
      
                FrameLayout fl = new FrameLayout(context);  
                fl.setId(android.R.id.tabcontent);  
                ll.addView(fl, new LinearLayout.LayoutParams(0, 0, 0));  
      
                mRealTabContent = fl = new FrameLayout(context);  
                mRealTabContent.setId(mContainerId);  
                ll.addView(fl, new LinearLayout.LayoutParams(  
                        LinearLayout.LayoutParams.MATCH_PARENT, 0, 1));  
            }  
        }  
      
        /** 
         * @deprecated Don't call the original TabHost setup, you must instead call 
         *             {@link #setup(Context, FragmentManager)} or 
         *             {@link #setup(Context, FragmentManager, int)}. 
         */  
        @Override  
        @Deprecated  
        public void setup() {  
            throw new IllegalStateException(  
                    "Must call setup() that takes a Context and FragmentManager");  
        }  
      
        public void setup(Context context, FragmentManager manager) {  
            ensureHierarchy(context); // Ensure views required by super.setup()  
            super.setup();  
            mContext = context;  
            mFragmentManager = manager;  
            ensureContent();  
        }  
      
        public void setup(Context context, FragmentManager manager, int containerId) {  
            ensureHierarchy(context); // Ensure views required by super.setup()  
            super.setup();  
            mContext = context;  
            mFragmentManager = manager;  
            mContainerId = containerId;  
            ensureContent();  
            mRealTabContent.setId(containerId);  
      
            // We must have an ID to be able to save/restore our state. If  
            // the owner hasn't set one at this point, we will set it ourself.  
            if (getId() == View.NO_ID) {  
                setId(android.R.id.tabhost);  
            }  
        }  
      
        private void ensureContent() {  
            if (mRealTabContent == null) {  
                mRealTabContent = (FrameLayout) findViewById(mContainerId);  
                if (mRealTabContent == null) {  
                    throw new IllegalStateException(  
                            "No tab content FrameLayout found for id "  
                                    + mContainerId);  
                }  
            }  
        }  
      
        @Override  
        public void setOnTabChangedListener(OnTabChangeListener l) {  
            mOnTabChangeListener = l;  
        }  
      
        public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {  
            tabSpec.setContent(new DummyTabFactory(mContext));  
            String tag = tabSpec.getTag();  
      
            TabInfo info = new TabInfo(tag, clss, args);  
      
            if (mAttached) {  
                // If we are already attached to the window, then check to make  
                // sure this tab's fragment is inactive if it exists. This shouldn't  
                // normally happen.  
                info.fragment = mFragmentManager.findFragmentByTag(tag);  
                if (info.fragment != null && !info.fragment.isDetached()) {  
                    FragmentTransaction ft = mFragmentManager.beginTransaction();  
    //              ft.detach(info.fragment);  
                    ft.hide(info.fragment);  
                    ft.commit();  
                }  
            }  
      
            mTabs.add(info);  
            addTab(tabSpec);  
        }  
      
        @Override  
        protected void onAttachedToWindow() {  
            super.onAttachedToWindow();  
      
            String currentTab = getCurrentTabTag();  
      
            // Go through all tabs and make sure their fragments match  
            // the correct state.  
            FragmentTransaction ft = null;  
            for (int i = 0; i < mTabs.size(); i++) {  
                TabInfo tab = mTabs.get(i);  
                tab.fragment = mFragmentManager.findFragmentByTag(tab.tag);  
    //          if (tab.fragment != null && !tab.fragment.isDetached()) {  
                if (tab.fragment != null) {  
                    if (tab.tag.equals(currentTab)) {  
                        // The fragment for this tab is already there and  
                        // active, and it is what we really want to have  
                        // as the current tab. Nothing to do.  
                        mLastTab = tab;  
                    } else {  
                        // This fragment was restored in the active state,  
                        // but is not the current tab. Deactivate it.  
                        if (ft == null) {  
                            ft = mFragmentManager.beginTransaction();  
                        }  
    //                  ft.detach(tab.fragment);  
                        ft.hide(tab.fragment);  
                    }  
                }  
            }  
      
            // We are now ready to go. Make sure we are switched to the  
            // correct tab.  
            mAttached = true;  
            ft = doTabChanged(currentTab, ft);  
            if (ft != null) {  
                ft.commit();  
                mFragmentManager.executePendingTransactions();  
            }  
        }  
      
        @Override  
        protected void onDetachedFromWindow() {  
            super.onDetachedFromWindow();  
            mAttached = false;  
        }  
      
        @Override  
        protected Parcelable onSaveInstanceState() {  
            Parcelable superState = super.onSaveInstanceState();  
            SavedState ss = new SavedState(superState);  
            ss.curTab = getCurrentTabTag();  
            return ss;  
        }  
      
        @Override  
        protected void onRestoreInstanceState(Parcelable state) {  
            SavedState ss = (SavedState) state;  
            super.onRestoreInstanceState(ss.getSuperState());  
            setCurrentTabByTag(ss.curTab);  
        }  
      
        @Override  
        public void onTabChanged(String tabId) {  
            if (mAttached) {  
                FragmentTransaction ft = doTabChanged(tabId, null);  
                if (ft != null) {  
                    ft.commit();  
                }  
            }  
            if (mOnTabChangeListener != null) {  
                mOnTabChangeListener.onTabChanged(tabId);  
            }  
        }  
      
        private FragmentTransaction doTabChanged(String tabId,  
                FragmentTransaction ft) {  
            TabInfo newTab = null;  
            for (int i = 0; i < mTabs.size(); i++) {  
                TabInfo tab = mTabs.get(i);  
                if (tab.tag.equals(tabId)) {  
                    newTab = tab;  
                }  
            }  
            if (newTab == null) {  
                throw new IllegalStateException("No tab known for tag " + tabId);  
            }  
            if (mLastTab != newTab) {  
                if (ft == null) {  
                    ft = mFragmentManager.beginTransaction();  
                }  
                if (mLastTab != null) {  
                    if (mLastTab.fragment != null) {  
    //                  ft.detach(mLastTab.fragment);  
                        ft.hide(mLastTab.fragment);  
                    }  
                }  
                if (newTab != null) {  
                    if (newTab.fragment == null) {  
                        newTab.fragment = Fragment.instantiate(mContext,  
                                newTab.clss.getName(), newTab.args);  
                        ft.add(mContainerId, newTab.fragment, newTab.tag);  
                    } else {  
    //                  ft.attach(newTab.fragment);  
                        ft.show(newTab.fragment);  
                    }  
                }  
      
                mLastTab = newTab;  
            }  
            return ft;  
        }  
    }  
    
    FragmentTabHost.png
    • 在主界面布局activity_main.xml上添加FragmentTabHost:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="news.huoren.com.cnshop.MainActivity">
    
        <!--此FrameLayout是真正装载Fragment的控件-->
       <FrameLayout
           android:id="@+id/realtabcontent"
           android:layout_width="match_parent"
           android:layout_height="0dp"
           android:layout_weight="1"
           android:background="@color/bg_color"
           ></FrameLayout>
    
        <news.huoren.com.cnshop.widget.FragmentTabHost
            android:id="@android:id/tabhost" <!--id必须为@android:id/tabhost-->
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
            <!--官方要求此FrameLayout这样设置,id为@android:id/tabcontent-->
            <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_weight="0"></FrameLayout>
        </news.huoren.com.cnshop.widget.FragmentTabHost>
    </LinearLayout>
    
    • 然后编写MainActivity:
    package news.huoren.com.cnshop;
    
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.TabHost;
    import android.widget.TextView;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import news.huoren.com.cnshop.bean.Tab;
    import news.huoren.com.cnshop.fragment.CartFragment;
    import news.huoren.com.cnshop.fragment.CategoryFragment;
    import news.huoren.com.cnshop.fragment.HomeFragment;
    import news.huoren.com.cnshop.fragment.HotFragment;
    import news.huoren.com.cnshop.fragment.MineFragment;
    import news.huoren.com.cnshop.widget.FragmentTabHost;
    
    public class MainActivity extends AppCompatActivity {
    
        private LayoutInflater inflater;
        private FragmentTabHost mTabHost;
        private List<Tab> mTabList = new ArrayList<>(5);
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            initTab();
        }
    
        private void initTab() {
    
            Tab tab_home = new Tab(R.drawable.selector_icon_home, R.string.home, HomeFragment.class);
            Tab tab_hot = new Tab(R.drawable.selector_icon_hot, R.string.hot, HotFragment.class);
            Tab tab_category = new Tab(R.drawable.selector_icon_category, R.string.category, CategoryFragment.class);
            Tab tab_cart = new Tab(R.drawable.selector_icon_cart, R.string.cart, CartFragment.class);
            Tab tab_mine = new Tab(R.drawable.selector_icon_mine, R.string.mine, MineFragment.class);
    
            mTabList.add(tab_home);
            mTabList.add(tab_hot);
            mTabList.add(tab_category);
            mTabList.add(tab_cart);
            mTabList.add(tab_mine);
    
    
            //初始化inflater
            inflater = LayoutInflater.from(this);
            mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
            mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
    
    
            for (Tab tab : mTabList){
                TabHost.TabSpec tabspec = mTabHost.newTabSpec(getString(tab.getTitle()));
                tabspec.setIndicator(buildIndicator(tab));
                mTabHost.addTab(tabspec, tab.getFragment(), null);
            }
    
            mTabHost.getTabWidget().setShowDividers(LinearLayout.SHOW_DIVIDER_NONE);
            mTabHost.setCurrentTab(0);
    
    
        }
    
        private View buildIndicator(Tab tab){
            View view = inflater.inflate(R.layout.tab_indicator, null);
            <!--view的findViewById-->
            ImageView tab_img = (ImageView) view.findViewById(R.id.icon_tab);
            TextView tab_title = (TextView) view.findViewById(R.id.txt_indicator);
    
    
            tab_img.setBackgroundResource(tab.getIcon());
            tab_title.setText(tab.getTitle());
    
            return view;
        }
    }
    
    • 上述代码使用了一个Tab的bean类,用来存放底部栏Tab的图标和文字,以及五个Fragment:
    package news.huoren.com.cnshop.bean;
    
    public class Tab {
        private int title;
        private int icon;
        private Class fragment;
    
        public Tab(int icon, int title, Class fragment) {
            this.title = title;
            this.icon = icon;
            this.fragment = fragment;
        }
    
        public int getTitle() {
            return title;
        }
    
        public void setTitle(int title) {
            this.title = title;
        }
    
        public int getIcon() {
            return icon;
        }
    
        public void setIcon(int icon) {
            this.icon = icon;
        }
    
        public Class getFragment() {
            return fragment;
        }
    
        public void setFragment(Class fragment) {
            this.fragment = fragment;
        }
    }
    package news.huoren.com.cnshop.bean;
    
    public class Tab {
        private int title;
        private int icon;
        private Class fragment;
    
        public Tab(int icon, int title, Class fragment) {
            this.title = title;
            this.icon = icon;
            this.fragment = fragment;
        }
    
        public int getTitle() {
            return title;
        }
    
        public void setTitle(int title) {
            this.title = title;
        }
    
        public int getIcon() {
            return icon;
        }
    
        public void setIcon(int icon) {
            this.icon = icon;
        }
    
        public Class getFragment() {
            return fragment;
        }
    
        public void setFragment(Class fragment) {
            this.fragment = fragment;
        }
    }
    
    • 还用到了selector设置图标和文字的切换变色:
      其中的一个图标的切换selector_icon_home.xml:
    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    
        <!-- 没获取到焦点状态 -->
        <item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@mipmap/icon_home" />
        <item android:state_focused="false" android:state_selected="true" android:state_pressed="false" android:drawable="@mipmap/icon_home_press" />
        <!-- 获取到焦点状态 -->
        <item android:state_focused="true" android:state_selected="false" android:state_pressed="false" android:drawable="@mipmap/icon_home_press" />
        <item android:state_focused="true" android:state_selected="true" android:state_pressed="false" android:drawable="@mipmap/icon_home_press" />
        <!-- 按压状态 -->
        <item android:state_selected="true" android:state_pressed="true" android:drawable="@mipmap/icon_home_press" />
        <item android:state_pressed="true" android:drawable="@mipmap/icon_home_press" />
    
    </selector>
    

    Tab的文字颜色变换:

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_selected="true" android:color="#eb4f38" />
        <item android:state_active="true" android:color="#eb4f38"/>
        <item android:state_selected="false" android:color="#a9b7b7" />
        <item android:state_active="false" android:color="#a9b7b7"/>
    </selector>
    

    相关文章

      网友评论

          本文标题:FragmentTabHost实现App主界面

          本文链接:https://www.haomeiwen.com/subject/vpqzuttx.html