Android自定义简单步骤指示器

一,背景

写Android也有些年头了,今年也打算把自己绘制过的自定义View从简单到复杂做一个整合,温故而知新,也希望能给从事Android开发的弟兄们一个参考,喜欢就多多支持,入下图,本文主要讲解如何绘制一个圆角矩形的指示器。

二,绘制流程

  • 首先,回忆一下我们自定义View必走的三部曲,测量宽高(onMeasure),布置布局(onLayout),绘制内容(onDraw),无论我们绘制多炫酷复杂效果的View都离不开这三个步骤
  • 那么回到主题,我们首先要初始化两支画笔,一支用于绘制被选中的tab(紫色),一直用于绘制未被选中的tab(白色),并将画笔的样式设置为填充内部
ini 复制代码
    mUnSelectedPaint = new Paint();
    mUnSelectedPaint.setColor(mUnSelectedColor);
    mUnSelectedPaint.setStyle(Paint.Style.FILL);
    mUnSelectedPaint.setStrokeWidth(10f);

    mSelectedPaint = new Paint();
    mSelectedPaint.setColor(mSelectedColor);
    mSelectedPaint.setStyle(Paint.Style.FILL);
    mSelectedPaint.setStrokeWidth(10f);
  • 测量宽高,这里我没有把宽高写死也没有自定义宽高的逻辑,所以直接走Android默认的测量逻辑,仅仅记录View的宽度,稍后用于计算每个tab的宽度
java 复制代码
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
    }
  • 因为我们是自定义View而并非是ViewGroup,所以没有子View需要布置(所谓布置就是将子View进行摆放),就没有onLayout这个步骤(onLayout是ViewGroup的方法)

  • 绘制圆角矩形,先通过View的总宽度减去每个tab之间的边距,就能得到可用的总宽度,可用总宽度除以tab的个数,就能得到每个tab的宽度,循环遍历绘制所有的tab,left进行累加,就能计算出每个tab开始的x坐标,y坐标=left+width,然后根据左右,上下的坐标绘制一个个圆角矩形

ini 复制代码
    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
        //计算每个item的大小
        float useWidth = mWidth - (mHorizontalSpace * (mCount - 1));
        float width = useWidth / mCount;

        //绘制圆角矩形
        float left = 0;
        for (int i = 0; i < mCount; i++) {
            RectF rect = new RectF(left, 0, left + width, mHeight);
            left = rect.right + mHorizontalSpace;
            canvas.drawRoundRect(rect, mRound, mRound, mCurrentCount >= i ? mSelectedPaint : mUnSelectedPaint);
        }
    }
  • 完整代码
java 复制代码
/**
 * create by lijianhui
 * on 2022-7-7
 * <p>
 * description: 引导进度条
 */
public class GuideProgressView extends View {
    private final Paint mUnSelectedPaint;
    private final Paint mSelectedPaint;
    private float mWidth;
    private float mHeight = DensityUtil.dp2px(5);
    private float mHorizontalSpace = DensityUtil.dp2px(6);
    private float mRound = DensityUtil.dp2px(3);
    private int mCount = 4;
    private int mCurrentCount = 0;
    private int mSelectedColor = Color.parseColor("#A599FF");
    private int mUnSelectedColor = Color.WHITE;

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

    public GuideProgressView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GuideProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mUnSelectedPaint = new Paint();
        mUnSelectedPaint.setColor(mUnSelectedColor);
        mUnSelectedPaint.setStyle(Paint.Style.FILL);
        mUnSelectedPaint.setStrokeWidth(10f);

        mSelectedPaint = new Paint();
        mSelectedPaint.setColor(mSelectedColor);
        mSelectedPaint.setStyle(Paint.Style.FILL);
        mSelectedPaint.setStrokeWidth(10f);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
    }

    /**
     * 绘制
     *
     * @param canvas 画布
     */
    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
        //计算每个item的大小
        float useWidth = mWidth - (mHorizontalSpace * (mCount - 1));
        float width = useWidth / mCount;

        //绘制圆角矩形
        float left = 0;
        for (int i = 0; i < mCount; i++) {
            RectF rect = new RectF(left, 0, left + width, mHeight);
            left = rect.right + mHorizontalSpace;
            canvas.drawRoundRect(rect, mRound, mRound, mCurrentCount >= i ? mSelectedPaint : mUnSelectedPaint);
        }
    }

    /**
     * 设置高度
     *
     * @param height 高度
     */
    public void setGuideItemHeight(float height) {
        this.mHeight = height;
    }

    /**
     * 设置水平间距
     *
     * @param horizontalSpace 水平间距
     */
    public void setHorizontalSpace(float horizontalSpace) {
        this.mHorizontalSpace = horizontalSpace;
    }

    /**
     * 设置圆角
     *
     * @param round 圆角大小
     */
    public void setGuideItemRound(float round) {
        this.mRound = round;
    }

    /**
     * 设置个数
     *
     * @param count 个数
     */
    public void setGuideItemCount(int count) {
        this.mCount = count;
        invalidate();
    }

    /**
     * 选中下一个
     */
    public void selectedNext() {
        mCurrentCount++;
        invalidate();
    }

    /**
     * 获取当前步骤
     *
     * @return 步骤数
     */
    public int getCurrentPos() {
        return mCurrentCount;
    }

    /**
     * 设置选中的颜色
     *
     * @param selectedColor 颜色
     */
    public void setSelectedGuideItemColor(int selectedColor) {
        this.mSelectedColor = selectedColor;
    }

    /**
     * 设置未选中的颜色
     *
     * @param unSelectedColor 颜色
     */
    public void setUnSelectedGuideItemColor(int unSelectedColor) {
        this.mUnSelectedColor = unSelectedColor;
    }
}
相关推荐
_一条咸鱼_3 小时前
揭秘 Android TextInputLayout:从源码深度剖析其使用原理
android·java·面试
_一条咸鱼_3 小时前
揭秘!Android VideoView 使用原理大起底
android·java·面试
_一条咸鱼_3 小时前
深度揭秘!Android TextView 使用原理全解析
android·java·面试
_一条咸鱼_3 小时前
深度剖析:Android Canvas 使用原理全揭秘
android·java·面试
_一条咸鱼_3 小时前
深度剖析!Android TextureView 使用原理全揭秘
android·java·面试
_一条咸鱼_3 小时前
揭秘!Android CheckBox 使用原理全解析
android·java·面试
_一条咸鱼_3 小时前
深度揭秘:Android Toolbar 使用原理的源码级剖析
android·java·面试
_一条咸鱼_3 小时前
揭秘 Java ArrayList:从源码深度剖析其使用原理
android·java·面试