工作一段时间,我们肯定会接触到自定义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的配置封装,感兴趣的朋友,请持续关注~
冰冻三尺非一日之寒~大家一起努力!