效果图

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();

}

转载于:https://www.cnblogs.com/xiaoxiaing/p/5846155.html

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

  1. Android为TV端助力 转载:Java 泛型

    一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下面这段简短的代码: 1 public class GenericTest {2 3 public static void main(Strin ...

  2. Android为TV端助力 最详细的动画大全,包括如何在代码和在XML中使用

    一.动画类型 Android的animation由四种类型组成:alpha.scale.translate.rotate XML配置文件中 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画 ...

  3. Android为TV端助力 浅谈Aidl 通讯机制

    服务端: 首先是编写一个aidl文件,注意AIDL只支持方法,不能定义静态成员,并且方法也不能有类似public等的修饰符:AIDL运行方法有任何类型的参数和返回值,在java的类型中,以下的类型使用 ...

  4. Android为TV端助力之热修复原理

    通过源码我们知道Android加载类是通过ClassLoad类里面的findClass先去查找的,如下图所示 通过看源码我们知道,ClassLoad是一个抽象类,它本身并没有实现findclass() ...

  5. Android为TV端助力context转换类型

    转载于:https://www.cnblogs.com/xiaoxiaing/p/7246761.html

  6. Android为TV端助力 同时setTag两次,保存多种值

    示例代码: view.setTag(R.string.action_settings,hodler.content); 接收两个值,一个是key值,必须是唯一值,而且要写在values/ids.xml ...

  7. Android为TV端助力 post带数据请求方式,传递的数据格式包括json和map

    如下: public static String httpPost(String url, String json) { try { URL u = new URL(url); HttpURLConn ...

  8. Android 手机TV端屏幕共享助手

    最近一段时间辞职在家,无聊写了个小程序也算是个demo.这里和大家分享一下,还望各位同仁多多指点.主要功能是实现同局域网下,手机端和TV端连接并分享手机屏幕画面(手机端只支持Android 5.0级以 ...

  9. [自定义控件]android自定义view实战之太极图

    android自定义view实战之太极图 尊重原创,转载请注明出处: http://blog.csdn.net/qq137722697 自定义view是Android工程师进阶不可避免要接触的,我的学 ...

最新文章

  1. ValueTransformer
  2. CRMEBv3.0版本更新了什么?
  3. 两数之和Python解法
  4. Android高效开发:
  5. linux df du命令
  6. javascript 西瓜一期 12 八进制的数数进位解析
  7. 201612-2工资计算
  8. Summernote个性化定制使用帮助(三)
  9. 第13章 用序列化保存模型
  10. 16位灰度数据成像_DICOM Pixel Data核心图像信息数据介绍
  11. java snakeyaml_java – 使用SnakeYAML的嵌套构造
  12. OPNsense用户手册-用户界面
  13. 什么是电感器与电感?
  14. Chrome下载文件,文件名出现乱码解决
  15. 如何更改SpringBoot控制台图标?只需一步!
  16. 快速设置Revit模型材质(Dynamo和API)
  17. android12.0(S) 如何区分U盘和TF卡
  18. 几种主流快速开发平台选择
  19. 【送谷歌定制礼品】一起寻找最初的写作热忱 即刻动笔!
  20. ae怎么设置gpu渲染_AE/PR mac版如何开启GPU加速渲染?

热门文章

  1. oracle查询 :一个角色包括的系统权限,对象权限,Oracle有多少种角色,某个用户有什么角色
  2. 自己写的socket 多线程 通讯
  3. 记一次曲折的后台getshell过程
  4. 批量生成印刷字体字库
  5. Spark SQL 之SQLContext(二)
  6. 无偏PU learning简介
  7. Https原理及流程
  8. Java操作HDFS文件
  9. Kafka设计解析(六)- Kafka高性能架构之道
  10. CMS之promotion failedconcurrent mode failure