2 * Copyright (C) 2011 Jake Wharton
3 * Copyright (C) 2011 Patrik Akerfeldt
4 * Copyright (C) 2011 Francisco Figueiredo Jr.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package com.viewpagerindicator;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.content.res.TypedArray;
23 import android.graphics.Canvas;
24 import android.graphics.Paint;
25 import android.graphics.Path;
26 import android.graphics.Rect;
27 import android.graphics.Typeface;
28 import android.graphics.drawable.Drawable;
29 import android.os.Parcel;
30 import android.os.Parcelable;
31 import android.support.v4.view.MotionEventCompat;
32 import android.support.v4.view.ViewConfigurationCompat;
33 import android.support.v4.view.ViewPager;
34 import android.util.AttributeSet;
35 import android.view.MotionEvent;
36 import android.view.View;
37 import android.view.ViewConfiguration;
39 import com.cyngn.eleven.R;
41 import java.util.ArrayList;
44 * A TitlePageIndicator is a PageIndicator which displays the title of left view
45 * (if exist), the title of the current select view (centered) and the title of
46 * the right view (if exist). When the user scrolls the ViewPager then titles are
49 public class TitlePageIndicator extends View implements PageIndicator {
51 * Percentage indicating what percentage of the screen width away from
52 * center should the underline be fully faded. A value of 0.25 means that
53 * halfway between the center of the screen and an edge.
55 private static final float SELECTION_FADE_PERCENTAGE = 0.25f;
58 * Percentage indicating what percentage of the screen width away from
59 * center should the selected text bold turn off. A value of 0.05 means
60 * that 10% between the center and an edge.
62 private static final float BOLD_FADE_PERCENTAGE = 0.05f;
65 * Title text used when no title is provided by the adapter.
67 private static final String EMPTY_TITLE = "";
70 * Interface for a callback when the center item has been clicked.
72 public interface OnCenterItemClickListener {
74 * Callback when the center item has been clicked.
76 * @param position Position of the current center item.
78 void onCenterItemClick(int position);
81 public enum IndicatorStyle {
82 None(0), Triangle(1), Underline(2);
84 public final int value;
86 private IndicatorStyle(int value) {
90 public static IndicatorStyle fromValue(int value) {
91 for (IndicatorStyle style : IndicatorStyle.values()) {
92 if (style.value == value) {
100 public enum LinePosition {
103 public final int value;
105 private LinePosition(int value) {
109 public static LinePosition fromValue(int value) {
110 for (LinePosition position : LinePosition.values()) {
111 if (position.value == value) {
119 private ViewPager mViewPager;
120 private ViewPager.OnPageChangeListener mListener;
121 private int mCurrentPage = -1;
122 private float mPageOffset;
123 private int mScrollState;
124 private final Paint mPaintText = new Paint();
125 private boolean mBoldText;
126 private boolean mBoldAll;
127 private int mColorText;
128 private int mColorSelected;
129 private Path mPath = new Path();
130 private final Rect mBounds = new Rect();
131 private final Paint mPaintFooterLine = new Paint();
132 private IndicatorStyle mFooterIndicatorStyle;
133 private LinePosition mLinePosition;
134 private final Paint mPaintFooterIndicator = new Paint();
135 private float mFooterIndicatorHeight;
136 private float mFooterIndicatorUnderlinePadding;
137 private float mFooterPadding;
138 private float mTitlePadding;
139 private float mTopPadding;
140 /** Left and right side padding for not active view titles. */
141 private float mClipPadding;
142 private float mFooterLineHeight;
144 private static final int INVALID_POINTER = -1;
146 private int mTouchSlop;
147 private float mLastMotionX = -1;
148 private int mActivePointerId = INVALID_POINTER;
149 private boolean mIsDragging;
151 private OnCenterItemClickListener mCenterItemClickListener;
154 public TitlePageIndicator(Context context) {
158 public TitlePageIndicator(Context context, AttributeSet attrs) {
159 this(context, attrs, R.attr.vpiTitlePageIndicatorStyle);
162 public TitlePageIndicator(Context context, AttributeSet attrs, int defStyle) {
163 super(context, attrs, defStyle);
164 if (isInEditMode()) return;
166 //Load defaults from resources
167 final Resources res = getResources();
168 final int defaultFooterColor = res.getColor(R.color.default_title_indicator_footer_color);
169 final float defaultFooterLineHeight = res.getDimension(R.dimen.default_title_indicator_footer_line_height);
170 final int defaultFooterIndicatorStyle = res.getInteger(R.integer.default_title_indicator_footer_indicator_style);
171 final float defaultFooterIndicatorHeight = res.getDimension(R.dimen.default_title_indicator_footer_indicator_height);
172 final float defaultFooterIndicatorUnderlinePadding = res.getDimension(R.dimen.default_title_indicator_footer_indicator_underline_padding);
173 final float defaultFooterPadding = res.getDimension(R.dimen.default_title_indicator_footer_padding);
174 final int defaultLinePosition = res.getInteger(R.integer.default_title_indicator_line_position);
175 final int defaultSelectedColor = res.getColor(R.color.default_title_indicator_selected_color);
176 final boolean defaultSelectedBold = res.getBoolean(R.bool.default_title_indicator_selected_bold);
177 final boolean defaultBoldAll = res.getBoolean(R.bool.default_title_indicator_bold_all);
178 final int defaultTextColor = res.getColor(R.color.default_title_indicator_text_color);
179 final float defaultTextSize = res.getDimension(R.dimen.default_title_indicator_text_size);
180 final float defaultTitlePadding = res.getDimension(R.dimen.default_title_indicator_title_padding);
181 final float defaultClipPadding = res.getDimension(R.dimen.default_title_indicator_clip_padding);
182 final float defaultTopPadding = res.getDimension(R.dimen.default_title_indicator_top_padding);
184 //Retrieve styles attributes
185 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TitlePageIndicator, defStyle, 0);
187 //Retrieve the colors to be used for this view and apply them.
188 mFooterLineHeight = a.getDimension(R.styleable.TitlePageIndicator_footerLineHeight, defaultFooterLineHeight);
189 mFooterIndicatorStyle = IndicatorStyle.fromValue(a.getInteger(R.styleable.TitlePageIndicator_footerIndicatorStyle, defaultFooterIndicatorStyle));
190 mFooterIndicatorHeight = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorHeight, defaultFooterIndicatorHeight);
191 mFooterIndicatorUnderlinePadding = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorUnderlinePadding, defaultFooterIndicatorUnderlinePadding);
192 mFooterPadding = a.getDimension(R.styleable.TitlePageIndicator_footerPadding, defaultFooterPadding);
193 mLinePosition = LinePosition.fromValue(a.getInteger(R.styleable.TitlePageIndicator_linePosition, defaultLinePosition));
194 mTopPadding = a.getDimension(R.styleable.TitlePageIndicator_topPadding, defaultTopPadding);
195 mTitlePadding = a.getDimension(R.styleable.TitlePageIndicator_titlePadding, defaultTitlePadding);
196 mClipPadding = a.getDimension(R.styleable.TitlePageIndicator_clipPadding, defaultClipPadding);
197 mColorSelected = a.getColor(R.styleable.TitlePageIndicator_selectedColor, defaultSelectedColor);
198 mColorText = a.getColor(R.styleable.TitlePageIndicator_android_textColor, defaultTextColor);
199 mBoldText = a.getBoolean(R.styleable.TitlePageIndicator_selectedBold, defaultSelectedBold);
200 mBoldAll = a.getBoolean(R.styleable.TitlePageIndicator_boldAll, defaultBoldAll);
202 final float textSize = a.getDimension(R.styleable.TitlePageIndicator_android_textSize, defaultTextSize);
203 final int footerColor = a.getColor(R.styleable.TitlePageIndicator_footerColor, defaultFooterColor);
204 mPaintText.setTextSize(textSize);
205 mPaintText.setAntiAlias(true);
206 mPaintFooterLine.setStyle(Paint.Style.FILL_AND_STROKE);
207 mPaintFooterLine.setStrokeWidth(mFooterLineHeight);
208 mPaintFooterLine.setColor(footerColor);
209 mPaintFooterIndicator.setStyle(Paint.Style.FILL_AND_STROKE);
210 mPaintFooterIndicator.setColor(footerColor);
212 Drawable background = a.getDrawable(R.styleable.TitlePageIndicator_android_background);
213 if (background != null) {
214 setBackgroundDrawable(background);
219 final ViewConfiguration configuration = ViewConfiguration.get(context);
220 mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
224 public int getFooterColor() {
225 return mPaintFooterLine.getColor();
228 public void setFooterColor(int footerColor) {
229 mPaintFooterLine.setColor(footerColor);
230 mPaintFooterIndicator.setColor(footerColor);
234 public float getFooterLineHeight() {
235 return mFooterLineHeight;
238 public void setFooterLineHeight(float footerLineHeight) {
239 mFooterLineHeight = footerLineHeight;
240 mPaintFooterLine.setStrokeWidth(mFooterLineHeight);
244 public float getFooterIndicatorHeight() {
245 return mFooterIndicatorHeight;
248 public void setFooterIndicatorHeight(float footerTriangleHeight) {
249 mFooterIndicatorHeight = footerTriangleHeight;
253 public float getFooterIndicatorPadding() {
254 return mFooterPadding;
257 public void setFooterIndicatorPadding(float footerIndicatorPadding) {
258 mFooterPadding = footerIndicatorPadding;
262 public IndicatorStyle getFooterIndicatorStyle() {
263 return mFooterIndicatorStyle;
266 public void setFooterIndicatorStyle(IndicatorStyle indicatorStyle) {
267 mFooterIndicatorStyle = indicatorStyle;
271 public LinePosition getLinePosition() {
272 return mLinePosition;
275 public void setLinePosition(LinePosition linePosition) {
276 mLinePosition = linePosition;
280 public int getSelectedColor() {
281 return mColorSelected;
284 public void setSelectedColor(int selectedColor) {
285 mColorSelected = selectedColor;
289 public boolean isSelectedBold() {
293 public void setSelectedBold(boolean selectedBold) {
294 mBoldText = selectedBold;
298 public boolean isBoldAll() {
302 public void setBoldAll(boolean boldAll) {
307 public int getTextColor() {
311 public void setTextColor(int textColor) {
312 mPaintText.setColor(textColor);
313 mColorText = textColor;
317 public float getTextSize() {
318 return mPaintText.getTextSize();
321 public void setTextSize(float textSize) {
322 mPaintText.setTextSize(textSize);
326 public float getTitlePadding() {
327 return this.mTitlePadding;
330 public void setTitlePadding(float titlePadding) {
331 mTitlePadding = titlePadding;
335 public float getTopPadding() {
336 return this.mTopPadding;
339 public void setTopPadding(float topPadding) {
340 mTopPadding = topPadding;
344 public float getClipPadding() {
345 return this.mClipPadding;
348 public void setClipPadding(float clipPadding) {
349 mClipPadding = clipPadding;
353 public void setTypeface(Typeface typeface) {
354 mPaintText.setTypeface(typeface);
358 public Typeface getTypeface() {
359 return mPaintText.getTypeface();
365 * @see android.view.View#onDraw(android.graphics.Canvas)
368 protected void onDraw(Canvas canvas) {
369 super.onDraw(canvas);
371 if (mViewPager == null) {
374 final int count = mViewPager.getAdapter().getCount();
379 // mCurrentPage is -1 on first start and after orientation changed. If so, retrieve the correct index from viewpager.
380 if (mCurrentPage == -1 && mViewPager != null) {
381 mCurrentPage = mViewPager.getCurrentItem();
384 //Calculate views bounds
385 ArrayList<Rect> bounds = calculateAllBounds(mPaintText);
386 final int boundsSize = bounds.size();
388 //Make sure we're on a page that still exists
389 if (mCurrentPage >= boundsSize) {
390 setCurrentItem(boundsSize - 1);
394 final int countMinusOne = count - 1;
395 final float halfWidth = getWidth() / 2f;
396 final int left = getLeft();
397 final float leftClip = left + mClipPadding;
398 final int width = getWidth();
399 int height = getHeight();
400 final int right = left + width;
401 final float rightClip = right - mClipPadding;
403 int page = mCurrentPage;
405 if (mPageOffset <= 0.5) {
406 offsetPercent = mPageOffset;
409 offsetPercent = 1 - mPageOffset;
411 final boolean currentSelected = (offsetPercent <= SELECTION_FADE_PERCENTAGE);
412 final boolean currentBold = (offsetPercent <= BOLD_FADE_PERCENTAGE);
413 final float selectedPercent = (SELECTION_FADE_PERCENTAGE - offsetPercent) / SELECTION_FADE_PERCENTAGE;
415 //Verify if the current view must be clipped to the screen
416 Rect curPageBound = bounds.get(mCurrentPage);
417 float curPageWidth = curPageBound.right - curPageBound.left;
418 if (curPageBound.left < leftClip) {
419 //Try to clip to the screen (left side)
420 clipViewOnTheLeft(curPageBound, curPageWidth, left);
422 if (curPageBound.right > rightClip) {
423 //Try to clip to the screen (right side)
424 clipViewOnTheRight(curPageBound, curPageWidth, right);
427 //Left views starting from the current position
428 if (mCurrentPage > 0) {
429 for (int i = mCurrentPage - 1; i >= 0; i--) {
430 Rect bound = bounds.get(i);
431 //Is left side is outside the screen
432 if (bound.left < leftClip) {
433 int w = bound.right - bound.left;
434 //Try to clip to the screen (left side)
435 clipViewOnTheLeft(bound, w, left);
436 //Except if there's an intersection with the right view
437 Rect rightBound = bounds.get(i + 1);
439 if (bound.right + mTitlePadding > rightBound.left) {
440 bound.left = (int) (rightBound.left - w - mTitlePadding);
441 bound.right = bound.left + w;
446 //Right views starting from the current position
447 if (mCurrentPage < countMinusOne) {
448 for (int i = mCurrentPage + 1 ; i < count; i++) {
449 Rect bound = bounds.get(i);
450 //If right side is outside the screen
451 if (bound.right > rightClip) {
452 int w = bound.right - bound.left;
453 //Try to clip to the screen (right side)
454 clipViewOnTheRight(bound, w, right);
455 //Except if there's an intersection with the left view
456 Rect leftBound = bounds.get(i - 1);
458 if (bound.left - mTitlePadding < leftBound.right) {
459 bound.left = (int) (leftBound.right + mTitlePadding);
460 bound.right = bound.left + w;
467 int colorTextAlpha = mColorText >>> 24;
468 for (int i = 0; i < count; i++) {
470 Rect bound = bounds.get(i);
471 //Only if one side is visible
472 if ((bound.left > left && bound.left < right) || (bound.right > left && bound.right < right)) {
473 final boolean currentPage = (i == page);
474 final CharSequence pageTitle = getTitle(i);
476 //Only set bold if we are within bounds
477 mPaintText.setFakeBoldText(mBoldAll || (currentPage && currentBold && mBoldText));
479 //Draw text as unselected
480 mPaintText.setColor(mColorText);
481 if(currentPage && currentSelected && !mBoldAll) {
482 //Fade out/in unselected text as the selected text fades in/out
483 mPaintText.setAlpha(colorTextAlpha - (int)(colorTextAlpha * selectedPercent));
486 //Except if there's an intersection with the right view
487 if (i < boundsSize - 1) {
488 Rect rightBound = bounds.get(i + 1);
490 if (bound.right + mTitlePadding > rightBound.left) {
491 int w = bound.right - bound.left;
492 bound.left = (int) (rightBound.left - w - mTitlePadding);
493 bound.right = bound.left + w;
496 canvas.drawText(pageTitle, 0, pageTitle.length(), bound.left, bound.bottom + mTopPadding, mPaintText);
498 //If we are within the selected bounds draw the selected text
499 if (currentPage && currentSelected) {
500 mPaintText.setColor(mColorSelected);
501 mPaintText.setAlpha((int)((mColorSelected >>> 24) * selectedPercent));
502 canvas.drawText(pageTitle, 0, pageTitle.length(), bound.left, bound.bottom + mTopPadding, mPaintText);
507 //If we want the line on the top change height to zero and invert the line height to trick the drawing code
508 float footerLineHeight = mFooterLineHeight;
509 float footerIndicatorLineHeight = mFooterIndicatorHeight;
510 if (mLinePosition == LinePosition.Top) {
512 footerLineHeight = -footerLineHeight;
513 footerIndicatorLineHeight = -footerIndicatorLineHeight;
516 //Draw the footer line
518 mPath.moveTo(0, height - footerLineHeight / 2f);
519 mPath.lineTo(width, height - footerLineHeight / 2f);
521 canvas.drawPath(mPath, mPaintFooterLine);
523 float heightMinusLine = height - footerLineHeight;
524 switch (mFooterIndicatorStyle) {
527 mPath.moveTo(halfWidth, heightMinusLine - footerIndicatorLineHeight);
528 mPath.lineTo(halfWidth + footerIndicatorLineHeight, heightMinusLine);
529 mPath.lineTo(halfWidth - footerIndicatorLineHeight, heightMinusLine);
531 canvas.drawPath(mPath, mPaintFooterIndicator);
535 if (!currentSelected || page >= boundsSize) {
539 Rect underlineBounds = bounds.get(page);
540 final float rightPlusPadding = underlineBounds.right + mFooterIndicatorUnderlinePadding;
541 final float leftMinusPadding = underlineBounds.left - mFooterIndicatorUnderlinePadding;
542 final float heightMinusLineMinusIndicator = heightMinusLine - footerIndicatorLineHeight;
545 mPath.moveTo(leftMinusPadding, heightMinusLine);
546 mPath.lineTo(rightPlusPadding, heightMinusLine);
547 mPath.lineTo(rightPlusPadding, heightMinusLineMinusIndicator);
548 mPath.lineTo(leftMinusPadding, heightMinusLineMinusIndicator);
551 mPaintFooterIndicator.setAlpha((int)(0xFF * selectedPercent));
552 canvas.drawPath(mPath, mPaintFooterIndicator);
553 mPaintFooterIndicator.setAlpha(0xFF);
558 public boolean onTouchEvent(android.view.MotionEvent ev) {
559 if (super.onTouchEvent(ev)) {
562 if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {
566 final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
568 case MotionEvent.ACTION_DOWN:
569 mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
570 mLastMotionX = ev.getX();
573 case MotionEvent.ACTION_MOVE: {
574 final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
575 final float x = MotionEventCompat.getX(ev, activePointerIndex);
576 final float deltaX = x - mLastMotionX;
579 if (Math.abs(deltaX) > mTouchSlop) {
586 if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {
587 mViewPager.fakeDragBy(deltaX);
594 case MotionEvent.ACTION_CANCEL:
595 case MotionEvent.ACTION_UP:
597 final int count = mViewPager.getAdapter().getCount();
598 final int width = getWidth();
599 final float halfWidth = width / 2f;
600 final float sixthWidth = width / 6f;
601 final float leftThird = halfWidth - sixthWidth;
602 final float rightThird = halfWidth + sixthWidth;
603 final float eventX = ev.getX();
605 if (eventX < leftThird) {
606 if (mCurrentPage > 0) {
607 if (action != MotionEvent.ACTION_CANCEL) {
608 mViewPager.setCurrentItem(mCurrentPage - 1);
612 } else if (eventX > rightThird) {
613 if (mCurrentPage < count - 1) {
614 if (action != MotionEvent.ACTION_CANCEL) {
615 mViewPager.setCurrentItem(mCurrentPage + 1);
621 if (mCenterItemClickListener != null && action != MotionEvent.ACTION_CANCEL) {
622 mCenterItemClickListener.onCenterItemClick(mCurrentPage);
628 mActivePointerId = INVALID_POINTER;
629 if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();
632 case MotionEventCompat.ACTION_POINTER_DOWN: {
633 final int index = MotionEventCompat.getActionIndex(ev);
634 mLastMotionX = MotionEventCompat.getX(ev, index);
635 mActivePointerId = MotionEventCompat.getPointerId(ev, index);
639 case MotionEventCompat.ACTION_POINTER_UP:
640 final int pointerIndex = MotionEventCompat.getActionIndex(ev);
641 final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
642 if (pointerId == mActivePointerId) {
643 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
644 mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
646 mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId));
654 * Set bounds for the right textView including clip padding.
656 * @param curViewBound
658 * @param curViewWidth
661 private void clipViewOnTheRight(Rect curViewBound, float curViewWidth, int right) {
662 curViewBound.right = (int) (right - mClipPadding);
663 curViewBound.left = (int) (curViewBound.right - curViewWidth);
667 * Set bounds for the left textView including clip padding.
669 * @param curViewBound
671 * @param curViewWidth
674 private void clipViewOnTheLeft(Rect curViewBound, float curViewWidth, int left) {
675 curViewBound.left = (int) (left + mClipPadding);
676 curViewBound.right = (int) (mClipPadding + curViewWidth);
680 * Calculate views bounds and scroll them according to the current index
685 private ArrayList<Rect> calculateAllBounds(Paint paint) {
686 ArrayList<Rect> list = new ArrayList<Rect>();
687 //For each views (If no values then add a fake one)
688 final int count = mViewPager.getAdapter().getCount();
689 final int width = getWidth();
690 final int halfWidth = width / 2;
691 for (int i = 0; i < count; i++) {
692 Rect bounds = calcBounds(i, paint);
693 int w = bounds.right - bounds.left;
694 int h = bounds.bottom - bounds.top;
695 bounds.left = (int)(halfWidth - (w / 2f) + ((i - mCurrentPage - mPageOffset) * width));
696 bounds.right = bounds.left + w;
706 * Calculate the bounds for a view's title
712 private Rect calcBounds(int index, Paint paint) {
713 //Calculate the text bounds
714 Rect bounds = new Rect();
715 CharSequence title = getTitle(index);
716 bounds.right = (int) paint.measureText(title, 0, title.length());
717 bounds.bottom = (int) (paint.descent() - paint.ascent());
722 public void setViewPager(ViewPager view) {
723 if (mViewPager == view) {
726 if (mViewPager != null) {
727 mViewPager.setOnPageChangeListener(null);
729 if (view.getAdapter() == null) {
730 throw new IllegalStateException("ViewPager does not have adapter instance.");
733 mViewPager.setOnPageChangeListener(this);
738 public void setViewPager(ViewPager view, int initialPosition) {
740 setCurrentItem(initialPosition);
744 public void notifyDataSetChanged() {
749 * Set a callback listener for the center item click.
751 * @param listener Callback instance.
753 public void setOnCenterItemClickListener(OnCenterItemClickListener listener) {
754 mCenterItemClickListener = listener;
758 public void setCurrentItem(int item) {
759 if (mViewPager == null) {
760 throw new IllegalStateException("ViewPager has not been bound.");
762 mViewPager.setCurrentItem(item);
768 public void onPageScrollStateChanged(int state) {
769 mScrollState = state;
771 if (mListener != null) {
772 mListener.onPageScrollStateChanged(state);
777 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
778 mCurrentPage = position;
779 mPageOffset = positionOffset;
782 if (mListener != null) {
783 mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
788 public void onPageSelected(int position) {
789 if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
790 mCurrentPage = position;
794 if (mListener != null) {
795 mListener.onPageSelected(position);
800 public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
801 mListener = listener;
805 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
806 //Measure our width in whatever mode specified
807 final int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
809 //Determine our height
811 final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
812 if (heightSpecMode == MeasureSpec.EXACTLY) {
813 //We were told how big to be
814 height = MeasureSpec.getSize(heightMeasureSpec);
816 //Calculate the text bounds
818 mBounds.bottom = (int) (mPaintText.descent() - mPaintText.ascent());
819 height = mBounds.bottom - mBounds.top + mFooterLineHeight + mFooterPadding + mTopPadding;
820 if (mFooterIndicatorStyle != IndicatorStyle.None) {
821 height += mFooterIndicatorHeight;
824 final int measuredHeight = (int)height;
826 setMeasuredDimension(measuredWidth, measuredHeight);
830 public void onRestoreInstanceState(Parcelable state) {
831 SavedState savedState = (SavedState)state;
832 super.onRestoreInstanceState(savedState.getSuperState());
833 mCurrentPage = savedState.currentPage;
838 public Parcelable onSaveInstanceState() {
839 Parcelable superState = super.onSaveInstanceState();
840 SavedState savedState = new SavedState(superState);
841 savedState.currentPage = mCurrentPage;
845 static class SavedState extends BaseSavedState {
848 public SavedState(Parcelable superState) {
852 private SavedState(Parcel in) {
854 currentPage = in.readInt();
858 public void writeToParcel(Parcel dest, int flags) {
859 super.writeToParcel(dest, flags);
860 dest.writeInt(currentPage);
863 @SuppressWarnings("UnusedDeclaration")
864 public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
866 public SavedState createFromParcel(Parcel in) {
867 return new SavedState(in);
871 public SavedState[] newArray(int size) {
872 return new SavedState[size];
877 private CharSequence getTitle(int i) {
878 CharSequence title = mViewPager.getAdapter().getPageTitle(i);