com.runmit.sweedee.view.CirclePageIndicator.java Source code

Java tutorial

Introduction

Here is the source code for com.runmit.sweedee.view.CirclePageIndicator.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.runmit.sweedee.view;

import java.util.ArrayList;
import java.util.List;

import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
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.os.Parcel;
import android.os.Parcelable;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.View;

import com.runmit.sweedee.R;
import com.runmit.sweedee.util.XL_Log;

/**
 * Draws circles (one for each view). The current view position is filled and
 * others are only stroked.
 */
public class CirclePageIndicator extends View implements PageIndicator {

    XL_Log log = new XL_Log(CirclePageIndicator.class);

    private float mRadius;

    private ViewPager mViewPager;

    private ViewPager.OnPageChangeListener mListener;

    private int mCurrentPage;

    private int mScrollState;

    int defaultFillColor = 0;

    int defaultCommonColor = 0;

    private List<CircleItem> mCircleItemList;

    private ArgbEvaluator mColorEvaluator;

    private int realCount = 0;

    AnimatorUpdateListener mAnimatorUpdateListener = new AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            invalidate();
        }
    };

    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;

        final Resources res = getResources();
        defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color);
        defaultCommonColor = res.getColor(R.color.default_circle_indicator_stroke_color);
        final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, 0);
        mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius);
        a.recycle();
        mColorEvaluator = new ArgbEvaluator();
    }

    public void setRadius(float radius) {
        mRadius = radius;
        invalidate();
    }

    public float getRadius() {
        return mRadius;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mCircleItemList == null || mCircleItemList.size() == 0) {
            return;
        }
        for (int i = 0, size = mCircleItemList.size(); i < size; i++) {
            mCircleItemList.get(i).draw(canvas, mRadius);
        }
    }

    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);

        mCircleItemList = new ArrayList<CircleItem>();
        float dX;
        float dY;

        int longPaddingBefore = getPaddingLeft();
        int shortPaddingBefore = getPaddingTop();

        final float threeRadius = mRadius * 3;
        final float shortOffset = shortPaddingBefore + mRadius;

        float longOffset = longPaddingBefore + mRadius;
        realCount = ((RecyclingPagerAdapter) mViewPager.getAdapter()).getRealCount();

        for (int iLoop = 0, size = realCount; iLoop < size; iLoop++) {
            float drawLong = longOffset + (iLoop * threeRadius);
            dX = drawLong;
            dY = shortOffset;

            CircleItem mItem = new CircleItem(iLoop, dX, dY);
            mItem.setBackgroundColor(iLoop == 0 ? defaultFillColor : defaultCommonColor);
            mCircleItemList.add(mItem);
        }
        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;
        switch (state) {
        case ViewPager.SCROLL_STATE_IDLE:// ?
            break;
        case ViewPager.SCROLL_STATE_DRAGGING:// 
            break;
        case ViewPager.SCROLL_STATE_SETTLING://
            break;
        default:
            break;
        }
        if (mListener != null) {
            mListener.onPageScrollStateChanged(state);
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        Integer color = (Integer) mColorEvaluator.evaluate(positionOffset, defaultFillColor, defaultCommonColor);
        mCircleItemList.get(position % realCount).setBackgroundColor(color);
        color = (Integer) mColorEvaluator.evaluate(positionOffset, defaultCommonColor, defaultFillColor);
        mCircleItemList.get((position + 1) % realCount).setBackgroundColor(color);

        invalidate();
        mCurrentPage = position;
        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;
    }

    /*
     * (non-Javadoc)
     * 
     * @see android.view.View#onMeasure(int, int)
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(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);
        log.debug("specMode=" + specMode + ",specSize=" + specSize + ",mViewPager=" + mViewPager);
        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 = realCount;
            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;
        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];
            }
        };
    }

    class CircleItem {
        float dx;
        float dy;
        int backgroundColor;
        Paint mPaint;
        int id;

        CircleItem(int id, float x, float y) {
            this.dx = x;
            this.dy = y;
            this.id = id;

            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setStyle(Style.FILL);
        }

        void draw(Canvas canvas, float mRadius) {
            mPaint.setColor(backgroundColor);
            canvas.drawCircle(dx, dy, mRadius, mPaint);
        }

        public int getBackgroundColor() {
            return backgroundColor;
        }

        public void setBackgroundColor(int color) {
            this.backgroundColor = color;
        }
    }
}