小巧实用的自定义Loading

作者: 我的阿福 | 来源:发表于2018-09-30 11:46 被阅读270次

    今天一起来做一个简单实用的自定义Loading控件,效果如图所示:

    image

    说一下思路:

    1.绘制两个圆环

    2.圆环中绘制弧形,填充进度。

    3.根据中间控件计算文字合适大小,绘制文字。

    第一步,绘制两个圆环。

    采用绘制弧形方法绘制,因为考虑到可能是其他形状的进度,比如椭圆等。如这种:

    绘制弧形的方法为:

    image

    其中,第一个参数oval是一个矩形,它的作用是用来定位咱们要绘制的圆的具体大小和位置,其实说白了就是我们要绘制的圆(或者弧)的外切矩形。当然啦,这个矩形的形状可能是长方形也可能是正方形,如果是正方形咱们绘制出来的就是一个规范的圆的一段,如果是长方形,绘制出来的就是椭圆的一段。

    第二个参数是说绘制的这个弧线开始的角度,一般是-90,代表从圆的顶点开始,如果设置为0则从三点钟方向。

    第四个参数就是代表要绘制的这段弧线从开始到结束要划过的角度,如果是360,那画出来就是一个完整的圆形或者椭圆咯,注意,这个和startAngle参数没有太大的关系,startAngle只是看你弧形开始的位置而已。

    第五个参数是是否需要绘制出圆心,我们这里不需要,设置成false即可。

    接下来定义矩形的大小,

    外层圆:rectFOut =new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());

    内层圆:rectFIn =new RectF(ringWidth, ringWidth, getMeasuredWidth() -ringWidth, getMeasuredHeight() -ringWidth);

    getMeasuredWidth和getMeasuredHeight就是取出自身控件在测量完毕后的宽和高,这里我们就让圆环紧贴控件本身大小即可。ringWidth为定义的环的宽度,这里按照比例来取,例子中取的是getMeasuredWidth() /20。

    第二步,绘制进度

    绘制进度一样是采用绘制弧形的方法来做,只不过这里是一段一段的绘制。

    和上面不同的是需要计算当前需要绘制的弧形的宽度和开始的位置,这里我将画笔的宽度直接设置成圆环的宽度,即上面的ringWidth变量的值。进度圆弧的外切矩形如何定义呢?想象一下,笔的宽度和圆环的宽度一样,那简单,我们绘制进度肯定是从圆环的中间位置绘制了。所以,定义进度圆弧矩形如下:

    paint.setStrokeWidth(ringWidth);//先将画笔宽度设置为圆环的宽度。

    ringRec =new RectF(ringWidth /2, ringWidth /2, getMeasuredWidth() -ringWidth /2, getMeasuredHeight() -ringWidth /2);

    第三步,绘制文字

    绘制文字,肯定是绘制在控件的中央了。那么如何确定文字字体大小呢?(因为要考虑到文字是否会越过圆环)

    首先,咱们先固定文字只能占用的大小比例,这里我规定,文字只能占用内圆直径的3/5,

    final float textMaxCanUseWidth =3 * (getMeasuredWidth() -ringWidth *2) /5;

    然后,先定义一个文字的初始大小值,float textSize = (getMeasuredWidth() -ringWidth) /textSizeScale;//第一次文字大小。textSizeScale为一个文字所占内圆直径的大小,这里我取6。

    接下来,就将画笔字体设置为当前值,测量要绘制的文字的宽度是否超过了规定值,如果是则不断的减1,循环判断。

    paint.setTextSize(textSize);
    
    float nowWidth =paint.measureText(text);
    
    while (nowWidth > textMaxCanUseWidth) {
    
    textSize--;
    
        paint.setTextSize(textSize);
    
        nowWidth =paint.measureText(text);
    
    }
    

    好了,整个步骤就这么多,最后暴露出进度设置的方法,然后可以将各种属性放到xml中,方便调用。完整代码如下:

    public class LoadingView extends View {
        private static final String TAG = "LoadingView";
    
        Paint paint;
        int textColor;
        int circleColor;
        int fillColor;
        int startProgress = -90;
        private float goProgress = 0;
        float ringWidth = 0;
        int textSizeScale = 6;
        int defaultColor = 0;
        //////
        RectF ringRec, rectFOut, rectFIn;
    
        public LoadingView(Context context) {
            super(context);
        }
    
        public LoadingView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            Log.e(TAG, "LoadingView: ");
            init(context, attrs);
        }
    
        private void init(Context context, AttributeSet attrs) {
            //获取自定义属性
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LoadingView);
            circleColor = array.getColor(R.styleable.LoadingView_circleColor, defaultColor);
            fillColor = array.getColor(R.styleable.LoadingView_fillColor, defaultColor);
            textColor = array.getColor(R.styleable.LoadingView_textColor, defaultColor);
            startProgress = array.getColor(R.styleable.LoadingView_startDegre, startProgress);
            paint = new Paint();
            paint.setColor(circleColor);
            paint.setStyle(Paint.Style.STROKE);
            paint.setAntiAlias(true);
        }
    
        /**
         * 设置当前进度
         * @param progress
         */
        public void setProgress(int progress) {
            goProgress = (float) (360 * progress * 0.01);
            invalidate();
        }
    
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            paint.setColor(circleColor);
            paint.setStyle(Paint.Style.STROKE);
            //外圆环
            canvas.drawArc(rectFOut, -90, 360, false, paint);
            //内圆环
            canvas.drawArc(rectFIn, -90, 360, false, paint);
            //进度
            paint.setStrokeWidth(ringWidth);
            paint.setColor(fillColor);
            canvas.drawArc(ringRec, startProgress, goProgress, false, paint);
            //文字
            String text = (int) (goProgress / 3.6) + "%";
            float size = getTextSize(text);
            paint.setTextSize(size);
            Rect rect = new Rect();
            paint.getTextBounds(text, 0, text.length(), rect);
            float textHeight = rect.bottom - rect.top;
            float textWidth = rect.right - rect.left;
            paint.setStrokeWidth(1);
            paint.setStyle(Paint.Style.FILL);
            float y = ringWidth + ((getMeasuredHeight() - 2 * ringWidth - textHeight) / 2) + textHeight;
            float x = ringWidth + ((getMeasuredWidth() - (2 * ringWidth) - textWidth) / 2);
            paint.setColor(textColor);
            canvas.drawText(text, x, y, paint);
        }
    
        /**
         * 计算出合适的文字字体大小
         *
         * @param text
         * @return
         */
        private float getTextSize(String text) {
            final float textMaxCanUseWidth = 3 * (getMeasuredWidth() - ringWidth * 2) / 5;
            float textSize = (getMeasuredWidth() - ringWidth) / textSizeScale;//第一次文字大小
            paint.setTextSize(textSize);
            float nowWidth = paint.measureText(text);
            while (nowWidth > textMaxCanUseWidth) {
                textSize--;
                paint.setTextSize(textSize);
                nowWidth = paint.measureText(text);
            }
            return textSize;
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            ringWidth = getMeasuredWidth() / 20;
            rectFOut = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());
            rectFIn = new RectF(ringWidth, ringWidth, getMeasuredWidth() - ringWidth, getMeasuredHeight() - ringWidth);
            ringRec = new RectF(ringWidth / 2, ringWidth / 2, getMeasuredWidth() - ringWidth / 2, getMeasuredHeight() - ringWidth / 2);
        }
    
    }
    
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="LoadingView">
            <attr name="textColor" format="color" />
            <attr name="circleColor" format="color"></attr>
            <attr name="fillColor" format="color"></attr>
            <attr name="startDegre" format="integer"></attr>
        </declare-styleable>
    </resources>
    
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.vcyber.view.loading.LoadingView
            android:id="@+id/load"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_centerInParent="true"
            app:circleColor="#66CD00"
            app:fillColor="#9966CD00"
            app:textColor="#66CD00" />
    </RelativeLayout>
    

    相关文章

      网友评论

        本文标题:小巧实用的自定义Loading

        本文链接:https://www.haomeiwen.com/subject/yrqwoftx.html