转载:android自定义view实战(温度控制表)!
效果图

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实战(温度控制表)!的更多相关文章
- Android为TV端助力 转载:android自定义view实战(温度控制表)!
效果图 package cn.ljuns.temperature.view; import com.example.mvp.R; import android.content.Context;impo ...
- Android自定义View实战(SlideTab-可滑动的选择器)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/52178553 本文出自:[openXu的博客] 目录: 初步分析重写onDraw绘制 重写o ...
- Android自定义View(三、深入解析控件测量onMeasure)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51490283 本文出自:[openXu的博客] 目录: onMeasure什么时候会被调用 ...
- Android自定义View之CircleView
Android自定义View之CircleView 版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请表明出处:http://www.cnblogs.com/cavalier-/p/5999 ...
- Android 自定义View及其在布局文件中的使用示例(三):结合Android 4.4.2_r1源码分析onMeasure过程
转载请注明出处 http://www.cnblogs.com/crashmaker/p/3549365.html From crash_coder linguowu linguowu0622@gami ...
- Android 自定义View及其在布局文件中的使用示例(二)
转载请注明出处 http://www.cnblogs.com/crashmaker/p/3530213.html From crash_coder linguowu linguowu0622@gami ...
- Android自定义View (二) 进阶
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24300125 继续自定义View之旅,前面已经介绍过一个自定义View的基础的例 ...
- Android自定义View
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24252901 很多的Android入门程序猿来说对于Android自定义View ...
- Android 自定义View (二) 进阶
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24300125 继续自定义View之旅,前面已经介绍过一个自定义View的基础的例 ...
随机推荐
- UML类图(上):类、继承和实现
面向对象设计 对于一个程序员来说,在工作的开始阶段通常都是别人把东西设计好,你来做.伴随着个人的成长,这个过程将慢慢变成自己设计一部分功能来实现,自己实现.如果要自己设计,无论是给自己看,还是给别人看 ...
- MyBatis4:动态SQL
什么是动态SQL MyBatis的一个强大特性之一通常是它的动态SQL能力.如果你有使用JDBC或其他相似框架的经验,你就明白条件串联SQL字符串在一起是多么地痛苦,确保不能忘了空格或者在列表的最后的 ...
- 【Java并发编程实战】-----“J.U.C”:CAS操作
CAS,即Compare and Swap,中文翻译为"比较并交换". 对于JUC包中,CAS理论是实现整个java并发包的基石.从整体来看,concurrent包的实现示意图如下 ...
- eclipse下查看maven下载的源码中文乱码问题
在项目中设置通过maven下载源代码和javadoc,直接通过eclipse浏览源代码时,发现中文为乱码.解决方法如下: 修改Eclipse中文本文件的默认编码:windows->Prefere ...
- 戴尔灵越15-5000/3558等系列修改BIOS设置U盘启动
今天在电脑群遇到一个群友的机型是戴尔灵越15-5000,他问我这款机器怎么设置U盘启动. 看到它的BIOS界面之后,我来了点兴趣.. 本文供图:辽宁沈阳-打老虎(921407164) 电脑群:电脑爱好 ...
- Spark 开发中遇到的一些问题
1.StackOverflowError 问题:简单代码记录 : for (day <- days){ rdd = rdd.union(sc.textFile(/path/to/day) ... ...
- 动态给textView加图片
Drawable img = layout.getResources().getDrawable(R.drawable.icon); // 调用setCompoundDrawables时,必须调用Dr ...
- shell script 执行常用的两种方式
2016-11-17 直接输入脚本名执行 ./script #!/bin/bash# /root/shell/001 # 2016-11-17 test for script running name ...
- 【.NET深呼吸】(WPF)跨窗口完成绑定
在99.999975%的情形下,可视化元素之间的相互绑定都会在同一个容器中完成,如同一个窗口,同一个UserControl等.但在极少数的情况下,为了从最大限度减少代码赋值的前提下考虑,是可以使用跨窗 ...
- 【.NET深呼吸】元组数据(Tuple)
各位观众,大家好,欢迎收看由火星电视台直播的<老周吹牛>节目,注意:本节目没有任何技术含量,如果您没有兴趣,请砸掉电视机. 今天说一下System命名空间下的一个数据类型——Tuple,翻 ...