女朋友想減肥,程式設計師花了三天寫了個卡路里計數器
“ 女朋友說想減肥了,該怎麼回答她?”
有的男同胞饒有信心,認為這是一道送分題 ——當然是毫不遲疑地告訴女友: ”不用減,你一點也不胖,仔細琢磨還有點瘦…… ”
No !還是太年輕,你們可能在一段感情的炙熱裡,但一看就不懂生活。答案早就更新 n個版本了!我廠阿強近期也被提了此問,他給出了一個有技術含量的暖心答案 ——女友關心體態和體重器上的數量,阿強更關心她的身體健康。於是阿強發揮自己看家本事,寫了個卡路里計數器,把對關懷女友精確到數字,用數字告訴她日常應該保持怎樣的健康飲食和運動量,同時陪她一起踐行健康生活。
阿強把整個開發過程分享出來,供廣大男同胞學習和借鑑。以下便是如何透過 Android Studio的 Kotlin開發一個簡單的卡路里計數器應用的全過程。
實現原理:
華為運動健康服務 允許使用者儲存智慧手機或其他裝置收集的運動健康資料,如智慧手錶、智慧手環和計步器上的資料。這些資料可以在生態系統中安全共享。
主要功能
· 資料儲存: 輕鬆儲存你的運動與健康資料。
· 資料開放: 除了提供許多運動和保健資料介面外,它還支援共享各種運動與健康資料,包括步數、體重和心率。
· 資料訪問授權管理: 使用者可以管理開發人員對其運動與健康資料的訪問,保障自身的資料隱私和合法權利。
· 裝置訪問: 可以透過藍芽測量硬體裝置的資料,並允許我們上傳這些資料。
應用功能
這個卡路里計數器應用包含兩個介面。透過華為
Account Kit
的首頁,點選“登入華為賬號”按鈕登入應用。登陸後進入下一個介面,在這個頁面上,可新增“卡路里”和“體重”資訊。資訊以圖形的方式顯示,藉助了
MPAndroidChart的免費庫。
示例程式碼已在相關社群進行開源,歡迎開發者關注、下載並提供寶貴意見:
Github官方地址:
1. 整合 HUAWEI HMS Core
首先,我們需要在 Console 上建立一個帳戶,然後建立一個專案,並將其整合到應用中。可以按照 此連結 中概述的步驟快速完成此操作,也可以藉助官方 codelab 來完成操作。
2. 整合 華為運動健康服務
申請獲取 運動健康服務( Health Kit )。透過 此連結 登入 Console後,單擊下圖中顯示的“ Health Kit”。
然後,單擊“申請 Health Kit”即可完成申請。
接下來需要請求應用使用資料的許可,包括“體重”和“卡路里”資料。只申請必要的資料許可權,即“訪問和新增身高和體重資料”和“訪問和新增卡路里(包括 基礎代謝率 BMR )資料”
然後單擊“提交”按鈕,完成所有流程。
需要注意的是,你會看到下圖中的某些選項被鎖定,因為它們是敏感資料。如果你想在應用中使用敏感資料,還需要傳送電子郵件至 hihealth@huawei.com 郵箱,標題命名為“申請 Health Kit開放許可權”。對方會盡快回復。你可以從點選 此連結 獲得更多詳細資訊。
在 Console獲得必要的許可權後,開啟 Android Studio繼續開發應用。
點選“ build.gradle (工程級)”,然後將所需的依賴新增到專案級的“ build.gradle”檔案中。
注意:我們為圖形庫新增了 jitpack連結。
maven {url ‘https://developer.huawei.com/repo/'} maven { url ‘}
點選開啟“ build.gradle (應用級)”檔案。以下依賴關係對於執行 Health Kit 來說就足夠了,但我們還將新增 Account Kit 和圖形庫的依賴關係。
implementation 'com.huawei.agconnect:agconnect-core:1.4.2.301' implementation 'com.huawei.hms:hwid:5.1.0.301' implementation 'com.huawei.hms:health:5.1.0.301' implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
最後,開啟“
AndroidManifest.xml
”檔案,將
App ID
作為後設資料資訊新增到“
Application
”標籤中。可以透過以下兩種方法獲取我們的應用
ID
:
1.
進入
Console
,單擊開發部分的“華為
ID
”,然後選擇專案,檢視應用
ID
。
2.
在“
agconnect-services.json
”檔案中找到應用
ID
。
<meta-data android:name="com.huawei.hms.client.appid" android:value="您的應用 Id"/>
3. 開發應用
Health Kit 為我們提供了 3個 API:
· DataController : 新增、更新、刪除和讀取運動與健康資料。
· ActivityRecordsController : 將活動記錄寫入平臺並更新記錄。
· AutoRecordController : 讀取實時運動與健康資料。
我們使用 DataController在應用中處理卡路里和體重資料。 Health Kit提供了安全可靠的資料服務,所以我們會請求使用者允許使用他們的健康資料。
“ activity_main.xml”檔案 包含 logo、應用名、輸入按鈕等資訊。
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android=" xmlns:app=" xmlns:tools=" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">
<TextView android:id="@+id/tvAppName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/app_name" android:textColor="@color/colorPrimary" android:textSize="24sp" android:textStyle="bold" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/ivLogo" />
<com.huawei.hms.support.hwid.ui.HuaweiIdAuthButton android:id="@+id/btnLogin" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.7" />
<ImageView android:id="@+id/ivLogo" android:layout_width="172dp" android:layout_height="113dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.497" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.3" app:srcCompat="@drawable/ic_logo" /> </androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
包含登入過程所需的程式碼。
package com.huawei.healthtracker import android.content.Intent import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity import com.huawei.hms.common.ApiException import com.huawei.hms.hihealth.data.Scopes import com.huawei.hms.support.api.entity.auth.Scope import com.huawei.hms.support.hwid.HuaweiIdAuthManager import com.huawei.hms.support.hwid.request.HuaweiIdAuthParams import com.huawei.hms.support.hwid.request.HuaweiIdAuthParamsHelper import com.huawei.hms.support.hwid.service.HuaweiIdAuthService import com.huawei.hms.support.hwid.ui.HuaweiIdAuthButton class MainActivity : AppCompatActivity() { private val TAG = "MainActivity" private lateinit var btnLogin: HuaweiIdAuthButton private lateinit var mAuthParam: HuaweiIdAuthParams private lateinit var mAuthService: HuaweiIdAuthService private val REQUEST_SIGN_IN_LOGIN = 1001 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) btnLogin = findViewById(R.id.btnLogin) btnLogin.setOnClickListener { signIn() } } private fun signIn() { val scopeList = listOf( Scope(Scopes.HEALTHKIT_CALORIES_BOTH), Scope(Scopes.HEALTHKIT_HEIGHTWEIGHT_BOTH), ) mAuthParam = HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM).apply { setIdToken() .setAccessToken() .setScopeList(scopeList) }.createParams() mAuthService = HuaweiIdAuthManager.getService(this, mAuthParam) val authHuaweiIdTask = mAuthService.silentSignIn() authHuaweiIdTask.addOnSuccessListener { val intent = Intent(this, CalorieTrackerActivity::class.java) startActivity(intent) } .addOnFailureListener { startActivityForResult(mAuthService.signInIntent, REQUEST_SIGN_IN_LOGIN) } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { REQUEST_SIGN_IN_LOGIN -> { val authHuaweiIdTask = HuaweiIdAuthManager.parseAuthResultFromIntent(data) if (authHuaweiIdTask.isSuccessful) { val intent = Intent(this, CalorieTrackerActivity::class.java) startActivity(intent) } else { Log.i( TAG, "signIn failed: ${(authHuaweiIdTask.exception as ApiException).statusCode}" ) } } } } }
確保你已將資料的許可權設定為“範圍”。單擊登入按鈕時,使用者將看到授權頁面。並且,授權頁面在“範圍”欄位中顯示許可權。預設情況下不標記這些許可權,因此使用者應標記它們。
在“ CalorieTrackerActivity ”頁面上,可以新增和檢視卡路里和體重資訊。
“ activity_calorie_tracker.xml” 包含卡路里計數器頁面的設計程式碼。
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android=" xmlns:app=" xmlns:tools=" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".CalorieTrackerActivity">
<Button android:id="@+id/btnShowConsCal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="32dp" android:text="Show Cons. Cal." android:textAllCaps="false" app:layout_constraintEnd_toStartOf="@+id/guideline" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/btnAddConsumedCal" />
<Button android:id="@+id/btnShowWeight" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="32dp" android:text="Show Weight" android:textAllCaps="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="@+id/guideline" app:layout_constraintTop_toBottomOf="@+id/btnAddWeight" />
<Button android:id="@+id/btnAddConsumedCal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="Add" android:textAllCaps="false" app:layout_constraintEnd_toStartOf="@+id/guideline" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/etConsumedCal" />
<Button android:id="@+id/btnAddWeight" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="Add" android:textAllCaps="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="@+id/guideline" app:layout_constraintTop_toBottomOf="@+id/etBurntCal" />
<TextView android:id="@+id/textView" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:text="Consumed Calorie" android:textColor="@color/black" android:textSize="18sp" android:textStyle="bold" app:layout_constraintEnd_toStartOf="@+id/guideline" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" />
<TextView android:id="@+id/textView2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:text="Weight" android:textColor="@color/black" android:textSize="18sp" android:textStyle="bold" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@+id/guideline" app:layout_constraintTop_toTopOf="parent" />
<EditText android:id="@+id/etConsumedCal" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginEnd="32dp" android:ems="10" android:hint="Kcal" android:inputType="number" android:maxLength="4" app:layout_constraintEnd_toStartOf="@+id/guideline" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView" />
<EditText android:id="@+id/etBurntCal" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginEnd="32dp" android:ems="10" android:hint="Kg" android:inputType="number" android:maxLength="3" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@+id/guideline" app:layout_constraintTop_toBottomOf="@+id/textView2" />
<androidx.constraintlayout.widget.Guideline android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.5" app:layout_constraintTop_toTopOf="parent" />
<View android:id="@+id/view_vertical" android:layout_width="1dp" android:layout_height="0dp" android:layout_marginTop="16dp" android:layout_marginBottom="16dp" android:background="@android:color/darker_gray" app:layout_constraintBottom_toBottomOf="@+id/btnAddConsumedCal" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" />
<androidx.cardview.widget.CardView android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:layout_marginBottom="32dp" android:background="@color/colorCardBackground" app:cardCornerRadius="4dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/btnShowWeight">
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<TextView android:id="@+id/tvChartHead" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:gravity="center_horizontal" android:text="Weekly Consumed Calories" android:textColor="@color/black" android:textSize="18sp" android:textStyle="bold" />
<com.github.mikephil.charting.charts.BarChart android:id="@+id/barchartWeeklyCal" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="16dp" android:background="@android:color/white" /> </LinearLayout> </androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
剛剛我們介紹了
Data Controllers
,現在就來建立一個
Data Controller
,然後將要使用的資料寫入許可權。
class CalorieTrackerActivity : AppCompatActivity() { // ... private lateinit var dataController: DataController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_calorie_tracker) initDataController() //... } private fun initDataController() { val hiHealthOptions = HiHealthOptions.builder() .addDataType(DataType.DT_CONTINUOUS_CALORIES_CONSUMED, HiHealthOptions.ACCESS_READ) .addDataType(DataType.DT_CONTINUOUS_CALORIES_CONSUMED, HiHealthOptions.ACCESS_WRITE) .addDataType(DataType.DT_INSTANTANEOUS_BODY_WEIGHT, HiHealthOptions.ACCESS_READ) .addDataType(DataType.DT_INSTANTANEOUS_BODY_WEIGHT, HiHealthOptions.ACCESS_WRITE) .build() val signInHuaweiId = HuaweiIdAuthManager.getExtendedAuthResult(hiHealthOptions) dataController = HuaweiHiHealth.getDataController(this, signInHuaweiId) } }
透過
addConsumedCalorie
方法來記錄資料。此前,需要設定一個時間間隔:將當前時間作為結束時間,並將其之前的一秒作為開始時間。
private fun addConsumedCalorie(calorie: Float) { val dataCollector: DataCollector = DataCollector.Builder().setPackageName(this) .setDataType(DataType.DT_CONTINUOUS_CALORIES_CONSUMED) .setDataGenerateType(DataCollector.DATA_TYPE_RAW) .build() val sampleSet = SampleSet.create(dataCollector) val currentTime = System.currentTimeMillis() val samplePoint = sampleSet.createSamplePoint() .setTimeInterval(currentTime - 1, currentTime, TimeUnit.MILLISECONDS) samplePoint.getFieldValue(Field.FIELD_CALORIES).setFloatValue(calorie) sampleSet.addSample(samplePoint) val insertTask: Task<Void> = dataController.insert(sampleSet) insertTask.addOnSuccessListener { Toast.makeText(this, "Calorie added successfully", Toast.LENGTH_SHORT).show() }.addOnFailureListener { e -> Toast.makeText(this, e.message.toString(), Toast.LENGTH_LONG).show() } }
我們建立了一個名為 addWightData的方法,類似於 addConsumedCalori方法。但這一次,輸入的開始時間和結束時間的值必須相同。否則,當輸入重量資訊時,應用將出現 崩潰 。我們還更改了資料型別。
private fun addWeightData(weight: Float) { val dataCollector: DataCollector = DataCollector.Builder().setPackageName(this) .setDataType(DataType.DT_INSTANTANEOUS_BODY_WEIGHT) .setDataGenerateType(DataCollector.DATA_TYPE_RAW) .build() val sampleSet = SampleSet.create(dataCollector) val currentTime = System.currentTimeMillis() val samplePoint = sampleSet.createSamplePoint() .setTimeInterval(currentTime, currentTime, TimeUnit.MILLISECONDS) samplePoint.getFieldValue(Field.FIELD_BODY_WEIGHT).setFloatValue(weight) sampleSet.addSample(samplePoint) val insertTask: Task<Void> = dataController.insert(sampleSet) insertTask.addOnSuccessListener { Toast.makeText(this, "Weight added successfully", Toast.LENGTH_SHORT).show() }.addOnFailureListener { e -> Toast.makeText(this, e.message.toString(), Toast.LENGTH_SHORT).show() } }
接下來使用 readConsumedData 方法來讀取消耗的卡路里資料。我們選擇了一週前到當前的時間範圍,然後檢索了這個時間範圍內的所有資料,並將其作為時間值放在 Map 上。最後,呼叫 showCaloriesWeekly 方法在條形圖中顯示這些資料。
private fun readConsumedData() { val caloriesMap = mutableMapOf<Long, Float>() val endDate = System.currentTimeMillis() val startDate = endDate - SIX_DAY_MILLIS val readOptions = ReadOptions.Builder() .read(DataType.DT_CONTINUOUS_CALORIES_CONSUMED) .setTimeRange(startDate, endDate, TimeUnit.MILLISECONDS).build() val readReplyTask = dataController.read(readOptions) readReplyTask.addOnSuccessListener { readReply -> for (sampleSet in readReply.sampleSets) { if (sampleSet.isEmpty.not()) { sampleSet.samplePoints.forEach { caloriesMap.put( it.getStartTime(TimeUnit.MILLISECONDS), it.getFieldValue(Field.FIELD_CALORIES).asFloatValue() ) } } else { Toast.makeText(this, "No data to show", Toast.LENGTH_SHORT).show() } } showCaloriesWeekly(caloriesMap) }.addOnFailureListener { Log.i(TAG, it.message.toString()) } }
使用
readWightData
方法來檢索記錄的體重資訊。
private fun readWeightData() { val weightsMap = mutableMapOf<Long, Float>() val endDate = System.currentTimeMillis() val startDate = endDate - SIX_DAY_MILLIS val readOptions = ReadOptions.Builder().read(DataType.DT_INSTANTANEOUS_BODY_WEIGHT) .setTimeRange(startDate, endDate, TimeUnit.MILLISECONDS).build() val readReplyTask = dataController.read(readOptions) readReplyTask.addOnSuccessListener { readReply -> for (sampleSet in readReply.sampleSets) { if (sampleSet.isEmpty.not()) { sampleSet.samplePoints.forEach { weightsMap.put( it.getStartTime(TimeUnit.MILLISECONDS), it.getFieldValue(Field.FIELD_BODY_WEIGHT).asFloatValue() ) } } else { Toast.makeText(this, "No data to show", Toast.LENGTH_SHORT).show() } } showWeightsWeekly(weightsMap) }.addOnFailureListener { Log.i(TAG, it.message.toString()) } }
使用 showCaloriesWeekly方法獲取上週的資料作為時間值。在獲取值後,將上週每天的資料相加。最後呼叫 initBarChart方法在條形圖上顯示每日資料。
private fun showCaloriesWeekly(dataList: Map<Long, Float>) { val arrangedValuesAsMap = mutableMapOf<Long, Float>() val currentTimeMillis = System.currentTimeMillis() var firstDayCal = 0f var secondDayCal = 0f var thirdDayCal = 0f var fourthDayCal = 0f var fifthDayCal = 0f var sixthDayCal = 0f var seventhDayCal = 0f dataList.forEach { (time, value) -> when (time) { in getTodayStartInMillis()..currentTimeMillis -> { seventhDayCal += value } in getTodayStartInMillis() - ONE_DAY_MILLIS until getTodayStartInMillis() -> { sixthDayCal += value } in getTodayStartInMillis() - ONE_DAY_MILLIS * 2 until getTodayStartInMillis() - ONE_DAY_MILLIS -> { fifthDayCal += value } in getTodayStartInMillis() - ONE_DAY_MILLIS * 3 until getTodayStartInMillis() - ONE_DAY_MILLIS * 2 -> { fourthDayCal += value } in getTodayStartInMillis() - ONE_DAY_MILLIS * 4 until getTodayStartInMillis() - ONE_DAY_MILLIS * 3 -> { thirdDayCal += value } in getTodayStartInMillis() - ONE_DAY_MILLIS * 5 until getTodayStartInMillis() - ONE_DAY_MILLIS * 4 -> { secondDayCal += value } in getTodayStartInMillis() - ONE_DAY_MILLIS * 6 until getTodayStartInMillis() - ONE_DAY_MILLIS * 5 -> { firstDayCal += value } } } arrangedValuesAsMap.put(getTodayStartInMillis() - ONE_DAY_MILLIS * 6, firstDayCal) arrangedValuesAsMap.put(getTodayStartInMillis() - ONE_DAY_MILLIS * 5, secondDayCal) arrangedValuesAsMap.put(getTodayStartInMillis() - ONE_DAY_MILLIS * 4, thirdDayCal) arrangedValuesAsMap.put(getTodayStartInMillis() - ONE_DAY_MILLIS * 3, fourthDayCal) arrangedValuesAsMap.put(getTodayStartInMillis() - ONE_DAY_MILLIS * 2, fifthDayCal) arrangedValuesAsMap.put(getTodayStartInMillis() - ONE_DAY_MILLIS, sixthDayCal) arrangedValuesAsMap.put(getTodayStartInMillis(), seventhDayCal) initBarChart(arrangedValuesAsMap) }
showWightWeekly
的工作原理幾乎與
showCaloriesWeekly方法相似。它們之間唯一的區別是,我們不在
showWightWeekly方法中求和每日值,而是隻獲得每日的一個最新值。
private fun showWeightsWeekly(dataList: Map<Long, Float>) { val arrangedValuesAsMap = mutableMapOf<Long, Float>() val currentTimeMillis = System.currentTimeMillis() var firstDayWeight = 0f var secondDayWeight = 0f var thirdDayWeight = 0f var fourthDayWeight = 0f var fifthDayWeight = 0f var sixthDayWeight = 0f var seventhDayWeight = 0f dataList.forEach { (time, value) -> when (time) { in getTodayStartInMillis()..currentTimeMillis -> { seventhDayWeight = value } in getTodayStartInMillis() - ONE_DAY_MILLIS until getTodayStartInMillis() -> { sixthDayWeight = value } in getTodayStartInMillis() - ONE_DAY_MILLIS * 2 until getTodayStartInMillis() - ONE_DAY_MILLIS -> { fifthDayWeight = value } in getTodayStartInMillis() - ONE_DAY_MILLIS * 3 until getTodayStartInMillis() - ONE_DAY_MILLIS * 2 -> { fourthDayWeight = value } in getTodayStartInMillis() - ONE_DAY_MILLIS * 4 until getTodayStartInMillis() - ONE_DAY_MILLIS * 3 -> { thirdDayWeight = value } in getTodayStartInMillis() - ONE_DAY_MILLIS * 5 until getTodayStartInMillis() - ONE_DAY_MILLIS * 4 -> { secondDayWeight = value } in getTodayStartInMillis() - ONE_DAY_MILLIS * 6 until getTodayStartInMillis() - ONE_DAY_MILLIS * 5 -> { firstDayWeight = value } } } arrangedValuesAsMap.put(getTodayStartInMillis() - ONE_DAY_MILLIS * 6, firstDayWeight) arrangedValuesAsMap.put(getTodayStartInMillis() - ONE_DAY_MILLIS * 5, secondDayWeight) arrangedValuesAsMap.put(getTodayStartInMillis() - ONE_DAY_MILLIS * 4, thirdDayWeight) arrangedValuesAsMap.put(getTodayStartInMillis() - ONE_DAY_MILLIS * 3, fourthDayWeight) arrangedValuesAsMap.put(getTodayStartInMillis() - ONE_DAY_MILLIS * 2, fifthDayWeight) arrangedValuesAsMap.put(getTodayStartInMillis() - ONE_DAY_MILLIS, sixthDayWeight) arrangedValuesAsMap.put(getTodayStartInMillis(), seventhDayWeight) initBarChart(arrangedValuesAsMap) }
InitBarChart
以圖形形式顯示資料。
private fun initBarChart(values: MutableMap<Long, Float>) { var barIndex = 0f val labelWeekdayNames = arrayListOf<String>() val entries = ArrayList<BarEntry>() val simpleDateFormat = SimpleDateFormat("E", Locale.US) values.forEach { (time, value) -> labelWeekdayNames.add(simpleDateFormat.format(time)) entries.add(BarEntry(barIndex, value)) barIndex++ } barChart.apply { setDrawBarShadow(false) setDrawValueAboveBar(false) description.isEnabled = false setDrawGridBackground(false) isDoubleTapToZoomEnabled = false } barChart.xAxis.apply { setDrawGridLines(false) position = XAxis.XAxisPosition.BOTTOM granularity = 1f setDrawLabels(true) setDrawAxisLine(false) valueFormatter = IndexAxisValueFormatter(labelWeekdayNames) axisMaximum = labelWeekdayNames.size.toFloat() } barChart.axisRight.isEnabled = false val legend = barChart.legend legend.isEnabled = false val dataSets = arrayListOf<IBarDataSet>() val barDataSet = BarDataSet(entries, " ") barDataSet.color = Color.parseColor("#76C33A") barDataSet.setDrawValues(false) dataSets.add(barDataSet) val data = BarData(dataSets) barChart.data = data barChart.invalidate() barChart.animateY(1500) }
此外
除了新增和讀取運動與健康資料外, Health Kit還提供更新資料、刪除資料和清除所有資料的功能。雖然我們沒有在應用中使用這些功能,但也可以快速瞭解一下。
updateWight :可以在指定的時間範圍內更新資料。如果想使用體重資訊,那就需要將開始時間和結束時間設定為相同的時間值。但如果我們想更新卡路里值,就需要設定為一個稍長的時間範圍。如果指定時間範圍內沒有要更新的值時,它將自動新增新的重量或卡路里值。
private fun updateWeight(weight: Float, startTimeInMillis: Long, endTimeInMillis: Long) { val dataCollector: DataCollector = DataCollector.Builder().setPackageName(this) .setDataType(DataType.DT_INSTANTANEOUS_BODY_WEIGHT) .setDataGenerateType(DataCollector.DATA_TYPE_RAW) .build() val sampleSet = SampleSet.create(dataCollector) val samplePoint = sampleSet.createSamplePoint() .setTimeInterval(startTimeInMillis, endTimeInMillis, TimeUnit.MILLISECONDS) samplePoint.getFieldValue(Field.FIELD_BODY_WEIGHT).setFloatValue(weight) sampleSet.addSample(samplePoint) val updateOptions = UpdateOptions.Builder() .setTimeInterval(startTimeInMillis, endTimeInMillis, TimeUnit.MILLISECONDS) .setSampleSet(sampleSet) .build() dataController.update(updateOptions) .addOnSuccessListener { Toast.makeText(this, "Weight has been updated successfully", Toast.LENGTH_SHORT) .show() } .addOnFailureListener { e -> Toast.makeText(this, e.message.toString(), Toast.LENGTH_SHORT).show() } }
deleteWight
:刪除指定範圍內的值。
private fun deleteWeight(startTimeInMillis: Long, endTimeInMillis: Long) { val dataCollector: DataCollector = DataCollector.Builder().setPackageName(this) .setDataType(DataType.DT_INSTANTANEOUS_BODY_WEIGHT) .setDataGenerateType(DataCollector.DATA_TYPE_RAW) .build() val deleteOptions = DeleteOptions.Builder() .addDataCollector(dataCollector) .setTimeInterval(startTimeInMillis, endTimeInMillis, TimeUnit.MILLISECONDS) .build() dataController.delete(deleteOptions).addOnSuccessListener { Toast.makeText(this, "Weight has been deleted successfully", Toast.LENGTH_SHORT) .show() } .addOnFailureListener { e -> Toast.makeText(this, e.message.toString(), Toast.LENGTH_SHORT).show() } }
clearHealthData
:刪除
Health Kit中的所有資料。
private fun clearHealthData() { dataController.clearAll() .addOnSuccessListener { Toast.makeText(this, "All Health Kit data has been deleted.", Toast.LENGTH_SHORT) .show() } .addOnFailureListener { e -> Toast.makeText(this, e.message.toString(), Toast.LENGTH_SHORT).show() } }
提示和技巧
⚠ ️ 確保已將資料的許可權設定為範圍。否則,將返回錯誤程式碼 50005。
⚠ ️ 使用 data controller寫入資料時,請確保使用正確的時間間隔。否則,當你嘗試寫入資料時,應用將發生崩潰。
欲瞭解
HMS Core
更多詳情,請參閱:
>>
華為開發者聯盟官網
>>
獲取開發指導文件
>>
參與開發者討論請到
CSDN社群
或者
Reddit
社群
>>
下載
demo
和示例程式碼請到
Github
或者
Gitee
>>
解決整合問題請到
Stack Overflow
原文連結: https://developer.huawei.com/consumer/cn/forum/topic/0201557803166550254?fid=18
原作者:胡椒
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69970551/viewspace-2777260/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 第一個想取代程式設計師的AI程式設計師,失敗了?程式設計師AI
- 花了20分鐘,給女朋友們寫了一個web版群聊程式Web
- 程式設計師用寫程式碼的方式找女朋友程式設計師
- 程式設計師女朋友如何給程式設計師男朋友過生日?程式設計師
- 《C++ API 設計》——寫給想寫好類庫的程式設計師C++API程式設計師
- 雜想程式設計師程式設計師
- 當程式設計師寫不出程式碼了……程式設計師
- 戲精程式設計師,用python開發了一個女朋友,天天秀恩愛程式設計師Python
- 三個程式設計師在寫程式碼程式設計師
- 一個老程式設計師的程式設計之路,寫給年輕的程式設計師們程式設計師
- 程式設計師如何發現女朋友出軌?程式設計師
- 程式設計師到底有沒有女朋友?程式設計師
- 我就差一個程式設計師了!程式設計師
- 當一個程式設計師寫不出程式碼了,該怎麼辦?程式設計師
- 誰說程式設計師找不到女朋友,程式設計師明明那麼有市場!程式設計師
- 一個想轉型的程式設計師的困惑!程式設計師
- 程式設計師寫了一個新手都寫不出的低階bug,被罵慘了。程式設計師
- 如何教女朋友程式設計?程式設計
- 程式設計師,請你不要在坑程式設計師了?程式設計師
- 隨想錄(程式設計師和收入)程式設計師
- 當設計師、產品經理和程式設計師去交友站點找女朋友程式設計師
- 當,程式設計師突然想畫畫,AI+機器人就該登場了程式設計師AI機器人
- 程式設計師是怎樣把女朋友聊沒的?程式設計師
- 當程式設計師寫不出程式碼了,該怎麼辦?程式設計師
- 程式設計師與寫作程式設計師
- 當一個Web前端程式設計師想改我的後臺程式碼時程式設計師的樣子Web前端程式設計師
- 扎心了,程式設計師!程式設計師
- 程式設計師想獨立賺錢的幾個注意點程式設計師
- 程式設計師也想改 Lottie 動畫?是的!程式設計師動畫
- 跑在路上的程式設計師隨想程式設計師
- 《程式設計師的數學》第2版來了!程式設計師
- 九種水果助您輕鬆減肥! - 健康程式設計師,至尚生活!程式設計師
- 怪不得這位程式設計師有女朋友,原來是因為這個!程式設計師
- 兩個程式設計師程式設計師
- 做個程式設計師程式設計師
- 光棍節專題:python程式設計師如何找女朋友Python程式設計師
- 女朋友掉水裡,各類程式設計師怎麼救?程式設計師
- 程式設計師如何寫出好程式碼?程式設計師