summaryrefslogtreecommitdiffstats
path: root/src/com/viewpagerindicator/UnderlinePageIndicator.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/viewpagerindicator/UnderlinePageIndicator.java')
-rw-r--r--src/com/viewpagerindicator/UnderlinePageIndicator.java402
1 files changed, 402 insertions, 0 deletions
diff --git a/src/com/viewpagerindicator/UnderlinePageIndicator.java b/src/com/viewpagerindicator/UnderlinePageIndicator.java
new file mode 100644
index 0000000..dc6f82a
--- /dev/null
+++ b/src/com/viewpagerindicator/UnderlinePageIndicator.java
@@ -0,0 +1,402 @@
+/*
+ * 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<SavedState> CREATOR = new Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+} \ No newline at end of file