Android重写FragmentTabHost来实现状态保存

分类: android 2014-06-27 17:57 2077人阅读 评论(0) 收藏 举报

FragmentTabHost

       最近要做一个类似QQ底部有气泡的功能,试了几个方案不太好,我想很多开发者使用TabHost都会知道它不保存状态,每次都要重新加载布局,为了保存状态,使用RadioGroup来实现,状态是可以保存了,问题是无法实现气泡功能,不能自定义布局,因为RadioGroup里面只能包含RadioButton,不然状态切换不起用作,这个可以查看RadioGroup源码,为了既能保存状态又能实现气泡功能,所以只能自己修改控件了或者自己写一个类似的切换功能,查看了FragmentTabHost的源码,可以知道FragmentTabHost不保存状态是因为切换fragment的时候是使用detach和attach来Fragment的隐藏和显示的,这样的话每次切换肯定要重新加载布局,处理使用detach和attach,我们还可以使用show和hide来实现显示和隐藏,这样可以保存状态,方案出来了就是修改FragmentTabHost源码将切换Fragment的方式detach和attach改为hide和show。

下面就是修改后的FragmentTabHost的源码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片

/* 

 * 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. 

 */  

  

package com.jwzhangjie.com;  

  

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;  

    }  

}  


你可能感兴趣的:(Android重写FragmentTabHost来实现状态保存)