Android自定义控件-等级条

写一个等级条view效果如下:

Android自定义控件-等级条_第1张图片


前言:对于熟悉自定义view的同学来说,这个效果实在是太简单了。自己写的时候发现,在写之前一定要事先把各个测量点,绘制的点计算方法想好,不然还是要调试一番才能完善,当然我这个肯定还不够完善,勉强看的过去了。


用到知识点:

1、view测量

2、shader

3、属性动画

重点:等级划分的时候,每个等级有一个开始点和结束点,比如0-50,50-200;在这个效果中,每个等级的宽度是一样的,所以比如100的时候,它的宽度就是,一分宽度,加(100-50)/(200-50)*一个等级宽度,有了这个思维之后其他都是小问题了。


1、测量高度,宽度不用测量了,因为你定义view的时候肯定会告诉我一个固定的宽度吧(mathparent也是一个精确宽度)

这个如果要写自定义view需要先定义字体大小,图片大小,进度条宽度等等,为了方便直接用变量写死,想要支撑自定义属性的时候直接取出来赋值就可以了。这里偷懒跳过。。。

首先拿到图片的宽高和字体的高度

  public void getLevelImgWH() {
        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), levelimgs_gray[0], opts);
        this.levelImgWidth = opts.outWidth;
        this.levelImgHeight = opts.outHeight;
        Log.i(Tag, "levelImgWidth:" + levelImgWidth + ",levelImgHeight:" + levelImgHeight);

    }

再写个方法计算字符串的宽高

  public Rect getTextWH(String text, int textSize) {
        Paint pFont = new Paint();
        Rect rect = new Rect();
        pFont.setTextSize(textSize);
        pFont.getTextBounds(text, 0, text.length(), rect);
        return rect;
    }

这样我们再规定好,进度条和字符串的间隔

    //图片等级与线条的间距
    private int padding_img = 20;
    //线条与上面文字的间距
    private int padding_text = 20;

由于我们的进度条首尾是有弧度的,我们绘制的时候,进不能从0到最后的,这样弧度效果就没了。。。

于是由于规定死了进度条的宽度为20.那么我就指定笔触的宽度为10,这个在进度条的首尾留出笔触的宽度,就可以看到弧度效果了。

    private int cap = 10;//笔触宽度
    private int lineWidth = 20;//进度条宽度

基本上所有的高度都有了,现在设置控件的高度

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int height = getPaddingTop() + getPaddingBottom() + lineWidth + padding_img + levelImgHeight + 20 + textHeight + padding_text;
        setMeasuredDimension(getMeasuredWidth(), height);
    }

2、绘制

      先绘制没有设置进度值的时候,默认六个都是灰色的图片,当我们有等级值的时候,我们就要根据等级,计算出下面的等级图片,很简单,哪一个数组来存放这些图片,每次设置等级值的时候都重新计算,就可以达到动态显示效果了。

首先根据等级值,计算等级,并且计算它实际应该有多宽

   //根据当前值,按照比例,计算它该有的长度,每个刻度的长度一样,但是每个刻度的差值不一样,
    //这里就根据比例来计算
    public int getCurrentDialLength() {

        getDialAllLength();
        if (allLength > 0 && datas.size() > 0 && currentDialvalue > 0) {
            int pre = allLength / (datas.size());
            for (int i = 0; i < datas.size(); i++) {
                UserLevelDial dial = datas.get(i);
                if (currentDialvalue <= dial.dial_e && currentDialvalue >= dial.dial_s) {
                    this.currentImg = i;
                    cDialLenth = pre * i + (int) (pre * ((currentDialvalue - dial.dial_s) / ((float) dial.dial_e - dial.dial_s)));
                    Log.i(Tag, "i:" + i);
                    break;
                }
            }
        }
        if (currentDialvalue > datas.get(datas.size() - 1).dial_e) {
            this.currentImg = datas.size();
            cDialLenth = allLength;
        }
        Log.i(Tag, "cDialLenth:" + cDialLenth);
        //重新设置等级图片
        setLevelImgs();
        return cDialLenth;
    }

然后根据等级,设置下面的等级图片

    private int[] levelimgs_gray = {R.drawable.grade_v1, R.drawable.grade_v2, R.drawable.grade_v3,
            R.drawable.grade_v4, R.drawable.grade_v5, R.drawable.grade_v6};
    private int[] levelimgs_light = {R.drawable.grade_v1_arrive, R.drawable.grade_v2_arrive, R.drawable.grade_v3_arrive,
            R.drawable.grade_v4_arrive, R.drawable.grade_v5_arrive, R.drawable.grade_v6_arrive};
    private ArrayList bitmap_levelimgs_gray = new ArrayList();
    private ArrayList bitmap_levelimgs_light = new ArrayList();
    private ArrayList bitmap_levelimgs_show = new ArrayList();

  public void setLevelImgs() {
        bitmap_levelimgs_show.clear();
        for (int i = 0; i < bitmap_levelimgs_gray.size(); i++) {
            if (i <= currentImg) {
                bitmap_levelimgs_show.add(bitmap_levelimgs_light.get(i));
            } else {
                bitmap_levelimgs_show.add(bitmap_levelimgs_gray.get(i));
            }
        }
    }

接着,绘制

  @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);//利用父类去绘制背景
//        clearScr(canvas);

        if (cDialLenth > 0) {//cDialLenth当前等级的刻度的长度,大于0才绘制上门的文字x/1500 
            //绘制文字
            Rect rect = getTextWH(currentDialvalue + "", textSize);
            Rect rect2 = getTextWH("/" + maxValue, textSize);
            int text_pw = cDialLenth < rect.width() ? 0 : cDialLenth - rect.width();
            text_pw += getPaddingLeft();
            //修正边界
            int maxw = allLength - rect.width() - rect2.width() + cap * 2 + getPaddingLeft() - 5;
            text_pw = text_pw > maxw ? maxw : text_pw;
            Log.i(Tag, "textgetPaddingTop:" + getPaddingTop());
            int text_ph = getPaddingTop() +rect.height();
            String v_str = String.valueOf(currentDialvalue);
            canvas.drawText(v_str, text_pw, text_ph, paint_text_value);
            int text_pw2 = text_pw + rect.width() + 5;
            canvas.drawText("/" + maxValue, text_pw2, text_ph, paint_text_total);
        }
        int bgline_ph = lineWidth / 2 + 1 + getPaddingTop() + textHeight + padding_text;
        canvas.drawLine(getPaddingLeft() + cap, bgline_ph, getMeasuredWidth() - getPaddingRight() - cap, bgline_ph, paint_bg);
        linearGradient = new LinearGradient(getPaddingLeft(), 0, cDialLenth, 0, new int[]{getResources().getColor(R.color.userlevels1), getResources().getColor(R.color.userlevels2)}, null, LinearGradient.TileMode.CLAMP);
        paint_first.setShader(linearGradient);

        canvas.drawLine(getPaddingLeft() + cap, bgline_ph, cDialLenth + getPaddingLeft() + cap, bgline_ph, paint_first);
        drawLevel(canvas);
    }

最后,来一个值动画,从0变到等级值,就ok了。

/**
     * 设置当前刻度
     */
    public void setCurrentDialvalue(int dial) {
        if (null != valueAnimator && valueAnimator.isRunning()) {
            valueAnimator.cancel();
        }
        valueAnimator = ValueAnimator.ofInt(0, dial);

        valueAnimator.setDuration(dial > 500 ? 2000 : 1000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                currentDialvalue = (int) valueAnimator.getAnimatedValue();
                getCurrentDialLength();
                invalidate();
            }
        });
        valueAnimator.start();


    }



源码下载



你可能感兴趣的:(自定义控件)