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

实现方向盘

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

实现思路

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

  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);}@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

在需要显示方向盘状态的 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;@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的相关问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/878730.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【mysql】mysql修改sql_mode之后无法启动

现象&#xff1a;修改后mysql无法启动&#xff0c;不报错 原因&#xff1a;MySQL在8以后sql_mode已经取消了NO_AUTO_CREATE_USER这个关键字。去掉这个关键字后&#xff0c;启动就可以了 修改前&#xff1a; sql_modeSTRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR…

C++学习笔记(7)

181、string 容器 string 是字符容器&#xff0c;内部维护了一个动态的字符数组。 与普通的字符数组相比&#xff0c;string 容器有三个优点&#xff1a;1&#xff09;使用的时候&#xff0c;不必考虑内存分配和释放的问题&#xff1b; 2&#xff09;动态管理内存&#xff08;可…

docke实战扩展二(docker build -t nginx:latest . 实战详解)

docker build -t nginx:latest . 是 Docker 中用于构建镜像的命令。下面我们来详细解释这个命令,并通过一个具体的生产案例来演示其实际应用。 命令解释 docker build:这是 Docker CLI 中用于构建镜像的命令。-t nginx:latest:这是为构建的镜像指定标签(tag)。-t 是 --ta…

英文缩写大全(IT 领域和电子行业制造领域)

英文缩写大全&#xff08;IT 领域和电子行业制造领域&#xff09; 前言一、计算机通用二、WINDOWS三、LINUX四、编程语言1. 前端 / 设计2. JAVA / Android3. PHP4. Python 四、电子行业制造领域五、常识 前言 本文收集了各类英文缩写大全&#xff0c;方便查阅&#xff0c;主要…

使用 Nginx 部署前端 Vue.js 项目

引言 Vue.js 是一个流行的前端框架&#xff0c;用于构建用户界面。当涉及到生产环境的部署时&#xff0c;选择一个合适的 web 服务器是非常重要的。Nginx 是一个高性能的 HTTP 和反向代理服务器&#xff0c;非常适合用来部署前端应用程序。本文将指导你如何使用 Nginx 部署一个…

ACM个人模板总结

一切的开始 初始模板 // o2 o3 优化防止卡常 #pragma GCC optimize(2) #pragma GCC optimize(3) #include <bits/stdc.h> using namespace std; #define lowbit(x) (x&(-x)) #define endl "\n" #define LF(x) fixed<<setprecision(x)// c 保留小…

在线演示文稿应用PPTist本地化部署并实现无公网IP远程编辑PPT

文章目录 前言1. 本地安装PPTist2. PPTist 使用介绍3. 安装Cpolar内网穿透4. 配置公网地址5. 配置固定公网地址 前言 本文主要介绍如何在Windows系统环境本地部署开源在线演示文稿应用PPTist&#xff0c;并结合cpolar内网穿透工具实现随时随地远程访问与使用该项目。 PPTist …

C#编程语言及.NET 平台快速入门指南

Office Word 不显示 Citavi 插件&#xff0c;如何修复&#xff1f;_citavi安装后word无加载项-CSDN博客 https://blog.csdn.net/Viviane_2022/article/details/128946061?spm1001.2100.3001.7377&utm_mediumdistribute.pc_feed_blog_category.none-task-blog-classify_ta…

CSS选择器:一文带你区分CSS中的伪类和伪元素!

一、伪类选择器 1、什么是伪类选择器 伪类选择器&#xff0c;顾名思义&#xff0c;是一种特殊的选择器&#xff0c;它用来选择DOM元素在特定状态下的样式。这些特定状态并不是由文档结构决定的&#xff0c;而是由用户行为&#xff08;如点击、悬停&#xff09;或元素的状态&a…

Java SpringBoot构建传统文化网,三步实现信息展示,传承文化精髓

✍✍计算机毕业编程指导师** ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java…

大道至简,大厂官网基本都走简洁化设计路线。

「大道至简」是一种设计理念&#xff0c;强调设计应该追求简洁、直观、易用&#xff0c;而不是过多的修饰和繁琐的细节。 对于大厂的官网来说&#xff0c;简洁化设计路线的选择可能有以下几个原因&#xff1a; 1. 更好的用户体验&#xff1a; 简洁的设计可以让用户更容易地理…

磁盘调度管理中移臂调度和旋转调度

在磁盘调度管理中&#xff0c;移臂调度和旋转调度是两种不同的优化调度方法&#xff0c;用来提高磁盘读写效率。我们可以通过以下两种方式来理解它们&#xff1a; 1. 移臂调度&#xff08;Seek Scheduling&#xff09; 移臂调度是指磁盘驱动器的磁头在不同的柱面&#xff08;…

NTFS硬盘支持工具Paragon NTFS for Mac 15.4.44 中文破解版

Paragon NTFS for Mac 15.4.44 中文破解版是一个底层的文件系统驱动程序,专门开发用来弥合Windows和Mac OS X之间的不兼容性&#xff0c;通过在Mac OS X系统下提供对任何版本的NTFS文件系统完全的读写访问服务来弥合这种不兼容性。为您轻松解决Mac不能识别Windows NTFS文件难题…

华为鸿蒙系统和安卓的区别

华为鸿蒙系统和安卓系统在多个方面存在显著差异&#xff0c;以下是对两者区别的详细分析&#xff1a; 一、开发商与背景 鸿蒙系统&#xff1a;由中国的华为公司主导开发&#xff0c;旨在创造一个超级虚拟终端互联的世界&#xff0c;将人、设备、场景有机地联系在一起。自2012…

【深度学习】线性回归的从零开始实现与简洁实现

前言 我原本后面打算用李沐老师那本《动手学深度学习》继续“抄书”&#xff0c;他们团队也免费提供了电子版(https://zh-v2.d2l.ai/d2l-zh-pytorch.pdf)。但书里涉及到代码&#xff0c;一方面展示起来不太方便&#xff0c;另一方面我自己也有很多地方看不太懂。 这让我开始思…

Pepper佩盼尔wordpress模板

Pepper佩盼尔WordPress模板是一款专为追求简洁、现代和专业外观的网站设计者和开发者打造的高品质主题。它以简站为主题&#xff0c;强调“让建网站更简单”的理念&#xff0c;旨在为用户提供一个易于使用、功能丰富的平台来构建他们的在线业务或个人网站。 模板特点包括&…

手机玩黑神话悟空二周目 GameViewer远程助你手机畅玩黑神话悟空 解锁全成就全收集

用手机摸鱼完成黑神话悟空二周目全收集、成就全解锁&#xff0c;实现随时随地玩黑神话悟空&#xff0c;你可以用网易GameViewer远程。 这款远程控制软件专为游戏玩家打造&#xff0c;不管你是上班族&#xff0c;还是学生党&#xff0c;都可以用它在手机、平板上玩黑神话悟空&am…

Vue输入框模糊搜索的多种写法

&#xff08;1&#xff09;模板方案 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</…

音频如何低延时回声消除与降噪篇保姆级教程

目录 1 回声消除技术 1.1 回声产生的原因 1.2 传统回声消除方法 1.3 先进的回声消除算法 2 降噪技术 2.1 降噪技术的原理与方法 2.2 传统降噪方法 2.3 先进的降噪算法 3 低延时处理技术 3.1 低延时处理的重要性 3.2 低延时回声消除技术 3.3 低延时降噪技术 4 调试…

Netty系列-2 NioServerSocketChannel和NioSocketChannel介绍

背景 本文介绍Netty的通道组件NioServerSocketChannel和NioSocketChannel&#xff0c;从源码的角度介绍其实现原理。 1.NioServerSocketChannel Netty本质是对NIO的封装和增强&#xff0c;因此Netty框架中必然包含了对于ServerSocketChannel的构建、配置以及向选择器注册&am…