【指示器】自定義ViewPager指示器,和你見過的不太一樣。

這次要說的指示器可能和大家常見的不同,這個是純繪製出來的,即只 onDraw出來的。也許不夠完善,歡迎大家提出問題。

github:https://github.com/ai2101039/YLPagerIndicator

有人說,這個github 有輪子,可是光使用輪子只是一個搬運工啊,而且github的輪子是爲了適應各種情況,app 代碼量可增加不少;

有人說,你這個指示器,我們可以用select xml,可是 xml的佔用內存啊,而且也不靈活啊。

 

能學習到什麼?小編就當各位都會,那麼就溫習一下。

1、自定義View 屬性

2、onMeasure 中,wrap_content 設置最小值

3、繪製圓環 和 實心圓

重點:

圓環的直徑計算 = 空心圓直徑 + 線寬

比如:空心圓半徑 10,圓環線寬 2,則 圓環直徑爲 22。而空心圓直徑爲 18

如圖

 

也就是說 大家在 onDraw裏面,

空心圓

半徑爲10,線寬爲2,實際空心圓半徑 11,實際空白區9

 



import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.ColorInt;
import android.support.annotation.Nullable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.View;

/**
 * @author 高延榮
 * @date 2018/12/11 16:48
 * 描述: ViewPager 指示器
 */
public class YLPagerIndicator extends View {

    private Context mContext;
    private ViewPager mViewPager;
    private PagerAdapter mAdapter;
    /**
     * 指示器數量
     */
    private int count;
    /**
     * 用於繪製圓環的畫筆
     */
    private Paint paintArc;
    /**
     * 用於繪製是新的畫筆
     */
    private Paint paintFill;

    /**
     * 圓環直徑
     * 重點:圓環的直徑 = 2 * radiusArc + stockWidthArc
     * 即:空心圓直徑 + 線寬,而不是  空心圓直徑 + 2 * 線寬
     */
    private int diaArc;
    /**
     * 圓環中空心圓的半徑
     */
    private float radiusArc;
    /**
     * 圓環中線寬
     */
    private float stockWidthArc;
    /**
     * 圓環顏色
     */
    private int colorArc;

    /**
     * 實心半徑
     */
    private float radiusFill;

    /**
     * 實心顏色
     */
    private int colorFill;

    /**
     * 圓環間距
     */
    private float space;

    /**
     * 選中的position
     */
    private int selectPosition;

    public YLPagerIndicator(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mContext = context;

        //  圓環畫筆/空心/抗鋸齒
        paintArc = new Paint();
        paintArc.setStyle(Paint.Style.STROKE);
        paintArc.setAntiAlias(true);

        //  實心畫筆/抗鋸齒
        paintFill = new Paint();
        paintFill.setAntiAlias(true);

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.YLPagerIndicator);

        radiusArc = typedArray.getDimension(R.styleable.YLPagerIndicator_radiusArc, 6);
        stockWidthArc = typedArray.getDimension(R.styleable.YLPagerIndicator_stockWidthArc, 1);
        colorArc = typedArray.getColor(R.styleable.YLPagerIndicator_colorArc, Color.WHITE);
        radiusFill = typedArray.getDimension(R.styleable.YLPagerIndicator_radiusFill, 3);
        colorFill = typedArray.getColor(R.styleable.YLPagerIndicator_colorFill, Color.WHITE);
        count = typedArray.getInteger(R.styleable.YLPagerIndicator_count, 1);
        space = typedArray.getDimension(R.styleable.YLPagerIndicator_radiusFill, 10);
        typedArray.recycle();

        //  使用工具函數,將dp 轉爲 px
        translateSize(radiusArc, stockWidthArc, radiusFill, space);
        //  使用工具函數,給畫筆設置屬性
        setPaint();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize;
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize;

        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();

        //  圓環直徑,這裏是個重點,一定是 2 * 內圓的半徑 + 線寬,而不是  2 * 內圓的半徑 + 2 * 線寬
        diaArc = (int) (2 * radiusArc + stockWidthArc);
        //  指示器總寬度
        int contentWidth = count * diaArc + (int) ((count - 1) * space);
        //  如果是 wrap_content 則設置最小寬高
        if (widthMode == MeasureSpec.AT_MOST) {
            widthSize = contentWidth + paddingLeft + paddingRight;
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
        }
        if (heightMode == MeasureSpec.AT_MOST) {
            heightSize = diaArc + paddingTop + paddingBottom;
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
        }

        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);

    }


    /**
     * 設置圓環/實心圓
     *
     * @param radiusArc     圓環半徑
     * @param widthArc      圓環寬度
     * @param colorArc      圓環顏色
     * @param radiusFill    實心半徑
     * @param colorFill     實心顏色
     * @param space         圓環間距
     * @param requestLayout 重新測量還是重新繪製,首先,重繪是肯定的。
     *                      關於重新測量,比如 一開始使用的默認,那麼整體佈局高度是 11dp,
     *                      如果你那邊想要提高或者縮小這個高度,又使用的是 wrap_content,
     *                      那麼久需要進行重新測量
     */
    public void setIndicator(float radiusArc, float widthArc, @ColorInt int colorArc, 
                             float radiusFill, @ColorInt int colorFill, 
                             float space, boolean requestLayout) {
        translateSize(radiusArc, widthArc, radiusFill, space);
        this.colorArc = colorArc;
        this.colorFill = colorFill;
        setPaint();
        //  重繪
        if (requestLayout) {
            requestLayout();
        } else {
            invalidate();
        }
    }

    /**
     * 設置ViewPager
     *
     * @param viewPager
     * @param requestLayout 是否需要重新onMeasure,
     *                      比如,你未設置指示器個數,那麼默認就是 1個,
     *                      這時候設置ViewPager,也只是顯示1個指示器
     *                      所以可以通過 requestLayout() 來重新 onMeasure
     */
    public void setViewPager(ViewPager viewPager, boolean requestLayout) {
        mViewPager = viewPager;
        mAdapter = viewPager.getAdapter();
        mViewPager.addOnPageChangeListener(mPagerListener);
        count = mAdapter.getCount();
        if (requestLayout) {
            requestLayout();
        }
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 先繪製圓環
        for (int i = 0; i < count; i++) {
            float cx = (i + 0.5F) * diaArc + getPaddingLeft() + i * space;
            float cy = diaArc / 2 + getPaddingTop();
            canvas.drawCircle(cx, cy, radiusArc, paintArc);
        }

        //  繪製實心圓
        float cx = (selectPosition + 0.5F) * diaArc + getPaddingLeft() + selectPosition * space;
        float cy = diaArc / 2 + getPaddingTop();
        canvas.drawCircle(cx, cy, radiusFill, paintFill);
    }


    private ViewPager.OnPageChangeListener mPagerListener = new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int position) {
            selectPosition = position;
            invalidate();
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    };

    ///////////////////////////////     工具函數    ///////////////////////////////

    /**
     * 將dp 轉換爲 px
     *
     * @param radiusArc  圓環內 空心圓半徑
     * @param widthArc   圓環線寬
     * @param radiusFill 實心圓半徑
     * @param space      圓環間距
     */
    private void translateSize(float radiusArc, float widthArc, float radiusFill, float space) {
        this.radiusArc = YLUtil.dp2px(mContext, radiusArc);
        this.stockWidthArc = YLUtil.dp2px(mContext, widthArc);
        this.radiusFill = YLUtil.dp2px(mContext, radiusFill);
        this.space = YLUtil.dp2px(mContext, space);
    }

    /**
     * 畫筆設置屬性
     */
    private void setPaint() {
        paintArc.setColor(colorArc);
        paintArc.setStrokeWidth(stockWidthArc);
        paintFill.setColor(colorFill);
    }
}

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章