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;

}

}

时间: 2024-11-01 19:45:53

Android重写FragmentTabHost来实现状态保存的相关文章

Android -- TabHost、Fragment、状态保存、通信

工程结构                                                                                       TabAFm到TabEFm都是Fragment,并且每个Fragment对应一个布局文件. TabAFm.java                                                                             package com.yydcdut.tabho

Android -- ViewPager、Fragment、状态保存、通信

工程架构                                                                                      TabAFm到TabEFm都是Fragment,并且每个Fragment对应一个布局文件. TabAFm.java                                                                            package com.yydcdut.viewpag

android fragment+ FragmentTabHost+viewpager 切换状态不保存的问题

转载请注明出处:http://blog.csdn.net/djy1992/article/details/46674169 @author dujinyang 难得有时间上来写博客. fragment虽然好用,但个人还是觉得有利有弊. 网上大都说切换状态,有挺多问题, 有很多种解决方案. 第一种常常遇到的情况: if(rootView == null) rootView = inflater.inflate(R.layout.fragment_home, container,false); Vi

Android基础部分再学习---activity的状态保存

主要是bundle   这个參数 參考地址:http://blog.csdn.net/lonelyroamer/article/details/18715975 学习Activity的生命周期,我们知道,当Activity进入到paused或者stopped状态后,这个Activity的状态仍然保存着. 由于这个Activity对象仍然保存在内存中.它的全部信息和状态仍然是存在的.当这个Activity再次返回到前台是,它仍然保持着离开时候的样子. 然而.假设Activity进入到了后台.系统为

Android View状态保存

说到状态保存,就不得不提到Activity的onSaveInstanceState()方法,这个是大家经常用到的一个函数,就是当我们的Activity被置为后台,当我们再次进入这个Activity的时候,这个Activity需要被恢复,并且回调这个方法. 下面来看看这个方法 private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState"; protected void onSaveInsta

Android Fragment使用(三) Activity, Fragment, WebView的状态保存和恢复

Android中的状态保存和恢复 Android中的状态保存和恢复, 包括Activity和Fragment以及其中View的状态处理. Activity的状态除了其中的View和Fragment的状态之外, 还需要用户手动保存一些成员变量. Fragment的状态有它自己的实例状态和其中的View状态, 因为其生命周期的灵活性和实际需要的不同, 情况会多一些. 根据源码, 列出了Fragment中实例状态和View状态保存和恢复的几个入口, 便于分析查看. 最后专门讲了WebView状态保存和

Activity中使用注解进行状态保存

转载请注明出处:http://blog.csdn.net/allen315410/article/details/43567229 问题描述 一般开发中,当调用Activity生命周期方法onPause()和onStop()方法后,Activity的实例并没有被直接销毁,它仍然保存在内存中,Activity里面所有的信息和状态数据都将保存下来,当这个Activity重新回到前台的时候,所有的数据都会得到保留并且可被使用. 但是在一些特殊情况下,例如设备上装载了"XX大师""X

Android Fragment FragmentTabHost问题

今天需要做一个功能,实现tab切换功能,但是又不能向viewpager一样可以滑动,只能通过顶部的tab标签滑动,就是类似ActionBar的tab一样的切换. 然后我就去找例子,在ApiDemos中有FragmentTabs(extends Activity),FragmentTabsFragment(extends Fragment),两个例子,特别说一下FragmentTabsFragment,这个类中的TabManager是重写了FragmentTabHost,自己实现的状态保存等等,值

Activity生命周期与状态保存

弹出系统对话框,程序仍部分可见 onPause 对话框消失时 onResume 调用一个新的Activity,老的Activity不可见时 onPause->onStop 从新的Activity返回时 onRestart->onStart->onResume 正常运行的Activity正常结束时 onPause->onStop->onDestroy onResume 这不是用来判断Activity是否对用户可见的最好方法.即使这个方法被调用后,也可能有一些系统窗口在我们的Ac