一、引言
1.1 现状与背景:开启传感器应用的奇妙之旅
当今,Android 设备普及,设备里藏着加速度计和陀螺仪传感器,堪称 “隐藏高手”。在健身时,它能精准记录运动数据;玩游戏时,一晃手机,就能沉浸式体验;在 VR/AR 里,它更是让你感觉像穿越到另一个世界。不过,能把这俩传感器数据综合处理并展示出来的应用很少,开发指南也难找。所以,开发这么一款应用,简直是 “雪中送炭”!
1.2 实现思路:打造超酷应用的神奇魔法
我们的目标是做个超厉害的 Android 应用,实时展示手机使用时长、角速度和加速度信息。先在 Android Studio 搭建框架,准备好开发环境;再在 AndroidManifest.xml 里设置权限,添加 MPAndroidChart 库用来展示数据。界面设计上,用 ConstraintLayout 灵活布局,Chronometer 控件显示计时,TableLayout 展示数据,LineChart 绘制曲线,TextView 说明坐标系,Button 控制数据采集。最后在 MainActivity.java 里编写代码,让应用 “活” 起来。
1.3 关键步骤:打造爆款应用的秘密武器
- 布局:ConstraintLayout 像万能积木,轻松摆放各种组件,适配所有屏幕。
- 计时:Chronometer 控件像时间小卫士,放在顶部中间,时刻记录时间。
- 数据展示:TableLayout 把数据整理得明明白白,想看啥一眼找到。
- 图表:LineChart 把数据变成漂亮曲线,让数据 “跳舞”。
- 坐标系说明:TextView 像贴心导游,给用户讲清楚坐标系,不迷路。
- 按钮:Button 是神奇开关,一按就能控制数据采集。
实时数据采集
二、开发环境准备
2.1 开发工具与版本
- Android Studio 版本:Ladybug Feature Drop | 2024.2.2
- 编程语言:Java
- 最低支持的 SDK 版本:API 29(“Q”;Android 10.0)
- Gradle 版本:gradle-8.10.2-bin
- 构建配置语言:Kotlin DSL(build.gradle.kts)
2.2 创建 Android 项目
开启 Android Studio,选择 “Start a new Android Studio project”,选定 “Empty Views Activity” 模板,依照向导完成项目创建工作。
三、权限配置
注意:使用加速度计和陀螺仪一般无需额外权限。
在 “Project” 视图下,找到 app/src/main
目录中的 AndroidManifest.xml
文件,其内容如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SensorAssistant"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
此文件主要定义了应用的基本信息和主活动。
四、添加依赖
由于要使用 MPAndroidChart
库来绘制图表,需要在 app/build.gradle.kts
文件中添加依赖:
dependencies {
implementation(libs.mpandroidchart)
}
同时,在项目根目录的 settings.gradle.kts
文件中进行如下更改:
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
rootProject.name = "Sensor Assistant"
include(":app")
五、修改 strings.xml
文件
为了避免硬编码文本问题,在 app/src/main/res/values
目录下的 strings.xml
文件中进行如下修改:
<resources>
<!-- 应用名称 -->
<string name="app_name">Sensor Assistant</string>
<!-- 计时器初始显示文本 -->
<string name="timer_initial">00:00</string>
<!-- 开始按钮文本 -->
<string name="start_button">Start</string>
<!-- 结束按钮文本 -->
<string name="stop_button">Stop</string>
<!-- 重启按钮文本 -->
<string name="restart_button">Restart</string>
<!-- 表头 Axis -->
<string name="table_header_axis">Axis</string>
<!-- 角速度表头 -->
<string name="table_header_angular_speed">ω/(rad·s⁻¹)</string>
<!-- 加速度表头 -->
<string name="table_header_acceleration">a/(m·s⁻²)</string>
<!-- 坐标轴名称 -->
<string name="axis_x">X</string>
<string name="axis_y">Y</string>
<string name="axis_z">Z</string>
<!-- 图表描述 -->
<string name="acceleration_chart_description">Acceleration over Time</string>
<string name="angular_speed_chart_description">Angular Speed over Time</string>
<!-- 各轴加速度曲线标签 -->
<string name="x_acceleration">X Acceleration</string>
<string name="y_acceleration">Y Acceleration</string>
<string name="z_acceleration">Z Acceleration</string>
<!-- 各轴角速度曲线标签 -->
<string name="x_angular_speed">X Angular Speed</string>
<string name="y_angular_speed">Y Angular Speed</string>
<string name="z_angular_speed">Z Angular Speed</string>
<!-- 传感器注册失败提示 -->
<string name="sensor_registration_failed">Failed to register %s sensor</string>
<!-- 设备不支持加速度计传感器提示 -->
<string name="no_accelerometer_sensor">设备不支持加速度计传感器</string>
<!-- 设备不支持陀螺仪传感器提示 -->
<string name="no_gyroscope_sensor">设备不支持陀螺仪传感器</string>
<!-- 坐标系定义 -->
<string name="coordinate_system_definition">空间坐标系定义:\n原点:位于设备的几何中心。\nX 轴:从设备左侧指向右侧为正方向。\nY 轴:从设备底部指向顶部为正方向。\nZ 轴:垂直于屏幕向外为正方向。</string>
</resources>
六、设计布局文件 activity_main.xml
在 app/src/main/res/layout
目录下的 activity_main.xml
文件中设计应用的布局:
<?xml version="1.0" encoding="utf-8"?>
<!-- 定义 ConstraintLayout 根布局,声明必要命名空间,宽高填充满屏幕并设置 16dp 内边距 -->
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<!-- 顶部中间显示时间,使用 Chronometer 控件,Chronometer 是 Android 提供的用于计时的控件,可方便实现秒表功能 -->
<Chronometer
android:id="@+id/timer_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:format="%s"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- 合并后的表格布局,用于展示加速度和角速度的表头及各轴数据 -->
<TableLayout
android:id="@+id/tableLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="*"
app:layout_constraintTop_toBottomOf="@id/timer_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<!-- 表头行,包含坐标轴、加速度、角速度的表头 -->
<TableRow>
<!-- 坐标轴表头 -->
<TextView
android:text="@string/table_header_axis"
android:padding="8dp"
android:textStyle="bold" />
<!-- 加速度表头 -->
<TextView
android:text="@string/table_header_acceleration"
android:padding="8dp"
android:textStyle="bold" />
<!-- 角速度表头 -->
<TextView
android:text="@string/table_header_angular_speed"
android:padding="8dp"
android:textStyle="bold" />
</TableRow>
<!-- X 轴数据行,显示 X 轴的加速度和角速度 -->
<TableRow>
<!-- X 轴名称 -->
<TextView
android:text="@string/axis_x"
android:padding="8dp" />
<!-- 用于显示 X 轴加速度的文本视图 -->
<TextView
android:id="@+id/accel_x_text"
android:padding="8dp" />
<!-- 用于显示 X 轴角速度的文本视图 -->
<TextView
android:id="@+id/omega_x_text"
android:padding="8dp" />
</TableRow>
<!-- Y 轴数据行,显示 Y 轴的加速度和角速度 -->
<TableRow>
<!-- Y 轴名称 -->
<TextView
android:text="@string/axis_y"
android:padding="8dp" />
<!-- 用于显示 Y 轴加速度的文本视图 -->
<TextView
android:id="@+id/accel_y_text"
android:padding="8dp" />
<!-- 用于显示 Y 轴角速度的文本视图 -->
<TextView
android:id="@+id/omega_y_text"
android:padding="8dp" />
</TableRow>
<!-- Z 轴数据行,显示 Z 轴的加速度和角速度 -->
<TableRow>
<!-- Z 轴名称 -->
<TextView
android:text="@string/axis_z"
android:padding="8dp" />
<!-- 用于显示 Z 轴加速度的文本视图 -->
<TextView
android:id="@+id/accel_z_text"
android:padding="8dp" />
<!-- 用于显示 Z 轴角速度的文本视图 -->
<TextView
android:id="@+id/omega_z_text"
android:padding="8dp" />
</TableRow>
</TableLayout>
<!-- 用于绘制加速度曲线的 LineChart,使用 MPAndroidChart 库中的 LineChart 控件来绘制加速度随时间变化的曲线 -->
<com.github.mikephil.charting.charts.LineChart
android:id="@+id/acceleration_chart"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@id/tableLayout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:ignore="MissingClass" />
<!-- 用于绘制角速度曲线的 LineChart,同样使用 MPAndroidChart 库中的 LineChart 控件来绘制角速度随时间变化的曲线 -->
<com.github.mikephil.charting.charts.LineChart
android:id="@+id/angular_speed_chart"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@id/acceleration_chart"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:ignore="MissingClass" />
<!-- 坐标系定义文本,显示空间坐标系的定义信息 -->
<TextView
android:id="@+id/coordinate_system_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/coordinate_system_definition"
android:textSize="14sp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:gravity="start"
app:layout_constraintTop_toBottomOf="@id/angular_speed_chart"
app:layout_constraintBottom_toTopOf="@id/start_stop_button"
app:layout_constraintStart_toStartOf="parent" />
<!-- 底部中间显示“开始”按钮,点击该按钮可开始计时和数据采集,点击后文本会变为“结束”或“重启” -->
<Button
android:id="@+id/start_stop_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
该布局包含一个计时器,一个表格用于显示各轴的加速度和角速度数据,两个折线图分别用于显示加速度和角速度随时间的变化,以及一个开始 / 停止 / 重启按钮。
七、编写 MainActivity.java
代码
在 app/src/main/java/com/example/sensorassistant
目录下的 MainActivity.java
文件中编写核心代码:
// 声明当前类所在的包名,这有助于组织代码结构,避免命名冲突
package com.example.sensorassistant;
// 导入必要的类库,用于实现传感器数据采集、界面显示、图表绘制等功能
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.widget.Button;
import android.widget.Chronometer;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Description;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.formatter.ValueFormatter;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
// MainActivity 继承自 AppCompatActivity 并实现了 SensorEventListener 接口,
// 意味着这个活动可以监听传感器数据的变化
public class MainActivity extends AppCompatActivity implements SensorEventListener {
// 用于日志记录的标签,方便调试时查看日志信息
private static final String TAG = "MainActivity";
// 传感器管理器,用于管理设备上的各种传感器
private SensorManager sensorManager;
// 加速度计传感器对象
private Sensor accelerometer;
// 陀螺仪传感器对象
private Sensor gyroscope;
// 用于显示计时的 Chronometer 控件
private Chronometer timerText;
// 用于显示各轴角速度和加速度的文本视图
private TextView omegaXText, omegaYText, omegaZText, accelXText, accelYText, accelZText;
// 开始/停止/重启按钮
private Button startStopButton;
// 用于绘制加速度和角速度曲线的折线图控件
private LineChart accelerationChart, angularSpeedChart;
// 标记是否正在记录数据
private boolean isRecording = false;
// 记录开始时间
private long startTime;
// 标记是否是重启操作
private boolean isRestart = false;
// 加速度和角速度折线图的数据对象和数据集
private LineData accelerationLineData, angularSpeedLineData;
private LineDataSet accelXDataSet, accelYDataSet, accelZDataSet;
private LineDataSet angularSpeedXDataSet, angularSpeedYDataSet, angularSpeedZDataSet;
// 用于格式化小数,保留三位小数
private final DecimalFormat decimalFormat = new DecimalFormat("#.000");
// 活动创建时调用的方法,用于初始化界面、传感器、按钮事件和图表
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置当前活动显示的布局文件
setContentView(R.layout.activity_main);
// 初始化界面组件
initUI();
// 初始化传感器
initSensor();
// 设置按钮的点击事件
setupButtonAction();
// 初始化图表
initChart();
}
// 初始化界面组件的方法
private void initUI() {
// 通过布局文件中的 id 找到计时的 Chronometer 控件
timerText = findViewById(R.id.timer_text);
timerText.setFormat("%s");
timerText.setText(getString(R.string.timer_initial));
// 通过布局文件中的 id 找到各个显示数据的文本视图和按钮、图表控件
omegaXText = findViewById(R.id.omega_x_text);
omegaYText = findViewById(R.id.omega_y_text);
omegaZText = findViewById(R.id.omega_z_text);
accelXText = findViewById(R.id.accel_x_text);
accelYText = findViewById(R.id.accel_y_text);
accelZText = findViewById(R.id.accel_z_text);
startStopButton = findViewById(R.id.start_stop_button);
accelerationChart = findViewById(R.id.acceleration_chart);
angularSpeedChart = findViewById(R.id.angular_speed_chart);
}
// 初始化传感器的方法
private void initSensor() {
// 获取系统的传感器服务
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
// 获取加速度计传感器对象
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// 获取陀螺仪传感器对象
gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
// 如果设备不支持加速度计传感器,弹出提示并禁用按钮
if (accelerometer == null) {
Toast.makeText(this, getString(R.string.no_accelerometer_sensor), Toast.LENGTH_SHORT).show();
startStopButton.setEnabled(false);
}
// 如果设备不支持陀螺仪传感器,弹出提示并禁用按钮
if (gyroscope == null) {
Toast.makeText(this, getString(R.string.no_gyroscope_sensor), Toast.LENGTH_SHORT).show();
startStopButton.setEnabled(false);
}
}
// 设置按钮点击事件的方法
private void setupButtonAction() {
startStopButton.setOnClickListener(v -> {
if (isRecording) {
// 正在记录时,停止记录
stopRecording();
} else {
if (isRestart) {
// 是重启操作时,重启记录
restartRecording();
} else {
// 正常开始记录
startRecording();
}
}
});
}
// 开始记录数据的方法
private void startRecording() {
if (accelerometer == null || gyroscope == null) {
return;
}
isRecording = true;
// 修改按钮文本为“停止”
startStopButton.setText(getString(R.string.stop_button));
// 开始计时
timerText.setBase(SystemClock.elapsedRealtime());
timerText.start();
startTime = System.currentTimeMillis();
// 注册加速度计和陀螺仪传感器的监听器
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
sensorManager.registerListener(this, gyroscope, SensorManager.SENSOR_DELAY_NORMAL);
}
// 停止记录数据的方法
private void stopRecording() {
isRecording = false;
// 修改按钮文本为“重启”
startStopButton.setText(getString(R.string.restart_button));
// 停止计时
timerText.stop();
// 注销传感器监听器
sensorManager.unregisterListener(this);
isRestart = true;
}
// 重启记录数据的方法
private void restartRecording() {
// 清除图表数据
clearAccelerationChartData();
clearAngularSpeedChartData();
isRecording = true;
// 修改按钮文本为“停止”
startStopButton.setText(getString(R.string.stop_button));
// 重新开始计时
timerText.setBase(SystemClock.elapsedRealtime());
timerText.start();
startTime = System.currentTimeMillis();
// 重新注册传感器监听器
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
sensorManager.registerListener(this, gyroscope, SensorManager.SENSOR_DELAY_NORMAL);
isRestart = false;
}
// 初始化图表的方法
private void initChart() {
initAccelerationChart();
initAngularSpeedChart();
}
// 初始化加速度图表的方法
private void initAccelerationChart() {
List<Entry> accelXEntries = new ArrayList<>();
List<Entry> accelYEntries = new ArrayList<>();
List<Entry> accelZEntries = new ArrayList<>();
// 创建各轴加速度的数据集
accelXDataSet = createLineDataSet(accelXEntries, getString(R.string.x_acceleration), android.R.color.holo_red_light);
accelYDataSet = createLineDataSet(accelYEntries, getString(R.string.y_acceleration), android.R.color.holo_green_light);
accelZDataSet = createLineDataSet(accelZEntries, getString(R.string.z_acceleration), android.R.color.holo_blue_light);
List<ILineDataSet> accelDataSets = new ArrayList<>();
accelDataSets.add(accelXDataSet);
accelDataSets.add(accelYDataSet);
accelDataSets.add(accelZDataSet);
// 创建加速度折线图的数据对象
accelerationLineData = new LineData(accelDataSets);
// 设置加速度图表的属性
setupChart(accelerationChart, accelerationLineData, getString(R.string.acceleration_chart_description));
}
// 初始化角速度图表的方法
private void initAngularSpeedChart() {
List<Entry> angularSpeedXEntries = new ArrayList<>();
List<Entry> angularSpeedYEntries = new ArrayList<>();
List<Entry> angularSpeedZEntries = new ArrayList<>();
// 创建各轴角速度的数据集
angularSpeedXDataSet = createLineDataSet(angularSpeedXEntries, getString(R.string.x_angular_speed), android.R.color.holo_purple);
angularSpeedYDataSet = createLineDataSet(angularSpeedYEntries, getString(R.string.y_angular_speed), android.R.color.holo_orange_light);
angularSpeedZDataSet = createLineDataSet(angularSpeedZEntries, getString(R.string.z_angular_speed), android.R.color.holo_blue_dark);
List<ILineDataSet> angularSpeedDataSets = new ArrayList<>();
angularSpeedDataSets.add(angularSpeedXDataSet);
angularSpeedDataSets.add(angularSpeedYDataSet);
angularSpeedDataSets.add(angularSpeedZDataSet);
// 创建角速度折线图的数据对象
angularSpeedLineData = new LineData(angularSpeedDataSets);
// 设置角速度图表的属性
setupChart(angularSpeedChart, angularSpeedLineData, getString(R.string.angular_speed_chart_description));
}
// 创建折线图数据集的方法
private LineDataSet createLineDataSet(List<Entry> entries, String label, int colorId) {
LineDataSet dataSet = new LineDataSet(entries, label);
dataSet.setColor(ContextCompat.getColor(this, colorId));
dataSet.setCircleColor(ContextCompat.getColor(this, colorId));
dataSet.setLineWidth(2f);
dataSet.setCircleRadius(3f);
dataSet.setValueTextSize(10f);
dataSet.setValueFormatter(new ValueFormatter() {
@Override
public String getFormattedValue(float value) {
return decimalFormat.format(value);
}
});
return dataSet;
}
// 设置图表属性的方法
private void setupChart(LineChart chart, LineData lineData, String description) {
chart.setData(lineData);
chart.setDrawGridBackground(false);
Description chartDescription = new Description();
chartDescription.setText(description);
chart.setDescription(chartDescription);
chart.setPinchZoom(false);
chart.setDragEnabled(true);
chart.setScaleEnabled(true);
XAxis xAxis = chart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setDrawGridLines(true);
YAxis leftYAxis = chart.getAxisLeft();
leftYAxis.setDrawGridLines(true);
YAxis rightYAxis = chart.getAxisRight();
rightYAxis.setEnabled(false);
setupLegend(chart.getLegend());
chart.invalidate();
}
// 设置图表图例的方法
private void setupLegend(Legend legend) {
legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER);
legend.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
legend.setOrientation(Legend.LegendOrientation.HORIZONTAL);
legend.setDrawInside(false);
}
// 清除加速度图表数据的方法
private void clearAccelerationChartData() {
accelXDataSet.clear();
accelYDataSet.clear();
accelZDataSet.clear();
accelerationLineData.notifyDataChanged();
accelerationChart.notifyDataSetChanged();
accelerationChart.invalidate();
}
// 清除角速度图表数据的方法
private void clearAngularSpeedChartData() {
angularSpeedXDataSet.clear();
angularSpeedYDataSet.clear();
angularSpeedZDataSet.clear();
angularSpeedLineData.notifyDataChanged();
angularSpeedChart.notifyDataSetChanged();
angularSpeedChart.invalidate();
}
// 传感器数据变化时调用的方法
@Override
public void onSensorChanged(SensorEvent event) {
if (isRecording) {
long currentTime = System.currentTimeMillis();
long elapsedTime = currentTime - startTime;
float elapsedTimeInSeconds = elapsedTime / 1000f;
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float ax = roundToThreeDecimals(event.values[0]);
float ay = roundToThreeDecimals(event.values[1]);
float az = roundToThreeDecimals(event.values[2]);
// 更新加速度文本显示
updateAccelerationText(ax, ay, az);
// 更新加速度图表显示
updateAccelerationChart(ax, ay, az, elapsedTimeInSeconds);
} else if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
float gx = roundToThreeDecimals(event.values[0]);
float gy = roundToThreeDecimals(event.values[1]);
float gz = roundToThreeDecimals(event.values[2]);
// 更新角速度文本显示
updateAngularSpeedText(gx, gy, gz);
// 更新角速度图表显示
updateAngularSpeedChart(gx, gy, gz, elapsedTimeInSeconds);
}
}
}
// 将浮点数保留三位小数的方法
private float roundToThreeDecimals(float value) {
return Float.parseFloat(decimalFormat.format(value));
}
// 更新加速度文本显示的方法
private void updateAccelerationText(float ax, float ay, float az) {
accelXText.setText(String.format(Locale.US, "%.3f", ax));
accelYText.setText(String.format(Locale.US, "%.3f", ay));
accelZText.setText(String.format(Locale.US, "%.3f", az));
}
// 更新角速度文本显示的方法
private void updateAngularSpeedText(float gx, float gy, float gz) {
omegaXText.setText(String.format(Locale.US, "%.3f", gx));
omegaYText.setText(String.format(Locale.US, "%.3f", gy));
omegaZText.setText(String.format(Locale.US, "%.3f", gz));
}
// 更新加速度图表显示的方法
private void updateAccelerationChart(float ax, float ay, float az, float elapsedTimeInSeconds) {
accelXDataSet.addEntry(new Entry(elapsedTimeInSeconds, ax));
accelYDataSet.addEntry(new Entry(elapsedTimeInSeconds, ay));
accelZDataSet.addEntry(new Entry(elapsedTimeInSeconds, az));
accelXDataSet.notifyDataSetChanged();
accelYDataSet.notifyDataSetChanged();
accelZDataSet.notifyDataSetChanged();
accelerationLineData.notifyDataChanged();
accelerationChart.notifyDataSetChanged();
accelerationChart.setVisibleXRangeMaximum(5f);
accelerationChart.moveViewToX(elapsedTimeInSeconds);
setupLegend(accelerationChart.getLegend());
accelerationChart.invalidate();
}
// 更新角速度图表显示的方法
private void updateAngularSpeedChart(float gx, float gy, float gz, float elapsedTimeInSeconds) {
angularSpeedXDataSet.addEntry(new Entry(elapsedTimeInSeconds, gx));
angularSpeedYDataSet.addEntry(new Entry(elapsedTimeInSeconds, gy));
angularSpeedZDataSet.addEntry(new Entry(elapsedTimeInSeconds, gz));
angularSpeedXDataSet.notifyDataSetChanged();
angularSpeedYDataSet.notifyDataSetChanged();
angularSpeedZDataSet.notifyDataSetChanged();
angularSpeedLineData.notifyDataChanged();
angularSpeedChart.notifyDataSetChanged();
angularSpeedChart.setVisibleXRangeMaximum(5f);
angularSpeedChart.moveViewToX(elapsedTimeInSeconds);
setupLegend(angularSpeedChart.getLegend());
angularSpeedChart.invalidate();
}
// 传感器精度变化时调用的方法
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
Log.d(TAG, "Sensor accuracy changed: " + sensor.getName() + ", accuracy: " + accuracy);
}
}
八、总结
8.1 项目成果大揭秘
咱们成功搞出了一个超酷的 Android 应用!借助设备里的加速度计和陀螺仪,能精准抓到三维加速度和角速度信息。数据展示那叫一个清晰,表格实时显示,折线图动态变化,用户点几下按钮就能轻松控制数据采集,体验感直接拉满!
8.2 技术学习乐无穷
开发过程就像一场刺激的冒险,我们学会了配置开发环境,搞定权限和依赖,设计好看又好用的界面。用 SensorManager
管理传感器,就像驯兽师驯服小动物一样得心应手。编写 MainActivity.java
代码时,处理界面交互和数据更新也不在话下,Java 语言玩得那叫一个溜,代码的可维护性和扩展性也大大提升啦!
8.3 项目拓展超期待
这个项目就像一个宝藏盒子,还有好多潜力可以挖掘呢!
- 数据存储与分析:可以把采集的数据存起来,就像把宝贝放进仓库。之后还能分析这些数据,看看用户的运动习惯啥的,说不定能发现好多有趣的秘密!
- 增加传感器支持:除了现有的两个传感器,还能让应用支持更多类型的传感器,比如磁力计、气压计。这样应用就像长了更多的 “眼睛” 和 “耳朵”,功能更强大啦!
- 优化交互体验:让界面变得更漂亮,增加数据分享功能,就像给应用穿上一件时尚的外衣。还能加点动画和特效,让数据展示变得像看电影一样有趣!
- 提升性能稳定:给应用做个体检,优化性能,让它在各种设备上都能稳稳运行,就像给汽车做保养,让它跑得又快又稳!
希望这篇文章能给想开发类似应用的小伙伴带来灵感,大家可以尽情发挥创意,把这个应用变得更棒!