615 lines
22 KiB
Java
615 lines
22 KiB
Java
/*
|
|
* Copyright (C) 2006 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.widget;
|
|
|
|
import static android.view.flags.Flags.enableArrowIconOnHoverWhenClickable;
|
|
import static android.view.flags.Flags.FLAG_ENABLE_ARROW_ICON_ON_HOVER_WHEN_CLICKABLE;
|
|
|
|
import android.annotation.DrawableRes;
|
|
import android.annotation.FlaggedApi;
|
|
import android.annotation.Nullable;
|
|
import android.compat.annotation.UnsupportedAppUsage;
|
|
import android.content.Context;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Rect;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Build;
|
|
import android.util.AttributeSet;
|
|
import android.view.MotionEvent;
|
|
import android.view.PointerIcon;
|
|
import android.view.View;
|
|
import android.view.View.OnFocusChangeListener;
|
|
import android.view.ViewGroup;
|
|
import android.view.accessibility.AccessibilityEvent;
|
|
|
|
import com.android.internal.R;
|
|
|
|
/**
|
|
*
|
|
* Displays a list of tab labels representing each page in the parent's tab
|
|
* collection.
|
|
* <p>
|
|
* The container object for this widget is {@link android.widget.TabHost TabHost}.
|
|
* When the user selects a tab, this object sends a message to the parent
|
|
* container, TabHost, to tell it to switch the displayed page. You typically
|
|
* won't use many methods directly on this object. The container TabHost is
|
|
* used to add labels, add the callback handler, and manage callbacks. You
|
|
* might call this object to iterate the list of tabs, or to tweak the layout
|
|
* of the tab list, but most methods should be called on the containing TabHost
|
|
* object.
|
|
*
|
|
* @attr ref android.R.styleable#TabWidget_divider
|
|
* @attr ref android.R.styleable#TabWidget_tabStripEnabled
|
|
* @attr ref android.R.styleable#TabWidget_tabStripLeft
|
|
* @attr ref android.R.styleable#TabWidget_tabStripRight
|
|
*
|
|
* @deprecated new applications should use fragment APIs instead of this class:
|
|
* Use <a href="{@docRoot}guide/navigation/navigation-swipe-view">TabLayout and ViewPager</a>
|
|
* instead.
|
|
*/
|
|
@Deprecated
|
|
public class TabWidget extends LinearLayout implements OnFocusChangeListener {
|
|
private final Rect mBounds = new Rect();
|
|
|
|
private OnTabSelectionChanged mSelectionChangedListener;
|
|
|
|
// This value will be set to 0 as soon as the first tab is added to TabHost.
|
|
@UnsupportedAppUsage(trackingBug = 137825207, maxTargetSdk = Build.VERSION_CODES.Q,
|
|
publicAlternatives = "Use {@code androidx.viewpager.widget.ViewPager} and "
|
|
+ "{@code com.google.android.material.tabs.TabLayout} instead.\n"
|
|
+ "See <a href=\"{@docRoot}guide/navigation/navigation-swipe-view"
|
|
+ "\">TabLayout and ViewPager</a>")
|
|
private int mSelectedTab = -1;
|
|
|
|
@Nullable
|
|
private Drawable mLeftStrip;
|
|
|
|
@Nullable
|
|
private Drawable mRightStrip;
|
|
|
|
@UnsupportedAppUsage(trackingBug = 137825207, maxTargetSdk = Build.VERSION_CODES.Q,
|
|
publicAlternatives = "Use {@code androidx.viewpager.widget.ViewPager} and "
|
|
+ "{@code com.google.android.material.tabs.TabLayout} instead.\n"
|
|
+ "See <a href=\"{@docRoot}guide/navigation/navigation-swipe-view"
|
|
+ "\">TabLayout and ViewPager</a>")
|
|
private boolean mDrawBottomStrips = true;
|
|
private boolean mStripMoved;
|
|
|
|
// When positive, the widths and heights of tabs will be imposed so that
|
|
// they fit in parent.
|
|
private int mImposedTabsHeight = -1;
|
|
private int[] mImposedTabWidths;
|
|
|
|
public TabWidget(Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
public TabWidget(Context context, AttributeSet attrs) {
|
|
this(context, attrs, com.android.internal.R.attr.tabWidgetStyle);
|
|
}
|
|
|
|
public TabWidget(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
this(context, attrs, defStyleAttr, 0);
|
|
}
|
|
|
|
public TabWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
super(context, attrs, defStyleAttr, defStyleRes);
|
|
|
|
final TypedArray a = context.obtainStyledAttributes(
|
|
attrs, R.styleable.TabWidget, defStyleAttr, defStyleRes);
|
|
saveAttributeDataForStyleable(context, R.styleable.TabWidget,
|
|
attrs, a, defStyleAttr, defStyleRes);
|
|
|
|
mDrawBottomStrips = a.getBoolean(R.styleable.TabWidget_tabStripEnabled, mDrawBottomStrips);
|
|
|
|
// Tests the target SDK version, as set in the Manifest. Could not be
|
|
// set using styles.xml in a values-v? directory which targets the
|
|
// current platform SDK version instead.
|
|
final boolean isTargetSdkDonutOrLower =
|
|
context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT;
|
|
|
|
final boolean hasExplicitLeft = a.hasValueOrEmpty(R.styleable.TabWidget_tabStripLeft);
|
|
if (hasExplicitLeft) {
|
|
mLeftStrip = a.getDrawable(R.styleable.TabWidget_tabStripLeft);
|
|
} else if (isTargetSdkDonutOrLower) {
|
|
mLeftStrip = context.getDrawable(R.drawable.tab_bottom_left_v4);
|
|
} else {
|
|
mLeftStrip = context.getDrawable(R.drawable.tab_bottom_left);
|
|
}
|
|
|
|
final boolean hasExplicitRight = a.hasValueOrEmpty(R.styleable.TabWidget_tabStripRight);
|
|
if (hasExplicitRight) {
|
|
mRightStrip = a.getDrawable(R.styleable.TabWidget_tabStripRight);
|
|
} else if (isTargetSdkDonutOrLower) {
|
|
mRightStrip = context.getDrawable(R.drawable.tab_bottom_right_v4);
|
|
} else {
|
|
mRightStrip = context.getDrawable(R.drawable.tab_bottom_right);
|
|
}
|
|
|
|
a.recycle();
|
|
|
|
setChildrenDrawingOrderEnabled(true);
|
|
}
|
|
|
|
@Override
|
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
|
mStripMoved = true;
|
|
|
|
super.onSizeChanged(w, h, oldw, oldh);
|
|
}
|
|
|
|
@Override
|
|
protected int getChildDrawingOrder(int childCount, int i) {
|
|
if (mSelectedTab == -1) {
|
|
return i;
|
|
} else {
|
|
// Always draw the selected tab last, so that drop shadows are drawn
|
|
// in the correct z-order.
|
|
if (i == childCount - 1) {
|
|
return mSelectedTab;
|
|
} else if (i >= mSelectedTab) {
|
|
return i + 1;
|
|
} else {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
void measureChildBeforeLayout(View child, int childIndex, int widthMeasureSpec, int totalWidth,
|
|
int heightMeasureSpec, int totalHeight) {
|
|
if (!isMeasureWithLargestChildEnabled() && mImposedTabsHeight >= 0) {
|
|
widthMeasureSpec = MeasureSpec.makeMeasureSpec(
|
|
totalWidth + mImposedTabWidths[childIndex], MeasureSpec.EXACTLY);
|
|
heightMeasureSpec = MeasureSpec.makeMeasureSpec(mImposedTabsHeight,
|
|
MeasureSpec.EXACTLY);
|
|
}
|
|
|
|
super.measureChildBeforeLayout(child, childIndex,
|
|
widthMeasureSpec, totalWidth, heightMeasureSpec, totalHeight);
|
|
}
|
|
|
|
@Override
|
|
void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
|
|
if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) {
|
|
super.measureHorizontal(widthMeasureSpec, heightMeasureSpec);
|
|
return;
|
|
}
|
|
|
|
// First, measure with no constraint
|
|
final int width = MeasureSpec.getSize(widthMeasureSpec);
|
|
final int unspecifiedWidth = MeasureSpec.makeSafeMeasureSpec(width,
|
|
MeasureSpec.UNSPECIFIED);
|
|
mImposedTabsHeight = -1;
|
|
super.measureHorizontal(unspecifiedWidth, heightMeasureSpec);
|
|
|
|
int extraWidth = getMeasuredWidth() - width;
|
|
if (extraWidth > 0) {
|
|
final int count = getChildCount();
|
|
|
|
int childCount = 0;
|
|
for (int i = 0; i < count; i++) {
|
|
final View child = getChildAt(i);
|
|
if (child.getVisibility() == GONE) continue;
|
|
childCount++;
|
|
}
|
|
|
|
if (childCount > 0) {
|
|
if (mImposedTabWidths == null || mImposedTabWidths.length != count) {
|
|
mImposedTabWidths = new int[count];
|
|
}
|
|
for (int i = 0; i < count; i++) {
|
|
final View child = getChildAt(i);
|
|
if (child.getVisibility() == GONE) continue;
|
|
final int childWidth = child.getMeasuredWidth();
|
|
final int delta = extraWidth / childCount;
|
|
final int newWidth = Math.max(0, childWidth - delta);
|
|
mImposedTabWidths[i] = newWidth;
|
|
// Make sure the extra width is evenly distributed, no int division remainder
|
|
extraWidth -= childWidth - newWidth; // delta may have been clamped
|
|
childCount--;
|
|
mImposedTabsHeight = Math.max(mImposedTabsHeight, child.getMeasuredHeight());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Measure again, this time with imposed tab widths and respecting
|
|
// initial spec request.
|
|
super.measureHorizontal(widthMeasureSpec, heightMeasureSpec);
|
|
}
|
|
|
|
/**
|
|
* Returns the tab indicator view at the given index.
|
|
*
|
|
* @param index the zero-based index of the tab indicator view to return
|
|
* @return the tab indicator view at the given index
|
|
*/
|
|
public View getChildTabViewAt(int index) {
|
|
return getChildAt(index);
|
|
}
|
|
|
|
/**
|
|
* Returns the number of tab indicator views.
|
|
*
|
|
* @return the number of tab indicator views
|
|
*/
|
|
public int getTabCount() {
|
|
return getChildCount();
|
|
}
|
|
|
|
/**
|
|
* Sets the drawable to use as a divider between the tab indicators.
|
|
*
|
|
* @param drawable the divider drawable
|
|
* @attr ref android.R.styleable#TabWidget_divider
|
|
*/
|
|
@Override
|
|
public void setDividerDrawable(@Nullable Drawable drawable) {
|
|
super.setDividerDrawable(drawable);
|
|
}
|
|
|
|
/**
|
|
* Sets the drawable to use as a divider between the tab indicators.
|
|
*
|
|
* @param resId the resource identifier of the drawable to use as a divider
|
|
* @attr ref android.R.styleable#TabWidget_divider
|
|
*/
|
|
public void setDividerDrawable(@DrawableRes int resId) {
|
|
setDividerDrawable(mContext.getDrawable(resId));
|
|
}
|
|
|
|
/**
|
|
* Sets the drawable to use as the left part of the strip below the tab
|
|
* indicators.
|
|
*
|
|
* @param drawable the left strip drawable
|
|
* @see #getLeftStripDrawable()
|
|
* @attr ref android.R.styleable#TabWidget_tabStripLeft
|
|
*/
|
|
public void setLeftStripDrawable(@Nullable Drawable drawable) {
|
|
mLeftStrip = drawable;
|
|
requestLayout();
|
|
invalidate();
|
|
}
|
|
|
|
/**
|
|
* Sets the drawable to use as the left part of the strip below the tab
|
|
* indicators.
|
|
*
|
|
* @param resId the resource identifier of the drawable to use as the left
|
|
* strip drawable
|
|
* @see #getLeftStripDrawable()
|
|
* @attr ref android.R.styleable#TabWidget_tabStripLeft
|
|
*/
|
|
public void setLeftStripDrawable(@DrawableRes int resId) {
|
|
setLeftStripDrawable(mContext.getDrawable(resId));
|
|
}
|
|
|
|
/**
|
|
* @return the drawable used as the left part of the strip below the tab
|
|
* indicators, may be {@code null}
|
|
* @see #setLeftStripDrawable(int)
|
|
* @see #setLeftStripDrawable(Drawable)
|
|
* @attr ref android.R.styleable#TabWidget_tabStripLeft
|
|
*/
|
|
@Nullable
|
|
public Drawable getLeftStripDrawable() {
|
|
return mLeftStrip;
|
|
}
|
|
|
|
/**
|
|
* Sets the drawable to use as the right part of the strip below the tab
|
|
* indicators.
|
|
*
|
|
* @param drawable the right strip drawable
|
|
* @see #getRightStripDrawable()
|
|
* @attr ref android.R.styleable#TabWidget_tabStripRight
|
|
*/
|
|
public void setRightStripDrawable(@Nullable Drawable drawable) {
|
|
mRightStrip = drawable;
|
|
requestLayout();
|
|
invalidate();
|
|
}
|
|
|
|
/**
|
|
* Sets the drawable to use as the right part of the strip below the tab
|
|
* indicators.
|
|
*
|
|
* @param resId the resource identifier of the drawable to use as the right
|
|
* strip drawable
|
|
* @see #getRightStripDrawable()
|
|
* @attr ref android.R.styleable#TabWidget_tabStripRight
|
|
*/
|
|
public void setRightStripDrawable(@DrawableRes int resId) {
|
|
setRightStripDrawable(mContext.getDrawable(resId));
|
|
}
|
|
|
|
/**
|
|
* @return the drawable used as the right part of the strip below the tab
|
|
* indicators, may be {@code null}
|
|
* @see #setRightStripDrawable(int)
|
|
* @see #setRightStripDrawable(Drawable)
|
|
* @attr ref android.R.styleable#TabWidget_tabStripRight
|
|
*/
|
|
@Nullable
|
|
public Drawable getRightStripDrawable() {
|
|
return mRightStrip;
|
|
}
|
|
|
|
/**
|
|
* Controls whether the bottom strips on the tab indicators are drawn or
|
|
* not. The default is to draw them. If the user specifies a custom
|
|
* view for the tab indicators, then the TabHost class calls this method
|
|
* to disable drawing of the bottom strips.
|
|
* @param stripEnabled true if the bottom strips should be drawn.
|
|
*/
|
|
public void setStripEnabled(boolean stripEnabled) {
|
|
mDrawBottomStrips = stripEnabled;
|
|
invalidate();
|
|
}
|
|
|
|
/**
|
|
* Indicates whether the bottom strips on the tab indicators are drawn
|
|
* or not.
|
|
*/
|
|
public boolean isStripEnabled() {
|
|
return mDrawBottomStrips;
|
|
}
|
|
|
|
@Override
|
|
public void childDrawableStateChanged(View child) {
|
|
if (getTabCount() > 0 && child == getChildTabViewAt(mSelectedTab)) {
|
|
// To make sure that the bottom strip is redrawn
|
|
invalidate();
|
|
}
|
|
super.childDrawableStateChanged(child);
|
|
}
|
|
|
|
@Override
|
|
public void dispatchDraw(Canvas canvas) {
|
|
super.dispatchDraw(canvas);
|
|
|
|
// Do nothing if there are no tabs.
|
|
if (getTabCount() == 0) return;
|
|
|
|
// If the user specified a custom view for the tab indicators, then
|
|
// do not draw the bottom strips.
|
|
if (!mDrawBottomStrips) {
|
|
// Skip drawing the bottom strips.
|
|
return;
|
|
}
|
|
|
|
final View selectedChild = getChildTabViewAt(mSelectedTab);
|
|
|
|
final Drawable leftStrip = mLeftStrip;
|
|
final Drawable rightStrip = mRightStrip;
|
|
|
|
if (leftStrip != null) {
|
|
leftStrip.setState(selectedChild.getDrawableState());
|
|
}
|
|
if (rightStrip != null) {
|
|
rightStrip.setState(selectedChild.getDrawableState());
|
|
}
|
|
|
|
if (mStripMoved) {
|
|
final Rect bounds = mBounds;
|
|
bounds.left = selectedChild.getLeft();
|
|
bounds.right = selectedChild.getRight();
|
|
final int myHeight = getHeight();
|
|
if (leftStrip != null) {
|
|
leftStrip.setBounds(Math.min(0, bounds.left - leftStrip.getIntrinsicWidth()),
|
|
myHeight - leftStrip.getIntrinsicHeight(), bounds.left, myHeight);
|
|
}
|
|
if (rightStrip != null) {
|
|
rightStrip.setBounds(bounds.right, myHeight - rightStrip.getIntrinsicHeight(),
|
|
Math.max(getWidth(), bounds.right + rightStrip.getIntrinsicWidth()),
|
|
myHeight);
|
|
}
|
|
mStripMoved = false;
|
|
}
|
|
|
|
if (leftStrip != null) {
|
|
leftStrip.draw(canvas);
|
|
}
|
|
if (rightStrip != null) {
|
|
rightStrip.draw(canvas);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the current tab.
|
|
* <p>
|
|
* This method is used to bring a tab to the front of the Widget,
|
|
* and is used to post to the rest of the UI that a different tab
|
|
* has been brought to the foreground.
|
|
* <p>
|
|
* Note, this is separate from the traditional "focus" that is
|
|
* employed from the view logic.
|
|
* <p>
|
|
* For instance, if we have a list in a tabbed view, a user may be
|
|
* navigating up and down the list, moving the UI focus (orange
|
|
* highlighting) through the list items. The cursor movement does
|
|
* not effect the "selected" tab though, because what is being
|
|
* scrolled through is all on the same tab. The selected tab only
|
|
* changes when we navigate between tabs (moving from the list view
|
|
* to the next tabbed view, in this example).
|
|
* <p>
|
|
* To move both the focus AND the selected tab at once, please use
|
|
* {@link #focusCurrentTab}. Normally, the view logic takes care of
|
|
* adjusting the focus, so unless you're circumventing the UI,
|
|
* you'll probably just focus your interest here.
|
|
*
|
|
* @param index the index of the tab that you want to indicate as the
|
|
* selected tab (tab brought to the front of the widget)
|
|
* @see #focusCurrentTab
|
|
*/
|
|
public void setCurrentTab(int index) {
|
|
if (index < 0 || index >= getTabCount() || index == mSelectedTab) {
|
|
return;
|
|
}
|
|
|
|
if (mSelectedTab != -1) {
|
|
getChildTabViewAt(mSelectedTab).setSelected(false);
|
|
}
|
|
mSelectedTab = index;
|
|
getChildTabViewAt(mSelectedTab).setSelected(true);
|
|
mStripMoved = true;
|
|
}
|
|
|
|
@Override
|
|
public CharSequence getAccessibilityClassName() {
|
|
return TabWidget.class.getName();
|
|
}
|
|
|
|
/** @hide */
|
|
@Override
|
|
public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
|
|
super.onInitializeAccessibilityEventInternal(event);
|
|
event.setItemCount(getTabCount());
|
|
event.setCurrentItemIndex(mSelectedTab);
|
|
}
|
|
|
|
/**
|
|
* Sets the current tab and focuses the UI on it.
|
|
* This method makes sure that the focused tab matches the selected
|
|
* tab, normally at {@link #setCurrentTab}. Normally this would not
|
|
* be an issue if we go through the UI, since the UI is responsible
|
|
* for calling TabWidget.onFocusChanged(), but in the case where we
|
|
* are selecting the tab programmatically, we'll need to make sure
|
|
* focus keeps up.
|
|
*
|
|
* @param index The tab that you want focused (highlighted in orange)
|
|
* and selected (tab brought to the front of the widget)
|
|
*
|
|
* @see #setCurrentTab
|
|
*/
|
|
public void focusCurrentTab(int index) {
|
|
final int oldTab = mSelectedTab;
|
|
|
|
// set the tab
|
|
setCurrentTab(index);
|
|
|
|
// change the focus if applicable.
|
|
if (oldTab != index) {
|
|
getChildTabViewAt(index).requestFocus();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setEnabled(boolean enabled) {
|
|
super.setEnabled(enabled);
|
|
|
|
final int count = getTabCount();
|
|
for (int i = 0; i < count; i++) {
|
|
final View child = getChildTabViewAt(i);
|
|
child.setEnabled(enabled);
|
|
}
|
|
}
|
|
|
|
@FlaggedApi(FLAG_ENABLE_ARROW_ICON_ON_HOVER_WHEN_CLICKABLE)
|
|
@Override
|
|
public void addView(View child) {
|
|
if (child.getLayoutParams() == null) {
|
|
final LinearLayout.LayoutParams lp = new LayoutParams(
|
|
0, ViewGroup.LayoutParams.MATCH_PARENT, 1.0f);
|
|
lp.setMargins(0, 0, 0, 0);
|
|
child.setLayoutParams(lp);
|
|
}
|
|
|
|
// Ensure you can navigate to the tab with the keyboard, and you can touch it
|
|
child.setFocusable(true);
|
|
child.setClickable(true);
|
|
|
|
// By default the pointer icon is an arrow. More specifically, when the pointer icon is set
|
|
// to null, it will be an arrow. Therefore, we don't need to change the icon when
|
|
// enableArrowIconOnHoverWhenClickable() and the pointer icon is a null. We only need to do
|
|
// that when we want the hand icon for hover.
|
|
if (!enableArrowIconOnHoverWhenClickable() && child.getPointerIcon() == null) {
|
|
child.setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_HAND));
|
|
}
|
|
|
|
super.addView(child);
|
|
|
|
// TODO: detect this via geometry with a tabwidget listener rather
|
|
// than potentially interfere with the view's listener
|
|
child.setOnClickListener(new TabClickListener(getTabCount() - 1));
|
|
}
|
|
|
|
@Override
|
|
public void removeAllViews() {
|
|
super.removeAllViews();
|
|
mSelectedTab = -1;
|
|
}
|
|
|
|
@Override
|
|
public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
|
|
if (!isEnabled()) {
|
|
return null;
|
|
}
|
|
return super.onResolvePointerIcon(event, pointerIndex);
|
|
}
|
|
|
|
/**
|
|
* Provides a way for {@link TabHost} to be notified that the user clicked
|
|
* on a tab indicator.
|
|
*/
|
|
@UnsupportedAppUsage(trackingBug = 137825207, maxTargetSdk = Build.VERSION_CODES.Q,
|
|
publicAlternatives = "Use {@code androidx.viewpager.widget.ViewPager} and "
|
|
+ "{@code com.google.android.material.tabs.TabLayout} instead.\n"
|
|
+ "See <a href=\"{@docRoot}guide/navigation/navigation-swipe-view"
|
|
+ "\">TabLayout and ViewPager</a>")
|
|
void setTabSelectionListener(OnTabSelectionChanged listener) {
|
|
mSelectionChangedListener = listener;
|
|
}
|
|
|
|
@Override
|
|
public void onFocusChange(View v, boolean hasFocus) {
|
|
// No-op. Tab selection is separate from keyboard focus.
|
|
}
|
|
|
|
// registered with each tab indicator so we can notify tab host
|
|
private class TabClickListener implements OnClickListener {
|
|
private final int mTabIndex;
|
|
|
|
private TabClickListener(int tabIndex) {
|
|
mTabIndex = tabIndex;
|
|
}
|
|
|
|
public void onClick(View v) {
|
|
mSelectionChangedListener.onTabSelectionChanged(mTabIndex, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lets {@link TabHost} know that the user clicked on a tab indicator.
|
|
*/
|
|
interface OnTabSelectionChanged {
|
|
/**
|
|
* Informs the TabHost which tab was selected. It also indicates
|
|
* if the tab was clicked/pressed or just focused into.
|
|
*
|
|
* @param tabIndex index of the tab that was selected
|
|
* @param clicked whether the selection changed due to a touch/click or
|
|
* due to focus entering the tab through navigation.
|
|
* {@code true} if it was due to a press/click and
|
|
* {@code false} otherwise.
|
|
*/
|
|
void onTabSelectionChanged(int tabIndex, boolean clicked);
|
|
}
|
|
}
|