com.example.anumbrella.viewpager.UnderlinePagerIndicator.java Source code

Java tutorial

Introduction

Here is the source code for com.example.anumbrella.viewpager.UnderlinePagerIndicator.java

Source

/*
 * 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.example.anumbrella.viewpager;

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;

/**
 * Created by anumbrella on 15-11-4.
 * <p/>
 * <p/>
 * ??Jake Wharton,??,
 * <p/>
 * UnderlinePagerIndicator????
 */
public class UnderlinePagerIndicator extends View implements PagerIndicator {

    /**
     * 
     */
    private static final int INVALID_POINTER = -1;

    /**
     * ???
     */
    private static final int FADE_TIME = 30;

    /**
     * ?
     */
    private boolean mFades;

    /**
     * 
     */
    private int mFadeDelay;

    /**
     * 
     */
    private int mFadeLength;

    private ViewPager mViewPager;

    /**
     * ViewPager???
     */
    private ViewPager.OnPageChangeListener pageChangeListener;

    /**
     * ??
     */
    private int mCurrentPage;

    /**
     * ??
     */
    private int mTouchSlop;

    /**
     * ?
     */
    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    /**
     * 
     */
    private boolean isDragging = false;

    /**
     * 
     */
    private int mActivePointerId = INVALID_POINTER;

    /**
     * ?x??
     */
    private float mLastMotionX = -1;

    /**
     * ???
     */
    private int mFadeBy;

    /**
     * ????
     */
    private float mPositionOffset;

    /**
     * ??
     */
    private int mScrollState;

    /**
     * ???
     */
    private final Runnable mFadeRunable = new Runnable() {
        @Override
        public void run() {
            if (!mFades) {
                return;
            }
            //?????0
            final int alpha = Math.max(mPaint.getAlpha() - mFadeBy, 0);
            mPaint.setAlpha(alpha);
            invalidate();
            //?0???
            if (alpha > 0) {
                postDelayed(this, FADE_TIME);
            }
        }

    };

    public UnderlinePagerIndicator(Context context) {
        this(context, null);
    }

    public UnderlinePagerIndicator(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.vpiUnderlinePagerIndicatorStyle);
    }

    public UnderlinePagerIndicator(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        //
        if (isInEditMode()) {
            return;
        }

        //??
        final Resources res = getResources();

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

        //?xml
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.UnderlinePagerIndicator, defStyle, 0);

        setFades(array.getBoolean(R.styleable.UnderlinePagerIndicator_fades, defaultFades));
        setSelectedColor(array.getColor(R.styleable.UnderlinePagerIndicator_selectedColor, defaultSelectedColor));
        setFadeDelay(array.getInteger(R.styleable.UnderlinePagerIndicator_fadeDelay, defaultFadeDelay));
        setFadeLength(array.getInteger(R.styleable.UnderlinePagerIndicator_fadeLength, defaultFadeLength));

        Drawable background = array.getDrawable(R.styleable.UnderlinePagerIndicator_android_background);
        if (background != null) {
            setBackgroundDrawable(background);
        }
        array.recycle();

        final ViewConfiguration configuration = ViewConfiguration.get(context);
        //????(?????)
        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
    }

    /**
     * ?
     *
     * @param isFades
     */
    public void setFades(boolean isFades) {
        if (mFades != isFades) {
            mFades = isFades;
            if (mFades) {
                post(mFadeRunable);
            } else {
                //?,?
                removeCallbacks(mFadeRunable);
                /**
                 * ?0---255,??
                 * 0xFF = 255
                 */
                mPaint.setAlpha(0xFF);
                invalidate();
            }
        }
    }

    /**
     * 
     *
     * @param fadeDelay
     */
    public void setFadeDelay(int fadeDelay) {
        mFadeDelay = fadeDelay;
    }

    /**
     * 
     *
     * @param fadeLength
     */
    public void setFadeLength(int fadeLength) {
        mFadeLength = fadeLength;

        //???
        // 0xFF = 255 ,
        // :mFadeLength = 400 ; FADE_TIME = 30
        // mFadeBy = 19.125
        // mFadeBy19
        mFadeBy = 0xFF / (mFadeLength / FADE_TIME);
    }

    /**
     * ?
     *
     * @param selectedColor
     */

    public void setSelectedColor(int selectedColor) {
        mPaint.setColor(selectedColor);
        invalidate();
    }

    public boolean getFades() {
        return mFades;
    }

    public int getFadeDelay() {
        return mFadeDelay;
    }

    public int getFadeLength() {
        return mFadeLength;
    }

    public int getSelectedColor() {
        return mPaint.getColor();
    }

    /**
     * 
     */
    @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 paddingLeft = getPaddingLeft();
        final float paddingRight = getPaddingRight();
        final float pageWidth = (getWidth() - paddingLeft - paddingRight) / (1f * count);
        final float left = paddingLeft + pageWidth * (mCurrentPage + mPositionOffset);
        final float top = getPaddingTop();
        final float bottom = getHeight() - getPaddingBottom();
        final float right = left + pageWidth;
        //
        canvas.drawRect(left, top, right, bottom, mPaint);
    }

    /**
     * ??
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (super.onTouchEvent(event)) {
            return true;
        }

        if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {
            return false;
        }

        /**
         * ACTION_POINTER_DOWN:??
         * ACTION_POINTER_UP:???
         * ACTION_MASK & ACTION ACTION_POINTER_DOWNACTION_POINTER_UP
         */
        final int action = event.getAction() & MotionEventCompat.ACTION_MASK;

        switch (action) {
        case MotionEvent.ACTION_DOWN:
            //getPointerId0getPointerCount-1,,?0
            mActivePointerId = MotionEventCompat.getPointerId(event, 0);
            mLastMotionX = event.getX();
            break;
        case MotionEvent.ACTION_MOVE:
            //?pointer?index
            final int activePointerIndex = MotionEventCompat.findPointerIndex(event, mActivePointerId);
            //?pointerx?
            final float x = MotionEventCompat.getX(event, activePointerIndex);
            //?x??
            final float deltaX = x - mLastMotionX;

            if (!isDragging) {
                if (Math.abs(deltaX) > mTouchSlop) {
                    isDragging = true;
                }
            }
            if (isDragging) {
                mLastMotionX = x;
                if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {
                    mViewPager.fakeDragBy(deltaX);
                }
            }
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:

            //??
            if (!isDragging) {
                final int count = mViewPager.getAdapter().getCount();
                final int width = getWidth();

                //??
                final float halfWidth = width / 2f;
                final float sixthWidth = width / 6f;
                //,?
                if ((mCurrentPage > 0) && (event.getX() < halfWidth - sixthWidth)) {
                    if (action != MotionEvent.ACTION_CANCEL) {
                        mViewPager.setCurrentItem(mCurrentPage - 1);
                    }
                    return true;
                } else if ((mCurrentPage < count - 1) && (event.getX() > halfWidth + sixthWidth)) {
                    if (action != MotionEvent.ACTION_CANCEL) {
                        mViewPager.setCurrentItem(mCurrentPage + 1);
                    }
                    return true;
                }
            }

            isDragging = false;
            mActivePointerId = INVALID_POINTER;
            if (mViewPager.isFakeDragging()) {
                mViewPager.endFakeDrag();
            }
            break;
        case MotionEventCompat.ACTION_POINTER_DOWN:
            final int index = MotionEventCompat.getActionIndex(event);
            mLastMotionX = MotionEventCompat.getX(event, index);
            mActivePointerId = MotionEventCompat.getPointerId(event, index);
            break;
        case MotionEventCompat.ACTION_POINTER_UP:
            final int pointerIndex = MotionEventCompat.getActionIndex(event);
            final int pointerId = MotionEventCompat.getPointerId(event, pointerIndex);
            if (pointerId == mActivePointerId) {
                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                mActivePointerId = MotionEventCompat.getPointerId(event, newPointerIndex);
            }
            mLastMotionX = MotionEventCompat.getX(event,
                    MotionEventCompat.findPointerIndex(event, mActivePointerId));
            break;
        }
        return true;
    }

    @Override
    public void setViewPager(ViewPager viewPager) {
        if (viewPager == mViewPager) {
            return;
        }

        if (mViewPager != null) {
            //??
            mViewPager.setOnPageChangeListener(null);
        }

        if (viewPager.getAdapter() == null) {
            throw new IllegalStateException("ViewPager dose not have adapter instance!");
        }

        mViewPager = viewPager;
        //??
        mViewPager.setOnPageChangeListener(this);
        invalidate();
        //?
        post(new Runnable() {
            @Override
            public void run() {
                if (mFades) {
                    post(mFadeRunable);
                }
            }
        });
    }

    @Override
    public void setViewPager(ViewPager viewPager, int initialPosition) {
        setViewPager(viewPager);
        setCurrentItem(initialPosition);
    }

    @Override
    public void setCurrentItem(int item) {
        if (mViewPager == null) {
            throw new IllegalStateException("ViewPager has not been found!");
        }
        mCurrentPage = item;
        mViewPager.setCurrentItem(item);
        invalidate();
    }

    @Override
    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
        pageChangeListener = listener;
    }

    @Override
    public void notifyDataSetChanged() {
        invalidate();
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        mCurrentPage = position;
        mPositionOffset = positionOffset;
        if (mFades) {
            //??
            if (positionOffsetPixels > 0) {
                removeCallbacks(mFadeRunable);
                mPaint.setAlpha(0xFF);
                //,?
            } else if (mScrollState != ViewPager.SCROLL_STATE_DRAGGING) {
                postDelayed(mFadeRunable, mFadeDelay);
            }
        }
        invalidate();

        if (pageChangeListener != null) {
            pageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
        }
    }

    @Override
    public void onPageSelected(int position) {
        //??
        if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
            mCurrentPage = position;
            mPositionOffset = 0;
            invalidate();
            mFadeRunable.run();
        }
        if (pageChangeListener != null) {
            pageChangeListener.onPageSelected(position);
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        mScrollState = state;
        if (pageChangeListener != null) {
            pageChangeListener.onPageScrollStateChanged(state);
        }
    }

    /**
     * ????
     *
     * @param state
     */
    @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState savedState = (SavedState) state;
        super.onRestoreInstanceState(savedState.getSuperState());
        mCurrentPage = savedState.currentPage;
        requestLayout();
    }

    /**
     * ????
     *
     * @return
     */
    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState savedState = new SavedState(superState);
        savedState.currentPage = mCurrentPage;
        return savedState;
    }

    /**
     * ?,BaseSavedState
     */
    public static class SavedState extends BaseSavedState {

        private int currentPage;

        private SavedState(Parcel in) {
            super(in);
            currentPage = in.readInt();
        }

        public SavedState(Parcelable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(currentPage);
        }

        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            @Override
            public SavedState createFromParcel(Parcel source) {
                return new SavedState(source);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }
}