超酷 Android 传感器应用开发收官!探索之旅超有趣

一、引言

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 项目拓展超期待

这个项目就像一个宝藏盒子,还有好多潜力可以挖掘呢!

  • 数据存储与分析:可以把采集的数据存起来,就像把宝贝放进仓库。之后还能分析这些数据,看看用户的运动习惯啥的,说不定能发现好多有趣的秘密!
  • 增加传感器支持:除了现有的两个传感器,还能让应用支持更多类型的传感器,比如磁力计、气压计。这样应用就像长了更多的 “眼睛” 和 “耳朵”,功能更强大啦!
  • 优化交互体验:让界面变得更漂亮,增加数据分享功能,就像给应用穿上一件时尚的外衣。还能加点动画和特效,让数据展示变得像看电影一样有趣!
  • 提升性能稳定:给应用做个体检,优化性能,让它在各种设备上都能稳稳运行,就像给汽车做保养,让它跑得又快又稳!

希望这篇文章能给想开发类似应用的小伙伴带来灵感,大家可以尽情发挥创意,把这个应用变得更棒!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值