将方向盘控件的实现转换为使用 LiveData
来管理和观察指针角度变化,能够更好地与 MVVM 架构相结合。通过 LiveData
,我们可以方便地将角度的变化传递给观察者(例如 UI 组件或 ViewModel),从而实现数据驱动的 UI 更新。
LiveData 定义:在 SteeringWheelView
中定义一个 MutableLiveData
用来存储和管理当前指针角度。
角度更新与通知:每当指针角度变化时,通过 LiveData
更新值,从而通知所有观察者。
观察 LiveData:在需要观察指针角度变化的地方(如 Activity 或 Fragment),观察 LiveData
并根据新角度更新 UI。
数据驱动的 UI 更新:通过 LiveData
,可以确保 UI 始终与方向盘的状态同步,方便在多处使用和响应角度变化。
与 MVVM 架构集成:LiveData
使得控件与 ViewModel
进行数据绑定更为方便,UI 的更新逻辑可以更好地与业务逻辑分离。
自动生命周期管理:LiveData
自动管理观察者的生命周期,当 Activity
或 Fragment
销毁时,观察者会自动取消订阅,避免内存泄漏。
通过这种方式,方向盘的角度变化不仅能够更新到自身的 UI 组件,也能传递到外部逻辑或显示控件,提供了更灵活和强大的功能扩展。
要实现一个自定义的游戏方向盘视图,当用户松手后,指针会自动回到初始状态(即默认角度),你可以使用 Animator 来实现平滑过渡。
• 平滑回旋:当用户松开方向盘时,ValueAnimator 会平滑地将指针的角度从当前角度恢复到初始角度(例如 0 度),持续时间为 500 毫秒。
• 自动回归:这个动画能够给用户一个更加自然的体验,使得方向盘在松手后自动回到原始位置。
这样,在用户松手后,方向盘指针会自动回旋至初始状态,实现了更为逼真的操作感。
要让方向盘在初始状态下显示指针并指向某个特定的角度(例如 0 度),我们需要在 SteeringWheelView 初始化时设置初始角度,并在 onDraw 方法中绘制指针。
MainActivity 及其他部分
MainActivity 和 activity_main.xml 的代码不需要修改,保持与之前的一致。
通过这些修改,方向盘的指针在初始状态下就会显示,并指向默认的 0 度。当用户拖动方向盘并松手后,指针会平滑地返回到初始角度,实现了一个完整的游戏方向盘功能。
SteeringWheelView
类package com.example.gamecontrol;
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.MotionEvent;
import android.view.View;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
public class SteeringWheelView extends View {
private Paint paint;
private float centerX, centerY, radius;
private MutableLiveData<Float> angleLiveData = new MutableLiveData<>();
private float initialAngle = 0f;
private static final float MIN_DRAG_DISTANCE = 20f; // 最小拖拽距离阈值
private float startX, startY;
private boolean isDragging = false;
private float initialAngleOffset = 0f;
public SteeringWheelView(Context context) {
super(context);
init();
}
public SteeringWheelView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SteeringWheelView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paint = new Paint();
paint.setColor(Color.GRAY);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);
// 设置初始角度为 0 度
angleLiveData.setValue(initialAngle);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
centerX = getWidth() / 2;
centerY = getHeight() / 2;
radius = Math.min(centerX, centerY) - 20;
canvas.drawCircle(centerX, centerY, radius, paint);
// 画一个指示方向的线
Float angle = angleLiveData.getValue();
if (angle != null) {
float indicatorX = (float) (centerX + radius * Math.cos(Math.toRadians(angle)));
float indicatorY = (float) (centerY + radius * Math.sin(Math.toRadians(angle)));
paint.setColor(Color.RED);
canvas.drawLine(centerX, centerY, indicatorX, indicatorY, paint);
paint.setColor(Color.GRAY); // 恢复颜色
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录初始点击位置和状态
startX = x;
startY = y;
isDragging = false;
// 计算触摸点与当前指针的角度偏移量
Float currentAngle = angleLiveData.getValue();
if (currentAngle != null) {
initialAngleOffset = calculateAngle(x, y) - currentAngle;
}
break;
case MotionEvent.ACTION_MOVE:
// 计算拖拽距离
float deltaX = x - startX;
float deltaY = y - startY;
float distance = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance > MIN_DRAG_DISTANCE) {
isDragging = true;
// 计算当前触摸点的角度,并应用初始偏移量
float angle = calculateAngle(x, y) - initialAngleOffset;
// 更新方向盘角度
updateSteeringWheelAngle(angle);
}
break;
case MotionEvent.ACTION_UP:
if (isDragging) {
// 当松手时,启动动画将指针恢复到初始位置
resetSteeringWheelAngle();
}
isDragging = false;
break;
}
return true;
}
private float calculateAngle(float x, float y) {
float angle = (float) Math.toDegrees(Math.atan2(centerY - y, centerX - x));
if (angle < 0) angle += 360;
return angle;
}
public void updateSteeringWheelAngle(float angle) {
angle = angle % 360;
if (angle < 0) angle += 360;
angleLiveData.setValue(angle);
invalidate();
}
private void resetSteeringWheelAngle() {
Float currentAngle = angleLiveData.getValue();
if (currentAngle != null) {
ValueAnimator animator = ValueAnimator.ofFloat(currentAngle, initialAngle);
animator.setDuration(500); // 动画持续时间,500ms
animator.addUpdateListener(animation -> {
float animatedValue = (float) animation.getAnimatedValue();
updateSteeringWheelAngle(animatedValue);
});
animator.start();
}
}
public LiveData<Float> getAngleLiveData() {
return angleLiveData;
}
}
在需要显示方向盘状态的 Activity
或 Fragment
中,我们可以通过 LiveData
来观察角度变化,并根据变化更新 UI。
package com.example.gamecontrol;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
public class MainActivity extends AppCompatActivity {
private SteeringWheelView steeringWheelView;
private TextView angleTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
steeringWheelView = findViewById(R.id.steeringWheelView);
angleTextView = findViewById(R.id.angleTextView);
// 观察方向盘角度变化
steeringWheelView.getAngleLiveData().observe(this, new Observer<Float>() {
@Override
public void onChanged(Float angle) {
// 更新角度显示
angleTextView.setText("Angle: " + angle + "°");
}
});
}
}
相关文章:
链接: Android实现自定义方向盘
链接: Android实现自定义方向盘-2添加陀螺仪
链接: Android实现自定义方向盘-3添加平滑处理
链接: Android实现自定义方向盘-4解决触摸时指针跳跃的问题
链接: Android实现自定义方向盘-5livedata实现
链接: Android实现自定义方向盘-6mvvm传递数据
链接: Android实现自定义方向盘-7livedata,viewmodel相关问题
链接: Android实现自定义方向盘-8自定义view的相关问题