ActionBar tab mode using fragments; needs styles.

Change-Id: I8f931dae447e7b64de827d3c17766e5f8ad794e2
This commit is contained in:
Adam Powell
2010-07-02 10:09:44 -07:00
parent 31a4dbf5ca
commit 661c908e4e
5 changed files with 721 additions and 7 deletions

View File

@@ -19457,6 +19457,19 @@
visibility="public"
>
</constructor>
<method name="addTab"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="tab" type="android.app.ActionBar.Tab">
</parameter>
</method>
<method name="finishContextMode"
return="void"
abstract="true"
@@ -19523,6 +19536,84 @@
visibility="public"
>
</method>
<method name="insertTab"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="tab" type="android.app.ActionBar.Tab">
</parameter>
<parameter name="position" type="int">
</parameter>
</method>
<method name="newTab"
return="android.app.ActionBar.Tab"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="removeTab"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="tab" type="android.app.ActionBar.Tab">
</parameter>
</method>
<method name="removeTabAt"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="position" type="int">
</parameter>
</method>
<method name="selectTab"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="tab" type="android.app.ActionBar.Tab">
</parameter>
</method>
<method name="selectTabAt"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="position" type="int">
</parameter>
</method>
<method name="setBackgroundDrawable"
return="void"
abstract="true"
@@ -19644,6 +19735,30 @@
<parameter name="subtitle" type="java.lang.CharSequence">
</parameter>
</method>
<method name="setTabNavigationMode"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="setTabNavigationMode"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="containerViewId" type="int">
</parameter>
</method>
<method name="setTitle"
return="void"
abstract="true"
@@ -19915,6 +20030,128 @@
</parameter>
</method>
</interface>
<class name="ActionBar.Tab"
extends="java.lang.Object"
abstract="true"
static="true"
final="false"
deprecated="not deprecated"
visibility="public"
>
<constructor name="ActionBar.Tab"
type="android.app.ActionBar.Tab"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</constructor>
<method name="getFragment"
return="android.app.Fragment"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getIcon"
return="android.graphics.drawable.Drawable"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getPosition"
return="int"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getText"
return="java.lang.CharSequence"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="select"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="setFragment"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="fragment" type="android.app.Fragment">
</parameter>
</method>
<method name="setIcon"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="icon" type="android.graphics.drawable.Drawable">
</parameter>
</method>
<method name="setText"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="text" type="java.lang.CharSequence">
</parameter>
</method>
<field name="INVALID_POSITION"
type="int"
transient="false"
volatile="false"
value="-1"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
</class>
<class name="Activity"
extends="android.view.ContextThemeWrapper"
abstract="false"

View File

@@ -206,9 +206,11 @@ public abstract class ActionBar {
*
* @return The current navigation mode.
*
* @see #setStandardNavigationMode()
* @see #setStandardNavigationMode(CharSequence)
* @see #setStandardNavigationMode(CharSequence, CharSequence)
* @see #setDropdownNavigationMode(SpinnerAdapter)
* @see #setTabNavigationMode()
* @see #setCustomNavigationMode(View)
*/
public abstract int getNavigationMode();
@@ -217,10 +219,98 @@ public abstract class ActionBar {
* @return The current set of display options.
*/
public abstract int getDisplayOptions();
/**
* Start a context mode controlled by <code>callback</code>.
* The {@link ContextModeCallback} will receive lifecycle events for the duration
* of the context mode.
*
* @param callback Callback handler that will manage this context mode.
*/
public abstract void startContextMode(ContextModeCallback callback);
/**
* Finish the current context mode.
*/
public abstract void finishContextMode();
/**
* Set the action bar into tabbed navigation mode.
*
* @see #addTab(Tab)
* @see #insertTab(Tab, int)
* @see #removeTab(Tab)
* @see #removeTabAt(int)
*/
public abstract void setTabNavigationMode();
/**
* Set the action bar into tabbed navigation mode.
*
* @param containerViewId Id of the container view where tab content fragments should appear.
*
* @see #addTab(Tab)
* @see #insertTab(Tab, int)
* @see #removeTab(Tab)
* @see #removeTabAt(int)
*/
public abstract void setTabNavigationMode(int containerViewId);
/**
* Create and return a new {@link Tab}.
* This tab will not be included in the action bar until it is added.
*
* @return A new Tab
*
* @see #addTab(Tab)
* @see #insertTab(Tab, int)
*/
public abstract Tab newTab();
/**
* Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list.
*
* @param tab Tab to add
*/
public abstract void addTab(Tab tab);
/**
* Insert a tab for use in tabbed navigation mode. The tab will be inserted at
* <code>position</code>.
*
* @param tab The tab to add
* @param position The new position of the tab
*/
public abstract void insertTab(Tab tab, int position);
/**
* Remove a tab from the action bar.
*
* @param tab The tab to remove
*/
public abstract void removeTab(Tab tab);
/**
* Remove a tab from the action bar.
*
* @param position Position of the tab to remove
*/
public abstract void removeTabAt(int position);
/**
* Select the specified tab. If it is not a child of this action bar it will be added.
*
* @param tab Tab to select
*/
public abstract void selectTab(Tab tab);
/**
* Select the tab at <code>position</code>
*
* @param position Position of the tab to select
*/
public abstract void selectTabAt(int position);
/**
* Represents a contextual mode of the Action Bar. Context modes can be used for
* modal interactions with activity content and replace the normal Action Bar until finished.
@@ -350,4 +440,74 @@ public abstract class ActionBar {
*/
public boolean onNavigationItemSelected(int itemPosition, long itemId);
}
/**
* A tab in the action bar.
*
* <p>Tabs manage the hiding and showing of {@link Fragment}s.
*/
public static abstract class Tab {
/**
* An invalid position for a tab.
*
* @see #getPosition()
*/
public static final int INVALID_POSITION = -1;
/**
* Return the current position of this tab in the action bar.
*
* @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in
* the action bar.
*/
public abstract int getPosition();
/**
* Return the icon associated with this tab.
*
* @return The tab's icon
*/
public abstract Drawable getIcon();
/**
* Return the text of this tab.
*
* @return The tab's text
*/
public abstract CharSequence getText();
/**
* Set the icon displayed on this tab.
*
* @param icon The drawable to use as an icon
*/
public abstract void setIcon(Drawable icon);
/**
* Set the text displayed on this tab. Text may be truncated if there is not
* room to display the entire string.
*
* @param text The text to display
*/
public abstract void setText(CharSequence text);
/**
* Returns the fragment that will be shown when this tab is selected.
*
* @return Fragment associated with this tab
*/
public abstract Fragment getFragment();
/**
* Set the fragment that will be shown when this tab is selected.
*
* @param fragment Fragment to associate with this tab
*/
public abstract void setFragment(Fragment fragment);
/**
* Select this tab. Only valid if the tab has been added to the action bar.
*/
public abstract void select();
}
}

View File

@@ -1676,11 +1676,11 @@ public class Activity extends ContextThemeWrapper
*/
private void initActionBar() {
Window window = getWindow();
if (!window.hasFeature(Window.FEATURE_ACTION_BAR)) {
if (!window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
return;
}
mActionBar = new ActionBarImpl(getWindow().getDecorView());
mActionBar = new ActionBarImpl(this);
}
/**

View File

@@ -22,6 +22,9 @@ import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.ActionBarView;
import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.Menu;
@@ -31,6 +34,8 @@ import android.widget.LinearLayout;
import android.widget.SpinnerAdapter;
import android.widget.ViewAnimator;
import java.util.ArrayList;
/**
* ActionBarImpl is the ActionBar implementation used
* by devices of all screen sizes. If it detects a compatible decor,
@@ -42,10 +47,21 @@ public class ActionBarImpl extends ActionBar {
private static final int NORMAL_VIEW = 0;
private static final int CONTEXT_VIEW = 1;
private static final int TAB_SWITCH_SHOW_HIDE = 0;
private static final int TAB_SWITCH_ADD_REMOVE = 1;
private Activity mActivity;
private ViewAnimator mAnimatorView;
private ActionBarView mActionView;
private ActionBarContextView mUpperContextView;
private LinearLayout mLowerContextView;
private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
private int mTabContainerViewId = android.R.id.content;
private TabImpl mSelectedTab;
private int mTabSwitchMode = TAB_SWITCH_ADD_REMOVE;
private ContextMode mContextMode;
@@ -64,7 +80,9 @@ public class ActionBarImpl extends ActionBar {
}
};
public ActionBarImpl(View decor) {
public ActionBarImpl(Activity activity) {
final View decor = activity.getWindow().getDecorView();
mActivity = activity;
mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
mUpperContextView = (ActionBarContextView) decor.findViewById(
com.android.internal.R.id.action_context_bar);
@@ -83,32 +101,54 @@ public class ActionBarImpl extends ActionBar {
}
public void setCustomNavigationMode(View view) {
cleanupTabs();
mActionView.setCustomNavigationView(view);
mActionView.setCallback(null);
}
public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
cleanupTabs();
mActionView.setCallback(callback);
mActionView.setNavigationMode(NAVIGATION_MODE_DROPDOWN_LIST);
mActionView.setDropdownAdapter(adapter);
}
public void setStandardNavigationMode() {
cleanupTabs();
mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
mActionView.setCallback(null);
}
public void setStandardNavigationMode(CharSequence title) {
cleanupTabs();
setStandardNavigationMode(title, null);
}
public void setStandardNavigationMode(CharSequence title, CharSequence subtitle) {
cleanupTabs();
mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
mActionView.setTitle(title);
mActionView.setSubtitle(subtitle);
mActionView.setCallback(null);
}
private void cleanupTabs() {
if (mSelectedTab != null) {
selectTab(null);
}
if (!mTabs.isEmpty()) {
if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
final FragmentTransaction trans = mActivity.openFragmentTransaction();
final int tabCount = mTabs.size();
for (int i = 0; i < tabCount; i++) {
trans.remove(mTabs.get(i).getFragment());
}
trans.commit();
}
mTabs.clear();
}
}
public void setTitle(CharSequence title) {
mActionView.setTitle(title);
}
@@ -174,6 +214,113 @@ public class ActionBarImpl extends ActionBar {
}
}
private void configureTab(Tab tab, int position) {
final TabImpl tabi = (TabImpl) tab;
final boolean isFirstTab = mTabs.isEmpty();
final FragmentTransaction trans = mActivity.openFragmentTransaction();
final Fragment frag = tabi.getFragment();
tabi.setPosition(position);
mTabs.add(position, tabi);
if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
if (!frag.isAdded()) {
trans.add(mTabContainerViewId, frag);
}
}
if (isFirstTab) {
if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
trans.show(frag);
} else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
trans.add(mTabContainerViewId, frag);
}
mSelectedTab = tabi;
} else {
if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
trans.hide(frag);
}
}
trans.commit();
}
@Override
public void addTab(Tab tab) {
mActionView.addTab(tab);
configureTab(tab, mTabs.size());
}
@Override
public void insertTab(Tab tab, int position) {
mActionView.insertTab(tab, position);
configureTab(tab, position);
}
@Override
public Tab newTab() {
return new TabImpl();
}
@Override
public void removeTab(Tab tab) {
removeTabAt(tab.getPosition());
}
@Override
public void removeTabAt(int position) {
mActionView.removeTabAt(position);
mTabs.remove(position);
final int newTabCount = mTabs.size();
for (int i = position; i < newTabCount; i++) {
mTabs.get(i).setPosition(i);
}
selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
}
@Override
public void setTabNavigationMode() {
mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
}
@Override
public void setTabNavigationMode(int containerViewId) {
mTabContainerViewId = containerViewId;
setTabNavigationMode();
}
@Override
public void selectTab(Tab tab) {
if (mSelectedTab == tab) {
return;
}
mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
final FragmentTransaction trans = mActivity.openFragmentTransaction();
if (mSelectedTab != null) {
if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
trans.hide(mSelectedTab.getFragment());
} else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
trans.remove(mSelectedTab.getFragment());
}
}
if (tab != null) {
if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
trans.show(tab.getFragment());
} else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
trans.add(mTabContainerViewId, tab.getFragment());
}
}
mSelectedTab = (TabImpl) tab;
trans.commit();
}
@Override
public void selectTabAt(int position) {
selectTab(mTabs.get(position));
}
/**
* @hide
*/
@@ -233,6 +380,60 @@ public class ActionBarImpl extends ActionBar {
if (!actionItem.invoke()) {
mCallback.onContextItemClicked(this, item);
}
}
}
/**
* @hide
*/
public class TabImpl extends ActionBar.Tab {
private Fragment mFragment;
private Drawable mIcon;
private CharSequence mText;
private int mPosition;
@Override
public Fragment getFragment() {
return mFragment;
}
@Override
public Drawable getIcon() {
return mIcon;
}
@Override
public int getPosition() {
return mPosition;
}
public void setPosition(int position) {
mPosition = position;
}
@Override
public CharSequence getText() {
return mText;
}
@Override
public void setFragment(Fragment fragment) {
mFragment = fragment;
}
@Override
public void setIcon(Drawable icon) {
mIcon = icon;
}
@Override
public void setText(CharSequence text) {
mText = text;
}
@Override
public void select() {
selectTab(this);
}
}
}

View File

@@ -31,8 +31,10 @@ import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
@@ -85,6 +87,7 @@ public class ActionBarView extends ViewGroup {
private TextView mTitleView;
private TextView mSubtitleView;
private Spinner mSpinner;
private LinearLayout mTabLayout;
private View mCustomNavView;
private boolean mShowMenu;
@@ -128,7 +131,8 @@ public class ActionBarView extends ViewGroup {
ApplicationInfo info = context.getApplicationInfo();
PackageManager pm = context.getPackageManager();
mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode, ActionBar.NAVIGATION_MODE_STANDARD);
mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode,
ActionBar.NAVIGATION_MODE_STANDARD);
mTitle = a.getText(R.styleable.ActionBar_title);
mSubtitle = a.getText(R.styleable.ActionBar_subtitle);
mDisplayOptions = a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT);
@@ -271,6 +275,11 @@ public class ActionBarView extends ViewGroup {
mCustomNavView = null;
}
break;
case ActionBar.NAVIGATION_MODE_TABS:
if (mTabLayout != null) {
removeView(mTabLayout);
mTabLayout = null;
}
}
switch (mode) {
@@ -286,6 +295,10 @@ public class ActionBarView extends ViewGroup {
case ActionBar.NAVIGATION_MODE_CUSTOM:
addView(mCustomNavView);
break;
case ActionBar.NAVIGATION_MODE_TABS:
mTabLayout = new LinearLayout(getContext());
addView(mTabLayout);
break;
}
mNavigationMode = mode;
requestLayout();
@@ -308,6 +321,35 @@ public class ActionBarView extends ViewGroup {
return mDisplayOptions;
}
private TabView createTabView(ActionBar.Tab tab) {
final TabView tabView = new TabView(getContext(), tab);
tabView.setFocusable(true);
tabView.setOnClickListener(new TabClickListener());
return tabView;
}
public void addTab(ActionBar.Tab tab) {
final boolean isFirst = mTabLayout.getChildCount() == 0;
final TabView tabView = createTabView(tab);
mTabLayout.addView(tabView);
if (isFirst) {
tabView.setSelected(true);
}
}
public void insertTab(ActionBar.Tab tab, int position) {
final boolean isFirst = mTabLayout.getChildCount() == 0;
final TabView tabView = createTabView(tab);
mTabLayout.addView(tabView, position);
if (isFirst) {
tabView.setSelected(true);
}
}
public void removeTabAt(int position) {
mTabLayout.removeViewAt(position);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
// Used by custom nav views if they don't supply layout params. Everything else
@@ -356,7 +398,7 @@ public class ActionBarView extends ViewGroup {
case ActionBar.NAVIGATION_MODE_TABS:
throw new UnsupportedOperationException(
"Tab navigation isn't supported yet!");
"Inflating tab navigation isn't supported yet!");
case ActionBar.NAVIGATION_MODE_CUSTOM:
if (mCustomNavView != null) {
@@ -381,6 +423,14 @@ public class ActionBarView extends ViewGroup {
addView(mTitleLayout);
}
public void setTabSelected(int position) {
final int tabCount = mTabLayout.getChildCount();
for (int i = 0; i < tabCount; i++) {
final View child = mTabLayout.getChildAt(i);
child.setSelected(i == position);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
@@ -442,6 +492,13 @@ public class ActionBarView extends ViewGroup {
MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode));
}
break;
case ActionBar.NAVIGATION_MODE_TABS:
if (mTabLayout != null) {
mTabLayout.measure(
MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
break;
}
setMeasuredDimension(contentWidth, mContentHeight);
@@ -486,6 +543,10 @@ public class ActionBarView extends ViewGroup {
x += positionChild(mCustomNavView, x, y, contentHeight) + mSpacing;
}
break;
case ActionBar.NAVIGATION_MODE_TABS:
if (mTabLayout != null) {
x += positionChild(mTabLayout, x, y, contentHeight) + mSpacing;
}
}
x = r - l - getPaddingRight();
@@ -515,4 +576,59 @@ public class ActionBarView extends ViewGroup {
return childWidth;
}
private static class TabView extends LinearLayout {
private ActionBar.Tab mTab;
public TabView(Context context, ActionBar.Tab tab) {
super(context);
mTab = tab;
// TODO Style tabs based on the theme
final Drawable icon = tab.getIcon();
final CharSequence text = tab.getText();
if (icon != null) {
ImageView iconView = new ImageView(context);
iconView.setImageDrawable(icon);
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.CENTER_VERTICAL;
iconView.setLayoutParams(lp);
addView(iconView);
}
if (text != null) {
TextView textView = new TextView(context);
textView.setText(text);
textView.setSingleLine();
textView.setEllipsize(TruncateAt.END);
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.CENTER_VERTICAL;
textView.setLayoutParams(lp);
addView(textView);
}
setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT, 1));
}
public ActionBar.Tab getTab() {
return mTab;
}
}
private class TabClickListener implements OnClickListener {
public void onClick(View view) {
TabView tabView = (TabView) view;
tabView.getTab().select();
final int tabCount = mTabLayout.getChildCount();
for (int i = 0; i < tabCount; i++) {
final View child = mTabLayout.getChildAt(i);
child.setSelected(child == view);
}
}
}
}