com.valtech.androidtoolkit.view.indicator.RectPageIndicator.java Source code

Java tutorial

Introduction

Here is the source code for com.valtech.androidtoolkit.view.indicator.RectPageIndicator.java

Source

/*
 * 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];
            }
        };
    }
}