实现方向盘
将方向盘控件的实现转换为使用 LiveData
来管理和观察指针角度变化,能够更好地与 MVVM 架构相结合。通过 LiveData
,我们可以方便地将角度的变化传递给观察者(例如 UI 组件或 ViewModel),从而实现数据驱动的 UI 更新。
实现思路
-
LiveData 定义:在
SteeringWheelView
中定义一个MutableLiveData<Float>
用来存储和管理当前指针角度。 -
角度更新与通知:每当指针角度变化时,通过
LiveData
更新值,从而通知所有观察者。 -
观察 LiveData:在需要观察指针角度变化的地方(如 Activity 或 Fragment),观察
LiveData
并根据新角度更新 UI。
说明与优势
-
数据驱动的 UI 更新:通过
LiveData
,可以确保 UI 始终与方向盘的状态同步,方便在多处使用和响应角度变化。 -
与 MVVM 架构集成:
LiveData
使得控件与ViewModel
进行数据绑定更为方便,UI 的更新逻辑可以更好地与业务逻辑分离。 -
自动生命周期管理:
LiveData
自动管理观察者的生命周期,当Activity
或Fragment
销毁时,观察者会自动取消订阅,避免内存泄漏。
通过这种方式,方向盘的角度变化不仅能够更新到自身的 UI 组件,也能传递到外部逻辑或显示控件,提供了更灵活和强大的功能扩展。
2、松手后,指针自动回旋至初始状态
要实现一个自定义的游戏方向盘视图,当用户松手后,指针会自动回到初始状态(即默认角度),你可以使用 Animator 来实现平滑过渡。
• 平滑回旋:当用户松开方向盘时,ValueAnimator 会平滑地将指针的角度从当前角度恢复到初始角度(例如 0 度),持续时间为 500 毫秒。
• 自动回归:这个动画能够给用户一个更加自然的体验,使得方向盘在松手后自动回到原始位置。
这样,在用户松手后,方向盘指针会自动回旋至初始状态,实现了更为逼真的操作感。
3、初始状态就显示指针
要让方向盘在初始状态下显示指针并指向某个特定的角度(例如 0 度),我们需要在 SteeringWheelView 初始化时设置初始角度,并在 onDraw 方法中绘制指针。
- 初始角度:在 init() 方法中,将 angleLiveData 的初始值设置为 initialAngle,即 0 度。这样,在视图绘制时,指针就会默认指向 0 度。
- 指针绘制:在 onDraw() 方法中,如果 angleLiveData 中有值(这里初始为 0 度),就会根据当前的角度绘制红色的指针线。
MainActivity 及其他部分
MainActivity 和 activity_main.xml 的代码不需要修改,保持与之前的一致。
通过这些修改,方向盘的指针在初始状态下就会显示,并指向默认的 0 度。当用户拖动方向盘并松手后,指针会平滑地返回到初始角度,实现了一个完整的游戏方向盘功能。
4、代码实现
1. 修改 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);}@Overrideprotected 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); // 恢复颜色}}@Overridepublic 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); // 动画持续时间,500msanimator.addUpdateListener(animation -> {float animatedValue = (float) animation.getAnimatedValue();updateSteeringWheelAngle(animatedValue);});animator.start();}}public LiveData<Float> getAngleLiveData() {return angleLiveData;}
}
2. 在 Activity 或 Fragment 中观察 LiveData
在需要显示方向盘状态的 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;@Overrideprotected 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>() {@Overridepublic 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的相关问题