android检测心率应用实例

参考博客:https://blog.csdn.net/qq_36982160/article/details/81260273

参考github:https://github.com/ZhaoYukai/HeartRate

如果运行时出现Program type already present: android.support.v4.app.BackStackRecord$Op错误,参考:https://*.com/questions/49917614/program-type-already-present-android-support-v4-app-backstackrecordop

 

首先膜拜以上大神的博客和github,本人在引用上方github项目时出现了些问题,所以记下来以备以后用到。先讲下改错的过程:本人在Android Studio新建了项目后,就把github上的代码粘贴了过来,然后在manifests删掉以下内容:

<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />

然后把github项目中的HeartRate-master\HeartRate-master\libs路径里的jar包复制到了AS模块里的libs路径下(后面看来android-support-v4.jar包用不到),选中后右键添加到库。然后在模块级的build.gradle中修改compileSdkVersion、targetSdkVersion为27(https://www.jianshu.com/p/808e1d127a33)。然后修改了两个implementation 如下:

implementation ‘com.android.support:appcompat-v7:27.1.1‘
implementation ‘com.android.support:support-v4:27.1.1‘

然后修改MainActivity的几个地方如下:

wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "DoNotDimScreen");  ====》

wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "myapp:wakeLock");

然后在运行app前授权允许使用照相机后即可运行app

运行效果图:

android检测心率应用实例

 

模块结构图:

android检测心率应用实例

 

模块级的build.gradle:

 1 apply plugin: com.android.application
 2 
 3 android {
 4     compileSdkVersion 27
 5 
 6 
 7 
 8     defaultConfig {
 9         applicationId "com.mingrisoft.heartdetect"
10         minSdkVersion 15
11         targetSdkVersion 27
12         versionCode 1
13         versionName "1.0"
14 
15         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
16 
17     }
18 
19     buildTypes {
20         release {
21             minifyEnabled false
22             proguardFiles getDefaultProguardFile(proguard-android.txt), proguard-rules.pro
23         }
24     }
25 
26 }
27 
28 dependencies {
29     implementation fileTree(include: [*.jar], dir: libs)
30     implementation com.android.support:appcompat-v7:27.1.1
31     implementation com.android.support:support-v4:27.1.1
32     implementation com.android.support.constraint:constraint-layout:1.1.3
33     testImplementation junit:junit:4.12
34     androidTestImplementation com.android.support.test:runner:1.0.2
35     androidTestImplementation com.android.support.test.espresso:espresso-core:3.0.2
36     implementation files(libs/achartengine-1.0.0.jar)
37 }

 

manifests:
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3     package="com.mingrisoft.heartdetect">
 4 
 5     <uses-permission android:name="android.permission.WAKE_LOCK" />
 6     <uses-permission android:name="android.permission.CAMERA" />
 7     <uses-feature android:name="android.hardware.camera" />
 8     <uses-feature android:name="android.hardware.camera.autofocus" />
 9 
10     <application
11         android:allowBackup="true"
12         android:icon="@mipmap/ic_launcher"
13         android:label="@string/app_name"
14         android:roundIcon="@mipmap/ic_launcher_round"
15         android:supportsRtl="true"
16         android:theme="@style/AppTheme">
17         <activity android:name=".MainActivity">
18             <intent-filter>
19                 <action android:name="android.intent.action.MAIN" />
20 
21                 <category android:name="android.intent.category.LAUNCHER" />
22             </intent-filter>
23         </activity>
24     </application>
25 
26 </manifest>

 

strings.xml:

1 <resources>
2     <string name="app_name">heartDetect</string>
3     <string name="hello_world">Hello world!</string>
4     <string name="action_settings">Settings</string>
5     <string name="show">显示</string>
6 
7 </resources>

 

activity_main.xml:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:app="http://schemas.android.com/apk/res-auto"
 4     xmlns:tools="http://schemas.android.com/tools"
 5     android:layout_width="match_parent"
 6     android:layout_height="match_parent"
 7     android:orientation="vertical"
 8     tools:context=".MainActivity">
 9 
10     <SurfaceView
11         android:id="@+id/id_preview"
12         android:layout_width="match_parent"
13         android:layout_height="200dp"
14         android:layout_marginLeft="50dp"
15         android:layout_marginRight="50dp" />
16 
17     <LinearLayout
18         android:id="@+id/id_linearLayout_graph"
19         android:layout_width="match_parent"
20         android:layout_height="200dp"
21         android:orientation="vertical" >
22     </LinearLayout>
23 
24     <TextView
25         android:id="@+id/id_tv_heart_rate"
26         android:layout_width="wrap_content"
27         android:layout_height="wrap_content"
28         android:layout_marginLeft="50dp"
29         android:layout_weight="1"
30         android:text="@string/show" >
31     </TextView>
32 
33     <TextView
34         android:id="@+id/id_tv_Avg_Pixel_Values"
35         android:layout_width="wrap_content"
36         android:layout_height="wrap_content"
37         android:layout_marginLeft="50dp"
38         android:layout_weight="1"
39         android:text="@string/show" >
40     </TextView>
41 
42     <TextView
43         android:id="@+id/id_tv_pulse"
44         android:layout_width="wrap_content"
45         android:layout_height="wrap_content"
46         android:layout_marginLeft="50dp"
47         android:layout_weight="1"
48         android:text="@string/show" >
49     </TextView>
50 
51 
52 </LinearLayout >

 

ImageProcessing:
 1 package com.mingrisoft.heartdetect;
 2 
 3 /**
 4  * 图像处理类
 5  */
 6 public abstract class ImageProcessing {
 7 
 8     /**
 9      * 内部调用的处理图片的方法
10      */
11     private static int decodeYUV420SPtoRedSum(byte[] yuv420sp , int width , int height) {
12         if (yuv420sp == null) {
13             return 0;
14         }
15 
16         final int frameSize = width * height;
17         int sum = 0;
18 
19         for (int j = 0 , yp = 0 ; j < height ; j++) {
20             int uvp = frameSize + (j >> 1) * width;
21             int u = 0;
22             int v = 0;
23             for (int i = 0 ; i < width ; i++, yp++) {
24                 int y = (0xff & ((int) yuv420sp[yp])) - 16;
25                 if (y < 0) {
26                     y = 0;
27                 }
28                 if ((i & 1) == 0) {
29                     v = (0xff & yuv420sp[uvp++]) - 128;
30                     u = (0xff & yuv420sp[uvp++]) - 128;
31                 }
32                 int y1192 = 1192 * y;
33                 int r = (y1192 + 1634 * v);
34                 int g = (y1192 - 833 * v - 400 * u);
35                 int b = (y1192 + 2066 * u);
36 
37                 if (r < 0) {
38                     r = 0;
39                 }
40                 else if (r > 262143) {
41                     r = 262143;
42                 }
43 
44                 if (g < 0) {
45                     g = 0;
46                 }
47                 else if (g > 262143) {
48                     g = 262143;
49                 }
50 
51                 if (b < 0) {
52                     b = 0;
53                 }
54                 else if (b > 262143) {
55                     b = 262143;
56                 }
57 
58                 int pixel = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
59                 int red = (pixel >> 16) & 0xff;
60                 sum += red;
61             }
62         }
63         return sum;
64     }
65 
66     /**
67      * 对外开放的图像处理方法
68      */
69     public static int decodeYUV420SPtoRedAvg(byte[] yuv420sp , int width , int height) {
70         if (yuv420sp == null) {
71             return 0;
72         }
73         final int frameSize = width * height;
74         int sum = decodeYUV420SPtoRedSum(yuv420sp, width, height);
75         return (sum / frameSize);
76     }
77 }

 

MainActivity:
  1 package com.mingrisoft.heartdetect;
  2 
  3 import android.app.Activity;
  4 import android.content.Context;
  5 import android.content.res.Configuration;
  6 import android.graphics.Color;
  7 import android.graphics.Paint.Align;
  8 import android.hardware.Camera;
  9 import android.hardware.Camera.PreviewCallback;
 10 import android.os.Bundle;
 11 import android.os.Handler;
 12 import android.os.Message;
 13 import android.os.PowerManager;
 14 import android.os.PowerManager.WakeLock;
 15 import android.util.Log;
 16 import android.view.SurfaceHolder;
 17 import android.view.SurfaceView;
 18 import android.view.ViewGroup.LayoutParams;
 19 import android.widget.LinearLayout;
 20 import android.widget.TextView;
 21 import android.widget.Toast;
 22 
 23 import org.achartengine.ChartFactory;
 24 import org.achartengine.GraphicalView;
 25 import org.achartengine.chart.PointStyle;
 26 import org.achartengine.model.XYMultipleSeriesDataset;
 27 import org.achartengine.model.XYSeries;
 28 import org.achartengine.renderer.XYMultipleSeriesRenderer;
 29 import org.achartengine.renderer.XYSeriesRenderer;
 30 
 31 import java.util.Timer;
 32 import java.util.TimerTask;
 33 import java.util.concurrent.atomic.AtomicBoolean;
 34 
 35 /**
 36  * 程序的主入口
 37  */
 38 public class MainActivity extends Activity {
 39     //曲线
 40     private Timer timer = new Timer();
 41     //Timer任务,与Timer配套使用
 42     private TimerTask task;
 43     private static int gx;
 44     private static int j;
 45 
 46     private static double flag = 1;
 47     private Handler handler;
 48     private String title = "pulse";
 49     private XYSeries series;
 50     private XYMultipleSeriesDataset mDataset;
 51     private GraphicalView chart;
 52     private XYMultipleSeriesRenderer renderer;
 53     private Context context;
 54     private int addX = -1;
 55     double addY;
 56     int[] xv = new int[300];
 57     int[] yv = new int[300];
 58     int[] hua=new int[]{9,10,11,12,13,14,13,12,11,10,9,8,7,6,7,8,9,10,11,10,10};
 59 
 60     private static final AtomicBoolean processing = new AtomicBoolean(false);
 61     //Android手机预览控件
 62     private static SurfaceView preview = null;
 63     //预览设置信息
 64     private static SurfaceHolder previewHolder = null;
 65     //Android手机相机句柄
 66     private static Camera camera = null;
 67     //private static View image = null;
 68     private static TextView mTV_Heart_Rate = null;
 69     private static TextView mTV_Avg_Pixel_Values = null;
 70     private static TextView mTV_pulse = null;
 71     private static WakeLock wakeLock = null;
 72     private static int averageIndex = 0;
 73     private static final int averageArraySize = 4;
 74     private static final int[] averageArray = new int[averageArraySize];
 75 
 76     /**
 77      * 类型枚举
 78      */
 79     public static enum TYPE {
 80         GREEN, RED
 81     };
 82 
 83     //设置默认类型
 84     private static TYPE currentType = TYPE.GREEN;
 85     //获取当前类型
 86     public static TYPE getCurrent() {
 87         return currentType;
 88     }
 89     //心跳下标值
 90     private static int beatsIndex = 0;
 91     //心跳数组的大小
 92     private static final int beatsArraySize = 3;
 93     //心跳数组
 94     private static final int[] beatsArray = new int[beatsArraySize];
 95     //心跳脉冲
 96     private static double beats = 0;
 97     //开始时间
 98     private static long startTime = 0;
 99 
100 
101 
102     @Override
103     public void onCreate(Bundle savedInstanceState) {
104         super.onCreate(savedInstanceState);
105         setContentView(R.layout.activity_main);
106 
107         initConfig();
108     }
109 
110     /**
111      * 初始化配置
112      */
113     @SuppressWarnings("deprecation")
114     private void initConfig() {
115         //曲线
116         context = getApplicationContext();
117 
118         //这里获得main界面上的布局,下面会把图表画在这个布局里面
119         LinearLayout layout = (LinearLayout)findViewById(R.id.id_linearLayout_graph);
120 
121         //这个类用来放置曲线上的所有点,是一个点的集合,根据这些点画出曲线
122         series = new XYSeries(title);
123 
124         //创建一个数据集的实例,这个数据集将被用来创建图表
125         mDataset = new XYMultipleSeriesDataset();
126 
127         //将点集添加到这个数据集中
128         mDataset.addSeries(series);
129 
130         //以下都是曲线的样式和属性等等的设置,renderer相当于一个用来给图表做渲染的句柄
131         int color = Color.GREEN;
132         PointStyle style = PointStyle.CIRCLE;
133         renderer = buildRenderer(color, style, true);
134 
135         //设置好图表的样式
136         setChartSettings(renderer, "X", "Y", 0, 300, 4, 16, Color.WHITE, Color.WHITE);
137 
138         //生成图表
139         chart = ChartFactory.getLineChartView(context, mDataset, renderer);
140 
141         //将图表添加到布局中去
142         layout.addView(chart, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
143 
144         //这里的Handler实例将配合下面的Timer实例,完成定时更新图表的功能
145         handler = new Handler() {
146             @Override
147             public void handleMessage(Message msg) {
148                 //刷新图表
149                 updateChart();
150                 super.handleMessage(msg);
151             }
152         };
153 
154         task = new TimerTask() {
155             @Override
156             public void run() {
157                 Message message = new Message();
158                 message.what = 1;
159                 handler.sendMessage(message);
160             }
161         };
162 
163         timer.schedule(task, 1,20);           //曲线
164         //获取SurfaceView控件
165         preview = (SurfaceView) findViewById(R.id.id_preview);
166         previewHolder = preview.getHolder();
167         previewHolder.addCallback(surfaceCallback);
168         previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
169 
170         mTV_Heart_Rate = (TextView) findViewById(R.id.id_tv_heart_rate);
171         mTV_Avg_Pixel_Values = (TextView) findViewById(R.id.id_tv_Avg_Pixel_Values);
172         mTV_pulse = (TextView) findViewById(R.id.id_tv_pulse);
173 
174         PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
175         wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "myapp:wakeLock");
176     }
177 
178     //    曲线
179     @Override
180     public void onDestroy() {
181         //当结束程序时关掉Timer
182         timer.cancel();
183         super.onDestroy();
184     };
185 
186     /**
187      * 创建图表
188      */
189     protected XYMultipleSeriesRenderer buildRenderer(int color, PointStyle style, boolean fill) {
190         XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
191 
192         //设置图表中曲线本身的样式,包括颜色、点的大小以及线的粗细等
193         XYSeriesRenderer r = new XYSeriesRenderer();
194         r.setColor(Color.RED);
195         r.setLineWidth(1);
196         renderer.addSeriesRenderer(r);
197         return renderer;
198     }
199 
200     /**
201      * 设置图标的样式
202      * @param renderer
203      * @param xTitle:x标题
204      * @param yTitle:y标题
205      * @param xMin:x最小长度
206      * @param xMax:x最大长度
207      * @param yMin:y最小长度
208      * @param yMax:y最大长度
209      * @param axesColor:颜色
210      * @param labelsColor:标签
211      */
212     protected void setChartSettings(XYMultipleSeriesRenderer renderer, String xTitle, String yTitle,
213                                     double xMin, double xMax, double yMin, double yMax, int axesColor, int labelsColor) {
214         //有关对图表的渲染可参看api文档
215         renderer.setChartTitle(title);
216         renderer.setXTitle(xTitle);
217         renderer.setYTitle(yTitle);
218         renderer.setXAxisMin(xMin);
219         renderer.setXAxisMax(xMax);
220         renderer.setYAxisMin(yMin);
221         renderer.setYAxisMax(yMax);
222         renderer.setAxesColor(axesColor);
223         renderer.setLabelsColor(labelsColor);
224         renderer.setShowGrid(true);
225         renderer.setGridColor(Color.GREEN);
226         renderer.setXLabels(20);
227         renderer.setYLabels(10);
228         renderer.setXTitle("Time");
229         renderer.setYTitle("mmHg");
230         renderer.setYLabelsAlign(Align.RIGHT);
231         renderer.setPointSize((float) 3 );
232         renderer.setShowLegend(false);
233     }
234 
235     /**
236      * 更新图标信息
237      */
238     private void updateChart() {
239         //设置好下一个需要增加的节点
240         if(flag == 1) {
241             addY = 10;
242         }
243         else {
244             flag = 1;
245             if(gx < 200){
246                 if(hua[20] > 1){
247                     Toast.makeText(MainActivity.this, "请用您的指尖盖住摄像头镜头!", Toast.LENGTH_SHORT).show();
248                     hua[20] = 0;
249                 }
250                 hua[20]++;
251                 return;
252             }
253             else {
254                 hua[20] = 10;
255             }
256             j = 0;
257         }
258         if(j < 20){
259             addY=hua[j];
260             j++;
261         }
262 
263         //移除数据集中旧的点集
264         mDataset.removeSeries(series);
265 
266         //判断当前点集中到底有多少点,因为屏幕总共只能容纳100个,所以当点数超过100时,长度永远是100
267         int length = series.getItemCount();
268         int bz = 0;
269         //addX = length;
270         if (length > 300) {
271             length = 300;
272             bz=1;
273         }
274         addX = length;
275         //将旧的点集中x和y的数值取出来放入backup中,并且将x的值加1,造成曲线向右平移的效果
276         for (int i = 0; i < length; i++) {
277             xv[i] = (int) series.getX(i) - bz;
278             yv[i] = (int) series.getY(i);
279         }
280 
281         //点集先清空,为了做成新的点集而准备
282         series.clear();
283         mDataset.addSeries(series);
284         //将新产生的点首先加入到点集中,然后在循环体中将坐标变换后的一系列点都重新加入到点集中
285         //这里可以试验一下把顺序颠倒过来是什么效果,即先运行循环体,再添加新产生的点
286         series.add(addX, addY);
287         for (int k = 0; k < length; k++) {
288             series.add(xv[k], yv[k]);
289         }
290         //在数据集中添加新的点集
291         //mDataset.addSeries(series);
292 
293         //视图更新,没有这一步,曲线不会呈现动态
294         //如果在非UI主线程中,需要调用postInvalidate(),具体参考api
295         chart.invalidate();
296     } //曲线
297 
298 
299     @Override
300     public void onConfigurationChanged(Configuration newConfig) {
301         super.onConfigurationChanged(newConfig);
302     }
303 
304     @Override
305     public void onResume() {
306         super.onResume();
307         wakeLock.acquire();
308         camera = Camera.open();
309         startTime = System.currentTimeMillis();
310     }
311 
312     @Override
313     public void onPause() {
314         super.onPause();
315         wakeLock.release();
316         camera.setPreviewCallback(null);
317         camera.stopPreview();
318         camera.release();
319         camera = null;
320     }
321 
322 
323     /**
324      * 相机预览方法
325      * 这个方法中实现动态更新界面UI的功能,
326      * 通过获取手机摄像头的参数来实时动态计算平均像素值、脉冲数,从而实时动态计算心率值。
327      */
328     private static PreviewCallback previewCallback = new PreviewCallback() {
329         public void onPreviewFrame(byte[] data, Camera cam) {
330             if (data == null) {
331                 throw new NullPointerException();
332             }
333             Camera.Size size = cam.getParameters().getPreviewSize();
334             if (size == null) {
335                 throw new NullPointerException();
336             }
337             if (!processing.compareAndSet(false, true)) {
338                 return;
339             }
340             int width = size.width;
341             int height = size.height;
342 
343             //图像处理
344             int imgAvg = ImageProcessing.decodeYUV420SPtoRedAvg(data.clone(),height,width);
345             gx = imgAvg;
346             mTV_Avg_Pixel_Values.setText("平均像素值是" + String.valueOf(imgAvg));
347 
348             if (imgAvg == 0 || imgAvg == 255) {
349                 processing.set(false);
350                 return;
351             }
352             //计算平均值
353             int averageArrayAvg = 0;
354             int averageArrayCnt = 0;
355             for (int i = 0; i < averageArray.length; i++) {
356                 if (averageArray[i] > 0) {
357                     averageArrayAvg += averageArray[i];
358                     averageArrayCnt++;
359                 }
360             }
361 
362             //计算平均值
363             int rollingAverage = (averageArrayCnt > 0)?(averageArrayAvg/averageArrayCnt):0;
364             TYPE newType = currentType;
365             if (imgAvg < rollingAverage) {
366                 newType = TYPE.RED;
367                 if (newType != currentType) {
368                     beats++;
369                     flag=0;
370                     mTV_pulse.setText("脉冲数是" + String.valueOf(beats));
371                 }
372             } else if (imgAvg > rollingAverage) {
373                 newType = TYPE.GREEN;
374             }
375 
376             if(averageIndex == averageArraySize) {
377                 averageIndex = 0;
378             }
379             averageArray[averageIndex] = imgAvg;
380             averageIndex++;
381 
382             if (newType != currentType) {
383                 currentType = newType;
384             }
385 
386             //获取系统结束时间(ms)
387             long endTime = System.currentTimeMillis();
388             double totalTimeInSecs = (endTime - startTime) / 1000d;
389             if (totalTimeInSecs >= 2) {
390                 double bps = (beats / totalTimeInSecs);
391                 int dpm = (int) (bps * 60d);
392                 if (dpm < 30 || dpm > 180|| imgAvg < 200) {
393                     //获取系统开始时间(ms)
394                     startTime = System.currentTimeMillis();
395                     //beats心跳总数
396                     beats = 0;
397                     processing.set(false);
398                     return;
399                 }
400 
401                 if(beatsIndex == beatsArraySize) {
402                     beatsIndex = 0;
403                 }
404                 beatsArray[beatsIndex] = dpm;
405                 beatsIndex++;
406 
407                 int beatsArrayAvg = 0;
408                 int beatsArrayCnt = 0;
409                 for (int i = 0; i < beatsArray.length; i++) {
410                     if (beatsArray[i] > 0) {
411                         beatsArrayAvg += beatsArray[i];
412                         beatsArrayCnt++;
413                     }
414                 }
415                 int beatsAvg = (beatsArrayAvg / beatsArrayCnt);
416                 mTV_Heart_Rate.setText("您的心率是"+String.valueOf(beatsAvg) +
417                         "  值:" + String.valueOf(beatsArray.length) +
418                         "    " + String.valueOf(beatsIndex) +
419                         "    " + String.valueOf(beatsArrayAvg) +
420                         "    " + String.valueOf(beatsArrayCnt));
421                 //获取系统时间(ms)
422                 startTime = System.currentTimeMillis();
423                 beats = 0;
424             }
425             processing.set(false);
426         }
427     };
428 
429     /**
430      * 预览回调接口
431      */
432     private static SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
433         //创建时调用
434         @Override
435         public void surfaceCreated(SurfaceHolder holder) {
436             try {
437                 camera.setPreviewDisplay(previewHolder);
438                 camera.setPreviewCallback(previewCallback);
439             } catch (Throwable t) {
440                 Log.e("PreviewDemo","Exception in setPreviewDisplay()", t);
441             }
442         }
443 
444         //当预览改变的时候回调此方法
445         @Override
446         public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
447             Camera.Parameters parameters = camera.getParameters();
448             parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
449             Camera.Size size = getSmallestPreviewSize(width, height, parameters);
450             if (size != null) {
451                 parameters.setPreviewSize(size.width, size.height);
452             }
453             camera.setParameters(parameters);
454             camera.startPreview();
455         }
456 
457         //销毁的时候调用
458         @Override
459         public void surfaceDestroyed(SurfaceHolder holder) {
460 
461         }
462     };
463 
464     /**
465      * 获取相机最小的预览尺寸
466      */
467     private static Camera.Size getSmallestPreviewSize(int width, int height, Camera.Parameters parameters) {
468         Camera.Size result = null;
469         for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
470             if (size.width <= width && size.height <= height) {
471                 if (result == null) {
472                     result = size;
473                 }
474                 else {
475                     int resultArea = result.width * result.height;
476                     int newArea = size.width * size.height;
477                     if (newArea < resultArea) {
478                         result = size;
479                     }
480                 }
481             }
482         }
483         return result;
484     }
485 }

 

android检测心率应用实例

上一篇:iOS Development Sites


下一篇:Android SlidingMenu 仿网易新闻客户端布局