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