【指示器】自定义ViewPager指示器,和你见过的不太一样。

本文介绍了一种自定义的ViewPager指示器实现方式,通过纯绘制方法创建,而非使用现成的轮子或XML选择器。重点讲解了自定义View属性、onMeasure中wrap_content设置最小值、圆环和实心圆的绘制技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这次要说的指示器可能和大家常见的不同,这个是纯绘制出来的,即只 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);
    }
}

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值