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_CLICKEFFECT_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)
  • 生命周期:在 onPauseonDestroy 取消振动和动画。
  • 用户体验:避免过长或频繁振动,防止打扰用户。
  • 调试:通过 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!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注