安卓应用开发学习:手机摇一摇功能应用尝试--摇骰子和摇红包
一、引言
前几天,我发布的日志《安卓应用开发学习:查看手机传感器信息》记录了如何查看手机传感器的信息,通过上述的方法,可以看到我的OPPO手机支持19种传感器。本篇日志就记录一下常见的加速度传感器的典型应用——“摇一摇”功能。本应用通过加速度传感器来实现摇骰子或摇红包。最终效果如下:
摇骰子

摇红包

游戏结束

二、功能实现
加速传感器是最常见的传感器之一,有很多应用的摇手机功能就是用到了这个传感器。本次通过学习相关资料,在我的手机上实现了摇骰子和摇红包两个小应用,并且在摇动手机的过程中手机还会振动。大体的实现方法如下:
1.实现振动功能
1.1先要在AndroidManifest.xml文件中添加如下权限。
<uses-permission android:name="android.permission.VIBRATE" />
1.2 在你创建的Activity中申明一个Vibrator对象。
private Vibrator mVibrator;
1.3在需要实现手机振动功能的代码块中,执行vibrate()方法。
mVibrator.vibrate(300); // 系统检测到摇一摇事件后,震动手机提示用户
该方法参数有两种形式:
一是形如 mVibrator.vibrate(300) 的单参数,表示让手机持续振动指定的毫秒数;
二是形如 mVibrator.vibrate({500, 200, 500}, -1) 的双参数,表示先振动500毫秒,然后停止200毫秒,再振动500毫秒。第二个参数为-1表示无循环,为正数,表示循环次数。
2.摇一摇功能的实现
2.1在你创建的Activity中申明一个SensorManager对象。
private SensorManager mSensorMgr;
2.2重写活动页面的onResume方法,在该方法中注册传感器监听事件,并指定待监听的传感器类型为加速度传感器。
@Overrideprotected void onResume() {super.onResume();// 给加速度传感器注册传感监听器mSensorMgr.registerListener(this,mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);}
2.3重写活动页面的onPause方法,在该方法中注销监听器。
@Overrideprotected void onPause() {super.onPause();mSensorMgr.unregisterListener(this); // 注销当前活动的传感监听器}
2.3编写一个传感器事件监听器,该监听器继承自SensorEventListener。
在活动页面名称后面添加“implements SensorEventListener”(如下),
public class ShakeActivity extends AppCompatActivity implements SensorEventListener {...}
然后按Alt + Enter,Android Studio 自动添加onSensorChanged方法和onAccuracyChanged方法。
onSensorChanged方法在感应信息变化时触发,业务逻辑就写在这里。在本应用中添加的代码是检测手机晃动的幅度是否大于阀值,一旦大于阀值,就让手机振动500毫秒。
onAccuracyChanged方法在精度改变时触发,一般无需处理。
@Overridepublic void onSensorChanged(SensorEvent event) {if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { // 加速度变更事件// values[0]:X轴,values[1]:Y轴,values[2]:Z轴float[] values = event.values;if ((Math.abs(values[0]) > 15 || Math.abs(values[1]) > 15|| Math.abs(values[2]) > 15)) {mVibrator.vibrate(300); // 系统检测到摇一摇事件后,震动手机提示用户}}}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {// 当传感器精度改变时回调该方法,一般无需处理}
3.游戏模式的选择
3.1本应用支持2种游戏模式,因此在界面设计上用到了RadioGroup和RadioButton。
<TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:gravity="center_horizontal"android:text="游戏模式:" /><RadioGroupandroid:id="@+id/rg_mode"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><RadioButtonandroid:id="@+id/rb_dice"android:layout_width="80dp"android:layout_height="20dp"android:checked="true"android:text="摇骰子" /><RadioButtonandroid:id="@+id/rb_welfare"android:layout_width="80dp"android:layout_height="20dp"android:text="摇红包" /></RadioGroup>
3.2在活动页面声明如下变量:
private int mMode; // 游戏模式private final int MODE_DICE = 0; // 游戏模式1:摇骰子private final int MODE_WELFARE = 1; // 游戏模式2:摇红包private boolean mState = false; // 游戏状态private int diceCount; // 统计摇骰子次数private int welfareNumber; // 统计红包个数private final int[] welfareArr = {1, 5, 10} ; // 红包,可根据情况调整
3.2在活动页面中编写单选按钮组事件监听器,该监听器继承自RadioGroup.OnCheckedChangeListener。通过单选按钮的改变触发响应事件,在onCheckedChanged方法中编写游戏模式变更的逻辑代码。
@Overridepublic void onCheckedChanged(RadioGroup group, int checkedId) {// 根据单选按钮结果,设置游戏模式if (checkedId == R.id.rb_dice) {mMode = MODE_DICE; // 摇骰子模式} else if (checkedId == R.id.rb_welfare) {mMode = MODE_WELFARE; // 摇红包模式}}
4.游戏的执行逻辑
4.1游戏模式选择。默认是选中了摇一摇模式。
4.2点“开始”按钮。当前是非游戏状态;将变量diceCount和welfareNumber都设为0;将游戏状态变量mState设为Ture;页面中显示“请开始摇手机”;将按钮文本改为停止。游戏过程中游戏模式可随时切换,不会终止游戏。
4.3只有mState为Ture时摇动手机,才会进行检测。当检测到有效摇动时,手机会振动300毫秒,并执行startGame方法(延时300毫秒后执行,避免此方法实际执行次数与振动次数有大的差异。
4.4startGame方法中首先检查游戏模式。
如果是摇骰子模式,则产生三个1-6的随机数(设定为三个骰子)。将本次的结果显示在页面上,并将摇骰子次数统计变量diceCount加1。
如果是摇红包模式,则产生一个1-10的随机数,将该随机数与数组welfareArr中的元素进行对比,如果该随机数在数组中,则在页面中显示中奖信息。获得红包统计变量welfareNumber加1。
5.游戏状态下点“停止”按钮,结束游戏。将按钮文本改为开始;页面中显示摇骰子次数和获得红包个数。
三、代码展示
最终的代码如下:
1. 界面设计文件 activity_shake.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".ShakeActivity"><TextViewandroid:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:text="摇一摇"android:textSize="28sp"android:textStyle="bold"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><LinearLayoutandroid:id="@+id/ll_mode"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="10dp"android:layout_marginTop="30dp"android:layout_marginEnd="10dp"android:orientation="horizontal"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/tv_title"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:gravity="center_horizontal"android:text="游戏模式:" /><RadioGroupandroid:id="@+id/rg_mode"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><RadioButtonandroid:id="@+id/rb_dice"android:layout_width="80dp"android:layout_height="20dp"android:checked="true"android:text="摇骰子" /><RadioButtonandroid:id="@+id/rb_welfare"android:layout_width="80dp"android:layout_height="20dp"android:text="摇红包" /></RadioGroup></LinearLayout><Buttonandroid:id="@+id/btn_start"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="30dp"android:text="开始"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/ll_mode" /><TextViewandroid:id="@+id/tv_shake"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="30dp"android:textSize="17sp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/btn_start" /></androidx.constraintlayout.widget.ConstraintLayout>
2.逻辑代码 ShakeActivity.java
import androidx.appcompat.app.AppCompatActivity;import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Vibrator;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.RadioGroup;
import android.widget.TextView;import java.util.Arrays;
import java.util.Locale;
import java.util.Random;public class ShakeActivity extends AppCompatActivity implements SensorEventListener,RadioGroup.OnCheckedChangeListener, View.OnClickListener {private final static String TAG = "ShakeActivity";private TextView tv_shake; // 声明一个文本视图对象private SensorManager mSensorMgr; // 声明一个传感管理器对象private Vibrator mVibrator; // 声明一个震动器对象private Button btn_start; // 开始按钮private int mMode; // 游戏模式private final int MODE_DICE = 0; // 游戏模式1:摇骰子private final int MODE_WELFARE = 1; // 游戏模式2:摇红包private boolean mState = false; // 游戏状态private int diceCount; // 统计摇骰子次数private int welfareNumber; // 统计红包个数private final int[] welfareArr = {1, 5, 10} ; // 红包 , 20, 50, 100, 500, 1000@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_shake);// 游戏模式选择按钮组RadioGroup rg_mode = findViewById(R.id.rg_mode);rg_mode.setOnCheckedChangeListener(this);tv_shake = findViewById(R.id.tv_shake);btn_start = findViewById(R.id.btn_start);btn_start.setOnClickListener(this); // 开始按钮点击监听// 从系统服务中获取传感管理器对象mSensorMgr = (SensorManager) getSystemService(Context.SENSOR_SERVICE);// 从系统服务中获取震动器对象mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);mMode = MODE_DICE; // 单选按钮初始状态选中的遥骰子}@Overrideprotected void onPause() {super.onPause();mSensorMgr.unregisterListener(this); // 注销当前活动的传感监听器}@Overrideprotected void onResume() {super.onResume();// 给加速度传感器注册传感监听器mSensorMgr.registerListener(this,mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);}@Overridepublic void onSensorChanged(SensorEvent event) {if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { // 加速度变更事件// values[0]:X轴,values[1]:Y轴,values[2]:Z轴float[] values = event.values;if ((Math.abs(values[0]) > 30 || Math.abs(values[1]) > 30|| Math.abs(values[2]) > 30)) {if (mState) {mVibrator.vibrate(300); // 系统检测到摇一摇事件后,震动手机提示用户new Handler().postDelayed(() -> {// 延时后要执行的代码startGame();}, 300); // 延迟时间毫秒}}}}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {// 当传感器精度改变时回调该方法,一般无需处理}@Overridepublic void onCheckedChanged(RadioGroup group, int checkedId) {// 根据单选按钮结果,设置游戏模式if (checkedId == R.id.rb_dice) {mMode = MODE_DICE; // 摇骰子模式} else if (checkedId == R.id.rb_welfare) {mMode = MODE_WELFARE; // 摇红包模式}}@Overridepublic void onClick(View v) {if (v.getId() == R.id.btn_start) {if (mState) { // 点击时处于游戏状态btn_start.setText("开始");String info = String.format(Locale.CHINESE, "%s%d次,%s%d个。","本次摇骰子", diceCount, "获得红包", welfareNumber);tv_shake.setText(info);} else { // 点击时处于非游戏状态diceCount = 0;welfareNumber = 0;tv_shake.setText("请开始摇手机");btn_start.setText("停止");}mState = !mState;}}private void startGame() {String info = ""; // 检查游戏模式if (mMode == MODE_DICE) { // 摇骰子// 设定有3个骰子,产生3个1-6的随机数Random random = new Random();int dice1 = random.nextInt(6) + 1;int dice2 = random.nextInt(6) + 1;int dice3 = random.nextInt(6) + 1;info = String.format(Locale.CHINESE,"%s%d,%d,%d。", "您摇出的骰子点数是:",dice1, dice2, dice3);diceCount +=1;} else if (mMode == MODE_WELFARE) { // 摇红包int lottery;info = "很遗憾,您没有中奖";// 生成一个0-10的随机数int randomNumber = (int)(Math.random() * 11); // 1001Log.d(TAG, "随机数为:" + randomNumber);// 检查该随机数是否在红包列表中boolean isInArray = Arrays.stream(welfareArr).anyMatch(n -> n == randomNumber);if (isInArray) {lottery = randomNumber;info = String.format(Locale.CHINESE,"%s%d%s", "恭喜您中了", lottery,"元!");welfareNumber +=1;}}tv_shake.setText(info);}
}
相关文章:
安卓应用开发学习:手机摇一摇功能应用尝试--摇骰子和摇红包
一、引言 前几天,我发布的日志《安卓应用开发学习:查看手机传感器信息》记录了如何查看手机传感器的信息,通过上述的方法,可以看到我的OPPO手机支持19种传感器。本篇日志就记录一下常见的加速度传感器的典型应用——“摇一摇”功…...
HTML中的<fieldset>标签元素框的使用
HTML 提供的 <fieldset> 标签用于在表单中分组相关元素。 <fieldset> 标签会在相关元素周围绘制一个框。 <legend> 标签为 fieldset 元素定义标题。 语法如下: <fieldset><legend>标题</legend><!-- 元素内容... -->…...
Linux驱动入门实验班——SR501红外模块驱动(附百问网视频链接)
目录 一、工作方式 二、接口图 三、编写思路 1.构造file_operations结构体 2.实现read函数 3.编写入口函数 4.编写中断处理函数 5.编写出口函数 6.声明出入口函数以及协议 四、源码 五、课程链接 一、工作方式 SR501人体红外感应模块有两种工作模式: …...
windows C++- Com技术简介(上)
在介绍C和winrt与COM组件技术的关系之前,有必要介绍一下com组件技术,这项技术比较古老,但是它一直作为windows的基石存在。COM 是一类独立于平台且面向对象的分布式系统,用于创建可交互的二进制软件组件。 COM 技术是 Microsoft O…...
Jenkins持续集成工具学习
一、从装修厨房看项目开发效率优化 二、持续集成工具 三、JavaEE项目部署方式对比 四、JenkinsSVN持续集成环境搭建 五、JenkinsGitHub持续集成环境搭建...
Redis:查询是否包含某个字符/字符串之三
上一篇:Redis:查询是否包含某个字符/字符串之二-CSDN博客 摘要: 遍历key,在跟进value的类型遍历value是否包含指定字符串 search_strings ,这里使用redis-py库,默认只能处理utf-8编码,如果存在…...
【Redis】数据类型详解及其应用场景
目录 Redis 常⻅数据类型预备知识基本全局命令小结 数据结构和内部编码单线程架构引出单线程模型为什么单线程还能这么快 Redis 常⻅数据类型 Redis 提供了 5 种数据结构,理解每种数据结构的特点对于 Redis 开发运维⾮常重要,同时掌握每种数据结构的常⻅…...
PARA-Drive:设计并行模型实现端到端自动驾驶
论文链接 https://openaccess.thecvf.com/content/CVPR2024/papers/Weng_PARA-Drive_Parallelized_Architecture_for_Real-time_Autonomous_Driving_CVPR_2024_paper.pdfhttps://openaccess.thecvf.com/content/CVPR2024/papers/Weng_PARA-Drive_Parallelized_Architecture_fo…...
vs2022 x64 C/C++和汇编混编 遇到的坑
vs2022 x64 C/C和汇编混编 遇到的坑 遇到的问题二、问题复现1.出错代码2.问题分析2.1 堆栈对齐问题 3.解决方案 总结奇数和偶数个寄存器的影响为什么 sub rsp, 8 对奇数个寄存器有用?结论 遇到的问题 0x00007FFFFAE24A29 (msvcp140.dll)处(位于 TestCompileConsole…...
PHP概述、环境搭建与基本语法讲解
目录 【学习目标、重难点知识】 什么是网站? 1. PHP 介绍 1.1. PHP 概述 1.1.1. PHP 是什么? 1.1.2. PHP 都能做什么? 1.2. PHP 环境搭建 1.2.1. PhpStudy 2. PHP 基本语法 2.1. PHP 语法入门 2.1.1. 第一个 PHP 程序 2.1.2. PHP …...
实现信创Linux麦克风摄像头录制(源码,银河麒麟、统信UOS)
随着信创国产化浪潮的来临,在国产操作系统上的应用开发的需求越来越多,其中一个就是需要在银河麒麟或统信UOS上实现录制摄像头视频和麦克风声音,将它们录制成一个mp4文件。那么这个要如何实现了? 一. 技术方案 要完成这些功能&a…...
深度学习9--目标检测
1.概念介绍 目标检测不仅可以检测数字,而且可以检测动物的种类、汽车的种类等。例如,自动驾驶车辆需要自动识别前方物体是车辆还是行人,需要自动识别道路两 旁的指示牌和前方的红绿灯颜色。对于自动检测的算法,有两个要求…...
第131天:内网安全-横向移动Kerberos 攻击SPN扫描WinRMWinRSRDP
案例一:域横向移动-RDP-明文&NTLM RDP利用的三种方式 1.直接在当前被控主机上进行远程连接 2.建立节点进行连接 3.端口转发,(访问当前主机的2222端口等于访问目标的3389) 第一种方式(动静太大) 直接利用被控主机进行远程连接…...
微信小程序的四种弹窗使用
在做小程序的过程中,弹窗也算是非常实用的功能了,这几天写的几个功能就用到了弹窗,也可能是初学者的问题,比较菜,想找一个可以带图片的自定义的弹窗,,这里简单介绍一下官方封装好的四个弹窗…...
我的第一个CUDA程序
MatAdd算法 实现两个矩阵对应元素相加 #include <stdio.h> #include <stdlib.h>// 矩阵加法函数 void MatAdd(int height, int width) {// 在主机内存中为 A、B 和 C 分配内存float* A (float*)malloc(height * width * sizeof(float));float* B (float*)malloc…...
workerman下的webman路由浏览器跨域的一种问题
软件版本 "php": ">7.2", "workerman/webman-framework": "^1.5.0",问题情景 使用“分组路由”做API接口前后端分离跨域,在接口测试工具调试是能正常获取数据的;但在网页浏览器上调试就遇到了CORS、404的错…...
Windows11 -MASKRCNN-部署测试
文章目录 Detectron2环境配置搭建python 环境安装Cuda \CUDNN 、PyTorch、 torchvision、cudatoolkit1、Cuda \CUDNN2、 PyTorch、 torchvision、cudatoolkit进入python测试:错误信息 3、detectron2环境在安装detecteron中,遇到报错:编译的时…...
函数(子程序)的常见、易混淆概念详解【对初学者有帮助】
C语⾔中的函数也被称做子程序,意思就是⼀个完成某项特定的任务的⼀小段代码。 C语⾔标准中提供了许多库函数,点击下面的链接可以查看c语言的库函数和头文件。 C/C官⽅的链接:https://zh.cppreference.com/w/c/header 目录 一、函数头与函…...
TiDB-从0到1-DM工具
TiDB从0到1系列 TiDB-从0到1-体系结构TiDB-从0到1-分布式存储TiDB-从0到1-分布式事务TiDB-从0到1-MVCCTiDB-从0到1-部署篇TiDB-从0到1-配置篇TiDB-从0到1-集群扩缩容TiDB-从0到1-数据导出导入TiDB-从0到1-BR工具 一、DM原理 支持全量抽取数据\检测新的数据变化同步到下游实例…...
AppScan——Web 应用安全扫描的得力工具
一、引言 在当今数字化时代,Web 应用成为企业业务的重要支撑,但同时也面临着各种安全威胁。AppScan 作为一款专业的 Web 应用安全扫描工具,为保障 Web 应用的安全性提供了有力的支持。本文将对 AppScan 进行详细介绍,包括其功能、…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
