Java tutorial
/* * 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.valtech.androidtoolkit.view.indicator; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.RectF; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.view.ViewConfigurationCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.View; import android.view.ViewConfiguration; import com.valtech.androidtoolkit.R; public class RectPageIndicator extends View implements PageIndicator { public static final int HORIZONTAL = 0; public static final int VERTICAL = 1; private final Paint mPaintPageFill; private final Paint mPaintStroke; private final Paint mPaintFill; private ViewPager mViewPager; private ViewPager.OnPageChangeListener mListener; private int mCurrentPage; private int mSnapPage; private int mCurrentOffset; private int mScrollState; private int mPageSize; private int mOrientation; private boolean mCentered; private boolean mSnap; private static final int INVALID_POINTER = -1; private int mTouchSlop; private float mLastMotionX = -1; private int mActivePointerId = INVALID_POINTER; private boolean mIsDragging; private float rectSize = 25; private int width; public RectPageIndicator(Context context) { this(context, null); } public RectPageIndicator(Context context, AttributeSet attrs) { this(context, attrs, R.attr.vpiCirclePageIndicatorStyle); } @SuppressWarnings("unused") public RectPageIndicator(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // 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, R.style.Widget_CirclePageIndicator); mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered); mOrientation = a.getInt(R.styleable.CirclePageIndicator_orientation, defaultOrientation); mPaintPageFill = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintPageFill.setStyle(Style.FILL); mPaintPageFill.setColor(a.getColor(R.styleable.CirclePageIndicator_pageColor, defaultPageColor)); mPaintStroke = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintStroke.setStyle(Style.STROKE); mPaintStroke.setColor(Color.TRANSPARENT); mPaintStroke .setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth)); mPaintFill = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintFill.setStyle(Style.FILL); mPaintFill.setColor(getResources().getColor(R.color.header_color)); mSnap = a.getBoolean(R.styleable.CirclePageIndicator_snap, defaultSnap); 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; updatePageSize(); 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 setSnap(boolean snap) { mSnap = snap; invalidate(); } public boolean isSnap() { return mSnap; } public void setWidth(int width) { this.width = width; } // TODO See if we can do that automatically according to the viewPager adapter (when // notifyDataSetChanged is called?). public void setNbPages(int size) { if (size != 0) { rectSize = (width / size); if (rectSize * size != width) rectSize++; invalidate(); } } /* * (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; } if (mCurrentPage >= count) { setCurrentItem(count - 1); return; } float dX; float dY; for (int iLoop = 0; iLoop < count; iLoop++) { dX = iLoop * rectSize; dY = 0; canvas.drawRect(new RectF(dX, dY, dX + rectSize, dY + 10), mPaintPageFill); canvas.drawRect(new RectF(dX, dY, dX + rectSize, dY + 10), mPaintStroke); } float cx = (mSnap ? mSnapPage : mCurrentPage); if (!mSnap && (mPageSize != 0)) { cx += (mCurrentOffset * 1.0f / mPageSize); } cx = ((mCurrentOffset * 1.0f) / width) * rectSize; dX = (mCurrentPage * rectSize) + cx; dY = 0; canvas.drawRect(new RectF(dX, dY, dX + rectSize, dY + 10), mPaintFill); } @Override public boolean onTouchEvent(android.view.MotionEvent ev) { return false; // if (super.onTouchEvent(ev)) { // return true; // } // if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) { // return false; // } // // final int action = ev.getAction(); // // switch (action & MotionEventCompat.ACTION_MASK) { // 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) { // if (!mViewPager.isFakeDragging()) { // mViewPager.beginFakeDrag(); // } // // mLastMotionX = x; // // 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)) { // mViewPager.setCurrentItem(mCurrentPage - 1); // return true; // } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) { // 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); // final float x = MotionEventCompat.getX(ev, index); // mLastMotionX = x; // 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 (view.getAdapter() == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } mViewPager = view; mViewPager.setOnPageChangeListener(this); updatePageSize(); invalidate(); } private void updatePageSize() { if (mViewPager != null) { mPageSize = (mOrientation == HORIZONTAL) ? mViewPager.getWidth() : mViewPager.getHeight(); } } @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; mCurrentOffset = positionOffsetPixels; updatePageSize(); 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 = 0; MeasureSpec.getMode(measureSpec); MeasureSpec.getSize(measureSpec); result = width; 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 = 10; 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); } public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } }