自定义view绕不开的OnMeasure

工作一段时间,我们肯定会接触到自定义view,因为并不是每一种酷炫的效果都可以用系统自定义的控件来完成,所以只能自己动手了,自己定义view怎么也绕不开的就是OnMeasure

首先我们来看看OnMeasure方法,什么是OnMeasure(啥?别告诉我你不知道,真不知道请绕道走吧)

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

来个简单粗暴的,只要你自定义 view在XML中指定了宽高,我们在这就能直接获取,别告诉我你不知道怎么在XML中指定宽高(那Textview你会不会用)就像这样

android:layout_width=”160dp”
android:layout_height=”160dp”

不用理会什么 SpecMode
MeasureSpec.EXACTLY , MeasureSpec.AT_MOST , MeasureSpec.UNSPECIFIED

据经验(本人的哈),大部分自定义控件,我们都会指定大小和显示区域,得到了宽高,就可以在自己指定大小的canvas上为所欲为了。只要你指定了大小,那么你的SpecMode就是MeasureSpec.EXACTLY,得到的宽高就是指定的宽高。

下面是我做的一个小示例,先上图,后附源码,把源码运行起来,再细细体会,是最好的进步方式。

这里写图片描述

是不是看着还可以呢,下面贴出主要代码

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout                     xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#51b8f2">
    <com.dahongwudi.dailytestresult.widget.ResultFormView
        android:id="@+id/feeling_rfv"
        android:layout_width="160dp"
        android:layout_height="160dp"
        android:layout_marginTop="30dp"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"/>
</RelativeLayout>

ResultFormView.java

package com.dahongwudi.dailytestresult.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;


/**
 * 每日测试结果
 */
public class ResultFormView extends View {

    /** 上下文 */
    private Context mContext;

    /** 圆圈 progress ring */
    private Paint circleColor;

    /** 当前情绪值 画笔*/
    private Paint paintText;

    /** 最小的背景 */
    private Paint firstShade;

    /** 中间的背景 */
    private Paint secondShade;

    /** 最大的背景 */
    private Paint thirdShade;

    /** 大圆的角度*/
    private int angle = 0;

    /** 大圆开始的角度 (12 O'clock */
    private int startAngle = 270;

    /** 画布的宽度 */
    private int width;

    /** 画布的高度 */
    private int height;

    /** 进度的最大值 */
    private int maxProgress = 100;

    /** 当前的进度 */
    private int progress;

    /** 最小背景半径 */
    private float outerRadius;

    /** 大圆圆心x */
    private float cx;

    /** 大圆圆心y */
    private float cy;

    /** 小圆圆心 x*/
    private float dx;

    /** 小圆圆心 y*/
    private float dy;

    /** 圆环背景图显示的区域 */
    private Rect rectShade = new Rect();

    /** 小圆起始点 x*/
    private float markPointX;

    /** 小圆起始点 y */
    private float markPointY;

    /** 弧线所在区域的左侧 */
    private float left;

    /** 弧线所在区域的右侧 */
    private float right;

    /** 弧线所在区域的上侧 */
    private float top;

    /** 弧线所在区域的下侧 */
    private float bottom;

    /**
     * 是否显示小圆环
     */
    private boolean SHOW_SEEKBAR = true;

    // 屏幕密度
    public float density;
    /**
     * 构造方法
     */
    public ResultFormView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        initDrawable();
    }

    /**
     * 构造方法
     */
    public ResultFormView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        initDrawable();
    }

    /**
     * 构造方法
     */
    public ResultFormView(Context context) {
        super(context);
        mContext = context;
        initDrawable();
    }

    /**
     * 初始化
     */
    public void initDrawable() {

        DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
        density = dm.density;

        circleColor = new Paint();
        paintText = new Paint();

        circleColor.setColor(Color.WHITE);
        paintText.setColor(Color.WHITE);

        paintText.setAntiAlias(true);
        circleColor.setAntiAlias(true);

        circleColor.setStrokeWidth(dp2px(1));
        paintText.setStrokeWidth(dp2px(1));
        paintText.setTextSize(dp2px(50));


        circleColor.setStyle(Paint.Style.STROKE);

        firstShade = new Paint();
        firstShade.setColor(Color.WHITE);
        firstShade.setAlpha(60);
        secondShade = new Paint();
        secondShade.setColor(Color.WHITE);
        secondShade.setAlpha(30);
        thirdShade = new Paint();
        thirdShade.setColor(Color.WHITE);
        thirdShade.setAlpha(18);

    }

    int diff;
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = MeasureSpec.getSize(widthMeasureSpec);
        height = MeasureSpec.getSize(heightMeasureSpec);
        cx = width / 2; // 大圆圆心
        cy = height / 2; // 大圆圆心
        diff = width/6;
        outerRadius = width/2-diff; // 弧线的半径

        markPointX = cx;// 圆环圆心
        markPointY = cy - outerRadius;// 圆环圆心

        left = cx - outerRadius;
        right = cx + outerRadius;
        top = cy - outerRadius;
        bottom = cy + outerRadius;
        rectShade.set((int)left, (int)top, (int)right,(int) bottom);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        canvas.drawCircle(cx, cy, outerRadius, firstShade);
        canvas.drawCircle(cx, cy, outerRadius+diff/2, secondShade);
        canvas.drawCircle(cx, cy, outerRadius+diff, thirdShade);

        canvas.drawArc(new RectF(rectShade), startAngle, -angle, false, circleColor);
        if(SHOW_SEEKBAR){
            dx = getXFromAngle();
            dy = getYFromAngle();
            drawMarkerAtProgress(canvas);
        }
        drawText(canvas);
        super.onDraw(canvas);
    }
    /**
     * 文字
     * @param canvas
     */
    private void drawText(Canvas canvas) {

        Paint.FontMetrics fontMetrics = paintText.getFontMetrics();
        float top = fontMetrics.top;
        float bottom = fontMetrics.bottom;

        float y = cy-(bottom-top)/2-top;

        int p_width = (int) paintText.measureText(progress+"");
        canvas.drawText(progress+"", cx-p_width/2, y, paintText);
    }

    /**
     * 绘制小圆
     */
    public void drawMarkerAtProgress(Canvas canvas) {
        RectF rectf = new RectF(dx-dp2px(6), dy-dp2px(6),
                dx+dp2px(6), dy+dp2px(6));

        canvas.drawArc(rectf, 0, 360, false, circleColor);
        Paint paint = new Paint();
        //  设置画笔颜色
        paint.setColor(Color.rgb(81, 184, 242));
        //  绘制实心圆
        canvas.drawCircle(dx, dy, dp2px(5), paint);
    }

    /**
     * 小圆圆心x
     */
    public float getXFromAngle() {
        return markPointX;
    }

    /**
     * 小圆圆心 y
     */
    public float getYFromAngle() {
        return markPointY;
    }

    /**
     * Set the angle.
     *
     * @param angle
     *            the new angle
     */
    public void setAngle(int angle) {
        this.angle = angle;
        markPointX = (float) (cx+Math.sin((angle+180)*Math.PI/180)*outerRadius);
        markPointY = (float) (cy+Math.cos((angle+180)*Math.PI/180)*outerRadius);
    }

    /**
     * 设置进度
     */
    public void setProgress(int progress) {
        if (this.progress != progress) {
            this.progress = progress;
            int newPercent = (this.progress * 100) / this.maxProgress;
            int newAngle = (newPercent * 360) / 100 ;
            this.setAngle(newAngle);
            invalidate();
            invalidate();
        }
    }

    private int dp2px(float dpValue) {
        return (int)(dpValue * density + 0.5f);
    }
}

MainActivity.java

package com.dahongwudi.dailytestresult;

import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

import com.dahongwudi.dailytestresult.widget.ResultFormView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    /**
     * 画图,取消重复划线
     */
    private boolean isDraw = false;
    private boolean isDraw2 = false;

    int promotScore = 0;

    int progress = 0;

    ResultFormView resultFromView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        resultFromView = (ResultFormView) findViewById(R.id.feeling_rfv);
        resultFromView.setOnClickListener(this);
    }

    private void startProgress(int promotScore){
        this.promotScore = promotScore;
        progress = 0;

        if(!isDraw2){
            isDraw = false;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    isDraw2 = true;
                    start();
                }
            }).start();
        }else{
            isDraw2 = false;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    isDraw = true;
                    start2();
                }
            }).start();
        }
    }

    private void start() {
        while (isDraw2) {
            try {
                Thread.sleep(33);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    if (progress == promotScore) {
                        isDraw2 = false;
                    }else{
                        resultFromView.setProgress(progress += 1);
                    }
                }
            });
        }
    }

    private void start2() {
        while (isDraw) {
            try {
                Thread.sleep(33);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    if (progress == promotScore) {
                        isDraw = false;
                    }else{
                        resultFromView.setProgress(progress += 1);
                    }
                }
            });
        }
    }

    private Handler handler = new Handler(){

    };
    @Override
    public void onClick(View view) {
        startProgress(70);
    }
}

注释写的比较清楚了~下面附上源码链接
源码链接

接下来的文章我会详细的从源码的角度分析一下onmeasure,onlayout,和自定义控件XML的配置封装,感兴趣的朋友,请持续关注~

冰冻三尺非一日之寒~大家一起努力!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值