Android 自定义蓝牙扫描动画:多波浪扩散效果
这是一个用于 Android 的自定义 View,模拟蓝牙扫描时的多波浪扩散动画效果。每个波浪的半径逐渐增大,透明度逐渐降低,形成连续的波纹扩散效果。通过调整动画的延迟时间和时长,确保波浪之间的间隙较小,动画流畅且美观。
主要特性:
多波浪扩散:
支持多个圆圈(波浪)依次扩散,形成连续的波纹效果。
每个圆圈的半径逐渐增大,透明度逐渐降低。
间隙较小:
通过调整动画的延迟时间和动画时长,确保波浪之间的间隙较小。
自定义View:
使用 Canvas 和 Paint 实现自定义绘制。
使用 ValueAnimator 实现平滑的动画效果。
适用场景:
蓝牙扫描界面。
雷达扫描效果。
其他需要波纹扩散动画的场景。
使用方法:
将 BluetoothScanView 添加到布局文件中。
在 Activity 中调用 startScan() 启动动画,调用 stopScan() 停止动画。
实现步骤
1. 自定义View
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
public class BluetoothScanView extends View {
private Paint scanPaint; // 画笔
private List<Circle> circles; // 存储圆圈的列表
private List<ValueAnimator> animators; // 存储动画的列表
public BluetoothScanView(Context context) {
super(context);
init();
}
public BluetoothScanView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BluetoothScanView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 初始化画笔
scanPaint = new Paint();
scanPaint.setColor(Color.BLUE); // 设置颜色
scanPaint.setStyle(Paint.Style.STROKE); // 设置样式为描边
scanPaint.setStrokeWidth(5); // 设置描边宽度
scanPaint.setAntiAlias(true); // 抗锯齿
// 初始化圆圈和动画列表
circles = new ArrayList<>();
animators = new ArrayList<>();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int centerX = getWidth() / 2; // 中心点X坐标
int centerY = getHeight() / 2; // 中心点Y坐标
// 绘制所有圆圈
for (Circle circle : circles) {
scanPaint.setAlpha(circle.alpha); // 设置透明度
canvas.drawCircle(centerX, centerY, circle.radius, scanPaint);
}
}
public void startScan() {
if (!animators.isEmpty()) {
return; // 如果动画已经在运行,直接返回
}
// 初始化3个圆圈
circles.clear();
circles.add(new Circle(0, 255)); // 第一个圆圈
circles.add(new Circle(0, 255)); // 第二个圆圈
circles.add(new Circle(0, 255)); // 第三个圆圈
// 为每个圆圈创建独立的动画
for (int i = 0; i < circles.size(); i++) {
final Circle circle = circles.get(i);
ValueAnimator animator = ValueAnimator.ofFloat(0, 1); // 动画范围从0到1
animator.setDuration(1500); // 动画时长1.5秒
animator.setStartDelay(i * 500); // 每个圆圈延迟0.5秒启动
animator.setRepeatCount(ValueAnimator.INFINITE); // 无限循环
animator.setRepeatMode(ValueAnimator.RESTART); // 重新开始
animator.addUpdateListener(animation -> {
float progress = (float) animation.getAnimatedValue(); // 获取当前进度
circle.radius = (int) (progress * getWidth() / 2); // 更新半径
circle.alpha = (int) (255 * (1 - progress)); // 更新透明度
invalidate(); // 触发重绘
});
animators.add(animator); // 添加到动画列表
animator.start(); // 启动动画
}
}
public void stopScan() {
// 停止所有动画
for (ValueAnimator animator : animators) {
animator.cancel();
}
animators.clear();
circles.clear();
invalidate(); // 刷新界面
}
// 圆圈类,用于存储半径和透明度
private static class Circle {
int radius; // 半径
int alpha; // 透明度
Circle(int radius, int alpha) {
this.radius = radius;
this.alpha = alpha;
}
}
}
2. Activity
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private BluetoothScanView bluetoothScanView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化自定义View
bluetoothScanView = findViewById(R.id.bluetoothScanView);
// 确保View尺寸已确定后启动动画
bluetoothScanView.post(() -> {
bluetoothScanView.startScan(); // 启动扫描动画
});
}
@Override
protected void onDestroy() {
super.onDestroy();
bluetoothScanView.stopScan(); // 停止扫描动画
}
}
3. 布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<!-- 自定义蓝牙扫描View -->
<com.example.BluetoothScanView
android:id="@+id/bluetoothScanView"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerInParent="true" />
</RelativeLayout>
运行效果
波浪扩散:
页面加载后,第一个圆圈开始扩散,随后第二个、第三个圆圈依次开始。
每个圆圈的半径逐渐增大,透明度逐渐降低。
间隙较小:
每个波浪之间的启动间隔为 500 毫秒,动画时长为 1500 毫秒,波浪之间的间隙较小。
连续波纹效果:
当一个圆圈的动画结束时,下一个圆圈的动画立即开始,形成连续的波纹效果。
动画循环:
动画无限循环,波纹效果持续不断。