先上效果图,如果这是你想要的效果可以往下看。不是的话也可以借鉴一下。

android 自定义view 布局根节点转为自定义view 自定义view的流程_List

1、自定义view的基本流程

1)measure();

主要作用是测量view的宽高

2)layout();

主要作用是计算子view的位置,一般是自定义viewgroup时才会用上。(这里可以忽略)

3)draw();

主要作用是绘制view,切记由于该方法会多次执行,切勿在这个方法里面过多的创建对象,以免引起内存泄漏。

2、核心思想

1)从效果图可以看出stepview是有多个步骤的,那么我们可以使用stepbean来封装每一步的属性值。具体内容如下:

/**
 * 步骤view的数据基类
 */
public class StepBean {
    private String name;
    private String time;
    private int icon;//图片icon
    private int lineColor;
    private int nameColor;
    private int timeColor;
    private boolean isIng = false;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }

    public int getIcon() {
        return icon;
    }

    public void setIcon(int icon) {
        this.icon = icon;
    }

    public int getLineColor() {
        return lineColor;
    }

    public void setLineColor(int lineColor) {
        this.lineColor = lineColor;
    }

    public boolean isIng() {
        return isIng;
    }

    public void setIng(boolean ing) {
        isIng = ing;
    }

    public int getTimeColor() {
        return timeColor;
    }

    public void setTimeColor(int timeColor) {
        this.timeColor = timeColor;
    }

    public int getNameColor() {
        return nameColor;
    }

    public void setNameColor(int nameColor) {
        this.nameColor = nameColor;
    }
}

2)stepview会传入一个List<StepBean>来决定绘制多少步。下面我们来看看具体实现:代码里面的注解也很详细,无非是画圆画线和绘制图片。

/**
 * 自定义步骤view
 */
public class StepView extends View {

    /**
     * 需要显示的步骤节点集合
     */
    private List<StepBean> stepBeanList = new ArrayList<>();
    private int mCurrViewWidth;//当前控件宽度
    private int mCurrViewHeight;//当前控件高度

    private Paint mPaint;//节点画笔
    private TextPaint mTextPaint;//文字描述的画笔
    private Paint mLinePaint;
    private int roundSize = 8;//圆的直径
    private int haloRoundSize = 0;//外圆的直径
    private int haloThickness = 1;//外圆的厚度
    private int haloAlpha = 50;//光晕透明度
    private int haloWidth = 4;//光晕的宽度
    private int textSize = 12;//文字大小
    private int lineHeight = 1;//步骤线的高度
    private int startX = 0;//X轴开始位置
    private int startY = 10;//Y轴开始位置
    private int itemWidth = 0;//每一项的宽度
    private int roundType = 0;//0空心圆,2光晕圆
    //所有位置以圆心为标准
    private int roundX = 0;//大圆心X
    private int roundY = 0;//大圆心Y
    private int bigRoundSize = 20;//圆的直径

    public StepView(Context context) {
        super(context);
    }

    public StepView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        //dp转px
        roundSize = DensityUtil.dip2px(context, roundSize);
        haloWidth = DensityUtil.dip2px(context, haloWidth);
        haloRoundSize = roundSize + haloWidth * 2;
        bigRoundSize = DensityUtil.dip2px(context, bigRoundSize);
        roundY = DensityUtil.dip2px(context, startY) + bigRoundSize / 2;
        haloThickness = DensityUtil.dip2px(context, haloThickness);
        lineHeight = DensityUtil.dip2px(context, lineHeight);
        textSize = DensityUtil.dip2px(context, textSize);

        mPaint = new Paint();
        mPaint.setAntiAlias(true); //消除锯齿
        mLinePaint = new Paint();
        //线高
        mLinePaint.setStrokeWidth(lineHeight);
        //虚线
        DashPathEffect effects = new DashPathEffect(new float[]{lineHeight * 3,
                lineHeight * 3}, 0);
        mLinePaint.setPathEffect(effects);
        mTextPaint = new TextPaint();
        mTextPaint.setAntiAlias(true); //消除锯齿
        mTextPaint.setTextSize(textSize);
        mTextPaint.setTypeface(Typeface.SANS_SERIF);
        //文字字体加粗
        mTextPaint.setFakeBoldText(false);

    }

    public void setStepBeanList(List<StepBean> stepBeanList) {
        if (stepBeanList != null)
            this.stepBeanList = stepBeanList;
        //计算控件开始位置
        if (stepBeanList.size() > 0)
            itemWidth = mCurrViewWidth / stepBeanList.size();
        roundX = itemWidth / 2 - bigRoundSize / 2;
        invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mCurrViewWidth = getMeasuredWidth();
        mCurrViewHeight = getMeasuredHeight();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < stepBeanList.size(); i++) {
            // 画圆
            mPaint.setColor(stepBeanList.get(i).getLineColor());
            mPaint.setStyle(Paint.Style.FILL); //绘制实心圆
            RectF rf2 = new RectF(roundX - roundSize / 2 + itemWidth * i, roundY - roundSize / 2, roundX + roundSize / 2 + itemWidth * i, roundY + roundSize / 2);
            canvas.drawOval(rf2, mPaint);
            if (roundType == 0) {
                // 画空心圆
                mPaint.setStyle(Paint.Style.STROKE); //绘制空心圆
                mPaint.setStrokeWidth(haloThickness);//设置空心圆的厚度
            } else {
                // 画圆光圈
                mPaint.setAlpha(haloAlpha);
            }
            RectF haloRectF = new RectF(roundX - haloRoundSize / 2 + itemWidth * i, roundY - haloRoundSize / 2, roundX + haloRoundSize / 2 + itemWidth * i, roundY + haloRoundSize / 2);
            canvas.drawOval(haloRectF, mPaint);
            // 绘制线,最后一个点不用绘制线
            if (i < stepBeanList.size() - 1) {
                mLinePaint.setColor(stepBeanList.get(i + 1).getLineColor());
                int x = roundX + haloRoundSize / 2;//开始位置
                canvas.drawLine(x + itemWidth * i, roundY, x + itemWidth * (i + 1) - haloRoundSize, roundY, mLinePaint);
            }
            //绘制正在进行的图片
            if (stepBeanList.get(i).isIng()) {
                Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), stepBeanList.get(i).getIcon());
                int w = mBitmap.getWidth();
                int h = mBitmap.getHeight();
                // 指定图片绘制区域(全图)
                Rect src = new Rect(0, 0, w, h);
                // 指定图片在屏幕上显示的区域(原图大小)
                Rect dst = new Rect(roundX - w / 2 + itemWidth * i, roundY - h / 2, roundX + w / 2 + itemWidth * i, roundY + h / 2);
                canvas.drawBitmap(mBitmap, src, dst, null);
            }
            // 绘制name文字
            mTextPaint.setColor(stepBeanList.get(i).getNameColor());
            int tx = roundX - getTextWidth(stepBeanList.get(i).getName(), mTextPaint) / 2 + itemWidth * i;
            int ty = roundY + bigRoundSize + getTextHeight(stepBeanList.get(i).getName(), mTextPaint);
            canvas.drawText(stepBeanList.get(i).getName(), tx, ty, mTextPaint);
            // 绘制time文字
            mTextPaint.setColor(stepBeanList.get(i).getTimeColor());
            tx = roundX - getTextWidth(stepBeanList.get(i).getTime(), mTextPaint) / 2 + itemWidth * i;
            ty = ty + getTextHeight(stepBeanList.get(i).getTime(), mTextPaint) * 2;
            canvas.drawText(stepBeanList.get(i).getTime(), tx, ty, mTextPaint);
        }
    }

    //获取文字宽度
    private int getTextWidth(String text, Paint paint) {
        Rect rect = new Rect(); // 文字所在区域的矩形
        paint.getTextBounds(text, 0, text.length(), rect);
        return rect.width();
    }

    //获取文字高度
    private int getTextHeight(String text, Paint paint) {
        Rect rect = new Rect();
        paint.getTextBounds(text, 0, text.length(), rect);
        return rect.height();
    }
}

3)这里需要注意一下,由于我这边的需求的步骤不多,所以我这边是按:屏幕的宽度/步骤总数 = 每一步的宽度;如果你的步骤过多的话会出现每一步的宽度很小,可以考虑采用滑动显示的方式来实现。

3、如何使用:

1)在布局页面编写以下代码:

<com.library_base.ui.view.stepview.StepView
                            android:id="@+id/stepView"
                            android:layout_width="match_parent"
                            android:layout_height="80dp"/>

2)在Activity里面使用:

StepView stepView = findViewById(R.id.stepView);

private void setStepBean(String type) {
        List<StepBean> stepBeans = new ArrayList<>();
        //待核销
        StepBean stepBean = new StepBean();
        stepBean.setName("待核销");
        stepBean.setTime(CommonUtils.getDateToString2(Long.parseLong(detailObject.getOrderTime())));
        stepBean.setNameColor(Color.parseColor("#333333"));
        stepBean.setTimeColor(Color.parseColor("#CCCCCC"));
        stepBean.setLineColor(Color.parseColor("#51B3F1"));
        stepBeans.add(stepBean);
        //核销中
        stepBean = new StepBean();
        stepBean.setName("核销中");
        stepBean.setTime(CommonUtils.getDateToString2(Long.parseLong(detailObject.getOrderTime())));
        stepBean.setLineColor(Color.parseColor("#51B3F1"));
        stepBean.setTimeColor(Color.parseColor("#CCCCCC"));
        if (type.equals("0")) {
            stepBean.setNameColor(Color.parseColor("#51B3F1"));
            stepBean.setIng(true);
            stepBean.setIcon(R.mipmap.check_state_img);
        } else {
            stepBean.setNameColor(Color.parseColor("#333333"));
        }
        stepBeans.add(stepBean);
        //核销完成
        stepBean = new StepBean();
        stepBean.setTimeColor(Color.parseColor("#CCCCCC"));
        if (type.equals("0")) {
            stepBean.setName("核销完成");
            stepBean.setTime("");
            stepBean.setNameColor(Color.parseColor("#333333"));
            stepBean.setLineColor(Color.parseColor("#D8D8D8"));
        } else if (type.equals("1")) {
            //核销不通过
            stepBean.setName("核销不通过");
            stepBean.setTime(CommonUtils.getDateToString2(Long.parseLong(detailObject.getVerificationTime())));
            stepBean.setNameColor(Color.parseColor("#FF4503"));
            stepBean.setLineColor(Color.parseColor("#FF4503"));
            stepBean.setIng(true);
            stepBean.setIcon(R.mipmap.check_state_un);
        } else if (type.equals("2")) {
            //核销通过
            stepBean.setName("核销通过");
            stepBean.setTime(CommonUtils.getDateToString2(Long.parseLong(detailObject.getVerificationTime())));
            stepBean.setNameColor(Color.parseColor("#51B3F1"));
            stepBean.setLineColor(Color.parseColor("#51B3F1"));
            stepBean.setIng(true);
            stepBean.setIcon(R.mipmap.check_state_ed);
        }
        stepBeans.add(stepBean);
        stepView.setStepBeanList(stepBeans);
    }

3)这里使用了单位转换的工具类

public class DensityUtil {

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    /**
     * 获取屏幕宽度
     *
     * @param context
     * @return
     */
    public static int getWidth(Context context) {
        WindowManager wm = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        return wm.getDefaultDisplay().getWidth();
    }

    /**
     * 获取屏幕高度
     *
     * @param context
     * @return
     */
    public static int getHeight(Context context) {
        WindowManager wm = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        return wm.getDefaultDisplay().getHeight();
    }

    /**
     * 获取图片的宽高
     *
     * @param context
     * @param r
     * @return
     */
    public static int getImageWidth(Context context, int r) {
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), r);
        return bitmap.getWidth();
    }
}

至此一个简单的自定义stepview就实现了。