效果图

package cn.ljuns.temperature.view;

import com.example.mvp.R;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;

/**
* 步骤:
* 1、整个背景圆(可有可无)
* 2、进度弧(分为三段,颜色分别为绿黄红)
* 3、进度弧上的文字(正常,预警,警告)
* 4、刻度弧(紧靠着进度弧内侧的黑色弧)
* 5、刻度
* 6、中间的圆
* 7、指针
* 8、当前温度
*/
public class TemperatureView extends View {

private float progressWidth;
private String tempText;
private float tempTextSize;

private Paint outCirclePaint; // 整个背景圆
private Paint progressPaint; // 进度
private Paint scaleArcPaint; // 刻度弧
private Paint scalePaint; // 刻度
private Paint panelTextPaint; // 表盘文字
private Paint progressTextPaint; // 进度条上的文字
private Paint pointPaint; // 中心圆
private Paint leftPointerPaint; // 表针左半部分
private Paint rightPointerPaint; // 表针右半部分
private Paint pointerCirclePaint; // 表针的圆轴

private int mSize; // 最终的大小
private static final int PADDING = 15; // 进度的宽度
private static final int OFFSET = 5;
private String scale; // 刻度数值
private int mTikeCount = 40; // 40条刻度(包括长短)
private int mLongTikeHeight = dp2px(10); // 长刻度
private int mShortTikeHeight = dp2px(5); // 短刻度
private int progressRadius; // 进度弧的半径
private int scaleArcRadius = 123; // 刻度弧的半径
private int pointRadius = dp2px(17); // 中心圆半径

private float currentTemp;

public TemperatureView(Context context) {
this(context, null);
}

public TemperatureView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public TemperatureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

// 获取自定义属性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.temperatureProgress);
//Dimension 指尺寸
progressWidth = ta.getDimension(R.styleable.temperatureProgress_progressWidth, PADDING);
//基本数据类型
tempText = ta.getString(R.styleable.temperatureProgress_tempText);
tempTextSize = ta.getDimension(R.styleable.temperatureProgress_tempTextSize, sp2px(15));
ta.recycle();

initPaint();
}
/**
* 测试的宽和高,如果测试的时候设置的宽或者高的属性不是match_parent,那就把宽高设为默认的200dp
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int realWidth = startMeasure(widthMeasureSpec);
int realHeight = startMeasure(heightMeasureSpec);
Log.i("TAG", "realWidth:"+realWidth);
Log.i("TAG", "realHeight:"+realHeight);
/**
* 因为是以正方形为基础
*/
mSize = Math.min(realHeight, realWidth);
setMeasuredDimension(mSize, mSize);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 将画布移到中央
canvas.translate(mSize /2, mSize /2);
// 画最外面的圆
drawOutCircle(canvas);
// // 画进度
drawProgress(canvas);
// // 画进度上的文字
drawProgressText(canvas);
// // 画表盘
drawPanel(canvas);
}

/**
* 进度上的文字
* @param canvas
*/
private void drawProgressText(Canvas canvas) {
canvas.save();
String normal = "正常";
String warn = "预警";
String danger = "警告";
// 因为文字在进度弧上,所以要旋转一定的角度
canvas.rotate(-60, 0, 0);
progressTextPaint.setTextSize(sp2px(12));
Log.i("TAG", "scaleArcRadius:"+scaleArcRadius);
canvas.drawText(normal, -dp2px(12), -scaleArcRadius - dp2px(4), progressTextPaint);
canvas.rotate(90, 0, 0);
canvas.drawText(warn, -dp2px(12), -scaleArcRadius - dp2px(4), progressTextPaint);
canvas.rotate(60, 0, 0);
canvas.drawText(danger, -dp2px(12), -scaleArcRadius - dp2px(4), progressTextPaint);
canvas.rotate(-60, 0, 0);
canvas.restore();
}

/**
* 进度弧
* @param canvas
*/
private void drawProgress(Canvas canvas) {
// dp2px(10):留一点位置(可有可无)
progressRadius = mSize /2 - dp2px(10);
//用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作
canvas.save();
Log.i("TAG", "progressRadius;:"+progressRadius);
RectF rectF = new RectF(-progressRadius, -progressRadius, progressRadius, progressRadius);
// 设置为圆角
progressPaint.setStrokeCap(Paint.Cap.ROUND);
progressPaint.setColor(Color.GREEN);
// 从150度位置开始,经过120度
canvas.drawArc(rectF, 150, 120, false, progressPaint);
progressPaint.setColor(Color.RED);
progressPaint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawArc(rectF, 330, 60, false, progressPaint);
progressPaint.setColor(Color.YELLOW);
progressPaint.setStrokeCap(Paint.Cap.BUTT);
canvas.drawArc(rectF, 270, 60, false, progressPaint);
// restore:用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响
canvas.restore();
}

/**
* 表盘
* @param canvas
*/
private void drawPanel(Canvas canvas) {
// 画刻度弧
drawScaleArc(canvas);
// 画中间圆
drawInPoint(canvas);
// 画指针
drawPointer(canvas);
// 绘制文字
drawPanelText(canvas);
}

/**
* 表盘上的文字
* @param canvas
*/
private void drawPanelText(Canvas canvas) {

canvas.save();
String text = "当前温度";
float length = panelTextPaint.measureText(text);
panelTextPaint.setTextSize(sp2px(15));
canvas.drawText(text, -length/2, scaleArcRadius/2 + dp2px(20), panelTextPaint);
String temp = currentTemp + " ℃";
panelTextPaint.setTextSize(sp2px(15));
// panelTextPaint.setColor(tempTextColor);
float tempTextLength = panelTextPaint.measureText(temp);
canvas.drawText(temp, -tempTextLength/2, scaleArcRadius, panelTextPaint);
canvas.restore();

}

/**
* 指针(这里分为左右部分是为了画出来的指针有立体感)
* @param canvas
*/
private void drawPointer(Canvas canvas) {
RectF rectF = new RectF(-pointRadius/2, -pointRadius/2, pointRadius/2, pointRadius/2);
canvas.save();
// 先将指针与刻度0位置对齐
canvas.rotate(60, 0, 0);
float angle = currentTemp * 6.0f;
canvas.rotate(angle, 0, 0);
// 表针左半部分
Path leftPointerPath = new Path();
leftPointerPath.moveTo(pointRadius/2, 0);//moveTo:设置路径起始点
leftPointerPath.addArc(rectF, 0, 360);//添加一个圆弧到路径
leftPointerPath.lineTo(0, scaleArcRadius - mLongTikeHeight - dp2px(OFFSET) - dp2px(15));
leftPointerPath.lineTo(-pointRadius/2, 0);
leftPointerPath.close();//闭合路径
// 表针右半部分
Path rightPointerPath = new Path();
rightPointerPath.moveTo(-pointRadius/2, 0);
rightPointerPath.addArc(rectF, 0, -180);
rightPointerPath.lineTo(0, scaleArcRadius - mLongTikeHeight - dp2px(OFFSET) - dp2px(15));
rightPointerPath.lineTo(0, pointRadius/2);
rightPointerPath.close();
// 表针的圆
Path circlePath = new Path();
circlePath.addCircle(0, 0, pointRadius/4, Path.Direction.CW);
canvas.drawPath(leftPointerPath, leftPointerPaint);
canvas.drawPath(rightPointerPath, rightPointerPaint);
canvas.drawPath(circlePath, pointerCirclePaint);
canvas.restore();
}

/**
* 中心圆
* @param canvas
*/
private void drawInPoint(Canvas canvas) {
canvas.save();
canvas.drawCircle(0, 0, pointRadius, pointPaint);
canvas.restore();
}

/**
* 刻度弧
* @param canvas
*/
private void drawScaleArc(Canvas canvas) {
// 刻度弧紧靠进度弧
scaleArcRadius = mSize/2 - (dp2px(15)+dp2px(PADDING)/4);
Log.i("TAG", "aaaa:"+scaleArcRadius);
canvas.save();
// 画弧
RectF rectF = new RectF(-scaleArcRadius, -scaleArcRadius,
scaleArcRadius, scaleArcRadius);
canvas.drawArc(rectF, 150, 240, false, scaleArcPaint);

// 旋转的角度
float mAngle = 240f / mTikeCount;
// 画右半部分的刻度
for (int i = 0; i <= mTikeCount/2; i++) {
// 5的倍数就画长刻度,并标上刻度数值
if (i % 5 == 0) {
scale = 20 + i + "";
panelTextPaint.setTextSize(sp2px(15));
float scaleWidth = panelTextPaint.measureText(scale);
canvas.drawLine(0, -scaleArcRadius, 0, -scaleArcRadius+mLongTikeHeight, scalePaint);
canvas.drawText(scale, -scaleWidth/2, -scaleArcRadius+mLongTikeHeight + dp2px(15), panelTextPaint);
} else {
canvas.drawLine(0, -scaleArcRadius, 0, -scaleArcRadius+ mShortTikeHeight, scalePaint);
}
canvas.rotate(mAngle, 0, 0);
}
// 画布回正
canvas.rotate(-mAngle * mTikeCount/2 - 6, 0, 0);
// 画左半部分的刻度
for (int i = 0; i <= mTikeCount/2; i++) {
if (i % 5 == 0) {
scale = 20 - i + "";
panelTextPaint.setTextSize(sp2px(15));
float scaleWidth = panelTextPaint.measureText(scale);
canvas.drawLine(0, -scaleArcRadius, 0, -scaleArcRadius+mLongTikeHeight, scalePaint);
canvas.drawText(scale, -scaleWidth/2, -scaleArcRadius + mLongTikeHeight + dp2px(15), panelTextPaint);
} else {
canvas.drawLine(0, -scaleArcRadius, 0, -scaleArcRadius+ mShortTikeHeight, scalePaint);
}
canvas.rotate(-mAngle, 0, 0);
}
// 画布回正
canvas.rotate(-mAngle * mTikeCount/2 + 6, 0, 0);
canvas.restore();
}

/**
* 最外面的圆
* @param canvas
*/
private void drawOutCircle(Canvas canvas) {
// 已经将画布移到中心,所以圆心为(0,0)
canvas.drawCircle(0, 0, mSize /2-dp2px(1), outCirclePaint);

canvas.save();
}

/**
* 测量大小
* @param whSpec
* @return
*/
private int startMeasure(int whSpec) {
int result = 0;
int size = MeasureSpec.getSize(whSpec);
int mode = MeasureSpec.getMode(whSpec);
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = dp2px(200);
}
return result;
}

/**
* 将 dp 转换为 px
* @param dp
* @return
*/
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}

private int sp2px(int sp){
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
}

/**
* 初始化画笔
*/
private void initPaint() {
outCirclePaint = new Paint();//外圆漆
progressPaint = new Paint();//进步涂料
scaleArcPaint = new Paint();
scalePaint = new Paint();
panelTextPaint = new Paint();
progressTextPaint = new Paint();
pointPaint = new Paint();
leftPointerPaint = new Paint();
rightPointerPaint = new Paint();
pointerCirclePaint = new Paint();
progressPaint.setAntiAlias(true);
progressPaint.setStrokeWidth(dp2px(PADDING));
progressPaint.setStyle(Paint.Style.STROKE);
progressPaint.setStrokeCap(Paint.Cap.ROUND);
progressPaint.setStrokeJoin(Paint.Join.ROUND);
outCirclePaint.setAntiAlias(true);
outCirclePaint.setStrokeWidth(5);
outCirclePaint.setColor((int) Long.parseLong("ffffffcc", 16));
outCirclePaint.setStyle(Paint.Style.FILL_AND_STROKE);
// outCirclePaint.setColor(getResources().getColor(R.color.temperatureBackground));
scaleArcPaint.setAntiAlias(true);
scaleArcPaint.setStrokeWidth(dp2px(2));
scaleArcPaint.setStyle(Paint.Style.STROKE);
scalePaint.setAntiAlias(true);
scalePaint.setStrokeWidth(5);
scalePaint.setStyle(Paint.Style.STROKE);
panelTextPaint.setAntiAlias(true);
panelTextPaint.setStyle(Paint.Style.FILL);
panelTextPaint.setColor(Color.BLACK);
progressTextPaint.setAntiAlias(true);
progressTextPaint.setStyle(Paint.Style.FILL);
progressTextPaint.setColor(Color.BLACK);
progressTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
pointPaint.setAntiAlias(true);
pointPaint.setStyle(Paint.Style.FILL);
pointPaint.setColor(Color.GRAY);
leftPointerPaint.setAntiAlias(true);
leftPointerPaint.setStyle(Paint.Style.FILL_AND_STROKE);
leftPointerPaint.setColor(getResources().getColor(R.color.leftPointer));
rightPointerPaint.setAntiAlias(true);
rightPointerPaint.setColor(getResources().getColor(R.color.rightPointer));
rightPointerPaint.setStyle(Paint.Style.FILL_AND_STROKE);
pointerCirclePaint.setAntiAlias(true);
pointerCirclePaint.setColor(Color.GRAY);
pointerCirclePaint.setStyle(Paint.Style.FILL);
pointerCirclePaint.setDither(true);
}

/**
* 设置当前温度
* @param currentTemp
*/
public void setCurrentTemp(float currentTemp) {
if (currentTemp < 0) {
currentTemp = 0;
} else if (currentTemp > 40) {
currentTemp = 40;
} else {
this.currentTemp = currentTemp;
postInvalidate();
}
}

public float getCurrentTemp() {
return currentTemp;
}

public float getProgressWidth() {
return progressWidth;
}

public void setProgressWidth(float progressWidth) {
this.progressWidth = progressWidth;
}

public String getTempText() {
return tempText;
}

public void setTempText(String tempText) {
this.tempText = tempText;
}

public float getTempTextSize() {
return tempTextSize;
}

public void setTempTextSize(float tempTextSize) {
this.tempTextSize = tempTextSize;
}
}

MainActivity的

public class MainActivity extends Activity implements ILoginView{

private Button button1;
private Button button2;
private EditText edit1;
private EditText edit2;
private ILoginPresenter presenterCompl;
private VelocityTracker vv;
private TemperatureView mTemperatureView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
Log.i("TAG", "Parent:"+getParent());
presenterCompl = new LoginPresenterCompl(this);
// button1 = (Button)findViewById(R.id.button1);
// button2 = (Button)findViewById(R.id.button2);
// edit1 = (EditText)findViewById(R.id.edit1);
// edit2 = (EditText)findViewById(R.id.edit2);
//
// button1.setOnClickListener(new OnClickListener() {
// @Override
// public void onClick(View v) {
// presenterCompl.doLogin(edit1.getText().toString(), edit2.getText().toString());
// }
// });
// button2.setOnClickListener(new OnClickListener() {
// @Override
// public void onClick(View v) {
// presenterCompl.clear();
// }
// });
mTemperatureView = (TemperatureView)findViewById(R.id.temperature_view);
mTemperatureView = (TemperatureView) findViewById(R.id.temperature_view);
new Thread(new Runnable() {
@Override
public void run() {
for (float i = 0; i <=40; i ++) {
mTemperatureView.setCurrentTemp(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();

}

转载:android自定义view实战(温度控制表)!的更多相关文章

  1. Android为TV端助力 转载:android自定义view实战(温度控制表)!

    效果图 package cn.ljuns.temperature.view; import com.example.mvp.R; import android.content.Context;impo ...

  2. Android自定义View实战(SlideTab-可滑动的选择器)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/52178553 本文出自:[openXu的博客] 目录: 初步分析重写onDraw绘制 重写o ...

  3. Android自定义View(三、深入解析控件测量onMeasure)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51490283 本文出自:[openXu的博客] 目录: onMeasure什么时候会被调用 ...

  4. Android自定义View之CircleView

    Android自定义View之CircleView 版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请表明出处:http://www.cnblogs.com/cavalier-/p/5999 ...

  5. Android 自定义View及其在布局文件中的使用示例(三):结合Android 4.4.2_r1源码分析onMeasure过程

    转载请注明出处 http://www.cnblogs.com/crashmaker/p/3549365.html From crash_coder linguowu linguowu0622@gami ...

  6. Android 自定义View及其在布局文件中的使用示例(二)

    转载请注明出处 http://www.cnblogs.com/crashmaker/p/3530213.html From crash_coder linguowu linguowu0622@gami ...

  7. Android自定义View (二) 进阶

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24300125 继续自定义View之旅,前面已经介绍过一个自定义View的基础的例 ...

  8. Android自定义View

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24252901 很多的Android入门程序猿来说对于Android自定义View ...

  9. Android 自定义View (二) 进阶

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24300125 继续自定义View之旅,前面已经介绍过一个自定义View的基础的例 ...

随机推荐

  1. CentOs7 +Jexus 5.8.2部署Asp.Net Core WebApi 1.0生产环境

    Jexus 是一款运行于 Linux 平台,以支持  ASP.NET.PHP 为特色的集高安全性和高性能为一体的 WEB 服务器和反向代理服务器.最新版 5.8.2 已经发布,有如下更新: 1,现在大 ...

  2. direction和unicode-bidi

    在做多语言页面,接触过阿利伯语.希伯来语的同学肯定了解书写方向的重要性,包括我们五四运动前的书写顺序也是从右到左的.css中 unicode-bidi和direction属性决定了HTML或XML文字 ...

  3. 使用python crontab设置linux定时任务

    熟悉linux的朋友应该知道在linux中可以使用crontab设置定时任务.可以通过命令crontab -e编写任务.当然也可以直接写配置文件设置任务. 但是有时候希望通过脚本自动设置,比如我们应用 ...

  4. 解析大型.NET ERP系统 通用附件管理功能

    大型系统具备一个通用的附件管理功能,对于单据中无法清晰表达的字段,用一个附件图片或附件文档表示是最好的方法了.比如物料清单附加一张CAD图纸,销售订单评审功能中附加客户的各种表格,通用附件功能对系统起 ...

  5. jQuery系列:DOM操作

    1. 访问元素 在访问页面时,需要与页面中的元素进行交互式的操作.在操作中,元素的访问主要包括对元素属性.内容.值.CSS的操作. 1.1 元素属性操作 1.1.1 设置或返回被选元素的属性值 语法格 ...

  6. .Net使用Redis详解之ServiceStack.Redis(七)

    序言 本篇从.Net如何接入Reis开始,直至.Net对Redis的各种操作,为了方便学习与做为文档的查看,我做一遍注释展现,其中会对list的阻塞功能和事务的运用做二个案例,进行记录学习. Redi ...

  7. 基本数据结构(1)——算法导论(11)

    1. 引言     从这篇博客开始,来介绍一些基本的数据结构知识.本篇及下一篇会介绍几种基本的数据结构:栈.队列.链表和有根树.此外还会介绍由数组构造对象和指针的方法.     这一篇主要介绍栈和队列 ...

  8. JS中的数学计算<之简单实例讲解>

    1.取余数   % var a=10%3; //a=1 2.取绝对值  Math.abs() var a=Math.abs(-102.1); var b=Math.abs(102.1); //a=10 ...

  9. Android动画效果之初识Property Animation(属性动画)

    前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

  10. ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理

    ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...