blob: 834035c5d9675f0f64cc8208e4610c1f64340524 [file] [log] [blame]
/*
* 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.v4.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
/**
* PagerTabStrip is an interactive indicator of the current, next,
* and previous pages of a {@link ViewPager}. It is intended to be used as a
* child view of a ViewPager widget in your XML layout.
* Add it as a child of a ViewPager in your layout file and set its
* android:layout_gravity to TOP or BOTTOM to pin it to the top or bottom
* of the ViewPager. The title from each page is supplied by the method
* {@link PagerAdapter#getPageTitle(int)} in the adapter supplied to
* the ViewPager.
*
* <p>For a non-interactive indicator, see {@link PagerTitleStrip}.</p>
*/
public class PagerTabStrip extends PagerTitleStrip {
private static final String TAG = "PagerTabStrip";
private static final int INDICATOR_HEIGHT = 3; // dp
private static final int MIN_PADDING_BOTTOM = INDICATOR_HEIGHT + 3; // dp
private static final int TAB_PADDING = 16; // dp
private static final int TAB_SPACING = 32; // dp
private static final int MIN_TEXT_SPACING = TAB_SPACING + TAB_PADDING * 2; // dp
private static final int FULL_UNDERLINE_HEIGHT = 1; // dp
private static final int MIN_STRIP_HEIGHT = 32; // dp
private int mIndicatorColor;
private int mIndicatorHeight;
private int mMinPaddingBottom;
private int mMinTextSpacing;
private int mMinStripHeight;
private int mTabPadding;
private final Paint mTabPaint = new Paint();
private final Rect mTempRect = new Rect();
private int mTabAlpha = 0xFF;
private boolean mDrawFullUnderline = false;
private boolean mDrawFullUnderlineSet = false;
private int mFullUnderlineHeight;
private boolean mIgnoreTap;
private float mInitialMotionX;
private float mInitialMotionY;
private int mTouchSlop;
public PagerTabStrip(Context context) {
this(context, null);
}
public PagerTabStrip(Context context, AttributeSet attrs) {
super(context, attrs);
mIndicatorColor = mTextColor;
mTabPaint.setColor(mIndicatorColor);
// Note: this follows the rules for Resources#getDimensionPixelOffset/Size:
// sizes round up, offsets round down.
final float density = context.getResources().getDisplayMetrics().density;
mIndicatorHeight = (int) (INDICATOR_HEIGHT * density + 0.5f);
mMinPaddingBottom = (int) (MIN_PADDING_BOTTOM * density + 0.5f);
mMinTextSpacing = (int) (MIN_TEXT_SPACING * density);
mTabPadding = (int) (TAB_PADDING * density + 0.5f);
mFullUnderlineHeight = (int) (FULL_UNDERLINE_HEIGHT * density + 0.5f);
mMinStripHeight = (int) (MIN_STRIP_HEIGHT * density + 0.5f);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
// Enforce restrictions
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
setTextSpacing(getTextSpacing());
setWillNotDraw(false);
mPrevText.setFocusable(true);
mPrevText.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
});
mNextText.setFocusable(true);
mNextText.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mPager.setCurrentItem(mPager.getCurrentItem() + 1);
}
});
if (getBackground() == null) {
mDrawFullUnderline = true;
}
}
/**
* Set the color of the tab indicator bar.
*
* @param color Color to set as an 0xRRGGBB value. The high byte (alpha) is ignored.
*/
public void setTabIndicatorColor(int color) {
mIndicatorColor = color;
mTabPaint.setColor(mIndicatorColor);
invalidate();
}
/**
* Set the color of the tab indicator bar from a color resource.
*
* @param resId Resource ID of a color resource to load
*/
public void setTabIndicatorColorResource(@ColorRes int resId) {
setTabIndicatorColor(getContext().getResources().getColor(resId));
}
/**
* @return The current tab indicator color as an 0xRRGGBB value.
*/
public int getTabIndicatorColor() {
return mIndicatorColor;
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
if (bottom < mMinPaddingBottom) {
bottom = mMinPaddingBottom;
}
super.setPadding(left, top, right, bottom);
}
@Override
public void setTextSpacing(int textSpacing) {
if (textSpacing < mMinTextSpacing) {
textSpacing = mMinTextSpacing;
}
super.setTextSpacing(textSpacing);
}
@Override
public void setBackgroundDrawable(Drawable d) {
super.setBackgroundDrawable(d);
if (!mDrawFullUnderlineSet) {
mDrawFullUnderline = d == null;
}
}
@Override
public void setBackgroundColor(int color) {
super.setBackgroundColor(color);
if (!mDrawFullUnderlineSet) {
mDrawFullUnderline = (color & 0xFF000000) == 0;
}
}
@Override
public void setBackgroundResource(@DrawableRes int resId) {
super.setBackgroundResource(resId);
if (!mDrawFullUnderlineSet) {
mDrawFullUnderline = resId == 0;
}
}
/**
* Set whether this tab strip should draw a full-width underline in the
* current tab indicator color.
*
* @param drawFull true to draw a full-width underline, false otherwise
*/
public void setDrawFullUnderline(boolean drawFull) {
mDrawFullUnderline = drawFull;
mDrawFullUnderlineSet = true;
invalidate();
}
/**
* Return whether or not this tab strip will draw a full-width underline.
* This defaults to true if no background is set.
*
* @return true if this tab strip will draw a full-width underline in the
* current tab indicator color.
*/
public boolean getDrawFullUnderline() {
return mDrawFullUnderline;
}
@Override
int getMinHeight() {
return Math.max(super.getMinHeight(), mMinStripHeight);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
if (action != MotionEvent.ACTION_DOWN && mIgnoreTap) {
return false;
}
// Any tap within touch slop to either side of the current item
// will scroll to prev/next.
final float x = ev.getX();
final float y = ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
mInitialMotionX = x;
mInitialMotionY = y;
mIgnoreTap = false;
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(x - mInitialMotionX) > mTouchSlop ||
Math.abs(y - mInitialMotionY) > mTouchSlop) {
mIgnoreTap = true;
}
break;
case MotionEvent.ACTION_UP:
if (x < mCurrText.getLeft() - mTabPadding) {
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
} else if (x > mCurrText.getRight() + mTabPadding) {
mPager.setCurrentItem(mPager.getCurrentItem() + 1);
}
break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final int height = getHeight();
final int bottom = height;
final int left = mCurrText.getLeft() - mTabPadding;
final int right = mCurrText.getRight() + mTabPadding;
final int top = bottom - mIndicatorHeight;
mTabPaint.setColor(mTabAlpha << 24 | (mIndicatorColor & 0xFFFFFF));
canvas.drawRect(left, top, right, bottom, mTabPaint);
if (mDrawFullUnderline) {
mTabPaint.setColor(0xFF << 24 | (mIndicatorColor & 0xFFFFFF));
canvas.drawRect(getPaddingLeft(), height - mFullUnderlineHeight,
getWidth() - getPaddingRight(), height, mTabPaint);
}
}
@Override
void updateTextPositions(int position, float positionOffset, boolean force) {
final Rect r = mTempRect;
int bottom = getHeight();
int left = mCurrText.getLeft() - mTabPadding;
int right = mCurrText.getRight() + mTabPadding;
int top = bottom - mIndicatorHeight;
r.set(left, top, right, bottom);
super.updateTextPositions(position, positionOffset, force);
mTabAlpha = (int) (Math.abs(positionOffset - 0.5f) * 2 * 0xFF);
left = mCurrText.getLeft() - mTabPadding;
right = mCurrText.getRight() + mTabPadding;
r.union(left, top, right, bottom);
invalidate(r);
}
}