Repository: JakeWharton/ViewPagerIndicator Branch: master Commit: 8cd549f23f3d Files: 86 Total size: 214.4 KB Directory structure: gitextract_2dg_mrm5/ ├── .gitignore ├── CHANGELOG.md ├── README.md ├── checkstyle.xml ├── library/ │ ├── AndroidManifest.xml │ ├── libs/ │ │ └── android-support-v4.jar │ ├── pom.xml │ ├── project.properties │ ├── res/ │ │ ├── color/ │ │ │ ├── vpi__dark_theme.xml │ │ │ └── vpi__light_theme.xml │ │ ├── drawable/ │ │ │ └── vpi__tab_indicator.xml │ │ └── values/ │ │ ├── vpi__attrs.xml │ │ ├── vpi__colors.xml │ │ ├── vpi__defaults.xml │ │ └── vpi__styles.xml │ └── src/ │ └── com/ │ └── viewpagerindicator/ │ ├── CirclePageIndicator.java │ ├── IconPageIndicator.java │ ├── IconPagerAdapter.java │ ├── IcsLinearLayout.java │ ├── LinePageIndicator.java │ ├── PageIndicator.java │ ├── TabPageIndicator.java │ ├── TitlePageIndicator.java │ └── UnderlinePageIndicator.java ├── pom.xml └── sample/ ├── AndroidManifest.xml ├── libs/ │ └── android-support-v4.jar ├── pom.xml ├── project.properties ├── psd/ │ ├── icon_hdpi.psd │ ├── icon_mdpi.psd │ ├── icon_xhdpi.psd │ ├── logo.psd │ └── web.psd ├── res/ │ ├── drawable/ │ │ ├── custom_tab_indicator.xml │ │ ├── perm_group_calendar.xml │ │ ├── perm_group_camera.xml │ │ ├── perm_group_device_alarms.xml │ │ └── perm_group_location.xml │ ├── layout/ │ │ ├── simple_circles.xml │ │ ├── simple_circles_vertical.xml │ │ ├── simple_icons.xml │ │ ├── simple_lines.xml │ │ ├── simple_tabs.xml │ │ ├── simple_titles.xml │ │ ├── simple_titles_bottom.xml │ │ ├── simple_underlines.xml │ │ ├── themed_circles.xml │ │ ├── themed_lines.xml │ │ ├── themed_titles.xml │ │ └── themed_underlines.xml │ ├── menu/ │ │ └── menu.xml │ └── values/ │ └── styles.xml └── src/ └── com/ └── viewpagerindicator/ └── sample/ ├── BaseSampleActivity.java ├── ListSamples.java ├── SampleCirclesDefault.java ├── SampleCirclesInitialPage.java ├── SampleCirclesSnap.java ├── SampleCirclesStyledLayout.java ├── SampleCirclesStyledMethods.java ├── SampleCirclesStyledTheme.java ├── SampleCirclesWithListener.java ├── SampleIconsDefault.java ├── SampleLinesDefault.java ├── SampleLinesStyledLayout.java ├── SampleLinesStyledMethods.java ├── SampleLinesStyledTheme.java ├── SampleTabsDefault.java ├── SampleTabsStyled.java ├── SampleTabsWithIcons.java ├── SampleTitlesBottom.java ├── SampleTitlesCenterClickListener.java ├── SampleTitlesDefault.java ├── SampleTitlesInitialPage.java ├── SampleTitlesStyledLayout.java ├── SampleTitlesStyledMethods.java ├── SampleTitlesStyledTheme.java ├── SampleTitlesTriangle.java ├── SampleTitlesWithListener.java ├── SampleUnderlinesDefault.java ├── SampleUnderlinesNoFade.java ├── SampleUnderlinesStyledLayout.java ├── SampleUnderlinesStyledMethods.java ├── SampleUnderlinesStyledTheme.java ├── TestFragment.java └── TestFragmentAdapter.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ #Android generated bin gen #Eclipse .project .classpath .settings #IntelliJ IDEA .idea *.iml classes #Maven target release.properties pom.xml.* #Command line local.properties build.xml proguard-project.txt ================================================ FILE: CHANGELOG.md ================================================ Change Log ========== Version 2.4.1 *(2012-09-11)* ---------------------------- * Fix: `TitlePageIndicator`, `LinePageIndicator`, and `UnderlinePageIndicator` support of `android:background`. Version 2.4.0 *(2012-09-06)* ---------------------------- * New `IconPageIndicator`! Uses state-list images to represent pages. * `TabPageIndicator` now supports icons via `IconPagerAdapter` interface. * Support `android:background` attribute on `Canvas`-based views. * Title indicator allows for drawing its line, underline, and/or triangle on top of the titles for placement underneath a `ViewPager`. * Tab indicator now supports ICS-style dividers (see styled sample). * Fix: Do not attempt to change the `ViewPager` page when a motion is cancelled. * Fix: Long titles no longer overlap when swiping to the right. Version 2.3.1 *(2012-05-19)* ---------------------------- * Fix: Corrected filename with erroneous lowercase letter. Version 2.3.0 *(2012-05-19)* ---------------------------- * New `LinePageIndicator`! Draws small indicators lines much like the circle indicator but much more subtle. * New `UnderlinePageIndicator`! Acts like the indicator on the Ice Cream Sandwich application launcher. * Circle indicator now uses `android:orientation` rather than custom attribute. * Title indicator adapter callback now uses the standard `getPageTitle(int)` method introduced in the r6 version of the support library. * Title indicator now uses `android:textSize` and `android:textColor` in its styles. * Fix: Do not create objects in drawing, layout, or measurement steps of each indicator. * Fix: Improve offset detection when page margins are in use on the pager. * Maven: The dependency on the support library is now using an artifact from central rather than requiring you to deploy your own locally Version 2.2.3 *(2012-01-26)* ---------------------------- * Correctly handle removing the last page when it is selected. * Use antialiased text for the title indicators. * New circle fill color for circle indicators. Version 2.2.2 *(2011-12-31)* ---------------------------- * Fix incorrect `R.java` imports in all of the sample activities. Version 2.2.1 *(2011-12-31)* ---------------------------- * New `setTypeface(Typeface)` and `getTypeface()` methods for title indicator. (Thanks Dimitri Fedorov) * Added styled tab indicator sample. * Support for widths other than those that could be measured exactly. Version 2.2.0 *(2011-12-13)* ---------------------------- * Default title indicator style is now 'underline'. * Title indicator now allows specifying an `OnCenterItemClickListener` which will give you callbacks when the current item title has been clicked. (Thanks Chris Banes) Version 2.1.0 *(2011-11-30)* ---------------------------- * Indicators now have a `notifyDataSetChanged` method which should be called when changes are made to the adapter. * Fix: Avoid `NullPointerException`s when the `ViewPager` is not immediately bound to the indicator. Version 2.0.0 *(2011-11-20)* ---------------------------- * New `TabPageIndicator`! Uses the Ice Cream Sandwich-style action bar tabs which fill the width of the view when there are only a few tabs or provide horizontal animated scrolling when there are many. * Update to link against ACLv4r4. This will now be required in all implementing applications. * Allow dragging the title and circle indicators to drag the pager. * Remove orientation example as the DirectionalViewPager library has not been updated to ACLv4r4. Version 1.2.1 *(2011-10-20)* ---------------------------- Maven 3 is now required when building from the command line. * Update to support ADT 14. Version 1.2.0 *(2011-10-04)* ---------------------------- * Move to `com.viewpagerindicator` package. * Move maven group and artifact to `com.viewpagerindicator:library`. Version 1.1.0 *(2011-10-02)* ---------------------------- * Package changed from `com.jakewharton.android.viewpagerindicator` to `com.jakewharton.android.view`. * Add vertical orientation support to the circle indicator. * Fix: Corrected drawing bug where a single frame would be drawn as if the pager had completed its scroll when in fact it was still scrolling. (Thanks SimonVT!) Version 1.0.1 *(2011-09-15)* ---------------------------- * Fade selected title color to normal text color during the swipe to and from the center position. * Fix: Ensure both the indicator and footer line are updated when changing the footer color via the `setFooterColor` method. Version 1.0.0 *(2011-08-07)* ---------------------------- Initial release. ================================================ FILE: README.md ================================================ Android ViewPagerIndicator ========================== Paging indicator widgets that are compatible with the `ViewPager` from the [Android Support Library][2] to improve discoverability of content. Try out the sample application [on the Android Market][10]. ![ViewPagerIndicator Sample Screenshots][9] These widgets can also be used in conjunction with [ActionBarSherlock][3]! Usage ===== *For a working implementation of this project see the `sample/` folder.* 1. Include one of the widgets in your view. This should usually be placed adjacent to the `ViewPager` it represents. 2. In your `onCreate` method (or `onCreateView` for a fragment), bind the indicator to the `ViewPager`. //Set the pager with an adapter ViewPager pager = (ViewPager)findViewById(R.id.pager); pager.setAdapter(new TestAdapter(getSupportFragmentManager())); //Bind the title indicator to the adapter TitlePageIndicator titleIndicator = (TitlePageIndicator)findViewById(R.id.titles); titleIndicator.setViewPager(pager); 3. *(Optional)* If you use an `OnPageChangeListener` with your view pager you should set it in the indicator rather than on the pager directly. //continued from above titleIndicator.setOnPageChangeListener(mPageChangeListener); Theming ------- There are three ways to style the look of the indicators. 1. **Theme XML**. An attribute for each type of indicator is provided in which you can specify a custom style. 2. **Layout XML**. Through the use of a custom namespace you can include any desired styles. 3. **Object methods**. Both styles have getters and setters for each style attribute which can be changed at any point. Each indicator has a demo which creates the same look using each of these methods. Including In Your Project ------------------------- Android-ViewPagerIndicator is presented as an [Android library project][7]. A standalone JAR is not possible due to the theming capabilities offered by the indicator widgets. You can include this project by [referencing it as a library project][8] in Eclipse or ant. If you are a Maven user you can easily include the library by specifying it as a dependency: com.viewpagerindicator library 2.4.1 apklib This project depends on the `ViewPager` class which is available in the [Android Support Library][2] or [ActionBarSherlock][3]. Details for including one of those libraries is available on their respecitve web sites. Developed By ============ * Jake Wharton - Credits ------- * [Patrik Åkerfeldt][1] - Author of [ViewFlow][4], a precursor to the ViewPager, which supports paged views and is the original source of both the title and circle indicators. * [Francisco Figueiredo Jr.][5] - Idea and [first implementation][6] for fragment support via ViewPager. License ======= Copyright 2012 Jake Wharton Copyright 2011 Patrik Åkerfeldt Copyright 2011 Francisco Figueiredo Jr. 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. [1]: https://github.com/pakerfeldt [2]: http://developer.android.com/sdk/compatibility-library.html [3]: http://actionbarsherlock.com [4]: https://github.com/pakerfeldt/android-viewflow [5]: https://github.com/franciscojunior [6]: https://gist.github.com/1122947 [7]: http://developer.android.com/guide/developing/projects/projects-eclipse.html [8]: http://developer.android.com/guide/developing/projects/projects-eclipse.html#ReferencingLibraryProject [9]: https://raw.github.com/JakeWharton/Android-ViewPagerIndicator/master/sample/screens.png [10]: https://play.google.com/store/apps/details?id=com.viewpagerindicator.sample ================================================ FILE: checkstyle.xml ================================================ ================================================ FILE: library/AndroidManifest.xml ================================================ ================================================ FILE: library/pom.xml ================================================ 4.0.0 library Android-ViewPagerIndicator apklib com.viewpagerindicator parent 2.4.1 ../pom.xml com.google.android android provided com.google.android support-v4 src com.jayway.maven.plugins.android.generation2 android-maven-plugin true org.apache.maven.plugins maven-checkstyle-plugin ../checkstyle.xml verify checkstyle ================================================ FILE: library/project.properties ================================================ # This file is automatically generated by Android Tools. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file must be checked in Version Control Systems. # # To customize properties used by the Ant build system use, # "ant.properties", and override values to adapt the script to your # project structure. android.library=true # Project target. target=android-16 ================================================ FILE: library/res/color/vpi__dark_theme.xml ================================================ ================================================ FILE: library/res/color/vpi__light_theme.xml ================================================ ================================================ FILE: library/res/drawable/vpi__tab_indicator.xml ================================================ ================================================ FILE: library/res/values/vpi__attrs.xml ================================================ ================================================ FILE: library/res/values/vpi__colors.xml ================================================ #ff000000 #fff3f3f3 @color/vpi__background_holo_light @color/vpi__background_holo_dark #ff4c4c4c #ffb2b2b2 @color/vpi__bright_foreground_holo_light @color/vpi__bright_foreground_holo_dark ================================================ FILE: library/res/values/vpi__defaults.xml ================================================ true #FFFFFFFF #00000000 0 3dp false #FFDDDDDD 1dp 12dp 4dp 1dp #FF33B5E5 #FFBBBBBB true 4dp #FF33B5E5 2dp 2 4dp 20dp 7dp 0 #FFFFFFFF true #BBFFFFFF 15dp 5dp 7dp true 300 400 #FF33B5E5 ================================================ FILE: library/res/values/vpi__styles.xml ================================================ ================================================ FILE: library/src/com/viewpagerindicator/CirclePageIndicator.java ================================================ /* * Copyright (C) 2011 Patrik Akerfeldt * Copyright (C) 2011 Jake Wharton * * 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 com.viewpagerindicator; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.ViewConfigurationCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import static android.graphics.Paint.ANTI_ALIAS_FLAG; import static android.widget.LinearLayout.HORIZONTAL; import static android.widget.LinearLayout.VERTICAL; /** * Draws circles (one for each view). The current view position is filled and * others are only stroked. */ public class CirclePageIndicator extends View implements PageIndicator { private static final int INVALID_POINTER = -1; private float mRadius; private final Paint mPaintPageFill = new Paint(ANTI_ALIAS_FLAG); private final Paint mPaintStroke = new Paint(ANTI_ALIAS_FLAG); private final Paint mPaintFill = new Paint(ANTI_ALIAS_FLAG); private ViewPager mViewPager; private ViewPager.OnPageChangeListener mListener; private int mCurrentPage; private int mSnapPage; private float mPageOffset; private int mScrollState; private int mOrientation; private boolean mCentered; private boolean mSnap; private int mTouchSlop; private float mLastMotionX = -1; private int mActivePointerId = INVALID_POINTER; private boolean mIsDragging; public CirclePageIndicator(Context context) { this(context, null); } public CirclePageIndicator(Context context, AttributeSet attrs) { this(context, attrs, R.attr.vpiCirclePageIndicatorStyle); } public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); if (isInEditMode()) return; //Load defaults from resources final Resources res = getResources(); final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color); final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color); final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation); final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color); final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width); final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius); final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered); final boolean defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap); //Retrieve styles attributes TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, 0); mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered); mOrientation = a.getInt(R.styleable.CirclePageIndicator_android_orientation, defaultOrientation); mPaintPageFill.setStyle(Style.FILL); mPaintPageFill.setColor(a.getColor(R.styleable.CirclePageIndicator_pageColor, defaultPageColor)); mPaintStroke.setStyle(Style.STROKE); mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor)); mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth)); mPaintFill.setStyle(Style.FILL); mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor)); mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius); mSnap = a.getBoolean(R.styleable.CirclePageIndicator_snap, defaultSnap); Drawable background = a.getDrawable(R.styleable.CirclePageIndicator_android_background); if (background != null) { setBackgroundDrawable(background); } a.recycle(); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); } public void setCentered(boolean centered) { mCentered = centered; invalidate(); } public boolean isCentered() { return mCentered; } public void setPageColor(int pageColor) { mPaintPageFill.setColor(pageColor); invalidate(); } public int getPageColor() { return mPaintPageFill.getColor(); } public void setFillColor(int fillColor) { mPaintFill.setColor(fillColor); invalidate(); } public int getFillColor() { return mPaintFill.getColor(); } public void setOrientation(int orientation) { switch (orientation) { case HORIZONTAL: case VERTICAL: mOrientation = orientation; requestLayout(); break; default: throw new IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL."); } } public int getOrientation() { return mOrientation; } public void setStrokeColor(int strokeColor) { mPaintStroke.setColor(strokeColor); invalidate(); } public int getStrokeColor() { return mPaintStroke.getColor(); } public void setStrokeWidth(float strokeWidth) { mPaintStroke.setStrokeWidth(strokeWidth); invalidate(); } public float getStrokeWidth() { return mPaintStroke.getStrokeWidth(); } public void setRadius(float radius) { mRadius = radius; invalidate(); } public float getRadius() { return mRadius; } public void setSnap(boolean snap) { mSnap = snap; invalidate(); } public boolean isSnap() { return mSnap; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mViewPager == null) { return; } final int count = mViewPager.getAdapter().getCount(); if (count == 0) { return; } if (mCurrentPage >= count) { setCurrentItem(count - 1); return; } int longSize; int longPaddingBefore; int longPaddingAfter; int shortPaddingBefore; if (mOrientation == HORIZONTAL) { longSize = getWidth(); longPaddingBefore = getPaddingLeft(); longPaddingAfter = getPaddingRight(); shortPaddingBefore = getPaddingTop(); } else { longSize = getHeight(); longPaddingBefore = getPaddingTop(); longPaddingAfter = getPaddingBottom(); shortPaddingBefore = getPaddingLeft(); } final float threeRadius = mRadius * 3; final float shortOffset = shortPaddingBefore + mRadius; float longOffset = longPaddingBefore + mRadius; if (mCentered) { longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) - ((count * threeRadius) / 2.0f); } float dX; float dY; float pageFillRadius = mRadius; if (mPaintStroke.getStrokeWidth() > 0) { pageFillRadius -= mPaintStroke.getStrokeWidth() / 2.0f; } //Draw stroked circles for (int iLoop = 0; iLoop < count; iLoop++) { float drawLong = longOffset + (iLoop * threeRadius); if (mOrientation == HORIZONTAL) { dX = drawLong; dY = shortOffset; } else { dX = shortOffset; dY = drawLong; } // Only paint fill if not completely transparent if (mPaintPageFill.getAlpha() > 0) { canvas.drawCircle(dX, dY, pageFillRadius, mPaintPageFill); } // Only paint stroke if a stroke width was non-zero if (pageFillRadius != mRadius) { canvas.drawCircle(dX, dY, mRadius, mPaintStroke); } } //Draw the filled circle according to the current scroll float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius; if (!mSnap) { cx += mPageOffset * threeRadius; } if (mOrientation == HORIZONTAL) { dX = longOffset + cx; dY = shortOffset; } else { dX = shortOffset; dY = longOffset + cx; } canvas.drawCircle(dX, dY, mRadius, mPaintFill); } public boolean onTouchEvent(android.view.MotionEvent ev) { if (super.onTouchEvent(ev)) { return true; } if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) { return false; } final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; switch (action) { case MotionEvent.ACTION_DOWN: mActivePointerId = MotionEventCompat.getPointerId(ev, 0); mLastMotionX = ev.getX(); break; case MotionEvent.ACTION_MOVE: { final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, activePointerIndex); final float deltaX = x - mLastMotionX; if (!mIsDragging) { if (Math.abs(deltaX) > mTouchSlop) { mIsDragging = true; } } if (mIsDragging) { mLastMotionX = x; if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) { mViewPager.fakeDragBy(deltaX); } } break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (!mIsDragging) { final int count = mViewPager.getAdapter().getCount(); final int width = getWidth(); final float halfWidth = width / 2f; final float sixthWidth = width / 6f; if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) { if (action != MotionEvent.ACTION_CANCEL) { mViewPager.setCurrentItem(mCurrentPage - 1); } return true; } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) { if (action != MotionEvent.ACTION_CANCEL) { mViewPager.setCurrentItem(mCurrentPage + 1); } return true; } } mIsDragging = false; mActivePointerId = INVALID_POINTER; if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag(); break; case MotionEventCompat.ACTION_POINTER_DOWN: { final int index = MotionEventCompat.getActionIndex(ev); mLastMotionX = MotionEventCompat.getX(ev, index); mActivePointerId = MotionEventCompat.getPointerId(ev, index); break; } case MotionEventCompat.ACTION_POINTER_UP: final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) { final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); } mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId)); break; } return true; } @Override public void setViewPager(ViewPager view) { if (mViewPager == view) { return; } if (mViewPager != null) { mViewPager.setOnPageChangeListener(null); } if (view.getAdapter() == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } mViewPager = view; mViewPager.setOnPageChangeListener(this); invalidate(); } @Override public void setViewPager(ViewPager view, int initialPosition) { setViewPager(view); setCurrentItem(initialPosition); } @Override public void setCurrentItem(int item) { if (mViewPager == null) { throw new IllegalStateException("ViewPager has not been bound."); } mViewPager.setCurrentItem(item); mCurrentPage = item; invalidate(); } @Override public void notifyDataSetChanged() { invalidate(); } @Override public void onPageScrollStateChanged(int state) { mScrollState = state; if (mListener != null) { mListener.onPageScrollStateChanged(state); } } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { mCurrentPage = position; mPageOffset = positionOffset; invalidate(); if (mListener != null) { mListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } } @Override public void onPageSelected(int position) { if (mSnap || mScrollState == ViewPager.SCROLL_STATE_IDLE) { mCurrentPage = position; mSnapPage = position; invalidate(); } if (mListener != null) { mListener.onPageSelected(position); } } @Override public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { mListener = listener; } /* * (non-Javadoc) * * @see android.view.View#onMeasure(int, int) */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mOrientation == HORIZONTAL) { setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec)); } else { setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec)); } } /** * Determines the width of this view * * @param measureSpec * A measureSpec packed into an int * @return The width of the view, honoring constraints from measureSpec */ private int measureLong(int measureSpec) { int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) { //We were told how big to be result = specSize; } else { //Calculate the width according the views count final int count = mViewPager.getAdapter().getCount(); result = (int)(getPaddingLeft() + getPaddingRight() + (count * 2 * mRadius) + (count - 1) * mRadius + 1); //Respect AT_MOST value if that was what is called for by measureSpec if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } /** * Determines the height of this view * * @param measureSpec * A measureSpec packed into an int * @return The height of the view, honoring constraints from measureSpec */ private int measureShort(int measureSpec) { int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { //We were told how big to be result = specSize; } else { //Measure the height result = (int)(2 * mRadius + getPaddingTop() + getPaddingBottom() + 1); //Respect AT_MOST value if that was what is called for by measureSpec if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState savedState = (SavedState)state; super.onRestoreInstanceState(savedState.getSuperState()); mCurrentPage = savedState.currentPage; mSnapPage = savedState.currentPage; requestLayout(); } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState savedState = new SavedState(superState); savedState.currentPage = mCurrentPage; return savedState; } static class SavedState extends BaseSavedState { int currentPage; public SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); currentPage = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(currentPage); } @SuppressWarnings("UnusedDeclaration") public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } } ================================================ FILE: library/src/com/viewpagerindicator/IconPageIndicator.java ================================================ /* * Copyright (C) 2011 The Android Open Source Project * Copyright (C) 2012 Jake Wharton * * 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 com.viewpagerindicator; import android.content.Context; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.widget.HorizontalScrollView; import android.widget.ImageView; import static android.view.ViewGroup.LayoutParams.FILL_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; /** * This widget implements the dynamic action bar tab behavior that can change * across different configurations or circumstances. */ public class IconPageIndicator extends HorizontalScrollView implements PageIndicator { private final IcsLinearLayout mIconsLayout; private ViewPager mViewPager; private OnPageChangeListener mListener; private Runnable mIconSelector; private int mSelectedIndex; public IconPageIndicator(Context context) { this(context, null); } public IconPageIndicator(Context context, AttributeSet attrs) { super(context, attrs); setHorizontalScrollBarEnabled(false); mIconsLayout = new IcsLinearLayout(context, R.attr.vpiIconPageIndicatorStyle); addView(mIconsLayout, new LayoutParams(WRAP_CONTENT, FILL_PARENT, Gravity.CENTER)); } private void animateToIcon(final int position) { final View iconView = mIconsLayout.getChildAt(position); if (mIconSelector != null) { removeCallbacks(mIconSelector); } mIconSelector = new Runnable() { public void run() { final int scrollPos = iconView.getLeft() - (getWidth() - iconView.getWidth()) / 2; smoothScrollTo(scrollPos, 0); mIconSelector = null; } }; post(mIconSelector); } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); if (mIconSelector != null) { // Re-post the selector we saved post(mIconSelector); } } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mIconSelector != null) { removeCallbacks(mIconSelector); } } @Override public void onPageScrollStateChanged(int arg0) { if (mListener != null) { mListener.onPageScrollStateChanged(arg0); } } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { if (mListener != null) { mListener.onPageScrolled(arg0, arg1, arg2); } } @Override public void onPageSelected(int arg0) { setCurrentItem(arg0); if (mListener != null) { mListener.onPageSelected(arg0); } } @Override public void setViewPager(ViewPager view) { if (mViewPager == view) { return; } if (mViewPager != null) { mViewPager.setOnPageChangeListener(null); } PagerAdapter adapter = view.getAdapter(); if (adapter == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } mViewPager = view; view.setOnPageChangeListener(this); notifyDataSetChanged(); } public void notifyDataSetChanged() { mIconsLayout.removeAllViews(); IconPagerAdapter iconAdapter = (IconPagerAdapter) mViewPager.getAdapter(); int count = iconAdapter.getCount(); for (int i = 0; i < count; i++) { ImageView view = new ImageView(getContext(), null, R.attr.vpiIconPageIndicatorStyle); view.setImageResource(iconAdapter.getIconResId(i)); mIconsLayout.addView(view); } if (mSelectedIndex > count) { mSelectedIndex = count - 1; } setCurrentItem(mSelectedIndex); requestLayout(); } @Override public void setViewPager(ViewPager view, int initialPosition) { setViewPager(view); setCurrentItem(initialPosition); } @Override public void setCurrentItem(int item) { if (mViewPager == null) { throw new IllegalStateException("ViewPager has not been bound."); } mSelectedIndex = item; mViewPager.setCurrentItem(item); int tabCount = mIconsLayout.getChildCount(); for (int i = 0; i < tabCount; i++) { View child = mIconsLayout.getChildAt(i); boolean isSelected = (i == item); child.setSelected(isSelected); if (isSelected) { animateToIcon(item); } } } @Override public void setOnPageChangeListener(OnPageChangeListener listener) { mListener = listener; } } ================================================ FILE: library/src/com/viewpagerindicator/IconPagerAdapter.java ================================================ package com.viewpagerindicator; public interface IconPagerAdapter { /** * Get icon representing the page at {@code index} in the adapter. */ int getIconResId(int index); // From PagerAdapter int getCount(); } ================================================ FILE: library/src/com/viewpagerindicator/IcsLinearLayout.java ================================================ package com.viewpagerindicator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.view.View; import android.widget.LinearLayout; /** * A simple extension of a regular linear layout that supports the divider API * of Android 4.0+. The dividers are added adjacent to the children by changing * their layout params. If you need to rely on the margins which fall in the * same orientation as the layout you should wrap the child in a simple * {@link android.widget.FrameLayout} so it can receive the margin. */ class IcsLinearLayout extends LinearLayout { private static final int[] LL = new int[] { /* 0 */ android.R.attr.divider, /* 1 */ android.R.attr.showDividers, /* 2 */ android.R.attr.dividerPadding, }; private static final int LL_DIVIDER = 0; private static final int LL_SHOW_DIVIDER = 1; private static final int LL_DIVIDER_PADDING = 2; private Drawable mDivider; private int mDividerWidth; private int mDividerHeight; private int mShowDividers; private int mDividerPadding; public IcsLinearLayout(Context context, int themeAttr) { super(context); TypedArray a = context.obtainStyledAttributes(null, LL, themeAttr, 0); setDividerDrawable(a.getDrawable(IcsLinearLayout.LL_DIVIDER)); mDividerPadding = a.getDimensionPixelSize(LL_DIVIDER_PADDING, 0); mShowDividers = a.getInteger(LL_SHOW_DIVIDER, SHOW_DIVIDER_NONE); a.recycle(); } public void setDividerDrawable(Drawable divider) { if (divider == mDivider) { return; } mDivider = divider; if (divider != null) { mDividerWidth = divider.getIntrinsicWidth(); mDividerHeight = divider.getIntrinsicHeight(); } else { mDividerWidth = 0; mDividerHeight = 0; } setWillNotDraw(divider == null); requestLayout(); } @Override protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final int index = indexOfChild(child); final int orientation = getOrientation(); final LayoutParams params = (LayoutParams) child.getLayoutParams(); if (hasDividerBeforeChildAt(index)) { if (orientation == VERTICAL) { //Account for the divider by pushing everything up params.topMargin = mDividerHeight; } else { //Account for the divider by pushing everything left params.leftMargin = mDividerWidth; } } final int count = getChildCount(); if (index == count - 1) { if (hasDividerBeforeChildAt(count)) { if (orientation == VERTICAL) { params.bottomMargin = mDividerHeight; } else { params.rightMargin = mDividerWidth; } } } super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); } @Override protected void onDraw(Canvas canvas) { if (mDivider != null) { if (getOrientation() == VERTICAL) { drawDividersVertical(canvas); } else { drawDividersHorizontal(canvas); } } super.onDraw(canvas); } private void drawDividersVertical(Canvas canvas) { final int count = getChildCount(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child != null && child.getVisibility() != GONE) { if (hasDividerBeforeChildAt(i)) { final android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) child.getLayoutParams(); final int top = child.getTop() - lp.topMargin/* - mDividerHeight*/; drawHorizontalDivider(canvas, top); } } } if (hasDividerBeforeChildAt(count)) { final View child = getChildAt(count - 1); int bottom = 0; if (child == null) { bottom = getHeight() - getPaddingBottom() - mDividerHeight; } else { //final LayoutParams lp = (LayoutParams) child.getLayoutParams(); bottom = child.getBottom()/* + lp.bottomMargin*/; } drawHorizontalDivider(canvas, bottom); } } private void drawDividersHorizontal(Canvas canvas) { final int count = getChildCount(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child != null && child.getVisibility() != GONE) { if (hasDividerBeforeChildAt(i)) { final android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) child.getLayoutParams(); final int left = child.getLeft() - lp.leftMargin/* - mDividerWidth*/; drawVerticalDivider(canvas, left); } } } if (hasDividerBeforeChildAt(count)) { final View child = getChildAt(count - 1); int right = 0; if (child == null) { right = getWidth() - getPaddingRight() - mDividerWidth; } else { //final LayoutParams lp = (LayoutParams) child.getLayoutParams(); right = child.getRight()/* + lp.rightMargin*/; } drawVerticalDivider(canvas, right); } } private void drawHorizontalDivider(Canvas canvas, int top) { mDivider.setBounds(getPaddingLeft() + mDividerPadding, top, getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight); mDivider.draw(canvas); } private void drawVerticalDivider(Canvas canvas, int left) { mDivider.setBounds(left, getPaddingTop() + mDividerPadding, left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding); mDivider.draw(canvas); } private boolean hasDividerBeforeChildAt(int childIndex) { if (childIndex == 0 || childIndex == getChildCount()) { return false; } if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) { boolean hasVisibleViewBefore = false; for (int i = childIndex - 1; i >= 0; i--) { if (getChildAt(i).getVisibility() != GONE) { hasVisibleViewBefore = true; break; } } return hasVisibleViewBefore; } return false; } } ================================================ FILE: library/src/com/viewpagerindicator/LinePageIndicator.java ================================================ /* * Copyright (C) 2012 Jake Wharton * * 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 com.viewpagerindicator; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.ViewConfigurationCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.util.FloatMath; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; /** * Draws a line for each page. The current page line is colored differently * than the unselected page lines. */ public class LinePageIndicator extends View implements PageIndicator { private static final int INVALID_POINTER = -1; private final Paint mPaintUnselected = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint mPaintSelected = new Paint(Paint.ANTI_ALIAS_FLAG); private ViewPager mViewPager; private ViewPager.OnPageChangeListener mListener; private int mCurrentPage; private boolean mCentered; private float mLineWidth; private float mGapWidth; private int mTouchSlop; private float mLastMotionX = -1; private int mActivePointerId = INVALID_POINTER; private boolean mIsDragging; public LinePageIndicator(Context context) { this(context, null); } public LinePageIndicator(Context context, AttributeSet attrs) { this(context, attrs, R.attr.vpiLinePageIndicatorStyle); } public LinePageIndicator(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); if (isInEditMode()) return; final Resources res = getResources(); //Load defaults from resources final int defaultSelectedColor = res.getColor(R.color.default_line_indicator_selected_color); final int defaultUnselectedColor = res.getColor(R.color.default_line_indicator_unselected_color); final float defaultLineWidth = res.getDimension(R.dimen.default_line_indicator_line_width); final float defaultGapWidth = res.getDimension(R.dimen.default_line_indicator_gap_width); final float defaultStrokeWidth = res.getDimension(R.dimen.default_line_indicator_stroke_width); final boolean defaultCentered = res.getBoolean(R.bool.default_line_indicator_centered); //Retrieve styles attributes TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LinePageIndicator, defStyle, 0); mCentered = a.getBoolean(R.styleable.LinePageIndicator_centered, defaultCentered); mLineWidth = a.getDimension(R.styleable.LinePageIndicator_lineWidth, defaultLineWidth); mGapWidth = a.getDimension(R.styleable.LinePageIndicator_gapWidth, defaultGapWidth); setStrokeWidth(a.getDimension(R.styleable.LinePageIndicator_strokeWidth, defaultStrokeWidth)); mPaintUnselected.setColor(a.getColor(R.styleable.LinePageIndicator_unselectedColor, defaultUnselectedColor)); mPaintSelected.setColor(a.getColor(R.styleable.LinePageIndicator_selectedColor, defaultSelectedColor)); Drawable background = a.getDrawable(R.styleable.LinePageIndicator_android_background); if (background != null) { setBackgroundDrawable(background); } a.recycle(); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); } public void setCentered(boolean centered) { mCentered = centered; invalidate(); } public boolean isCentered() { return mCentered; } public void setUnselectedColor(int unselectedColor) { mPaintUnselected.setColor(unselectedColor); invalidate(); } public int getUnselectedColor() { return mPaintUnselected.getColor(); } public void setSelectedColor(int selectedColor) { mPaintSelected.setColor(selectedColor); invalidate(); } public int getSelectedColor() { return mPaintSelected.getColor(); } public void setLineWidth(float lineWidth) { mLineWidth = lineWidth; invalidate(); } public float getLineWidth() { return mLineWidth; } public void setStrokeWidth(float lineHeight) { mPaintSelected.setStrokeWidth(lineHeight); mPaintUnselected.setStrokeWidth(lineHeight); invalidate(); } public float getStrokeWidth() { return mPaintSelected.getStrokeWidth(); } public void setGapWidth(float gapWidth) { mGapWidth = gapWidth; invalidate(); } public float getGapWidth() { return mGapWidth; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mViewPager == null) { return; } final int count = mViewPager.getAdapter().getCount(); if (count == 0) { return; } if (mCurrentPage >= count) { setCurrentItem(count - 1); return; } final float lineWidthAndGap = mLineWidth + mGapWidth; final float indicatorWidth = (count * lineWidthAndGap) - mGapWidth; final float paddingTop = getPaddingTop(); final float paddingLeft = getPaddingLeft(); final float paddingRight = getPaddingRight(); float verticalOffset = paddingTop + ((getHeight() - paddingTop - getPaddingBottom()) / 2.0f); float horizontalOffset = paddingLeft; if (mCentered) { horizontalOffset += ((getWidth() - paddingLeft - paddingRight) / 2.0f) - (indicatorWidth / 2.0f); } //Draw stroked circles for (int i = 0; i < count; i++) { float dx1 = horizontalOffset + (i * lineWidthAndGap); float dx2 = dx1 + mLineWidth; canvas.drawLine(dx1, verticalOffset, dx2, verticalOffset, (i == mCurrentPage) ? mPaintSelected : mPaintUnselected); } } public boolean onTouchEvent(android.view.MotionEvent ev) { if (super.onTouchEvent(ev)) { return true; } if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) { return false; } final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; switch (action) { case MotionEvent.ACTION_DOWN: mActivePointerId = MotionEventCompat.getPointerId(ev, 0); mLastMotionX = ev.getX(); break; case MotionEvent.ACTION_MOVE: { final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, activePointerIndex); final float deltaX = x - mLastMotionX; if (!mIsDragging) { if (Math.abs(deltaX) > mTouchSlop) { mIsDragging = true; } } if (mIsDragging) { mLastMotionX = x; if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) { mViewPager.fakeDragBy(deltaX); } } break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (!mIsDragging) { final int count = mViewPager.getAdapter().getCount(); final int width = getWidth(); final float halfWidth = width / 2f; final float sixthWidth = width / 6f; if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) { if (action != MotionEvent.ACTION_CANCEL) { mViewPager.setCurrentItem(mCurrentPage - 1); } return true; } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) { if (action != MotionEvent.ACTION_CANCEL) { mViewPager.setCurrentItem(mCurrentPage + 1); } return true; } } mIsDragging = false; mActivePointerId = INVALID_POINTER; if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag(); break; case MotionEventCompat.ACTION_POINTER_DOWN: { final int index = MotionEventCompat.getActionIndex(ev); mLastMotionX = MotionEventCompat.getX(ev, index); mActivePointerId = MotionEventCompat.getPointerId(ev, index); break; } case MotionEventCompat.ACTION_POINTER_UP: final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) { final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); } mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId)); break; } return true; } @Override public void setViewPager(ViewPager viewPager) { if (mViewPager == viewPager) { return; } if (mViewPager != null) { //Clear us from the old pager. mViewPager.setOnPageChangeListener(null); } if (viewPager.getAdapter() == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } mViewPager = viewPager; mViewPager.setOnPageChangeListener(this); invalidate(); } @Override public void setViewPager(ViewPager view, int initialPosition) { setViewPager(view); setCurrentItem(initialPosition); } @Override public void setCurrentItem(int item) { if (mViewPager == null) { throw new IllegalStateException("ViewPager has not been bound."); } mViewPager.setCurrentItem(item); mCurrentPage = item; invalidate(); } @Override public void notifyDataSetChanged() { invalidate(); } @Override public void onPageScrollStateChanged(int state) { if (mListener != null) { mListener.onPageScrollStateChanged(state); } } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (mListener != null) { mListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } } @Override public void onPageSelected(int position) { mCurrentPage = position; invalidate(); if (mListener != null) { mListener.onPageSelected(position); } } @Override public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { mListener = listener; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } /** * Determines the width of this view * * @param measureSpec * A measureSpec packed into an int * @return The width of the view, honoring constraints from measureSpec */ private int measureWidth(int measureSpec) { float result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) { //We were told how big to be result = specSize; } else { //Calculate the width according the views count final int count = mViewPager.getAdapter().getCount(); result = getPaddingLeft() + getPaddingRight() + (count * mLineWidth) + ((count - 1) * mGapWidth); //Respect AT_MOST value if that was what is called for by measureSpec if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return (int)FloatMath.ceil(result); } /** * Determines the height of this view * * @param measureSpec * A measureSpec packed into an int * @return The height of the view, honoring constraints from measureSpec */ private int measureHeight(int measureSpec) { float result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { //We were told how big to be result = specSize; } else { //Measure the height result = mPaintSelected.getStrokeWidth() + getPaddingTop() + getPaddingBottom(); //Respect AT_MOST value if that was what is called for by measureSpec if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return (int)FloatMath.ceil(result); } @Override public void onRestoreInstanceState(Parcelable state) { SavedState savedState = (SavedState)state; super.onRestoreInstanceState(savedState.getSuperState()); mCurrentPage = savedState.currentPage; requestLayout(); } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState savedState = new SavedState(superState); savedState.currentPage = mCurrentPage; return savedState; } static class SavedState extends BaseSavedState { int currentPage; public SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); currentPage = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(currentPage); } @SuppressWarnings("UnusedDeclaration") public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } } ================================================ FILE: library/src/com/viewpagerindicator/PageIndicator.java ================================================ /* * Copyright (C) 2011 Patrik Akerfeldt * Copyright (C) 2011 Jake Wharton * * 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 com.viewpagerindicator; import android.support.v4.view.ViewPager; /** * A PageIndicator is responsible to show an visual indicator on the total views * number and the current visible view. */ public interface PageIndicator extends ViewPager.OnPageChangeListener { /** * Bind the indicator to a ViewPager. * * @param view */ void setViewPager(ViewPager view); /** * Bind the indicator to a ViewPager. * * @param view * @param initialPosition */ void setViewPager(ViewPager view, int initialPosition); /** *

Set the current page of both the ViewPager and indicator.

* *

This must be used if you need to set the page before * the views are drawn on screen (e.g., default start page).

* * @param item */ void setCurrentItem(int item); /** * Set a page change listener which will receive forwarded events. * * @param listener */ void setOnPageChangeListener(ViewPager.OnPageChangeListener listener); /** * Notify the indicator that the fragment list has changed. */ void notifyDataSetChanged(); } ================================================ FILE: library/src/com/viewpagerindicator/TabPageIndicator.java ================================================ /* * Copyright (C) 2011 The Android Open Source Project * Copyright (C) 2011 Jake Wharton * * 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 com.viewpagerindicator; import android.content.Context; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import android.widget.TextView; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; /** * This widget implements the dynamic action bar tab behavior that can change * across different configurations or circumstances. */ public class TabPageIndicator extends HorizontalScrollView implements PageIndicator { /** Title text used when no title is provided by the adapter. */ private static final CharSequence EMPTY_TITLE = ""; /** * Interface for a callback when the selected tab has been reselected. */ public interface OnTabReselectedListener { /** * Callback when the selected tab has been reselected. * * @param position Position of the current center item. */ void onTabReselected(int position); } private Runnable mTabSelector; private final OnClickListener mTabClickListener = new OnClickListener() { public void onClick(View view) { TabView tabView = (TabView)view; final int oldSelected = mViewPager.getCurrentItem(); final int newSelected = tabView.getIndex(); mViewPager.setCurrentItem(newSelected); if (oldSelected == newSelected && mTabReselectedListener != null) { mTabReselectedListener.onTabReselected(newSelected); } } }; private final IcsLinearLayout mTabLayout; private ViewPager mViewPager; private ViewPager.OnPageChangeListener mListener; private int mMaxTabWidth; private int mSelectedTabIndex; private OnTabReselectedListener mTabReselectedListener; public TabPageIndicator(Context context) { this(context, null); } public TabPageIndicator(Context context, AttributeSet attrs) { super(context, attrs); setHorizontalScrollBarEnabled(false); mTabLayout = new IcsLinearLayout(context, R.attr.vpiTabPageIndicatorStyle); addView(mTabLayout, new ViewGroup.LayoutParams(WRAP_CONTENT, MATCH_PARENT)); } public void setOnTabReselectedListener(OnTabReselectedListener listener) { mTabReselectedListener = listener; } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int widthMode = MeasureSpec.getMode(widthMeasureSpec); final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY; setFillViewport(lockedExpanded); final int childCount = mTabLayout.getChildCount(); if (childCount > 1 && (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) { if (childCount > 2) { mMaxTabWidth = (int)(MeasureSpec.getSize(widthMeasureSpec) * 0.4f); } else { mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2; } } else { mMaxTabWidth = -1; } final int oldWidth = getMeasuredWidth(); super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int newWidth = getMeasuredWidth(); if (lockedExpanded && oldWidth != newWidth) { // Recenter the tab display if we're at a new (scrollable) size. setCurrentItem(mSelectedTabIndex); } } private void animateToTab(final int position) { final View tabView = mTabLayout.getChildAt(position); if (mTabSelector != null) { removeCallbacks(mTabSelector); } mTabSelector = new Runnable() { public void run() { final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2; smoothScrollTo(scrollPos, 0); mTabSelector = null; } }; post(mTabSelector); } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); if (mTabSelector != null) { // Re-post the selector we saved post(mTabSelector); } } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mTabSelector != null) { removeCallbacks(mTabSelector); } } private void addTab(int index, CharSequence text, int iconResId) { final TabView tabView = new TabView(getContext()); tabView.mIndex = index; tabView.setFocusable(true); tabView.setOnClickListener(mTabClickListener); tabView.setText(text); if (iconResId != 0) { tabView.setCompoundDrawablesWithIntrinsicBounds(iconResId, 0, 0, 0); } mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, MATCH_PARENT, 1)); } @Override public void onPageScrollStateChanged(int arg0) { if (mListener != null) { mListener.onPageScrollStateChanged(arg0); } } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { if (mListener != null) { mListener.onPageScrolled(arg0, arg1, arg2); } } @Override public void onPageSelected(int arg0) { setCurrentItem(arg0); if (mListener != null) { mListener.onPageSelected(arg0); } } @Override public void setViewPager(ViewPager view) { if (mViewPager == view) { return; } if (mViewPager != null) { mViewPager.setOnPageChangeListener(null); } final PagerAdapter adapter = view.getAdapter(); if (adapter == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } mViewPager = view; view.setOnPageChangeListener(this); notifyDataSetChanged(); } public void notifyDataSetChanged() { mTabLayout.removeAllViews(); PagerAdapter adapter = mViewPager.getAdapter(); IconPagerAdapter iconAdapter = null; if (adapter instanceof IconPagerAdapter) { iconAdapter = (IconPagerAdapter)adapter; } final int count = adapter.getCount(); for (int i = 0; i < count; i++) { CharSequence title = adapter.getPageTitle(i); if (title == null) { title = EMPTY_TITLE; } int iconResId = 0; if (iconAdapter != null) { iconResId = iconAdapter.getIconResId(i); } addTab(i, title, iconResId); } if (mSelectedTabIndex > count) { mSelectedTabIndex = count - 1; } setCurrentItem(mSelectedTabIndex); requestLayout(); } @Override public void setViewPager(ViewPager view, int initialPosition) { setViewPager(view); setCurrentItem(initialPosition); } @Override public void setCurrentItem(int item) { if (mViewPager == null) { throw new IllegalStateException("ViewPager has not been bound."); } mSelectedTabIndex = item; mViewPager.setCurrentItem(item); final int tabCount = mTabLayout.getChildCount(); for (int i = 0; i < tabCount; i++) { final View child = mTabLayout.getChildAt(i); final boolean isSelected = (i == item); child.setSelected(isSelected); if (isSelected) { animateToTab(item); } } } @Override public void setOnPageChangeListener(OnPageChangeListener listener) { mListener = listener; } private class TabView extends TextView { private int mIndex; public TabView(Context context) { super(context, null, R.attr.vpiTabPageIndicatorStyle); } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // Re-measure if we went beyond our maximum size. if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) { super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY), heightMeasureSpec); } } public int getIndex() { return mIndex; } } } ================================================ FILE: library/src/com/viewpagerindicator/TitlePageIndicator.java ================================================ /* * Copyright (C) 2011 Jake Wharton * Copyright (C) 2011 Patrik Akerfeldt * Copyright (C) 2011 Francisco Figueiredo Jr. * * 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 com.viewpagerindicator; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.ViewConfigurationCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import java.util.ArrayList; /** * A TitlePageIndicator is a PageIndicator which displays the title of left view * (if exist), the title of the current select view (centered) and the title of * the right view (if exist). When the user scrolls the ViewPager then titles are * also scrolled. */ public class TitlePageIndicator extends View implements PageIndicator { /** * Percentage indicating what percentage of the screen width away from * center should the underline be fully faded. A value of 0.25 means that * halfway between the center of the screen and an edge. */ private static final float SELECTION_FADE_PERCENTAGE = 0.25f; /** * Percentage indicating what percentage of the screen width away from * center should the selected text bold turn off. A value of 0.05 means * that 10% between the center and an edge. */ private static final float BOLD_FADE_PERCENTAGE = 0.05f; /** * Title text used when no title is provided by the adapter. */ private static final String EMPTY_TITLE = ""; /** * Interface for a callback when the center item has been clicked. */ public interface OnCenterItemClickListener { /** * Callback when the center item has been clicked. * * @param position Position of the current center item. */ void onCenterItemClick(int position); } public enum IndicatorStyle { None(0), Triangle(1), Underline(2); public final int value; private IndicatorStyle(int value) { this.value = value; } public static IndicatorStyle fromValue(int value) { for (IndicatorStyle style : IndicatorStyle.values()) { if (style.value == value) { return style; } } return null; } } public enum LinePosition { Bottom(0), Top(1); public final int value; private LinePosition(int value) { this.value = value; } public static LinePosition fromValue(int value) { for (LinePosition position : LinePosition.values()) { if (position.value == value) { return position; } } return null; } } private ViewPager mViewPager; private ViewPager.OnPageChangeListener mListener; private int mCurrentPage = -1; private float mPageOffset; private int mScrollState; private final Paint mPaintText = new Paint(); private boolean mBoldText; private int mColorText; private int mColorSelected; private Path mPath = new Path(); private final Rect mBounds = new Rect(); private final Paint mPaintFooterLine = new Paint(); private IndicatorStyle mFooterIndicatorStyle; private LinePosition mLinePosition; private final Paint mPaintFooterIndicator = new Paint(); private float mFooterIndicatorHeight; private float mFooterIndicatorUnderlinePadding; private float mFooterPadding; private float mTitlePadding; private float mTopPadding; /** Left and right side padding for not active view titles. */ private float mClipPadding; private float mFooterLineHeight; private static final int INVALID_POINTER = -1; private int mTouchSlop; private float mLastMotionX = -1; private int mActivePointerId = INVALID_POINTER; private boolean mIsDragging; private OnCenterItemClickListener mCenterItemClickListener; public TitlePageIndicator(Context context) { this(context, null); } public TitlePageIndicator(Context context, AttributeSet attrs) { this(context, attrs, R.attr.vpiTitlePageIndicatorStyle); } public TitlePageIndicator(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); if (isInEditMode()) return; //Load defaults from resources final Resources res = getResources(); final int defaultFooterColor = res.getColor(R.color.default_title_indicator_footer_color); final float defaultFooterLineHeight = res.getDimension(R.dimen.default_title_indicator_footer_line_height); final int defaultFooterIndicatorStyle = res.getInteger(R.integer.default_title_indicator_footer_indicator_style); final float defaultFooterIndicatorHeight = res.getDimension(R.dimen.default_title_indicator_footer_indicator_height); final float defaultFooterIndicatorUnderlinePadding = res.getDimension(R.dimen.default_title_indicator_footer_indicator_underline_padding); final float defaultFooterPadding = res.getDimension(R.dimen.default_title_indicator_footer_padding); final int defaultLinePosition = res.getInteger(R.integer.default_title_indicator_line_position); final int defaultSelectedColor = res.getColor(R.color.default_title_indicator_selected_color); final boolean defaultSelectedBold = res.getBoolean(R.bool.default_title_indicator_selected_bold); final int defaultTextColor = res.getColor(R.color.default_title_indicator_text_color); final float defaultTextSize = res.getDimension(R.dimen.default_title_indicator_text_size); final float defaultTitlePadding = res.getDimension(R.dimen.default_title_indicator_title_padding); final float defaultClipPadding = res.getDimension(R.dimen.default_title_indicator_clip_padding); final float defaultTopPadding = res.getDimension(R.dimen.default_title_indicator_top_padding); //Retrieve styles attributes TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TitlePageIndicator, defStyle, 0); //Retrieve the colors to be used for this view and apply them. mFooterLineHeight = a.getDimension(R.styleable.TitlePageIndicator_footerLineHeight, defaultFooterLineHeight); mFooterIndicatorStyle = IndicatorStyle.fromValue(a.getInteger(R.styleable.TitlePageIndicator_footerIndicatorStyle, defaultFooterIndicatorStyle)); mFooterIndicatorHeight = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorHeight, defaultFooterIndicatorHeight); mFooterIndicatorUnderlinePadding = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorUnderlinePadding, defaultFooterIndicatorUnderlinePadding); mFooterPadding = a.getDimension(R.styleable.TitlePageIndicator_footerPadding, defaultFooterPadding); mLinePosition = LinePosition.fromValue(a.getInteger(R.styleable.TitlePageIndicator_linePosition, defaultLinePosition)); mTopPadding = a.getDimension(R.styleable.TitlePageIndicator_topPadding, defaultTopPadding); mTitlePadding = a.getDimension(R.styleable.TitlePageIndicator_titlePadding, defaultTitlePadding); mClipPadding = a.getDimension(R.styleable.TitlePageIndicator_clipPadding, defaultClipPadding); mColorSelected = a.getColor(R.styleable.TitlePageIndicator_selectedColor, defaultSelectedColor); mColorText = a.getColor(R.styleable.TitlePageIndicator_android_textColor, defaultTextColor); mBoldText = a.getBoolean(R.styleable.TitlePageIndicator_selectedBold, defaultSelectedBold); final float textSize = a.getDimension(R.styleable.TitlePageIndicator_android_textSize, defaultTextSize); final int footerColor = a.getColor(R.styleable.TitlePageIndicator_footerColor, defaultFooterColor); mPaintText.setTextSize(textSize); mPaintText.setAntiAlias(true); mPaintFooterLine.setStyle(Paint.Style.FILL_AND_STROKE); mPaintFooterLine.setStrokeWidth(mFooterLineHeight); mPaintFooterLine.setColor(footerColor); mPaintFooterIndicator.setStyle(Paint.Style.FILL_AND_STROKE); mPaintFooterIndicator.setColor(footerColor); Drawable background = a.getDrawable(R.styleable.TitlePageIndicator_android_background); if (background != null) { setBackgroundDrawable(background); } a.recycle(); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); } public int getFooterColor() { return mPaintFooterLine.getColor(); } public void setFooterColor(int footerColor) { mPaintFooterLine.setColor(footerColor); mPaintFooterIndicator.setColor(footerColor); invalidate(); } public float getFooterLineHeight() { return mFooterLineHeight; } public void setFooterLineHeight(float footerLineHeight) { mFooterLineHeight = footerLineHeight; mPaintFooterLine.setStrokeWidth(mFooterLineHeight); invalidate(); } public float getFooterIndicatorHeight() { return mFooterIndicatorHeight; } public void setFooterIndicatorHeight(float footerTriangleHeight) { mFooterIndicatorHeight = footerTriangleHeight; invalidate(); } public float getFooterIndicatorPadding() { return mFooterPadding; } public void setFooterIndicatorPadding(float footerIndicatorPadding) { mFooterPadding = footerIndicatorPadding; invalidate(); } public IndicatorStyle getFooterIndicatorStyle() { return mFooterIndicatorStyle; } public void setFooterIndicatorStyle(IndicatorStyle indicatorStyle) { mFooterIndicatorStyle = indicatorStyle; invalidate(); } public LinePosition getLinePosition() { return mLinePosition; } public void setLinePosition(LinePosition linePosition) { mLinePosition = linePosition; invalidate(); } public int getSelectedColor() { return mColorSelected; } public void setSelectedColor(int selectedColor) { mColorSelected = selectedColor; invalidate(); } public boolean isSelectedBold() { return mBoldText; } public void setSelectedBold(boolean selectedBold) { mBoldText = selectedBold; invalidate(); } public int getTextColor() { return mColorText; } public void setTextColor(int textColor) { mPaintText.setColor(textColor); mColorText = textColor; invalidate(); } public float getTextSize() { return mPaintText.getTextSize(); } public void setTextSize(float textSize) { mPaintText.setTextSize(textSize); invalidate(); } public float getTitlePadding() { return this.mTitlePadding; } public void setTitlePadding(float titlePadding) { mTitlePadding = titlePadding; invalidate(); } public float getTopPadding() { return this.mTopPadding; } public void setTopPadding(float topPadding) { mTopPadding = topPadding; invalidate(); } public float getClipPadding() { return this.mClipPadding; } public void setClipPadding(float clipPadding) { mClipPadding = clipPadding; invalidate(); } public void setTypeface(Typeface typeface) { mPaintText.setTypeface(typeface); invalidate(); } public Typeface getTypeface() { return mPaintText.getTypeface(); } /* * (non-Javadoc) * * @see android.view.View#onDraw(android.graphics.Canvas) */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mViewPager == null) { return; } final int count = mViewPager.getAdapter().getCount(); if (count == 0) { return; } // mCurrentPage is -1 on first start and after orientation changed. If so, retrieve the correct index from viewpager. if (mCurrentPage == -1 && mViewPager != null) { mCurrentPage = mViewPager.getCurrentItem(); } //Calculate views bounds ArrayList bounds = calculateAllBounds(mPaintText); final int boundsSize = bounds.size(); //Make sure we're on a page that still exists if (mCurrentPage >= boundsSize) { setCurrentItem(boundsSize - 1); return; } final int countMinusOne = count - 1; final float halfWidth = getWidth() / 2f; final int left = getLeft(); final float leftClip = left + mClipPadding; final int width = getWidth(); int height = getHeight(); final int right = left + width; final float rightClip = right - mClipPadding; int page = mCurrentPage; float offsetPercent; if (mPageOffset <= 0.5) { offsetPercent = mPageOffset; } else { page += 1; offsetPercent = 1 - mPageOffset; } final boolean currentSelected = (offsetPercent <= SELECTION_FADE_PERCENTAGE); final boolean currentBold = (offsetPercent <= BOLD_FADE_PERCENTAGE); final float selectedPercent = (SELECTION_FADE_PERCENTAGE - offsetPercent) / SELECTION_FADE_PERCENTAGE; //Verify if the current view must be clipped to the screen Rect curPageBound = bounds.get(mCurrentPage); float curPageWidth = curPageBound.right - curPageBound.left; if (curPageBound.left < leftClip) { //Try to clip to the screen (left side) clipViewOnTheLeft(curPageBound, curPageWidth, left); } if (curPageBound.right > rightClip) { //Try to clip to the screen (right side) clipViewOnTheRight(curPageBound, curPageWidth, right); } //Left views starting from the current position if (mCurrentPage > 0) { for (int i = mCurrentPage - 1; i >= 0; i--) { Rect bound = bounds.get(i); //Is left side is outside the screen if (bound.left < leftClip) { int w = bound.right - bound.left; //Try to clip to the screen (left side) clipViewOnTheLeft(bound, w, left); //Except if there's an intersection with the right view Rect rightBound = bounds.get(i + 1); //Intersection if (bound.right + mTitlePadding > rightBound.left) { bound.left = (int) (rightBound.left - w - mTitlePadding); bound.right = bound.left + w; } } } } //Right views starting from the current position if (mCurrentPage < countMinusOne) { for (int i = mCurrentPage + 1 ; i < count; i++) { Rect bound = bounds.get(i); //If right side is outside the screen if (bound.right > rightClip) { int w = bound.right - bound.left; //Try to clip to the screen (right side) clipViewOnTheRight(bound, w, right); //Except if there's an intersection with the left view Rect leftBound = bounds.get(i - 1); //Intersection if (bound.left - mTitlePadding < leftBound.right) { bound.left = (int) (leftBound.right + mTitlePadding); bound.right = bound.left + w; } } } } //Now draw views int colorTextAlpha = mColorText >>> 24; for (int i = 0; i < count; i++) { //Get the title Rect bound = bounds.get(i); //Only if one side is visible if ((bound.left > left && bound.left < right) || (bound.right > left && bound.right < right)) { final boolean currentPage = (i == page); final CharSequence pageTitle = getTitle(i); //Only set bold if we are within bounds mPaintText.setFakeBoldText(currentPage && currentBold && mBoldText); //Draw text as unselected mPaintText.setColor(mColorText); if(currentPage && currentSelected) { //Fade out/in unselected text as the selected text fades in/out mPaintText.setAlpha(colorTextAlpha - (int)(colorTextAlpha * selectedPercent)); } //Except if there's an intersection with the right view if (i < boundsSize - 1) { Rect rightBound = bounds.get(i + 1); //Intersection if (bound.right + mTitlePadding > rightBound.left) { int w = bound.right - bound.left; bound.left = (int) (rightBound.left - w - mTitlePadding); bound.right = bound.left + w; } } canvas.drawText(pageTitle, 0, pageTitle.length(), bound.left, bound.bottom + mTopPadding, mPaintText); //If we are within the selected bounds draw the selected text if (currentPage && currentSelected) { mPaintText.setColor(mColorSelected); mPaintText.setAlpha((int)((mColorSelected >>> 24) * selectedPercent)); canvas.drawText(pageTitle, 0, pageTitle.length(), bound.left, bound.bottom + mTopPadding, mPaintText); } } } //If we want the line on the top change height to zero and invert the line height to trick the drawing code float footerLineHeight = mFooterLineHeight; float footerIndicatorLineHeight = mFooterIndicatorHeight; if (mLinePosition == LinePosition.Top) { height = 0; footerLineHeight = -footerLineHeight; footerIndicatorLineHeight = -footerIndicatorLineHeight; } //Draw the footer line mPath.reset(); mPath.moveTo(0, height - footerLineHeight / 2f); mPath.lineTo(width, height - footerLineHeight / 2f); mPath.close(); canvas.drawPath(mPath, mPaintFooterLine); float heightMinusLine = height - footerLineHeight; switch (mFooterIndicatorStyle) { case Triangle: mPath.reset(); mPath.moveTo(halfWidth, heightMinusLine - footerIndicatorLineHeight); mPath.lineTo(halfWidth + footerIndicatorLineHeight, heightMinusLine); mPath.lineTo(halfWidth - footerIndicatorLineHeight, heightMinusLine); mPath.close(); canvas.drawPath(mPath, mPaintFooterIndicator); break; case Underline: if (!currentSelected || page >= boundsSize) { break; } Rect underlineBounds = bounds.get(page); final float rightPlusPadding = underlineBounds.right + mFooterIndicatorUnderlinePadding; final float leftMinusPadding = underlineBounds.left - mFooterIndicatorUnderlinePadding; final float heightMinusLineMinusIndicator = heightMinusLine - footerIndicatorLineHeight; mPath.reset(); mPath.moveTo(leftMinusPadding, heightMinusLine); mPath.lineTo(rightPlusPadding, heightMinusLine); mPath.lineTo(rightPlusPadding, heightMinusLineMinusIndicator); mPath.lineTo(leftMinusPadding, heightMinusLineMinusIndicator); mPath.close(); mPaintFooterIndicator.setAlpha((int)(0xFF * selectedPercent)); canvas.drawPath(mPath, mPaintFooterIndicator); mPaintFooterIndicator.setAlpha(0xFF); break; } } public boolean onTouchEvent(android.view.MotionEvent ev) { if (super.onTouchEvent(ev)) { return true; } if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) { return false; } final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; switch (action) { case MotionEvent.ACTION_DOWN: mActivePointerId = MotionEventCompat.getPointerId(ev, 0); mLastMotionX = ev.getX(); break; case MotionEvent.ACTION_MOVE: { final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, activePointerIndex); final float deltaX = x - mLastMotionX; if (!mIsDragging) { if (Math.abs(deltaX) > mTouchSlop) { mIsDragging = true; } } if (mIsDragging) { mLastMotionX = x; if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) { mViewPager.fakeDragBy(deltaX); } } break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (!mIsDragging) { final int count = mViewPager.getAdapter().getCount(); final int width = getWidth(); final float halfWidth = width / 2f; final float sixthWidth = width / 6f; final float leftThird = halfWidth - sixthWidth; final float rightThird = halfWidth + sixthWidth; final float eventX = ev.getX(); if (eventX < leftThird) { if (mCurrentPage > 0) { if (action != MotionEvent.ACTION_CANCEL) { mViewPager.setCurrentItem(mCurrentPage - 1); } return true; } } else if (eventX > rightThird) { if (mCurrentPage < count - 1) { if (action != MotionEvent.ACTION_CANCEL) { mViewPager.setCurrentItem(mCurrentPage + 1); } return true; } } else { //Middle third if (mCenterItemClickListener != null && action != MotionEvent.ACTION_CANCEL) { mCenterItemClickListener.onCenterItemClick(mCurrentPage); } } } mIsDragging = false; mActivePointerId = INVALID_POINTER; if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag(); break; case MotionEventCompat.ACTION_POINTER_DOWN: { final int index = MotionEventCompat.getActionIndex(ev); mLastMotionX = MotionEventCompat.getX(ev, index); mActivePointerId = MotionEventCompat.getPointerId(ev, index); break; } case MotionEventCompat.ACTION_POINTER_UP: final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) { final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); } mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId)); break; } return true; } /** * Set bounds for the right textView including clip padding. * * @param curViewBound * current bounds. * @param curViewWidth * width of the view. */ private void clipViewOnTheRight(Rect curViewBound, float curViewWidth, int right) { curViewBound.right = (int) (right - mClipPadding); curViewBound.left = (int) (curViewBound.right - curViewWidth); } /** * Set bounds for the left textView including clip padding. * * @param curViewBound * current bounds. * @param curViewWidth * width of the view. */ private void clipViewOnTheLeft(Rect curViewBound, float curViewWidth, int left) { curViewBound.left = (int) (left + mClipPadding); curViewBound.right = (int) (mClipPadding + curViewWidth); } /** * Calculate views bounds and scroll them according to the current index * * @param paint * @return */ private ArrayList calculateAllBounds(Paint paint) { ArrayList list = new ArrayList(); //For each views (If no values then add a fake one) final int count = mViewPager.getAdapter().getCount(); final int width = getWidth(); final int halfWidth = width / 2; for (int i = 0; i < count; i++) { Rect bounds = calcBounds(i, paint); int w = bounds.right - bounds.left; int h = bounds.bottom - bounds.top; bounds.left = (int)(halfWidth - (w / 2f) + ((i - mCurrentPage - mPageOffset) * width)); bounds.right = bounds.left + w; bounds.top = 0; bounds.bottom = h; list.add(bounds); } return list; } /** * Calculate the bounds for a view's title * * @param index * @param paint * @return */ private Rect calcBounds(int index, Paint paint) { //Calculate the text bounds Rect bounds = new Rect(); CharSequence title = getTitle(index); bounds.right = (int) paint.measureText(title, 0, title.length()); bounds.bottom = (int) (paint.descent() - paint.ascent()); return bounds; } @Override public void setViewPager(ViewPager view) { if (mViewPager == view) { return; } if (mViewPager != null) { mViewPager.setOnPageChangeListener(null); } if (view.getAdapter() == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } mViewPager = view; mViewPager.setOnPageChangeListener(this); invalidate(); } @Override public void setViewPager(ViewPager view, int initialPosition) { setViewPager(view); setCurrentItem(initialPosition); } @Override public void notifyDataSetChanged() { invalidate(); } /** * Set a callback listener for the center item click. * * @param listener Callback instance. */ public void setOnCenterItemClickListener(OnCenterItemClickListener listener) { mCenterItemClickListener = listener; } @Override public void setCurrentItem(int item) { if (mViewPager == null) { throw new IllegalStateException("ViewPager has not been bound."); } mViewPager.setCurrentItem(item); mCurrentPage = item; invalidate(); } @Override public void onPageScrollStateChanged(int state) { mScrollState = state; if (mListener != null) { mListener.onPageScrollStateChanged(state); } } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { mCurrentPage = position; mPageOffset = positionOffset; invalidate(); if (mListener != null) { mListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } } @Override public void onPageSelected(int position) { if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { mCurrentPage = position; invalidate(); } if (mListener != null) { mListener.onPageSelected(position); } } @Override public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { mListener = listener; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //Measure our width in whatever mode specified final int measuredWidth = MeasureSpec.getSize(widthMeasureSpec); //Determine our height float height; final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); if (heightSpecMode == MeasureSpec.EXACTLY) { //We were told how big to be height = MeasureSpec.getSize(heightMeasureSpec); } else { //Calculate the text bounds mBounds.setEmpty(); mBounds.bottom = (int) (mPaintText.descent() - mPaintText.ascent()); height = mBounds.bottom - mBounds.top + mFooterLineHeight + mFooterPadding + mTopPadding; if (mFooterIndicatorStyle != IndicatorStyle.None) { height += mFooterIndicatorHeight; } } final int measuredHeight = (int)height; setMeasuredDimension(measuredWidth, measuredHeight); } @Override public void onRestoreInstanceState(Parcelable state) { SavedState savedState = (SavedState)state; super.onRestoreInstanceState(savedState.getSuperState()); mCurrentPage = savedState.currentPage; requestLayout(); } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState savedState = new SavedState(superState); savedState.currentPage = mCurrentPage; return savedState; } static class SavedState extends BaseSavedState { int currentPage; public SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); currentPage = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(currentPage); } @SuppressWarnings("UnusedDeclaration") public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } private CharSequence getTitle(int i) { CharSequence title = mViewPager.getAdapter().getPageTitle(i); if (title == null) { title = EMPTY_TITLE; } return title; } } ================================================ FILE: library/src/com/viewpagerindicator/UnderlinePageIndicator.java ================================================ /* * Copyright (C) 2012 Jake Wharton * * 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 com.viewpagerindicator; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.ViewConfigurationCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; /** * Draws a line for each page. The current page line is colored differently * than the unselected page lines. */ public class UnderlinePageIndicator extends View implements PageIndicator { private static final int INVALID_POINTER = -1; private static final int FADE_FRAME_MS = 30; private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private boolean mFades; private int mFadeDelay; private int mFadeLength; private int mFadeBy; private ViewPager mViewPager; private ViewPager.OnPageChangeListener mListener; private int mScrollState; private int mCurrentPage; private float mPositionOffset; private int mTouchSlop; private float mLastMotionX = -1; private int mActivePointerId = INVALID_POINTER; private boolean mIsDragging; private final Runnable mFadeRunnable = new Runnable() { @Override public void run() { if (!mFades) return; final int alpha = Math.max(mPaint.getAlpha() - mFadeBy, 0); mPaint.setAlpha(alpha); invalidate(); if (alpha > 0) { postDelayed(this, FADE_FRAME_MS); } } }; public UnderlinePageIndicator(Context context) { this(context, null); } public UnderlinePageIndicator(Context context, AttributeSet attrs) { this(context, attrs, R.attr.vpiUnderlinePageIndicatorStyle); } public UnderlinePageIndicator(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); if (isInEditMode()) return; final Resources res = getResources(); //Load defaults from resources final boolean defaultFades = res.getBoolean(R.bool.default_underline_indicator_fades); final int defaultFadeDelay = res.getInteger(R.integer.default_underline_indicator_fade_delay); final int defaultFadeLength = res.getInteger(R.integer.default_underline_indicator_fade_length); final int defaultSelectedColor = res.getColor(R.color.default_underline_indicator_selected_color); //Retrieve styles attributes TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UnderlinePageIndicator, defStyle, 0); setFades(a.getBoolean(R.styleable.UnderlinePageIndicator_fades, defaultFades)); setSelectedColor(a.getColor(R.styleable.UnderlinePageIndicator_selectedColor, defaultSelectedColor)); setFadeDelay(a.getInteger(R.styleable.UnderlinePageIndicator_fadeDelay, defaultFadeDelay)); setFadeLength(a.getInteger(R.styleable.UnderlinePageIndicator_fadeLength, defaultFadeLength)); Drawable background = a.getDrawable(R.styleable.UnderlinePageIndicator_android_background); if (background != null) { setBackgroundDrawable(background); } a.recycle(); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); } public boolean getFades() { return mFades; } public void setFades(boolean fades) { if (fades != mFades) { mFades = fades; if (fades) { post(mFadeRunnable); } else { removeCallbacks(mFadeRunnable); mPaint.setAlpha(0xFF); invalidate(); } } } public int getFadeDelay() { return mFadeDelay; } public void setFadeDelay(int fadeDelay) { mFadeDelay = fadeDelay; } public int getFadeLength() { return mFadeLength; } public void setFadeLength(int fadeLength) { mFadeLength = fadeLength; mFadeBy = 0xFF / (mFadeLength / FADE_FRAME_MS); } public int getSelectedColor() { return mPaint.getColor(); } public void setSelectedColor(int selectedColor) { mPaint.setColor(selectedColor); invalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mViewPager == null) { return; } final int count = mViewPager.getAdapter().getCount(); if (count == 0) { return; } if (mCurrentPage >= count) { setCurrentItem(count - 1); return; } final int paddingLeft = getPaddingLeft(); final float pageWidth = (getWidth() - paddingLeft - getPaddingRight()) / (1f * count); final float left = paddingLeft + pageWidth * (mCurrentPage + mPositionOffset); final float right = left + pageWidth; final float top = getPaddingTop(); final float bottom = getHeight() - getPaddingBottom(); canvas.drawRect(left, top, right, bottom, mPaint); } public boolean onTouchEvent(MotionEvent ev) { if (super.onTouchEvent(ev)) { return true; } if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) { return false; } final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; switch (action) { case MotionEvent.ACTION_DOWN: mActivePointerId = MotionEventCompat.getPointerId(ev, 0); mLastMotionX = ev.getX(); break; case MotionEvent.ACTION_MOVE: { final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, activePointerIndex); final float deltaX = x - mLastMotionX; if (!mIsDragging) { if (Math.abs(deltaX) > mTouchSlop) { mIsDragging = true; } } if (mIsDragging) { mLastMotionX = x; if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) { mViewPager.fakeDragBy(deltaX); } } break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (!mIsDragging) { final int count = mViewPager.getAdapter().getCount(); final int width = getWidth(); final float halfWidth = width / 2f; final float sixthWidth = width / 6f; if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) { if (action != MotionEvent.ACTION_CANCEL) { mViewPager.setCurrentItem(mCurrentPage - 1); } return true; } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) { if (action != MotionEvent.ACTION_CANCEL) { mViewPager.setCurrentItem(mCurrentPage + 1); } return true; } } mIsDragging = false; mActivePointerId = INVALID_POINTER; if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag(); break; case MotionEventCompat.ACTION_POINTER_DOWN: { final int index = MotionEventCompat.getActionIndex(ev); mLastMotionX = MotionEventCompat.getX(ev, index); mActivePointerId = MotionEventCompat.getPointerId(ev, index); break; } case MotionEventCompat.ACTION_POINTER_UP: final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) { final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); } mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId)); break; } return true; } @Override public void setViewPager(ViewPager viewPager) { if (mViewPager == viewPager) { return; } if (mViewPager != null) { //Clear us from the old pager. mViewPager.setOnPageChangeListener(null); } if (viewPager.getAdapter() == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } mViewPager = viewPager; mViewPager.setOnPageChangeListener(this); invalidate(); post(new Runnable() { @Override public void run() { if (mFades) { post(mFadeRunnable); } } }); } @Override public void setViewPager(ViewPager view, int initialPosition) { setViewPager(view); setCurrentItem(initialPosition); } @Override public void setCurrentItem(int item) { if (mViewPager == null) { throw new IllegalStateException("ViewPager has not been bound."); } mViewPager.setCurrentItem(item); mCurrentPage = item; invalidate(); } @Override public void notifyDataSetChanged() { invalidate(); } @Override public void onPageScrollStateChanged(int state) { mScrollState = state; if (mListener != null) { mListener.onPageScrollStateChanged(state); } } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { mCurrentPage = position; mPositionOffset = positionOffset; if (mFades) { if (positionOffsetPixels > 0) { removeCallbacks(mFadeRunnable); mPaint.setAlpha(0xFF); } else if (mScrollState != ViewPager.SCROLL_STATE_DRAGGING) { postDelayed(mFadeRunnable, mFadeDelay); } } invalidate(); if (mListener != null) { mListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } } @Override public void onPageSelected(int position) { if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { mCurrentPage = position; mPositionOffset = 0; invalidate(); mFadeRunnable.run(); } if (mListener != null) { mListener.onPageSelected(position); } } @Override public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { mListener = listener; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState savedState = (SavedState)state; super.onRestoreInstanceState(savedState.getSuperState()); mCurrentPage = savedState.currentPage; requestLayout(); } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState savedState = new SavedState(superState); savedState.currentPage = mCurrentPage; return savedState; } static class SavedState extends BaseSavedState { int currentPage; public SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); currentPage = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(currentPage); } @SuppressWarnings("UnusedDeclaration") public static final Creator CREATOR = new Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } } ================================================ FILE: pom.xml ================================================ 4.0.0 org.sonatype.oss oss-parent 7 com.viewpagerindicator parent pom 2.4.1 Android-ViewPagerIndicator (Parent) Android library for. https://github.com/JakeWharton/Android-ViewPagerIndicator 2011 library sample http://github.com/JakeWharton/Android-ViewPagerIndicator/ scm:git:git://github.com/JakeWharton/Android-ViewPagerIndicator.git scm:git:git@github.com:JakeWharton/Android-ViewPagerIndicator.git Jake Wharton jakewharton@gmail.com jakewharton http://jakewharton.com -5 developer Apache License Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt repo Jake Wharton http://jakewharton.com GitHub Issues https://github.com/JakeWharton/Android-ViewPagerIndicator/issues UTF-8 UTF-8 1.6 4.1.1.4 16 r7 3.3.0 com.google.android android ${android.version} com.google.android support-v4 ${android.support.version} org.apache.maven.plugins maven-compiler-plugin 2.3.2 ${java.version} ${java.version} com.jayway.maven.plugins.android.generation2 android-maven-plugin ${android-maven.version} ${android.platform} true true org.apache.maven.plugins maven-checkstyle-plugin 2.6 true org.apache.maven.plugins maven-release-plugin 2.1 true org.apache.maven.plugins maven-eclipse-plugin 2.9 android:android com.google.android.maps:maps bin com.android.ide.eclipse.adt.ANDROID_FRAMEWORK com.android.ide.eclipse.adt.AndroidNature org.eclipse.ajdt.ui.ajnature com.android.ide.eclipse.adt.ResourceManagerBuilder com.android.ide.eclipse.adt.PreCompilerBuilder org.eclipse.jdt.core.javabuilder com.android.ide.eclipse.adt.ApkBuilder false ================================================ FILE: sample/AndroidManifest.xml ================================================ ================================================ FILE: sample/pom.xml ================================================ 4.0.0 sample Android-ViewPagerIndicator Sample apk com.viewpagerindicator parent 2.4.1 ../pom.xml com.google.android android provided com.google.android support-v4 com.viewpagerindicator library ${project.version} apklib src ${project.artifactId}-unaligned com.jayway.maven.plugins.android.generation2 android-maven-plugin true org.apache.maven.plugins maven-checkstyle-plugin ../checkstyle.xml verify checkstyle release performRelease true com.jayway.maven.plugins.android.generation2 android-maven-plugin ${android-maven.version} true ${project.build.directory}/${project.build.finalName}.apk ${project.build.directory}/${project.artifactId}.apk alignApk package zipalign ================================================ FILE: sample/project.properties ================================================ # This file is automatically generated by Android Tools. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file must be checked in Version Control Systems. # # To customize properties used by the Ant build system use, # "ant.properties", and override values to adapt the script to your # project structure. # Project target. target=android-16 android.library.reference.1=../library ================================================ FILE: sample/res/drawable/custom_tab_indicator.xml ================================================ ================================================ FILE: sample/res/drawable/perm_group_calendar.xml ================================================ ================================================ FILE: sample/res/drawable/perm_group_camera.xml ================================================ ================================================ FILE: sample/res/drawable/perm_group_device_alarms.xml ================================================ ================================================ FILE: sample/res/drawable/perm_group_location.xml ================================================ ================================================ FILE: sample/res/layout/simple_circles.xml ================================================ ================================================ FILE: sample/res/layout/simple_circles_vertical.xml ================================================ ================================================ FILE: sample/res/layout/simple_icons.xml ================================================ ================================================ FILE: sample/res/layout/simple_lines.xml ================================================ ================================================ FILE: sample/res/layout/simple_tabs.xml ================================================ ================================================ FILE: sample/res/layout/simple_titles.xml ================================================ ================================================ FILE: sample/res/layout/simple_titles_bottom.xml ================================================ ================================================ FILE: sample/res/layout/simple_underlines.xml ================================================ ================================================ FILE: sample/res/layout/themed_circles.xml ================================================ ================================================ FILE: sample/res/layout/themed_lines.xml ================================================ ================================================ FILE: sample/res/layout/themed_titles.xml ================================================ ================================================ FILE: sample/res/layout/themed_underlines.xml ================================================ ================================================ FILE: sample/res/menu/menu.xml ================================================ ================================================ FILE: sample/res/values/styles.xml ================================================ ================================================ FILE: sample/src/com/viewpagerindicator/sample/BaseSampleActivity.java ================================================ package com.viewpagerindicator.sample; import java.util.Random; import com.viewpagerindicator.PageIndicator; import android.support.v4.app.FragmentActivity; import android.support.v4.view.ViewPager; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; public abstract class BaseSampleActivity extends FragmentActivity { private static final Random RANDOM = new Random(); TestFragmentAdapter mAdapter; ViewPager mPager; PageIndicator mIndicator; @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.random: final int page = RANDOM.nextInt(mAdapter.getCount()); Toast.makeText(this, "Changing to page " + page, Toast.LENGTH_SHORT); mPager.setCurrentItem(page); return true; case R.id.add_page: if (mAdapter.getCount() < 10) { mAdapter.setCount(mAdapter.getCount() + 1); mIndicator.notifyDataSetChanged(); } return true; case R.id.remove_page: if (mAdapter.getCount() > 1) { mAdapter.setCount(mAdapter.getCount() - 1); mIndicator.notifyDataSetChanged(); } return true; } return super.onOptionsItemSelected(item); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/ListSamples.java ================================================ package com.viewpagerindicator.sample; import java.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.ListActivity; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; import android.view.View; import android.widget.ListView; import android.widget.SimpleAdapter; public class ListSamples extends ListActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = getIntent(); String path = intent.getStringExtra("com.jakewharton.android.viewpagerindicator.sample.Path"); if (path == null) { path = ""; } setListAdapter(new SimpleAdapter(this, getData(path), android.R.layout.simple_list_item_1, new String[] { "title" }, new int[] { android.R.id.text1 })); getListView().setTextFilterEnabled(true); } protected List> getData(String prefix) { List> myData = new ArrayList>(); Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory("com.jakewharton.android.viewpagerindicator.sample.SAMPLE"); PackageManager pm = getPackageManager(); List list = pm.queryIntentActivities(mainIntent, 0); if (null == list) return myData; String[] prefixPath; String prefixWithSlash = prefix; if (prefix.equals("")) { prefixPath = null; } else { prefixPath = prefix.split("/"); prefixWithSlash = prefix + "/"; } int len = list.size(); Map entries = new HashMap(); for (int i = 0; i < len; i++) { ResolveInfo info = list.get(i); CharSequence labelSeq = info.loadLabel(pm); String label = labelSeq != null ? labelSeq.toString() : info.activityInfo.name; if (prefixWithSlash.length() == 0 || label.startsWith(prefixWithSlash)) { String[] labelPath = label.split("/"); String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length]; if ((prefixPath != null ? prefixPath.length : 0) == labelPath.length - 1) { addItem(myData, nextLabel, activityIntent( info.activityInfo.applicationInfo.packageName, info.activityInfo.name)); } else { if (entries.get(nextLabel) == null) { addItem(myData, nextLabel, browseIntent(prefix.equals("") ? nextLabel : prefix + "/" + nextLabel)); entries.put(nextLabel, true); } } } } Collections.sort(myData, NAME_COMPARATOR); return myData; } private final static Comparator> NAME_COMPARATOR = new Comparator>() { private final Collator collator = Collator.getInstance(); public int compare(Map map1, Map map2) { return collator.compare(map1.get("title"), map2.get("title")); } }; protected Intent activityIntent(String pkg, String componentName) { Intent result = new Intent(); result.setClassName(pkg, componentName); return result; } protected Intent browseIntent(String path) { Intent result = new Intent(); result.setClass(this, ListSamples.class); result.putExtra("com.jakewharton.android.viewpagerindicator.sample.Path", path); return result; } protected void addItem(List> data, String name, Intent intent) { Map temp = new HashMap(); temp.put("title", name); temp.put("intent", intent); data.add(temp); } @Override @SuppressWarnings("unchecked") protected void onListItemClick(ListView l, View v, int position, long id) { Map map = (Map)l.getItemAtPosition(position); Intent intent = (Intent) map.get("intent"); startActivity(intent); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleCirclesDefault.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.CirclePageIndicator; public class SampleCirclesDefault extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_circles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (CirclePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleCirclesInitialPage.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.CirclePageIndicator; public class SampleCirclesInitialPage extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_circles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (CirclePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); mIndicator.setCurrentItem(mAdapter.getCount() - 1); //You can also do: indicator.setViewPager(pager, initialPage); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleCirclesSnap.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.CirclePageIndicator; public class SampleCirclesSnap extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_circles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); CirclePageIndicator indicator = (CirclePageIndicator)findViewById(R.id.indicator); mIndicator = indicator; indicator.setViewPager(mPager); indicator.setSnap(true); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleCirclesStyledLayout.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.CirclePageIndicator; public class SampleCirclesStyledLayout extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.themed_circles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (CirclePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleCirclesStyledMethods.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.CirclePageIndicator; public class SampleCirclesStyledMethods extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_circles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); CirclePageIndicator indicator = (CirclePageIndicator)findViewById(R.id.indicator); mIndicator = indicator; indicator.setViewPager(mPager); final float density = getResources().getDisplayMetrics().density; indicator.setBackgroundColor(0xFFCCCCCC); indicator.setRadius(10 * density); indicator.setPageColor(0x880000FF); indicator.setFillColor(0xFF888888); indicator.setStrokeColor(0xFF000000); indicator.setStrokeWidth(2 * density); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleCirclesStyledTheme.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.CirclePageIndicator; public class SampleCirclesStyledTheme extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //The look of this sample is set via a style in the manifest setContentView(R.layout.simple_circles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (CirclePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleCirclesWithListener.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import android.widget.Toast; import com.viewpagerindicator.CirclePageIndicator; public class SampleCirclesWithListener extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_circles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (CirclePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); //We set this on the indicator, NOT the pager mIndicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { Toast.makeText(SampleCirclesWithListener.this, "Changed to page " + position, Toast.LENGTH_SHORT).show(); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageScrollStateChanged(int state) { } }); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleIconsDefault.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.IconPageIndicator; public class SampleIconsDefault extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_icons); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (IconPageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleLinesDefault.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.LinePageIndicator; public class SampleLinesDefault extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_lines); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (LinePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleLinesStyledLayout.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.LinePageIndicator; public class SampleLinesStyledLayout extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.themed_lines); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (LinePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleLinesStyledMethods.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.LinePageIndicator; public class SampleLinesStyledMethods extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_lines); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); LinePageIndicator indicator = (LinePageIndicator)findViewById(R.id.indicator); mIndicator = indicator; indicator.setViewPager(mPager); final float density = getResources().getDisplayMetrics().density; indicator.setSelectedColor(0x88FF0000); indicator.setUnselectedColor(0xFF888888); indicator.setStrokeWidth(4 * density); indicator.setLineWidth(30 * density); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleLinesStyledTheme.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.LinePageIndicator; public class SampleLinesStyledTheme extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //The look of this sample is set via a style in the manifest setContentView(R.layout.simple_lines); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (LinePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleTabsDefault.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import com.viewpagerindicator.TabPageIndicator; public class SampleTabsDefault extends FragmentActivity { private static final String[] CONTENT = new String[] { "Recent", "Artists", "Albums", "Songs", "Playlists", "Genres" }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_tabs); FragmentPagerAdapter adapter = new GoogleMusicAdapter(getSupportFragmentManager()); ViewPager pager = (ViewPager)findViewById(R.id.pager); pager.setAdapter(adapter); TabPageIndicator indicator = (TabPageIndicator)findViewById(R.id.indicator); indicator.setViewPager(pager); } class GoogleMusicAdapter extends FragmentPagerAdapter { public GoogleMusicAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return TestFragment.newInstance(CONTENT[position % CONTENT.length]); } @Override public CharSequence getPageTitle(int position) { return CONTENT[position % CONTENT.length].toUpperCase(); } @Override public int getCount() { return CONTENT.length; } } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleTabsStyled.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import com.viewpagerindicator.TabPageIndicator; public class SampleTabsStyled extends FragmentActivity { private static final String[] CONTENT = new String[] { "Recent", "Artists", "Albums", "Songs", "Playlists", "Genres" }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_tabs); FragmentPagerAdapter adapter = new GoogleMusicAdapter(getSupportFragmentManager()); ViewPager pager = (ViewPager)findViewById(R.id.pager); pager.setAdapter(adapter); TabPageIndicator indicator = (TabPageIndicator)findViewById(R.id.indicator); indicator.setViewPager(pager); } class GoogleMusicAdapter extends FragmentPagerAdapter { public GoogleMusicAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return TestFragment.newInstance(CONTENT[position % CONTENT.length]); } @Override public CharSequence getPageTitle(int position) { return CONTENT[position % CONTENT.length].toUpperCase(); } @Override public int getCount() { return CONTENT.length; } } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleTabsWithIcons.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import com.viewpagerindicator.IconPagerAdapter; import com.viewpagerindicator.TabPageIndicator; public class SampleTabsWithIcons extends FragmentActivity { private static final String[] CONTENT = new String[] { "Calendar", "Camera", "Alarms", "Location" }; private static final int[] ICONS = new int[] { R.drawable.perm_group_calendar, R.drawable.perm_group_camera, R.drawable.perm_group_device_alarms, R.drawable.perm_group_location, }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_tabs); FragmentPagerAdapter adapter = new GoogleMusicAdapter(getSupportFragmentManager()); ViewPager pager = (ViewPager)findViewById(R.id.pager); pager.setAdapter(adapter); TabPageIndicator indicator = (TabPageIndicator)findViewById(R.id.indicator); indicator.setViewPager(pager); } class GoogleMusicAdapter extends FragmentPagerAdapter implements IconPagerAdapter { public GoogleMusicAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return TestFragment.newInstance(CONTENT[position % CONTENT.length]); } @Override public CharSequence getPageTitle(int position) { return CONTENT[position % CONTENT.length].toUpperCase(); } @Override public int getIconResId(int index) { return ICONS[index]; } @Override public int getCount() { return CONTENT.length; } } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleTitlesBottom.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.TitlePageIndicator; public class SampleTitlesBottom extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_titles_bottom); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (TitlePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleTitlesCenterClickListener.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import android.widget.Toast; import com.viewpagerindicator.TitlePageIndicator; import com.viewpagerindicator.TitlePageIndicator.IndicatorStyle; import com.viewpagerindicator.TitlePageIndicator.OnCenterItemClickListener; public class SampleTitlesCenterClickListener extends BaseSampleActivity implements OnCenterItemClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_titles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); TitlePageIndicator indicator = (TitlePageIndicator)findViewById(R.id.indicator); indicator.setViewPager(mPager); indicator.setFooterIndicatorStyle(IndicatorStyle.Underline); indicator.setOnCenterItemClickListener(this); mIndicator = indicator; } @Override public void onCenterItemClick(int position) { Toast.makeText(this, "You clicked the center title!", Toast.LENGTH_SHORT).show(); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleTitlesDefault.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.TitlePageIndicator; public class SampleTitlesDefault extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_titles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (TitlePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleTitlesInitialPage.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.TitlePageIndicator; public class SampleTitlesInitialPage extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_titles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (TitlePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); mIndicator.setCurrentItem(mAdapter.getCount() - 1); //You can also do: indicator.setViewPager(pager, initialPage); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleTitlesStyledLayout.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.TitlePageIndicator; public class SampleTitlesStyledLayout extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.themed_titles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (TitlePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleTitlesStyledMethods.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.TitlePageIndicator; import com.viewpagerindicator.TitlePageIndicator.IndicatorStyle; public class SampleTitlesStyledMethods extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_titles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); TitlePageIndicator indicator = (TitlePageIndicator)findViewById(R.id.indicator); mIndicator = indicator; indicator.setViewPager(mPager); final float density = getResources().getDisplayMetrics().density; indicator.setBackgroundColor(0x18FF0000); indicator.setFooterColor(0xFFAA2222); indicator.setFooterLineHeight(1 * density); //1dp indicator.setFooterIndicatorHeight(3 * density); //3dp indicator.setFooterIndicatorStyle(IndicatorStyle.Underline); indicator.setTextColor(0xAA000000); indicator.setSelectedColor(0xFF000000); indicator.setSelectedBold(true); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleTitlesStyledTheme.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.TitlePageIndicator; public class SampleTitlesStyledTheme extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //The look of this sample is set via a style in the manifest setContentView(R.layout.simple_titles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (TitlePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleTitlesTriangle.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.TitlePageIndicator; import com.viewpagerindicator.TitlePageIndicator.IndicatorStyle; public class SampleTitlesTriangle extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_titles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); TitlePageIndicator indicator = (TitlePageIndicator)findViewById(R.id.indicator); indicator.setViewPager(mPager); indicator.setFooterIndicatorStyle(IndicatorStyle.Triangle); mIndicator = indicator; } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleTitlesWithListener.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import android.widget.Toast; import com.viewpagerindicator.TitlePageIndicator; public class SampleTitlesWithListener extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_titles); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (TitlePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); //We set this on the indicator, NOT the pager mIndicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { Toast.makeText(SampleTitlesWithListener.this, "Changed to page " + position, Toast.LENGTH_SHORT).show(); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageScrollStateChanged(int state) { } }); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleUnderlinesDefault.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.UnderlinePageIndicator; public class SampleUnderlinesDefault extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_underlines); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (UnderlinePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleUnderlinesNoFade.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.UnderlinePageIndicator; public class SampleUnderlinesNoFade extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_underlines); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); UnderlinePageIndicator indicator = (UnderlinePageIndicator)findViewById(R.id.indicator); indicator.setViewPager(mPager); indicator.setFades(false); mIndicator = indicator; } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleUnderlinesStyledLayout.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.UnderlinePageIndicator; public class SampleUnderlinesStyledLayout extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.themed_underlines); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (UnderlinePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleUnderlinesStyledMethods.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.UnderlinePageIndicator; public class SampleUnderlinesStyledMethods extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_underlines); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); UnderlinePageIndicator indicator = (UnderlinePageIndicator)findViewById(R.id.indicator); mIndicator = indicator; indicator.setViewPager(mPager); indicator.setSelectedColor(0xFFCC0000); indicator.setBackgroundColor(0xFFCCCCCC); indicator.setFadeDelay(1000); indicator.setFadeLength(1000); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/SampleUnderlinesStyledTheme.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.view.ViewPager; import com.viewpagerindicator.UnderlinePageIndicator; public class SampleUnderlinesStyledTheme extends BaseSampleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //The look of this sample is set via a style in the manifest setContentView(R.layout.simple_underlines); mAdapter = new TestFragmentAdapter(getSupportFragmentManager()); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter); mIndicator = (UnderlinePageIndicator)findViewById(R.id.indicator); mIndicator.setViewPager(mPager); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/TestFragment.java ================================================ package com.viewpagerindicator.sample; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.TextView; public final class TestFragment extends Fragment { private static final String KEY_CONTENT = "TestFragment:Content"; public static TestFragment newInstance(String content) { TestFragment fragment = new TestFragment(); StringBuilder builder = new StringBuilder(); for (int i = 0; i < 20; i++) { builder.append(content).append(" "); } builder.deleteCharAt(builder.length() - 1); fragment.mContent = builder.toString(); return fragment; } private String mContent = "???"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if ((savedInstanceState != null) && savedInstanceState.containsKey(KEY_CONTENT)) { mContent = savedInstanceState.getString(KEY_CONTENT); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { TextView text = new TextView(getActivity()); text.setGravity(Gravity.CENTER); text.setText(mContent); text.setTextSize(20 * getResources().getDisplayMetrics().density); text.setPadding(20, 20, 20, 20); LinearLayout layout = new LinearLayout(getActivity()); layout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); layout.setGravity(Gravity.CENTER); layout.addView(text); return layout; } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(KEY_CONTENT, mContent); } } ================================================ FILE: sample/src/com/viewpagerindicator/sample/TestFragmentAdapter.java ================================================ package com.viewpagerindicator.sample; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import com.viewpagerindicator.IconPagerAdapter; class TestFragmentAdapter extends FragmentPagerAdapter implements IconPagerAdapter { protected static final String[] CONTENT = new String[] { "This", "Is", "A", "Test", }; protected static final int[] ICONS = new int[] { R.drawable.perm_group_calendar, R.drawable.perm_group_camera, R.drawable.perm_group_device_alarms, R.drawable.perm_group_location }; private int mCount = CONTENT.length; public TestFragmentAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return TestFragment.newInstance(CONTENT[position % CONTENT.length]); } @Override public int getCount() { return mCount; } @Override public CharSequence getPageTitle(int position) { return TestFragmentAdapter.CONTENT[position % CONTENT.length]; } @Override public int getIconResId(int index) { return ICONS[index % ICONS.length]; } public void setCount(int count) { if (count > 0 && count <= 10) { mCount = count; notifyDataSetChanged(); } } }