6、实战:获取实时加速度值并显示在TextView上
好了,前面我们把加速度传感器的原理、API、参数都捋了一遍。说实话,光看不练容易忘。这一节咱们直接上手,写一个能跑起来的Demo。
目标很简单:打开App,屏幕上实时显示x、y、z三个轴的加速度值。数值会跟着你手机晃动而变化。嗯,就像你手里拿了个虚拟的水平仪。
6.1 整体思路
我个人习惯,写任何传感器相关的代码前,先想清楚三件事:
- 注册监听 —— 告诉系统我要用加速度传感器
- 处理数据 —— 在回调里拿到SensorEvent,提取三个轴的值
- 更新UI —— 把数值显示到TextView上
说白了,就是“监听→取值→显示”三步走。你想想看,是不是这个理?
核心逻辑流程图:
6.2 布局文件
先写一个简单的布局。三个TextView分别显示x、y、z的值,再加一个整体标题。嗯,这里要注意:TextView的id要起得有意义,别用tv1、tv2这种,回头自己都看不懂。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="24dp">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="实时加速度值"
android:textSize="22sp"
android:textStyle="bold"
android:layout_marginBottom="32dp"/>
<TextView
android:id="@+id/tv_accel_x"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="X轴: 0.00 m/s²"
android:textSize="18sp"
android:layout_marginBottom="12dp"/>
<TextView
android:id="@+id/tv_accel_y"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Y轴: 0.00 m/s²"
android:textSize="18sp"
android:layout_marginBottom="12dp"/>
<TextView
android:id="@+id/tv_accel_z"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Z轴: 0.00 m/s²"
android:textSize="18sp"/>
</LinearLayout>
6.3 Activity代码
接下来是核心代码。我习惯把传感器相关的逻辑单独封装成一个方法,这样代码看起来清爽。你在写的时候也可以这么干。
public class MainActivity extends AppCompatActivity implements SensorEventListener {
private SensorManager sensorManager;
private Sensor accelerometer;
private TextView tvX, tvY, tvZ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化TextView
tvX = findViewById(R.id.tv_accel_x);
tvY = findViewById(R.id.tv_accel_y);
tvZ = findViewById(R.id.tv_accel_z);
// 获取传感器服务
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
if (sensorManager != null) {
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
// 检查设备是否支持加速度传感器
if (accelerometer == null) {
tvX.setText("您的设备不支持加速度传感器");
tvY.setVisibility(View.GONE);
tvZ.setVisibility(View.GONE);
}
}
@Override
protected void onResume() {
super.onResume();
if (accelerometer != null) {
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
}
@Override
protected void onPause() {
super.onPause();
// 我曾经在项目里忘了取消注册,结果App退到后台还在耗电
// 被测试同学吐槽了好几次……所以现在每次都记得写
sensorManager.unregisterListener(this);
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
// 更新UI必须在主线程
runOnUiThread(new Runnable() {
@Override
public void run() {
tvX.setText(String.format("X轴: %.2f m/s²", x));
tvY.setText(String.format("Y轴: %.2f m/s²", y));
tvZ.setText(String.format("Z轴: %.2f m/s²", z));
}
});
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// 精度变化时一般不需要特殊处理,但可以打日志观察
// 我一般在这里记录一下,方便调试
}
}
💡 小技巧: 如果你发现数值跳动太厉害,可以试试把采样率从
SENSOR_DELAY_NORMAL改成SENSOR_DELAY_UI。后者频率更低,适合显示场景。我做过一个计步器项目,当时就是用了SENSOR_DELAY_UI,既省电又够用。
6.4 关键点解析
代码写完了,咱们挑几个重点聊一聊。
- 为什么在onResume注册,onPause取消? —— 说白了,就是为了省电。App切到后台时,传感器还在工作,白白浪费电量。你想想看,用户把手机放口袋里,传感器还在疯狂采样,多冤啊。
- 为什么用runOnUiThread? —— onSensorChanged回调是在传感器线程里执行的,不能直接更新UI。这是Android的线程模型决定的。我刚开始学的时候也踩过这个坑,直接更新TextView,结果App闪退,log里报"Only the original thread that created a view hierarchy can touch its views"。
- 为什么检查accelerometer是否为null? —— 有些低端设备或者平板可能没有加速度传感器。如果不做检查,直接调用registerListener会抛空指针。嗯,这里要注意,用户体验要做好。
6.5 运行效果
把代码跑起来,你会看到这样的效果:
| 手机状态 | X轴 | Y轴 | Z轴 |
|---|---|---|---|
| 平放在桌面(屏幕朝上) | ≈ 0.00 | ≈ 0.00 | ≈ 9.81 |
| 竖屏直立 | ≈ 0.00 | ≈ -9.81 | ≈ 0.00 |
| 横屏左侧 | ≈ -9.81 | ≈ 0.00 | ≈ 0.00 |
| 自由落体(快速下坠) | ≈ 0.00 | ≈ 0.00 | ≈ 0.00 |
⚠ 注意: 自由落体状态下三个轴都接近0,这是因为传感器本身也在做自由落体运动,相对加速度为零。这个现象在物理上叫“失重”。我在做手机防摔检测算法时,就是利用这个特征来判断手机是否处于跌落状态。
6.6 避坑指南
最后分享几个我实际项目中遇到的坑:
- 数值抖动问题 —— 传感器原始数据会有噪声。如果你需要平滑显示,可以加一个简单的低通滤波。比如取最近5次采样的平均值。我做过一个体感游戏,不加滤波的话,画面抖得跟帕金森似的。
- 屏幕旋转问题 —— 手机横竖屏切换时,Activity会重建。记得在onSaveInstanceState里保存传感器状态,或者在manifest里固定屏幕方向。我个人建议固定竖屏,省心。
- 多设备兼容问题 —— 不同手机的传感器精度和采样率不一样。华为、小米、三星的传感器表现各有差异。测试时尽量多找几台真机跑一跑。
好了,这一节的内容就到这。代码量不大,但涉及的知识点挺扎实。你把这个Demo跑通了,加速度传感器的基础用法就算拿下了。
公众号:蓝海资料掘金营,微信deep3321