Android实现自定义方向盘-5livedata实现

实现方向盘

将方向盘控件的实现转换为使用 LiveData 来管理和观察指针角度变化,能够更好地与 MVVM 架构相结合。通过 LiveData,我们可以方便地将角度的变化传递给观察者(例如 UI 组件或 ViewModel),从而实现数据驱动的 UI 更新。

实现思路

  1. LiveData 定义:在 SteeringWheelView 中定义一个 MutableLiveData 用来存储和管理当前指针角度。

  2. 角度更新与通知:每当指针角度变化时,通过 LiveData 更新值,从而通知所有观察者。

  3. 观察 LiveData:在需要观察指针角度变化的地方(如 Activity 或 Fragment),观察 LiveData 并根据新角度更新 UI。

说明与优势

  • 数据驱动的 UI 更新:通过 LiveData,可以确保 UI 始终与方向盘的状态同步,方便在多处使用和响应角度变化。

  • 与 MVVM 架构集成LiveData 使得控件与 ViewModel 进行数据绑定更为方便,UI 的更新逻辑可以更好地与业务逻辑分离。

  • 自动生命周期管理LiveData 自动管理观察者的生命周期,当 ActivityFragment 销毁时,观察者会自动取消订阅,避免内存泄漏。

通过这种方式,方向盘的角度变化不仅能够更新到自身的 UI 组件,也能传递到外部逻辑或显示控件,提供了更灵活和强大的功能扩展。

2、松手后,指针自动回旋至初始状态

要实现一个自定义的游戏方向盘视图,当用户松手后,指针会自动回到初始状态(即默认角度),你可以使用 Animator 来实现平滑过渡。
• 平滑回旋:当用户松开方向盘时,ValueAnimator 会平滑地将指针的角度从当前角度恢复到初始角度(例如 0 度),持续时间为 500 毫秒。
• 自动回归:这个动画能够给用户一个更加自然的体验,使得方向盘在松手后自动回到原始位置。

这样,在用户松手后,方向盘指针会自动回旋至初始状态,实现了更为逼真的操作感。

3、初始状态就显示指针

要让方向盘在初始状态下显示指针并指向某个特定的角度(例如 0 度),我们需要在 SteeringWheelView 初始化时设置初始角度,并在 onDraw 方法中绘制指针。

  1. 初始角度:在 init() 方法中,将 angleLiveData 的初始值设置为 initialAngle,即 0 度。这样,在视图绘制时,指针就会默认指向 0 度。
  2. 指针绘制:在 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);
    }

    @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;
    }
}
2. 在 Activity 或 Fragment 中观察 LiveData

在需要显示方向盘状态的 ActivityFragment 中,我们可以通过 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的相关问题

你可能感兴趣的:(android,android)