Vibrator(振动器)
使用 Vibrator (振动器)
Vibrator
是 Android 提供的一个系统服务类,用于控制设备的振动功能,常用于通知、触觉反馈或增强用户交互体验。结合 Android 动画合集的背景,Vibrator
可与动画(如按钮点击时的缩放动画)结合,为用户提供视觉和触觉的联合反馈。本文将详细介绍 Vibrator
的功能、使用方法、权限要求及注意事项,重点展示如何实现振动效果,并结合属性动画实现交互效果。
Vibrator 的作用与原理
- 作用:
Vibrator
允许开发者触发设备的振动马达,支持简单的单次振动、预定义模式振动或自定义波形振动(API 26+)。它适用于增强用户体验,如按钮点击、通知提醒或游戏反馈。 - 原理:
Vibrator
通过 Android 的硬件抽象层(HAL)与设备的振动马达通信,控制振动时长、模式或强度(部分设备支持)。它依赖设备的硬件支持,某些设备可能无振动功能。 - 应用场景:
- 按钮点击时的触觉反馈(如动画触发时振动)。
- 通知或警告(如错误提示时振动)。
- 游戏中事件反馈(如爆炸效果结合动画)。
- 与音频(如
SoundPool
)或动画(如ObjectAnimator
)结合,增强沉浸感。
权限要求
使用 Vibrator
需要在 AndroidManifest.xml
中声明以下权限:
<uses-permission android:name="android.permission.VIBRATE" />
- 动态权限:
VIBRATE
权限是普通权限,无需动态请求,用户安装应用时自动授予。 - 注意:API 33+(Android 13)引入了细粒度振动权限(如
VIBRATE_ALARM
),但大多数场景仍使用VIBRATE
。
获取 Vibrator
通过 Context
获取 Vibrator
实例:
import android.content.Context;
import android.os.Vibrator;
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
- 检查振动支持:
if (vibrator.hasVibrator()) {
// 设备支持振动
} else {
// 无振动硬件
}
常用功能与方法
以下是 Vibrator
的核心方法:
方法/功能 | 描述 | API 级别 | 用法示例 |
---|---|---|---|
vibrate(long milliseconds) | 单次振动指定时长(毫秒)。 | 1+ (API 26 弃用) | vibrator.vibrate(100); |
vibrate(long[] pattern, int repeat) | 按模式振动(时长数组,重复次数)。 | 1+ (API 26 弃用) | vibrator.vibrate(new long[]{0, 100, 200, 100}, -1); |
vibrate(VibrationEffect) | 使用振动效果(API 26+,支持强度、波形)。 | 26+ | vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); |
vibrate(VibrationEffect, AudioAttributes) | 结合音频属性振动(API 26+)。 | 26+ | vibrator.vibrate(effect, audioAttributes); |
cancel() | 取消当前振动。 | 1+ | vibrator.cancel(); |
hasVibrator() | 检查设备是否支持振动。 | 11+ | boolean hasVibrator = vibrator.hasVibrator(); |
hasAmplitudeControl() | 检查是否支持振动强度控制(API 26+)。 | 26+ | boolean hasAmplitude = vibrator.hasAmplitudeControl(); |
- VibrationEffect(API 26+):
createOneShot(long milliseconds, int amplitude)
:单次振动,时长和强度。createWaveform(long[] timings, int[] amplitudes, int repeat)
:自定义波形振动。createPredefined(int effectId)
(API 29+):预定义效果(如EFFECT_CLICK
、EFFECT_TICK
)。- AudioAttributes(API 26+):
- 用于指定振动场景(如通知、媒体),与
AudioManager
的音频流关联。
完整示例(振动 + 动画)
以下是一个结合 Vibrator
和属性动画的示例,点击按钮触发振动和缩放动画。
import android.content.Context;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.widget.Button;
import android.widget.Toast;
import android.animation.ObjectAnimator;
import androidx.appcompat.app.AppCompatActivity;
public class VibratorAnimationActivity extends AppCompatActivity {
private Vibrator vibrator;
private Button vibrateButton;
private ObjectAnimator scaleAnimator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_vibrator_animation);
vibrateButton = findViewById(R.id.vibrate_button);
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
// 初始化动画
scaleAnimator = ObjectAnimator.ofFloat(vibrateButton, "scaleX", 1f, 1.2f, 1f);
scaleAnimator.setDuration(200);
// 检查振动支持
if (!vibrator.hasVibrator()) {
Toast.makeText(this, "Device does not support vibration", Toast.LENGTH_SHORT).show();
vibrateButton.setEnabled(false);
return;
}
// 按钮点击触发振动和动画
vibrateButton.setOnClickListener(v -> {
// API 26+ 振动效果
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
} else {
// 旧版振动
vibrator.vibrate(100);
}
scaleAnimator.start();
Toast.makeText(this, "Vibrating", Toast.LENGTH_SHORT).show();
});
}
@Override
protected void onDestroy() {
super.onDestroy();
vibrator.cancel();
scaleAnimator.cancel();
}
}
- 布局 XML(res/layout/activity_vibrator_animation.xml):
<?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:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/vibrate_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Vibrate" />
</LinearLayout>
- AndroidManifest.xml(添加权限):
<uses-permission android:name="android.permission.VIBRATE" />
- 说明:
- 权限:声明
VIBRATE
权限,无需动态请求。 - 振动:API 26+ 使用
VibrationEffect.createOneShot
触发 100ms 振动,旧版使用vibrate(long)
。 - 动画:按钮点击时触发缩放动画(
scaleX
),与振动同步。 - 兼容性:检查
hasVibrator()
确保设备支持振动。 - 生命周期:
onDestroy
取消振动和动画。
自定义振动模式
实现复杂振动模式(如短长短):
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
long[] timings = new long[]{0, 100, 200, 100}; // 等待0ms,振100ms,暂停200ms,振100ms
int[] amplitudes = new int[]{0, 255, 0, 255}; // 强度(0-255)
vibrator.vibrate(VibrationEffect.createWaveform(timings, amplitudes, -1));
} else {
vibrator.vibrate(new long[]{0, 100, 200, 100}, -1);
}
预定义振动效果(API 29+)
使用预定义效果(如点击、双击):
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
vibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
}
结合 AudioManager
与 AudioManager
(前文介绍)结合,检查音量并触发振动:
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if (audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
vibrator.vibrate(VibrationEffect.createOneShot(200, VibrationEffect.DEFAULT_AMPLITUDE));
}
优缺点
- 优点:
- 简单易用,支持单次和模式振动。
- API 26+ 提供丰富的振动效果(强度、波形)。
- 与动画结合可增强用户体验。
- 缺点:
- 依赖硬件,部分设备无振动马达。
- 强度控制需 API 26+ 且硬件支持。
- 无法精细控制振动频率或复杂波形。
- 替代方案:
- HapticFeedback:View 提供的简单触觉反馈(如
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
)。 - VibratorManager(API 31+):支持多振动器设备。
- Third-party Libraries:如 Immersion Haptics(高级触觉)。
注意事项
- 权限:确保声明
VIBRATE
权限。 - 硬件支持:使用
hasVibrator()
检查设备支持。 - API 兼容性:API 26+ 使用
VibrationEffect
,旧版使用vibrate(long)
或vibrate(long[], int)
。 - 生命周期:在
onPause
或onDestroy
取消振动和动画。 - 用户体验:避免过长或频繁振动,防止打扰用户。
- 调试:通过 Log 验证振动触发,检查设备硬件支持。
扩展:Jetpack Compose 中的实现
在 Jetpack Compose 中,Vibrator
可结合 Composable 实现振动和动画:
import android.content.Context
import android.os.VibrationEffect
import android.os.Vibrator
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
@Composable
fun VibratorScreen() {
val context = LocalContext.current
val vibrator = remember { context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator }
var isVibrating by remember { mutableStateOf(false) }
val scale by animateFloatAsState(
targetValue = if (isVibrating) 1.2f else 1f,
animationSpec = tween(200)
)
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
if (!vibrator.hasVibrator()) {
Text("Device does not support vibration")
} else {
Button(
onClick = {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
vibrator.vibrate(100)
}
isVibrating = true
// Reset state after vibration
kotlinx.coroutines.GlobalScope.launch {
kotlinx.coroutines.delay(200)
isVibrating = false
}
},
modifier = Modifier.scale(scale),
enabled = vibrator.hasVibrator()
) {
Text("Vibrate")
}
}
}
DisposableEffect(Unit) {
onDispose {
vibrator.cancel()
}
}
}
- 说明:
- 使用
remember
缓存Vibrator
。 animateFloatAsState
实现按钮缩放动画。DisposableEffect
确保振动取消。- 兼容 API 26+ 和旧版振动方法。
示例:结合音效
结合 SoundPool
(前文介绍)实现音效和振动:
SoundPool soundPool = new SoundPool.Builder().setMaxStreams(1).build();
int soundId = soundPool.load(this, R.raw.click_sound, 1);
vibrateButton.setOnClickListener(v -> {
soundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f);
vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
scaleAnimator.start();
});
If you need more advanced features (e.g., custom waveform vibrations, integration with AudioManager
for volume-sensitive vibrations, or Canvas-based vibration visualizations) or want to explore other parts of the Android animation collection (e.g., transition animations, Lottie), please let me know!