Android重写FragmentTabHost来实现状态保存

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

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


FragmentTabHost工具类:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * Copyright (C) 2012 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.jwzhangjie.com;  
  18.   
  19. import java.util.ArrayList;  
  20.   
  21. import android.content.Context;  
  22. import android.content.res.TypedArray;  
  23. import android.os.Bundle;  
  24. import android.os.Parcel;  
  25. import android.os.Parcelable;  
  26. import android.support.v4.app.Fragment;  
  27. import android.support.v4.app.FragmentManager;  
  28. import android.support.v4.app.FragmentTransaction;  
  29. import android.util.AttributeSet;  
  30. import android.view.View;  
  31. import android.view.ViewGroup;  
  32. import android.widget.FrameLayout;  
  33. import android.widget.LinearLayout;  
  34. import android.widget.TabHost;  
  35. import android.widget.TabWidget;  
  36.   
  37. /** 
  38.  * Special TabHost that allows the use of {@link Fragment} objects for its tab 
  39.  * content. When placing this in a view hierarchy, after inflating the hierarchy 
  40.  * you must call {@link #setup(Context, FragmentManager, int)} to complete the 
  41.  * initialization of the tab host. 
  42.  *  
  43.  * <p> 
  44.  * Here is a simple example of using a FragmentTabHost in an Activity: 
  45.  *  
  46.  * {@sample 
  47.  * development/samples/Support4Demos/src/com/example/android/supportv4/app/ 
  48.  * FragmentTabs.java complete} 
  49.  *  
  50.  * <p> 
  51.  * This can also be used inside of a fragment through fragment nesting: 
  52.  *  
  53.  * {@sample 
  54.  * development/samples/Support4Demos/src/com/example/android/supportv4/app/ 
  55.  * FragmentTabsFragmentSupport.java complete} 
  56.  */  
  57. public class FragmentTabHost extends TabHost implements  
  58.         TabHost.OnTabChangeListener {  
  59.     private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();  
  60.     private FrameLayout mRealTabContent;  
  61.     private Context mContext;  
  62.     private FragmentManager mFragmentManager;  
  63.     private int mContainerId;  
  64.     private TabHost.OnTabChangeListener mOnTabChangeListener;  
  65.     private TabInfo mLastTab;  
  66.     private boolean mAttached;  
  67.   
  68.     static final class TabInfo {  
  69.         private final String tag;  
  70.         private final Class<?> clss;  
  71.         private final Bundle args;  
  72.         private Fragment fragment;  
  73.   
  74.         TabInfo(String _tag, Class<?> _class, Bundle _args) {  
  75.             tag = _tag;  
  76.             clss = _class;  
  77.             args = _args;  
  78.         }  
  79.     }  
  80.   
  81.     static class DummyTabFactory implements TabHost.TabContentFactory {  
  82.         private final Context mContext;  
  83.   
  84.         public DummyTabFactory(Context context) {  
  85.             mContext = context;  
  86.         }  
  87.   
  88.         @Override  
  89.         public View createTabContent(String tag) {  
  90.             View v = new View(mContext);  
  91.             v.setMinimumWidth(0);  
  92.             v.setMinimumHeight(0);  
  93.             return v;  
  94.         }  
  95.     }  
  96.   
  97.     static class SavedState extends BaseSavedState {  
  98.         String curTab;  
  99.   
  100.         SavedState(Parcelable superState) {  
  101.             super(superState);  
  102.         }  
  103.   
  104.         private SavedState(Parcel in) {  
  105.             super(in);  
  106.             curTab = in.readString();  
  107.         }  
  108.   
  109.         @Override  
  110.         public void writeToParcel(Parcel out, int flags) {  
  111.             super.writeToParcel(out, flags);  
  112.             out.writeString(curTab);  
  113.         }  
  114.   
  115.         @Override  
  116.         public String toString() {  
  117.             return "FragmentTabHost.SavedState{"  
  118.                     + Integer.toHexString(System.identityHashCode(this))  
  119.                     + " curTab=" + curTab + "}";  
  120.         }  
  121.   
  122.         public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {  
  123.             public SavedState createFromParcel(Parcel in) {  
  124.                 return new SavedState(in);  
  125.             }  
  126.   
  127.             public SavedState[] newArray(int size) {  
  128.                 return new SavedState[size];  
  129.             }  
  130.         };  
  131.     }  
  132.   
  133.     public FragmentTabHost(Context context) {  
  134.         // Note that we call through to the version that takes an AttributeSet,  
  135.         // because the simple Context construct can result in a broken object!  
  136.         super(context, null);  
  137.         initFragmentTabHost(context, null);  
  138.     }  
  139.   
  140.     public FragmentTabHost(Context context, AttributeSet attrs) {  
  141.         super(context, attrs);  
  142.         initFragmentTabHost(context, attrs);  
  143.     }  
  144.   
  145.     private void initFragmentTabHost(Context context, AttributeSet attrs) {  
  146.         TypedArray a = context.obtainStyledAttributes(attrs,  
  147.                 new int[] { android.R.attr.inflatedId }, 00);  
  148.         mContainerId = a.getResourceId(00);  
  149.         a.recycle();  
  150.   
  151.         super.setOnTabChangedListener(this);  
  152.     }  
  153.   
  154.     private void ensureHierarchy(Context context) {  
  155.         // If owner hasn't made its own view hierarchy, then as a convenience  
  156.         // we will construct a standard one here.  
  157.         if (findViewById(android.R.id.tabs) == null) {  
  158.             LinearLayout ll = new LinearLayout(context);  
  159.             ll.setOrientation(LinearLayout.VERTICAL);  
  160.             addView(ll, new FrameLayout.LayoutParams(  
  161.                     ViewGroup.LayoutParams.MATCH_PARENT,  
  162.                     ViewGroup.LayoutParams.MATCH_PARENT));  
  163.   
  164.             TabWidget tw = new TabWidget(context);  
  165.             tw.setId(android.R.id.tabs);  
  166.             tw.setOrientation(TabWidget.HORIZONTAL);  
  167.             ll.addView(tw, new LinearLayout.LayoutParams(  
  168.                     ViewGroup.LayoutParams.MATCH_PARENT,  
  169.                     ViewGroup.LayoutParams.WRAP_CONTENT, 0));  
  170.   
  171.             FrameLayout fl = new FrameLayout(context);  
  172.             fl.setId(android.R.id.tabcontent);  
  173.             ll.addView(fl, new LinearLayout.LayoutParams(000));  
  174.   
  175.             mRealTabContent = fl = new FrameLayout(context);  
  176.             mRealTabContent.setId(mContainerId);  
  177.             ll.addView(fl, new LinearLayout.LayoutParams(  
  178.                     LinearLayout.LayoutParams.MATCH_PARENT, 01));  
  179.         }  
  180.     }  
  181.   
  182.     /** 
  183.      * @deprecated Don't call the original TabHost setup, you must instead call 
  184.      *             {@link #setup(Context, FragmentManager)} or 
  185.      *             {@link #setup(Context, FragmentManager, int)}. 
  186.      */  
  187.     @Override  
  188.     @Deprecated  
  189.     public void setup() {  
  190.         throw new IllegalStateException(  
  191.                 "Must call setup() that takes a Context and FragmentManager");  
  192.     }  
  193.   
  194.     public void setup(Context context, FragmentManager manager) {  
  195.         ensureHierarchy(context); // Ensure views required by super.setup()  
  196.         super.setup();  
  197.         mContext = context;  
  198.         mFragmentManager = manager;  
  199.         ensureContent();  
  200.     }  
  201.   
  202.     public void setup(Context context, FragmentManager manager, int containerId) {  
  203.         ensureHierarchy(context); // Ensure views required by super.setup()  
  204.         super.setup();  
  205.         mContext = context;  
  206.         mFragmentManager = manager;  
  207.         mContainerId = containerId;  
  208.         ensureContent();  
  209.         mRealTabContent.setId(containerId);  
  210.   
  211.         // We must have an ID to be able to save/restore our state. If  
  212.         // the owner hasn't set one at this point, we will set it ourself.  
  213.         if (getId() == View.NO_ID) {  
  214.             setId(android.R.id.tabhost);  
  215.         }  
  216.     }  
  217.   
  218.     private void ensureContent() {  
  219.         if (mRealTabContent == null) {  
  220.             mRealTabContent = (FrameLayout) findViewById(mContainerId);  
  221.             if (mRealTabContent == null) {  
  222.                 throw new IllegalStateException(  
  223.                         "No tab content FrameLayout found for id "  
  224.                                 + mContainerId);  
  225.             }  
  226.         }  
  227.     }  
  228.   
  229.     @Override  
  230.     public void setOnTabChangedListener(OnTabChangeListener l) {  
  231.         mOnTabChangeListener = l;  
  232.     }  
  233.   
  234.     public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {  
  235.         tabSpec.setContent(new DummyTabFactory(mContext));  
  236.         String tag = tabSpec.getTag();  
  237.   
  238.         TabInfo info = new TabInfo(tag, clss, args);  
  239.   
  240.         if (mAttached) {  
  241.             // If we are already attached to the window, then check to make  
  242.             // sure this tab's fragment is inactive if it exists. This shouldn't  
  243.             // normally happen.  
  244.             info.fragment = mFragmentManager.findFragmentByTag(tag);  
  245.             if (info.fragment != null && !info.fragment.isDetached()) {  
  246.                 FragmentTransaction ft = mFragmentManager.beginTransaction();  
  247. //              ft.detach(info.fragment);  
  248.                 ft.hide(info.fragment);  
  249.                 ft.commit();  
  250.             }  
  251.         }  
  252.   
  253.         mTabs.add(info);  
  254.         addTab(tabSpec);  
  255.     }  
  256.   
  257.     @Override  
  258.     protected void onAttachedToWindow() {  
  259.         super.onAttachedToWindow();  
  260.   
  261.         String currentTab = getCurrentTabTag();  
  262.   
  263.         // Go through all tabs and make sure their fragments match  
  264.         // the correct state.  
  265.         FragmentTransaction ft = null;  
  266.         for (int i = 0; i < mTabs.size(); i++) {  
  267.             TabInfo tab = mTabs.get(i);  
  268.             tab.fragment = mFragmentManager.findFragmentByTag(tab.tag);  
  269. //          if (tab.fragment != null && !tab.fragment.isDetached()) {  
  270.             if (tab.fragment != null) {  
  271.                 if (tab.tag.equals(currentTab)) {  
  272.                     // The fragment for this tab is already there and  
  273.                     // active, and it is what we really want to have  
  274.                     // as the current tab. Nothing to do.  
  275.                     mLastTab = tab;  
  276.                 } else {  
  277.                     // This fragment was restored in the active state,  
  278.                     // but is not the current tab. Deactivate it.  
  279.                     if (ft == null) {  
  280.                         ft = mFragmentManager.beginTransaction();  
  281.                     }  
  282. //                  ft.detach(tab.fragment);  
  283.                     ft.hide(tab.fragment);  
  284.                 }  
  285.             }  
  286.         }  
  287.   
  288.         // We are now ready to go. Make sure we are switched to the  
  289.         // correct tab.  
  290.         mAttached = true;  
  291.         ft = doTabChanged(currentTab, ft);  
  292.         if (ft != null) {  
  293.             ft.commit();  
  294.             mFragmentManager.executePendingTransactions();  
  295.         }  
  296.     }  
  297.   
  298.     @Override  
  299.     protected void onDetachedFromWindow() {  
  300.         super.onDetachedFromWindow();  
  301.         mAttached = false;  
  302.     }  
  303.   
  304.     @Override  
  305.     protected Parcelable onSaveInstanceState() {  
  306.         Parcelable superState = super.onSaveInstanceState();  
  307.         SavedState ss = new SavedState(superState);  
  308.         ss.curTab = getCurrentTabTag();  
  309.         return ss;  
  310.     }  
  311.   
  312.     @Override  
  313.     protected void onRestoreInstanceState(Parcelable state) {  
  314.         SavedState ss = (SavedState) state;  
  315.         super.onRestoreInstanceState(ss.getSuperState());  
  316.         setCurrentTabByTag(ss.curTab);  
  317.     }  
  318.   
  319.     @Override  
  320.     public void onTabChanged(String tabId) {  
  321.         if (mAttached) {  
  322.             FragmentTransaction ft = doTabChanged(tabId, null);  
  323.             if (ft != null) {  
  324.                 ft.commit();  
  325.             }  
  326.         }  
  327.         if (mOnTabChangeListener != null) {  
  328.             mOnTabChangeListener.onTabChanged(tabId);  
  329.         }  
  330.     }  
  331.   
  332.     private FragmentTransaction doTabChanged(String tabId,  
  333.             FragmentTransaction ft) {  
  334.         TabInfo newTab = null;  
  335.         for (int i = 0; i < mTabs.size(); i++) {  
  336.             TabInfo tab = mTabs.get(i);  
  337.             if (tab.tag.equals(tabId)) {  
  338.                 newTab = tab;  
  339.             }  
  340.         }  
  341.         if (newTab == null) {  
  342.             throw new IllegalStateException("No tab known for tag " + tabId);  
  343.         }  
  344.         if (mLastTab != newTab) {  
  345.             if (ft == null) {  
  346.                 ft = mFragmentManager.beginTransaction();  
  347.             }  
  348.             if (mLastTab != null) {  
  349.                 if (mLastTab.fragment != null) {  
  350. //                  ft.detach(mLastTab.fragment);  
  351.                     ft.hide(mLastTab.fragment);  
  352.                 }  
  353.             }  
  354.             if (newTab != null) {  
  355.                 if (newTab.fragment == null) {  
  356.                     newTab.fragment = Fragment.instantiate(mContext,  
  357.                             newTab.clss.getName(), newTab.args);  
  358.                     ft.add(mContainerId, newTab.fragment, newTab.tag);  
  359.                 } else {  
  360. //                  ft.attach(newTab.fragment);  
  361.                     ft.show(newTab.fragment);  
  362.                 }  
  363.             }  
  364.   
  365.             mLastTab = newTab;  
  366.         }  
  367.         return ft;  
  368.     }  
  369. }  



xml布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <FrameLayout
        android:id="@+id/realtabcontent"
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1" />

    <com.jwzhangjie.com.FragmentTabHost
        android:id="@android:id/tabhost"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:background="@drawable/maintab_toolbar_bg">

        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="0" />            
    </com.jwzhangjie.com.FragmentTabHost>

</LinearLayout>




实现类:
package com.example.lin_tab_demo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TabHost;
import android.widget.TextView;

public class MainActivity extends FragmentActivity {
	private LayoutInflater layoutinflater;
	// 定义数组来存放Fragment界面
	private Class mfragmentArray[] = { yi_fragment.class, er_fragment.class,san_fragment.class };
	// 定义栏目的名称
	private String mtextviewArray[] = { "首页", "军事", "科技" };
	//定义关闭按钮的图片
	private int off_mimageArray[]={R.drawable.yi_off,R.drawable.er_off,R.drawable.san_off};
	//定义开启按钮的图片
	private int on_mimageArray[]={R.drawable.yi_on,R.drawable.er_on,R.drawable.san_on};
	FragmentTabHost tabhost;
	private List<Map<String, View>> tabViews = new ArrayList<Map<String, View>>();
	
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initview();
	}

	private void initview() {
		//layoutinflater.inflate(R.layout.item_layout, null);
		tabhost=(FragmentTabHost) findViewById(android.R.id.tabhost);
		//初始化tabhost
		tabhost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
		//给每一个tab添加视图(图片文字) 还有对应的fragment
		for (int i = 0; i < mtextviewArray.length; i++) {
			tabhost.addTab(tabhost.newTabSpec(i+"").setIndicator(createTab(i)),mfragmentArray[i],null);
		}
		// 设置Tab按钮的背景
		tabhost.getTabWidget().setDividerDrawable(R.color.white);
		// 设置Tab按钮的默认选项
		tabhost.setCurrentTab(0);
		// 设置Tab按钮的点击事件
		tabhost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
	            @Override
	            public void onTabChanged(String tabId) {
	                int index = Integer.parseInt(tabId);
	                setTabSelectedState(index, mfragmentArray.length);
	            }
	        });
		setTabSelectedState(0,3);
	}
	
	   //设置每个tab的样式
	   private View createTab(int id) {
	        View tabIndicator = LayoutInflater.from(this).inflate(R.layout.item_layout, null);
	        TextView normal_tv = (TextView) tabIndicator.findViewById(R.id.normal_tv);
	        TextView selected_tv = (TextView) tabIndicator.findViewById(R.id.selected_tv);
	        normal_tv.setText(mtextviewArray[id]);
	        selected_tv.setText(mtextviewArray[id]);
	        ImageView normal_iv = (ImageView) tabIndicator.findViewById(R.id.normal_iv);
	        ImageView selected_iv = (ImageView) tabIndicator.findViewById(R.id.selected_iv);
	        normal_iv.setImageResource(off_mimageArray[id]);
	        selected_iv.setImageResource(on_mimageArray[id]);

	        View normal_layout = tabIndicator.findViewById(R.id.normal_layout);
	        normal_layout.setAlpha(1f);
	        View selected_layout = tabIndicator.findViewById(R.id.selected_layout);
	        selected_layout.setAlpha(0f);

	        Map<String, View> map = new HashMap<String, View>();
	        map.put("ALPHA_NORMAL", normal_layout);
	        map.put("ALPHA_SELECTED", selected_layout);
	        tabViews.add(map);
	        return tabIndicator;
	    }
	   	//设置每个Tab点击样式切换
	    private void setTabSelectedState(int index, int tabCount) {
	        for (int i = 0; i < tabCount; i++) {
	            if (i == index) {
	                tabViews.get(i).get("ALPHA_NORMAL").setAlpha(0f);
	                tabViews.get(i).get("ALPHA_SELECTED").setAlpha(1f);
	            } else {
	                tabViews.get(i).get("ALPHA_NORMAL").setAlpha(1f);
	                tabViews.get(i).get("ALPHA_SELECTED").setAlpha(0f);
	            }
	        }
	    }

}


源码地址:http://download.csdn.net/download/jwzhangjie/7561781
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值