| /* |
| * 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 android.support.v13.app; |
| |
| import java.util.ArrayList; |
| |
| import android.app.Fragment; |
| import android.app.FragmentManager; |
| import android.app.FragmentTransaction; |
| import android.content.Context; |
| import android.content.res.TypedArray; |
| import android.os.Bundle; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| 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; |
| |
| /** |
| * Version of {@link android.support.v4.app.FragmentTabHost} that can be |
| * used with the platform {@link android.app.Fragment} APIs. You will not |
| * normally use this, instead using action bar tabs. |
| */ |
| 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.FILL_PARENT, |
| ViewGroup.LayoutParams.FILL_PARENT)); |
| |
| TabWidget tw = new TabWidget(context); |
| tw.setId(android.R.id.tabs); |
| tw.setOrientation(TabWidget.HORIZONTAL); |
| ll.addView(tw, new LinearLayout.LayoutParams( |
| ViewGroup.LayoutParams.FILL_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.FILL_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.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.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); |
| } |
| } |
| } |
| |
| // 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); |
| } |
| } |
| 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); |
| } |
| } |
| |
| mLastTab = newTab; |
| } |
| return ft; |
| } |
| } |