AndroidWearの心拍センサを使って、心拍数をリアルタイムに表示するアプリを作ります。 鼓動のドクドクも表現してみます。
はじめに
AndroidWearで心拍数を取得して表示するサンプルアプリを作っていきます. 検証した実機は Moto360 1st Gen(Wear1.0) HUAWEI WATCH(Wear2.0) の二つです.
手順
1.センサーマネージャの取得 2.イベントリスナーの登録 3.心拍数の取得 4.心拍数を表示 という流れになります.
1.センサーマネージャの取得
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
2.SensorEventListenerインタフェースの実装
public class MainActivity extends WearableActivity implements SensorEventListener{
onSensorChanged
心拍センサの値に変化があると、ここの部分が実行されます。 心拍数を表示する処理を記述します。
onAccuracyChanged
センサの精度に変更があったときに実行されます。
3.心拍数の取得
@Override
public void onSensorChanged(SensorEvent event) {
//ここで変数宣言すると,起動中は破棄されずメモリリークする
if(set==false)textView.setTextSize(60.0f);
if (event.sensor.getType() == Sensor.TYPE_HEART_RATE) {
hb = event.values[0];
//心拍数を表示
textView.setText(""+(int)hb);
set = true;
}
}
心拍数はevent.values[0];に格納されます。
4.心拍数を表示
心拍数は一分間の鼓動の回数です。 鼓動の間隔を求め、用意したハートの画像の大きさを変えることで心臓のドクドクする鼓動を表現します。
上のハートの画像をheart.pngとしてres/drawable内に保存します。
public void update(){
if(set) {
if (isDisp) {
backGround.setBackgroundColor(Color.argb(80, 231, 232, 226));
//heartTextView.setTextSize(100.0f);
textView.setTextSize(60.0f);
ImageView img = (ImageView) findViewById(R.id.imageView);
Resources res = getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.heart);
// bitmapの画像を250*250で作成する
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, 250, 250, false);
img.setImageBitmap(bitmap2);
} else {
backGround.setBackgroundColor(Color.argb(10, 231, 232, 226));
//heartTextView.setTextSize(800.0f);
textView.setTextSize(70.0f);
ImageView img = (ImageView) findViewById(R.id.imageView);
Resources res = getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.heart);
// bitmapの画像を300*300で作成する
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, 300, 300, false);
img.setImageBitmap(bitmap2);
}
}
isDisp = !isDisp;
}
//一定時間後にupdateを呼ぶためのオブジェクト
class LoopEngine extends Handler {
private boolean isUpdate;
public void start(){
this.isUpdate = true;
handleMessage(new Message());
}
public void stop(){
this.isUpdate = false;
}
@Override
public void handleMessage(Message msg) {
this.removeMessages(0);//既存のメッセージは削除
if(this.isUpdate){
MainActivity.this.update();//自信が発したメッセージを取得してupdateを実行
sendMessageDelayed(obtainMessage(0), (long)(60/hb*1000));//鼓動の間隔でメッセージを出力
}
}
};
最後にソースコード
MainActivity.java
package yokohama.mio.heartbeat;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Typeface;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.wearable.activity.WearableActivity;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import static android.graphics.Typeface.BOLD;
import static android.graphics.Typeface.DEFAULT_BOLD;
public class MainActivity extends WearableActivity implements SensorEventListener{
private final String TAG = MainActivity.class.getName();
private SensorManager mSensorManager;
public float hb=100.0f;
private TextView textView;
private TextView heartTextView;
public View backGround;
public boolean isDisp=true;
private LoopEngine loopEngine = new LoopEngine();
public ImageView imageView;
boolean set = false;
@Override
protected void onStart(){
super.onStart();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setAmbientEnabled();
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
textView = (TextView) findViewById(R.id.text);
heartTextView = (TextView) findViewById(R.id.text_heart);
backGround = (View) findViewById(R.id.View);
textView.setTextSize(20.0f);
heartTextView.setTextSize(0.0f);
loopEngine.start();
//heartTextView.setTextColor(Color.argb(80, 67, 135, 233));
textView.setTextColor(Color.argb(255, 140, 140, 140));
textView.setTypeface(Typeface.create(DEFAULT_BOLD, BOLD));
}
private LinearLayout.LayoutParams createParam(int w, int h){
return new LinearLayout.LayoutParams(w, h);
}
@Override
protected void onResume() {
super.onResume();
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
protected void onPause(){
super.onPause();
mSensorManager.unregisterListener(this);
}
@Override
public void onSensorChanged(SensorEvent event) {
//ここで変数宣言すると,起動中は破棄されずメモリリークする
if(set==false)textView.setTextSize(60.0f);
if (event.sensor.getType() == Sensor.TYPE_HEART_RATE) {
hb = event.values[0];
textView.setText(""+(int)hb);
set = true;
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
Log.d(TAG,"onAccuracyChanged!!");
}
public void update(){
if(set) {
if (isDisp) {
backGround.setBackgroundColor(Color.argb(80, 231, 232, 226));
//heartTextView.setTextSize(100.0f);
textView.setTextSize(60.0f);
ImageView img = (ImageView) findViewById(R.id.imageView);
Resources res = getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.heart);
// bitmapの画像を250*250で作成する
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, 250, 250, false);
img.setImageBitmap(bitmap2);
} else {
backGround.setBackgroundColor(Color.argb(10, 231, 232, 226));
//heartTextView.setTextSize(800.0f);
textView.setTextSize(70.0f);
ImageView img = (ImageView) findViewById(R.id.imageView);
Resources res = getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.heart);
// bitmapの画像を300*300で作成する
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, 300, 300, false);
img.setImageBitmap(bitmap2);
}
}
isDisp = !isDisp;
}
//一定時間後にupdateを呼ぶためのオブジェクト
class LoopEngine extends Handler {
private boolean isUpdate;
public void start(){
this.isUpdate = true;
handleMessage(new Message());
}
public void stop(){
this.isUpdate = false;
}
@Override
public void handleMessage(Message msg) {
this.removeMessages(0);//既存のメッセージは削除
if(this.isUpdate){
MainActivity.this.update();//自信が発したメッセージを取得してupdateを実行
sendMessageDelayed(obtainMessage(0), (long)(60/hb*1000));//鼓動の間隔でメッセージを出力
}
}
};
}
main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="yokohama.mio.heartbeat.MainActivity"
tools:deviceIds="wear">
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:id="@+id/View"
android:layout_alignParentBottom="true"
android:layout_gravity="center_vertical|center_horizontal"/>
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/heart"
android:scaleType="center"
android:contentDescription="heart"
android:layout_gravity="center_vertical|center_horizontal" />
<TextView
android:id="@+id/text_heart"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_box="all"
android:text="♥"
android:gravity="center_vertical|center_horizontal" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_box="all"
android:text="心拍数測定中\nちょっと待ってね"
android:textSize="40sp"
android:gravity="center_vertical|center_horizontal"
android:textColor="@color/black_54p"/>
</android.support.wearable.view.BoxInsetLayout>