-
-
Save Bhavdip/7b91f7c477b531a9d27c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2011 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.content.res.TypedArray; | |
import android.database.DataSetObserver; | |
import android.graphics.drawable.Drawable; | |
import android.text.TextUtils.TruncateAt; | |
import android.util.AttributeSet; | |
import android.util.TypedValue; | |
import android.view.Gravity; | |
import android.view.ViewGroup; | |
import android.view.ViewParent; | |
import android.widget.TextView; | |
import java.lang.ref.WeakReference; | |
/** | |
* SinglePagerTitleStrip is a non-interactive indicator of the current page | |
* 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. | |
* | |
* This is modification of {@link PagerTitleStrip}. All stuff related to | |
* non-primary pages was removed. | |
* | |
* <p>For an interactive indicator, see {@link PagerTabStrip}.</p> | |
*/ | |
public class SinglePagerTitleStrip extends ViewGroup implements ViewPager.Decor { | |
ViewPager mPager; | |
TextView mCurrText; | |
private int mLastKnownCurrentPage = -1; | |
private float mLastKnownPositionOffset = -1; | |
private int mScaledTextSpacing; | |
private int mGravity; | |
private boolean mUpdatingText; | |
private boolean mUpdatingPositions; | |
private final PageListener mPageListener = new PageListener(); | |
private WeakReference<PagerAdapter> mWatchingAdapter; | |
private static final int[] ATTRS = new int[] { | |
android.R.attr.textAppearance, | |
android.R.attr.textSize, | |
android.R.attr.textColor, | |
android.R.attr.gravity | |
}; | |
private static final int[] TEXT_ATTRS = new int[] { | |
0x0101038c // android.R.attr.textAllCaps | |
}; | |
private static final int TEXT_SPACING = 16; // dip | |
interface PagerTitleStripImpl { | |
void setSingleLineAllCaps(TextView text); | |
} | |
static class PagerTitleStripImplBase implements PagerTitleStripImpl { | |
public void setSingleLineAllCaps(TextView text) { | |
text.setSingleLine(); | |
} | |
} | |
static class PagerTitleStripImplIcs implements PagerTitleStripImpl { | |
public void setSingleLineAllCaps(TextView text) { | |
PagerTitleStripIcs.setSingleLineAllCaps(text); | |
} | |
} | |
private static final PagerTitleStripImpl IMPL; | |
static { | |
if (android.os.Build.VERSION.SDK_INT >= 14) { | |
IMPL = new PagerTitleStripImplIcs(); | |
} else { | |
IMPL = new PagerTitleStripImplBase(); | |
} | |
} | |
private static void setSingleLineAllCaps(TextView text) { | |
IMPL.setSingleLineAllCaps(text); | |
} | |
public SinglePagerTitleStrip(Context context) { | |
this(context, null); | |
} | |
public SinglePagerTitleStrip(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
addView(mCurrText = new TextView(context)); | |
final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS); | |
final int textAppearance = a.getResourceId(0, 0); | |
if (textAppearance != 0) { | |
mCurrText.setTextAppearance(context, textAppearance); | |
} | |
final int textSize = a.getDimensionPixelSize(1, 0); | |
if (textSize != 0) { | |
setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); | |
} | |
if (a.hasValue(2)) { | |
final int textColor = a.getColor(2, 0); | |
mCurrText.setTextColor(textColor); | |
} | |
mGravity = a.getInteger(3, Gravity.BOTTOM); | |
a.recycle(); | |
mCurrText.setEllipsize(TruncateAt.END); | |
boolean allCaps = false; | |
if (textAppearance != 0) { | |
final TypedArray ta = context.obtainStyledAttributes(textAppearance, TEXT_ATTRS); | |
allCaps = ta.getBoolean(0, false); | |
ta.recycle(); | |
} | |
if (allCaps) { | |
setSingleLineAllCaps(mCurrText); | |
} else { | |
mCurrText.setSingleLine(); | |
} | |
final float density = context.getResources().getDisplayMetrics().density; | |
mScaledTextSpacing = (int) (TEXT_SPACING * density); | |
} | |
/** | |
* Set the required spacing between title segments. | |
* | |
* @param spacingPixels Spacing between each title displayed in pixels | |
*/ | |
public void setTextSpacing(int spacingPixels) { | |
mScaledTextSpacing = spacingPixels; | |
requestLayout(); | |
} | |
/** | |
* @return The required spacing between title segments in pixels | |
*/ | |
public int getTextSpacing() { | |
return mScaledTextSpacing; | |
} | |
/** | |
* Set the color value used as the base color for all displayed page titles. | |
* Alpha will be ignored for non-primary page titles. See {@link #setNonPrimaryAlpha(float)}. | |
* | |
* @param color Color hex code in 0xAARRGGBB format | |
*/ | |
public void setTextColor(int color) { | |
mCurrText.setTextColor(color); | |
} | |
/** | |
* Set the default text size to a given unit and value. | |
* See {@link TypedValue} for the possible dimension units. | |
* | |
* <p>Example: to set the text size to 14px, use | |
* setTextSize(TypedValue.COMPLEX_UNIT_PX, 14);</p> | |
* | |
* @param unit The desired dimension unit | |
* @param size The desired size in the given units | |
*/ | |
public void setTextSize(int unit, float size) { | |
mCurrText.setTextSize(unit, size); | |
} | |
/** | |
* Set the {@link Gravity} used to position text within the title strip. | |
* Only the vertical gravity component is used. | |
* | |
* @param gravity {@link Gravity} constant for positioning title text | |
*/ | |
public void setGravity(int gravity) { | |
mGravity = gravity; | |
requestLayout(); | |
} | |
@Override | |
protected void onAttachedToWindow() { | |
super.onAttachedToWindow(); | |
final ViewParent parent = getParent(); | |
if (!(parent instanceof ViewPager)) { | |
throw new IllegalStateException( | |
"PagerTitleStrip must be a direct child of a ViewPager."); | |
} | |
final ViewPager pager = (ViewPager) parent; | |
final PagerAdapter adapter = pager.getAdapter(); | |
pager.setInternalPageChangeListener(mPageListener); | |
pager.setOnAdapterChangeListener(mPageListener); | |
mPager = pager; | |
updateAdapter(mWatchingAdapter != null ? mWatchingAdapter.get() : null, adapter); | |
} | |
@Override | |
protected void onDetachedFromWindow() { | |
super.onDetachedFromWindow(); | |
if (mPager != null) { | |
updateAdapter(mPager.getAdapter(), null); | |
mPager.setInternalPageChangeListener(null); | |
mPager.setOnAdapterChangeListener(null); | |
mPager = null; | |
} | |
} | |
void updateText(int currentItem, PagerAdapter adapter) { | |
final int itemCount = adapter != null ? adapter.getCount() : 0; | |
mUpdatingText = true; | |
mCurrText.setText(adapter != null && currentItem < itemCount ? | |
adapter.getPageTitle(currentItem) : null); | |
// Measure everything | |
final int width = getWidth() - getPaddingLeft() - getPaddingRight(); | |
final int childHeight = getHeight() - getPaddingTop() - getPaddingBottom(); | |
final int childWidthSpec = MeasureSpec.makeMeasureSpec((int) (width * 0.8f), | |
MeasureSpec.AT_MOST); | |
final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST); | |
mCurrText.measure(childWidthSpec, childHeightSpec); | |
mLastKnownCurrentPage = currentItem; | |
if (!mUpdatingPositions) { | |
updateTextPositions(currentItem, mLastKnownPositionOffset, false); | |
} | |
mUpdatingText = false; | |
} | |
@Override | |
public void requestLayout() { | |
if (!mUpdatingText) { | |
super.requestLayout(); | |
} | |
} | |
void updateAdapter(PagerAdapter oldAdapter, PagerAdapter newAdapter) { | |
if (oldAdapter != null) { | |
oldAdapter.unregisterDataSetObserver(mPageListener); | |
mWatchingAdapter = null; | |
} | |
if (newAdapter != null) { | |
newAdapter.registerDataSetObserver(mPageListener); | |
mWatchingAdapter = new WeakReference<PagerAdapter>(newAdapter); | |
} | |
if (mPager != null) { | |
mLastKnownCurrentPage = -1; | |
mLastKnownPositionOffset = -1; | |
updateText(mPager.getCurrentItem(), newAdapter); | |
requestLayout(); | |
} | |
} | |
void updateTextPositions(int position, float positionOffset, boolean force) { | |
if (position != mLastKnownCurrentPage) { | |
updateText(position, mPager.getAdapter()); | |
} else if (!force && positionOffset == mLastKnownPositionOffset) { | |
return; | |
} | |
mUpdatingPositions = true; | |
final int currWidth = mCurrText.getMeasuredWidth(); | |
final int halfCurrWidth = currWidth / 2; | |
final int stripWidth = getWidth(); | |
final int stripHeight = getHeight(); | |
final int paddingLeft = getPaddingLeft(); | |
final int paddingRight = getPaddingRight(); | |
final int paddingTop = getPaddingTop(); | |
final int paddingBottom = getPaddingBottom(); | |
final int textPaddedLeft = paddingLeft + halfCurrWidth; | |
final int textPaddedRight = paddingRight + halfCurrWidth; | |
final int contentWidth = stripWidth - textPaddedLeft - textPaddedRight; | |
float currOffset = positionOffset + 0.5f; | |
if (currOffset > 1.f) { | |
currOffset -= 1.f; | |
} | |
final int currCenter = stripWidth - textPaddedRight - (int) (contentWidth * currOffset); | |
final int currLeft = currCenter - currWidth / 2; | |
final int currRight = currLeft + currWidth; | |
final int alignedCurrHeight = mCurrText.getMeasuredHeight(); | |
final int vgrav = mGravity & Gravity.VERTICAL_GRAVITY_MASK; | |
int currTop; | |
switch (vgrav) { | |
default: | |
case Gravity.TOP: | |
currTop = paddingTop; | |
break; | |
case Gravity.CENTER_VERTICAL: | |
final int paddedHeight = stripHeight - paddingTop - paddingBottom; | |
final int centeredTop = (paddedHeight - alignedCurrHeight) / 2; | |
currTop = centeredTop ; | |
break; | |
case Gravity.BOTTOM: | |
final int bottomGravTop = stripHeight - paddingBottom - alignedCurrHeight; | |
currTop = bottomGravTop; | |
break; | |
} | |
mCurrText.layout(currLeft, currTop, currRight, | |
currTop + mCurrText.getMeasuredHeight()); | |
mLastKnownPositionOffset = positionOffset; | |
mUpdatingPositions = false; | |
} | |
@Override | |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |
final int widthMode = MeasureSpec.getMode(widthMeasureSpec); | |
final int heightMode = MeasureSpec.getMode(heightMeasureSpec); | |
final int widthSize = MeasureSpec.getSize(widthMeasureSpec); | |
final int heightSize = MeasureSpec.getSize(heightMeasureSpec); | |
if (widthMode != MeasureSpec.EXACTLY) { | |
throw new IllegalStateException("Must measure with an exact width"); | |
} | |
int childHeight = heightSize; | |
int minHeight = getMinHeight(); | |
int padding = 0; | |
padding = getPaddingTop() + getPaddingBottom(); | |
childHeight -= padding; | |
final int childWidthSpec = MeasureSpec.makeMeasureSpec((int) (widthSize * 0.8f), | |
MeasureSpec.AT_MOST); | |
final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST); | |
mCurrText.measure(childWidthSpec, childHeightSpec); | |
if (heightMode == MeasureSpec.EXACTLY) { | |
setMeasuredDimension(widthSize, heightSize); | |
} else { | |
int textHeight = mCurrText.getMeasuredHeight(); | |
setMeasuredDimension(widthSize, Math.max(minHeight, textHeight + padding)); | |
} | |
} | |
@Override | |
protected void onLayout(boolean changed, int l, int t, int r, int b) { | |
if (mPager != null) { | |
final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0; | |
updateTextPositions(mLastKnownCurrentPage, offset, true); | |
} | |
} | |
int getMinHeight() { | |
int minHeight = 0; | |
final Drawable bg = getBackground(); | |
if (bg != null) { | |
minHeight = bg.getIntrinsicHeight(); | |
} | |
return minHeight; | |
} | |
private class PageListener extends DataSetObserver implements ViewPager.OnPageChangeListener, | |
ViewPager.OnAdapterChangeListener { | |
private int mScrollState; | |
@Override | |
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { | |
if (positionOffset > 0.5f) { | |
// Consider ourselves to be on the next page when we're 50% of the way there. | |
position++; | |
} | |
updateTextPositions(position, positionOffset, false); | |
} | |
@Override | |
public void onPageSelected(int position) { | |
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { | |
// Only update the text here if we're not dragging or settling. | |
updateText(mPager.getCurrentItem(), mPager.getAdapter()); | |
final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0; | |
updateTextPositions(mPager.getCurrentItem(), offset, true); | |
} | |
} | |
@Override | |
public void onPageScrollStateChanged(int state) { | |
mScrollState = state; | |
} | |
@Override | |
public void onAdapterChanged(PagerAdapter oldAdapter, PagerAdapter newAdapter) { | |
updateAdapter(oldAdapter, newAdapter); | |
} | |
@Override | |
public void onChanged() { | |
updateText(mPager.getCurrentItem(), mPager.getAdapter()); | |
final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0; | |
updateTextPositions(mPager.getCurrentItem(), offset, true); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment