基于android平台的模拟温度计测量实时温度的实现--(surfaceView的熟练使用)
上来老规矩,先上图,这个温度计是我为一个商用的app专门开发的,商业原因,其他的截图还有代码不可以公布,但是这个温度计的源码是可以公布的,这个代码是我自己精心设计的,里面有不少的计算公式,感觉自己还是设计的很巧妙的模拟也很逼真,尤其是在测量温度的时候,温度计水银的高度变化,这个是要用有一个速度来控制的,尤其是这个温度的变化还是实时的,也是不可控的,所以我们要考虑到温度的随时变化,根据当前的目标温度还有当前水银柱的高度,老确定当前的水银上升速度是多少。当然了这只是一个方面,还有其他的,比如字体的大小还有刻度的长度,这些全部都是要根据画布的大小来变化的,这个在我的代码中也有体现。下面这个类的代码可以直接通过最后一个函数setTargetTemperature调用,设置实时的温度,温度计会显示出来实时的变化。希望大家可以用到。
1 package com.example.test; 2 3 4 5 import android.content.Context; 6 import android.graphics.Bitmap; 7 import android.graphics.Canvas; 8 import android.graphics.Color; 9 import android.graphics.Paint; 10 import android.graphics.RectF; 11 import android.util.AttributeSet; 12 import android.util.TypedValue; 13 import android.view.SurfaceHolder; 14 import android.view.SurfaceView; 15 16 17 18 import java.text.DecimalFormat; 19 20 /** 21 * Created by yinxiaofei on 2016/1/13. 22 */ 23 24 public class Thermometer extends SurfaceView implements SurfaceHolder.Callback ,Runnable{ 25 26 private SurfaceHolder mHolder; 27 private Canvas mCanvas; 28 29 //定义温度的范围 30 int temperatureRange=12; 31 //定义一个盘快的范围 32 private RectF mRange=new RectF(); 33 //定义温度计的宽度和中心宽度 34 int mWith; 35 int mHeight; 36 int centerWith; 37 int centerHeight; 38 //定义总的宽度 39 40 //定义温度计刻度总长度 41 int temperatureAllLong; 42 43 //定义一下水银的宽度 44 int MercuryWith; 45 //十的倍数的线长度 46 int MaxLineLong; 47 //五的倍数的线的长度 48 int MidLineLong; 49 //其他刻度线的长度 50 int MinLineLong; 51 //刻度间隔 52 int scaleLong; 53 //定义温度计距离画布的上宽度 54 int abHeight; 55 56 //绘制线条的画笔 57 private Paint LinePaint; 58 //绘制文本的画笔 59 private Paint TextPaint; 60 61 //设置温度上升的速度 62 private volatile float mSpeed=0; 63 64 //设置背景图 65 private Bitmap mBitmap; 66 67 /** 68 * 定义初始温度,当前显示正在变化也就是显示的温度,还有目标温度 69 * 其中,初始温度不变, 70 * 当前温度是有程序根据不同的速度和目标温度计算出来的, 71 * 目标温度则是由仪器传送过来的数据 72 */ 73 private float BeginTenperature= (float) 30; 74 private int EndTenperature=42; 75 private volatile float CurrentTemperature= (float) 30; 76 77 78 79 float TargetTemperature=39; 80 81 /** 82 * 定义每一秒绘制的次数 83 */ 84 int everySecondTime=100; 85 86 //设置文字的大小 87 private float mTextSize= TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SHIFT,25,getResources().getDisplayMetrics()); 88 private float mSymbolTextSize= TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SHIFT,35,getResources().getDisplayMetrics()); 89 private float mShowSymbolTextSize= TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SHIFT,45,getResources().getDisplayMetrics()); 90 /** 91 * 用户绘制的线程 92 */ 93 private Thread mThread; 94 /** 95 * 根据目标温度改变要显示的当前温度的线程 96 */ 97 private Thread mChangeTemperatureThread; 98 99 /** 100 * 设置一个标志位,用于线程的开启还是关闭的标志 101 * @param context 102 */ 103 private Boolean isRunning; 104 105 private DecimalFormat fomat;//格式化float 106 107 public Thermometer(Context context) { 108 this(context, null); 109 } 110 111 public Thermometer(Context context, AttributeSet attrs) { 112 super(context,attrs); 113 mHolder=getHolder(); 114 mHolder.addCallback(this); 115 116 } 117 @Override 118 protected void onMeasure(int with,int height){ 119 super.onMeasure(with, height); 120 this.mWith=getMeasuredWidth()/2; 121 this.mHeight=getMeasuredHeight(); 122 //这里先把中心设置在屏幕的中心 123 this.centerWith=mWith/2; 124 this.centerHeight=mHeight/2; 125 //设置水银的宽度,暂时设置为总宽度的十五分之一 126 MercuryWith=mWith/15; 127 MinLineLong=MercuryWith; 128 MidLineLong=MinLineLong*8/5; 129 MaxLineLong=MidLineLong*3/2; 130 //temperatureAllLong表示温度刻度总长度 131 temperatureAllLong=mHeight*7/10; 132 //设置刻度间隔,包含了刻度线的长度 133 scaleLong=temperatureAllLong/temperatureRange/10;//表示一个温度十个刻度 134 135 136 abHeight=mHeight/12; 137 } 138 @Override 139 public void surfaceCreated(SurfaceHolder surfaceHolder) { 140 //初始化画笔 141 LinePaint=new Paint(); 142 //去锯齿 143 LinePaint.setAntiAlias(true); 144 LinePaint.setColor(Color.BLACK); 145 LinePaint.setStyle(Paint.Style.STROKE); 146 LinePaint.setStrokeWidth(1); 147 //初始化画笔 148 TextPaint=new Paint(); 149 TextPaint.setColor(Color.BLACK); 150 TextPaint.setTextSize(mTextSize); 151 TextPaint.setShader(null); 152 //初始化温度计的范围 153 mRange=new RectF(0,0,mWith,mHeight); 154 isRunning=true; 155 mThread =new Thread(this); 156 mThread.start(); 157 158 } 159 160 @Override 161 public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { 162 163 } 164 165 @Override 166 public void surfaceDestroyed(SurfaceHolder surfaceHolder) { 167 168 isRunning=false; 169 170 } 171 172 @Override 173 public void run() { 174 //不断进行绘制 175 while(isRunning){ 176 long start=System.currentTimeMillis(); 177 draw(); 178 long end=System.currentTimeMillis(); 179 if(end-start<everySecondTime){ 180 //这里控制一下,一秒绘制二十次。也就是五十秒绘制一次 181 try { 182 Thread.sleep(everySecondTime-(end-start)); 183 } catch (InterruptedException e) { 184 e.printStackTrace(); 185 } 186 } 187 } 188 } 189 190 private void draw() { 191 192 try { 193 mCanvas=mHolder.lockCanvas(); 194 //这里要判断是不是为空,之因为在用户点击了home以后,可能已经执行到这里 195 if(mCanvas!=null) 196 { 197 //这里是开始绘制自己要的东西 198 //先绘制背景, 199 drawBg(); 200 //绘制水银的高度还有,显示体温 201 drawShowHeightAndShow(); 202 } 203 } catch (Exception e) { 204 // e.printStackTrace();这里的异常不处理, 205 } finally { 206 if(mCanvas!=null){ 207 mHolder.unlockCanvasAndPost(mCanvas); 208 } 209 } 210 211 } 212 213 private void drawShowHeightAndShow() { 214 215 //这里控制水银的上升速度 216 float difference=Math.abs(TargetTemperature-CurrentTemperature); 217 /** 218 * //这里定义一个boolean来控制是使用加法还是减法,其中true表示当前温度小于 219 * 目标温度,要使用加法,false表示当前温度大于目标温度,要使用减法。 220 */ 221 boolean addORsub=CurrentTemperature>=TargetTemperature?false:true; 222 if(difference==0||difference<=0.005){ 223 mSpeed=0; 224 CurrentTemperature=TargetTemperature; 225 }else{ 226 if (difference>2){ 227 mSpeed= (float) 0.03; 228 }else{ 229 if(difference>1){ 230 mSpeed= (float) 0.025; 231 }else{ 232 if(difference>0.5){ 233 mSpeed= (float) 0.015; 234 }else{ 235 if(difference>0.3){ 236 mSpeed= (float) 0.012; 237 }else{ 238 if(difference>0.2){ 239 mSpeed= (float) 0.009; 240 }else{ 241 mSpeed= (float) 0.008; 242 } 243 244 } 245 } 246 } 247 } 248 } 249 if(addORsub){ 250 CurrentTemperature+=20*mSpeed; 251 }else{ 252 CurrentTemperature-=20*mSpeed; 253 } 254 255 // 256 257 Paint RectPaint=new Paint(); 258 RectPaint.setStyle(Paint.Style.FILL); 259 // RectPaint.setColor(getResources().getColor(R.color.theme_color)); 260 // 这里主要是对温度的显示,画矩形的过程中,唯一改变的就是Top这一个值了 261 if(Math.abs(CurrentTemperature-TargetTemperature)>0.32) 262 mCanvas.drawRect(centerWith-MercuryWith/2, 263 (scaleLong)*10*(temperatureRange)+abHeight*2- 264 (CurrentTemperature-BeginTenperature)*10*scaleLong, 265 centerWith+MercuryWith/2, 266 (scaleLong)*10*(temperatureRange)+abHeight*2, 267 RectPaint); 268 else{ 269 mCanvas.drawRect(centerWith-MercuryWith/2, 270 (scaleLong)*10*(temperatureRange)+abHeight*2- 271 (TargetTemperature-BeginTenperature)*10*scaleLong, 272 centerWith+MercuryWith/2, 273 (scaleLong)*10*(temperatureRange)+abHeight*2, 274 RectPaint); 275 } 276 277 //这里开始画显示的数字 278 Paint ShowNumberTextPaint=new Paint(); 279 ShowNumberTextPaint.setColor(Color.BLACK); 280 ShowNumberTextPaint.setTextSize(mShowSymbolTextSize); 281 ShowNumberTextPaint.setShader(null); 282 fomat = new DecimalFormat("##0.0"); 283 float display = Float.parseFloat(fomat.format(trueTemperature)); 284 mCanvas.drawText(display + "", 285 mWith * 3 / 2 - ShowNumberTextPaint.getTextSize() * 2, 286 temperatureAllLong / 2 - ShowNumberTextPaint.getTextSize(), 287 ShowNumberTextPaint 288 ); 289 mCanvas.drawText(display + "", 290 mWith*3/2- ShowNumberTextPaint.getTextSize() * 2, 291 temperatureAllLong/2+ShowNumberTextPaint.getTextSize(), 292 ShowNumberTextPaint 293 ); 294 295 } 296 297 private void drawBg() { 298 mCanvas.drawColor(Color.WHITE); 299 // mCanvas.drawLine(0, 0, mWith, mHeight, LinePaint); 300 //画右边的刻度 301 //定义每一个长刻度的高度 302 float everyTemparaturHeight=(scaleLong)*10; 303 for(int i=0;i<temperatureRange;i++){ 304 mCanvas.drawLine(centerWith+MercuryWith/2, 305 everyTemparaturHeight*i+abHeight*2,//这里加上两倍的上距离 306 centerWith+MercuryWith/2+MaxLineLong, 307 everyTemparaturHeight*i+abHeight*2,LinePaint); 308 mCanvas.drawText(EndTenperature-i+"",centerWith+MercuryWith/2+MaxLineLong+MinLineLong/3, 309 everyTemparaturHeight*i+TextPaint.getTextSize()/2+abHeight*2,TextPaint); 310 for(int j=1;j<=9;j++){ 311 if(j==5){ 312 mCanvas.drawLine(centerWith+MercuryWith/2, 313 everyTemparaturHeight*i+j*(scaleLong)+abHeight*2, 314 centerWith+MercuryWith/2+MidLineLong, 315 everyTemparaturHeight*i+j*(scaleLong)+abHeight*2,LinePaint); 316 }else{ 317 mCanvas.drawLine(centerWith+MercuryWith/2, 318 everyTemparaturHeight*i+j*(scaleLong)+abHeight*2, 319 centerWith+MercuryWith/2+MinLineLong, 320 everyTemparaturHeight*i+j*(scaleLong)+abHeight*2,LinePaint); 321 } 322 323 } 324 //画最后一个刻度 325 if(i==temperatureRange-1){ 326 327 mCanvas.drawLine(centerWith+MercuryWith/2, 328 everyTemparaturHeight*(i+1)+abHeight*2,//这里加上两倍的上距离 329 centerWith+MercuryWith/2+MaxLineLong, 330 everyTemparaturHeight*(i+1)+abHeight*2,LinePaint); 331 mCanvas.drawText(EndTenperature-(i+1)+"",centerWith+MercuryWith/2+MaxLineLong+MinLineLong/3, 332 everyTemparaturHeight*(i+1)+TextPaint.getTextSize()/2+abHeight*2,TextPaint); 333 } 334 } 335 //画左边的刻度 336 for(int i=0;i<temperatureRange;i++){ 337 mCanvas.drawLine(centerWith-MercuryWith/2, 338 everyTemparaturHeight*i+abHeight*2, 339 centerWith-MercuryWith/2-MaxLineLong, 340 everyTemparaturHeight*i+abHeight*2,LinePaint); 341 mCanvas.drawText(EndTenperature-i+"", centerWith - (MercuryWith/2+MaxLineLong+MinLineLong/3)-TextPaint.getTextSize(), 342 everyTemparaturHeight * i + TextPaint.getTextSize() / 2+abHeight*2, TextPaint); 343 for(int j=1;j<=9;j++){ 344 if(j==5){ 345 mCanvas.drawLine(centerWith-MercuryWith/2, 346 everyTemparaturHeight*i+j*(scaleLong)+abHeight*2, 347 centerWith-MercuryWith/2-MidLineLong, 348 everyTemparaturHeight*i+j*(scaleLong)+abHeight*2,LinePaint); 349 }else{ 350 mCanvas.drawLine(centerWith-MercuryWith/2, 351 everyTemparaturHeight*i+j*(scaleLong)+abHeight*2, 352 centerWith-MercuryWith/2-MinLineLong, 353 everyTemparaturHeight*i+j*(scaleLong)+abHeight*2,LinePaint); 354 } 355 356 } 357 //画最后一个刻度 358 if(i==temperatureRange-1){ 359 mCanvas.drawLine(centerWith-MercuryWith/2, 360 everyTemparaturHeight*(i+1)+abHeight*2, 361 centerWith-MercuryWith/2-MaxLineLong, 362 everyTemparaturHeight*(i+1)+abHeight*2,LinePaint); 363 mCanvas.drawText(EndTenperature-(i+1)+"", centerWith - (MercuryWith/2+MaxLineLong+MinLineLong/3)-TextPaint.getTextSize(), 364 everyTemparaturHeight * (i+1) + TextPaint.getTextSize() / 2+abHeight*2, TextPaint); 365 } 366 } 367 //画红色的园 368 Paint CirclePaint=new Paint(); 369 CirclePaint.setStyle(Paint.Style.FILL); 370 // CirclePaint.setColor(getResources().getColor(R.color.theme_color)); 371 mCanvas.drawCircle(centerWith, 372 everyTemparaturHeight*(temperatureRange)+abHeight*2+MercuryWith, 373 MercuryWith*3/2,CirclePaint); 374 //画摄氏度的符号 375 Paint symbolTextPaint=new Paint(); 376 symbolTextPaint.setColor(Color.BLACK); 377 symbolTextPaint.setTextSize(mSymbolTextSize); 378 symbolTextPaint.setShader(null); 379 mCanvas.drawText("℃", 380 centerWith - MaxLineLong / 2 - MercuryWith / 2 - symbolTextPaint.getTextSize() / 2, 381 abHeight * 2 - symbolTextPaint.getTextSize(), 382 symbolTextPaint 383 ); 384 mCanvas.drawText("℃", 385 centerWith + MaxLineLong / 2 + MercuryWith / 2 - symbolTextPaint.getTextSize() / 2, 386 abHeight * 2 - symbolTextPaint.getTextSize(), 387 symbolTextPaint 388 ); 389 390 //绘制显示数字的符号和虚线 391 Paint ShowsymbolTextPaint=new Paint(); 392 ShowsymbolTextPaint.setColor(Color.BLACK); 393 ShowsymbolTextPaint.setTextSize(mShowSymbolTextSize); 394 ShowsymbolTextPaint.setShader(null); 395 mCanvas.drawText("℃", 396 mWith*3/2, 397 temperatureAllLong/2-ShowsymbolTextPaint.getTextSize(), 398 ShowsymbolTextPaint 399 ); 400 mCanvas.drawText("- - - - - - - -", 401 mWith+ShowsymbolTextPaint.getTextSize()*3, 402 temperatureAllLong/2, 403 ShowsymbolTextPaint 404 ); 405 mCanvas.drawText("℃", 406 mWith*3/2, 407 temperatureAllLong/2+ShowsymbolTextPaint.getTextSize(), 408 ShowsymbolTextPaint 409 ); 410 411 } 412 413 private float trueTemperature =0 ; 414 415 public void setTargetTemperature(float targetTemperature) { 416 trueTemperature = targetTemperature; 417 if(targetTemperature<30){ 418 targetTemperature = 30; 419 } 420 if(targetTemperature>EndTenperature){ 421 targetTemperature = EndTenperature; 422 } 423 TargetTemperature = targetTemperature; 424 } 425 }
布局文件直接在你的xml当中添加以下语句就可以了
<com.example.test.Sphygmomanometer android:layout_width="wrap_content" android:layout_height="wrap_content" />