安卓应用开发学习:手机摇一摇功能应用尝试--摇骰子和摇红包

news2024/9/24 11:27:58

一、引言

前几天,我发布的日志《安卓应用开发学习:查看手机传感器信息》记录了如何查看手机传感器的信息,通过上述的方法,可以看到我的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方法,在该方法中注册传感器监听事件,并指定待监听的传感器类型为加速度传感器。

    @Override
    protected void onResume() {
        super.onResume();
        // 给加速度传感器注册传感监听器
        mSensorMgr.registerListener(this,
                mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_NORMAL);
    }

2.3重写活动页面的onPause方法,在该方法中注销监听器。

    @Override
    protected 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方法在精度改变时触发,一般无需处理。

@Override
    public 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); // 系统检测到摇一摇事件后,震动手机提示用户
            }
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // 当传感器精度改变时回调该方法,一般无需处理
    }

3.游戏模式的选择

3.1本应用支持2种游戏模式,因此在界面设计上用到了RadioGroup和RadioButton。

<TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:gravity="center_horizontal"
            android:text="游戏模式:" />

        <RadioGroup
            android:id="@+id/rg_mode"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <RadioButton
                android:id="@+id/rb_dice"
                android:layout_width="80dp"
                android:layout_height="20dp"
                android:checked="true"
                android:text="摇骰子" />

            <RadioButton
                android: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方法中编写游戏模式变更的逻辑代码。

    @Override
    public 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">

    <TextView
        android: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" />

    <LinearLayout
        android: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">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:gravity="center_horizontal"
            android:text="游戏模式:" />

        <RadioGroup
            android:id="@+id/rg_mode"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <RadioButton
                android:id="@+id/rb_dice"
                android:layout_width="80dp"
                android:layout_height="20dp"
                android:checked="true"
                android:text="摇骰子" />

            <RadioButton
                android:id="@+id/rb_welfare"
                android:layout_width="80dp"
                android:layout_height="20dp"
                android:text="摇红包" />

        </RadioGroup>

    </LinearLayout>

    <Button
        android: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" />

    <TextView
        android: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


    @Override
    protected 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;  // 单选按钮初始状态选中的遥骰子
    }

    @Override
    protected void onPause() {
        super.onPause();
        mSensorMgr.unregisterListener(this); // 注销当前活动的传感监听器
    }

    @Override
    protected void onResume() {
        super.onResume();
        // 给加速度传感器注册传感监听器
        mSensorMgr.registerListener(this,
                mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    public 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); // 延迟时间毫秒
                }
            }
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // 当传感器精度改变时回调该方法,一般无需处理
    }


    @Override
    public 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;  // 摇红包模式
        }
    }

    @Override
    public 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);   // 1001
            Log.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);
    }
}

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

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

相关文章

ESP32添加组件02

1.接ESP32创建工程01&#xff0c;快捷键CtrlShiftp 2.输入组件的名称&#xff0c;然后按Enter键 3.创建结果如下图所示 目录结构如下 一级 component --文件夹 二级 led ---文件夹 led.c c文件 CMakeList.txt txt文件 三级 include 文件夹 led.h c头文件 4.主要代码 //led.c…

机器学习之 K 近邻算法图像识别实战

引言 在机器学习领域&#xff0c;K 近邻算法&#xff08;K-Nearest Neighbors, KNN&#xff09;是一种基于实例的学习方法&#xff0c;它可以根据样本之间的距离来进行分类或回归。本文将介绍如何使用 KNN 算法进行手写数字识别&#xff0c;并通过一个实际的例子来演示整个过程…

GPU纹理压缩格式(详解ETC1)

参考 纹理压缩格式原理概述&#xff1a; 你所需要了解的几种纹理压缩格式原理Compressed GPU texture formats – a review and compute shader decoders – part 1 ETC1格式&#xff1a;ETC1压缩纹理格式详解 ETC1 将4x4像素&#xff08;16RGB8位共384位&#xff0c;48字…

ChatGPT下的Java代码审计

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247485367&idx1&sn837891059c360ad60db7e9ac980a3321&chksmc0e47eebf793f7fdb8fcd7eed8ce29160cf79ba303b59858ba3a6660c6dac536774afb2a6330#rd 《网安面试指南》http://mp.weixin.qq.com/s?…

爬虫:scrapy基本使用:链家实例

scrapy基本使用 Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架&#xff0c;用途非常广泛。 框架的力量&#xff0c;用户只需要定制开发几个模块就可以轻松的实现一个爬虫&#xff0c;用来抓取网页内容以及各种图片&#xff0c;非常之方便。 安…

【原创】java+swing+mysql的KTV管理系统设计与实现

个人主页&#xff1a;程序员杨工 个人简介&#xff1a;从事软件开发多年&#xff0c;前后端均有涉猎&#xff0c;具有丰富的开发经验 博客内容&#xff1a;全栈开发&#xff0c;分享Java、Python、Php、小程序、前后端、数据库经验和实战 开发背景&#xff1a; 随着社会的发展…

语义分割:YOLOv5的分割模型训练自己的数据集(从代码下载到实例测试)

文章目录 前言一、环境搭建二、构建数据集三、修改配置文件①数据集文件配置②权重下载③模型文件配置④训练参数配置 四、模型训练和测试模型训练模型测试 总结 前言 提示&#xff1a;本文是YOLOv5的分割模型训练自己数据集的记录教程&#xff0c;在调试前需要本地已配置好CU…

测试资料1111

什么是软件 软件是控制计算机硬件工作的工具 软件产生过程 需求产生&#xff08;客户、用户&#xff09;、需求文档&#xff08;需求文档&#xff09;、设计效果图&#xff08;UI设计师&#xff09;、产品开发&#xff08;研发人员&#xff09;、产品测试&#xff08;测试人…

multiple definition of `xxxx`问题解决(vscode开发stm32)

一、背景 开发时遇到如下问题&#xff1a; 在Tim.h文件夹中定义了一个结构体变量 TIM_HandleTypeDef htim2;//定时器实例变量 其余的文件在引用此变量时都加上了#include "Tim.h" 但编译时&#xff0c;终端报错&#xff1a; multiple definition of htim2 全局搜…

hs_err_pid.log分析

hs_err_pid.log 文件是 Java 虚拟机&#xff08;JVM&#xff09;在遇到致命错误&#xff08;如崩溃或内部错误&#xff09;时生成的错误日志文件。这个文件包含了关于崩溃的详细信息&#xff0c;可以帮助开发者或系统管理员诊断和解决问题。 hs_err_pid.log文件位置和命名 文…

maven导入依赖后依然爆红?

今天遇到了maven导入依赖后依然爆红的问题&#xff0c;检查了本地仓库发现依赖包已经全有了但是IDEA这边的XML文件里面依然大量爆红&#xff1b;并且我重新将之前的本地仓库相关的依赖包甚至其上级目录进行了删除后重新导入&#xff0c;但问题依然没有解决&#xff1b;因此我非…

【大模拟】逻辑回环类

区块链 AcWing 3285. 区块链 - AcWing 区块链涉及密码学、哈希算法、拜占庭问题、共识算法、故障模型、网络模型等诸多知识&#xff0c;也在金融等领域有广泛的应用。 本题中&#xff0c;我们需要实现一个简单的区块链系统。 在一个分布式网络中&#xff0c;有 nn 个节点通…

最新软件库系统源码/ 免服务器源码/带后台功能/使用链接上传或服务器存储

源码简介&#xff1a; 最新软件库系统源码&#xff0c;它是免服务器的&#xff0c;而且带后台&#xff0c;实现超级管理动态发布&#xff0c;会员系统卡密系统充值的后台功能。注册用户的软件投稿&#xff0c;可以使用链接上传&#xff0c;或者服务器存储。 它带有后台功能&a…

Springboot 整合 Swagger3(springdoc-openapi)

使用springdoc-openapi这个库来生成swagger的api文档 官方Github仓库&#xff1a; https://github.com/springdoc/springdoc-openapi 官网地址&#xff1a;https://springdoc.org 目录题 1. 引入依赖2. 拦截器设置3. 访问接口页面3.1 添加配置项&#xff0c;使得访问路径变短…

饮水机功能构建指导思想以及最小试验

饮水机功能构建指导思想以及最小试验 引言 我们饮水机, 其实就只有两个必须使用的功能, 一个是控制加热, 一个是控制放水, 我们现在就可以直接实现这两个功能. 我们使用stm32单片机, 直接控制两个io口的高低电平, 通过继电器, 就可以控制大电流设备的开关. 没错这就已经结束了…

时间序列预测 | CEEMDAN+CNN+Transformer多变量时间序列预测(Python)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 时间序列预测 | CEEMDANCNNTransformer多变量时间序列预测&#xff08;Python&#xff09; 时间序列预测 创新点 多尺度特征提取&#xff1a;CEEMDAN将复杂的时间序列分解成多个IMFs&#xff0c;使得CNN和Transforme…

【TVM 教程】在 Adreno™ 上部署预训练模型

本文是一个逐步教程&#xff0c;演示如何在 Adreno 上&#xff08;不同精度&#xff09;部署预训练的 PyTorch ResNet-18 模型。 首先&#xff0c;我们需要安装 PyTorch 与 TorchVision&#xff0c;因为我们将使用它作为我们的模型库。 可以通过 pip 快速安装&#xff1a; p…

【Linux】Linux环境基础开发工具使用之Linux编译器-gcc/g++使用

目录 一、编译过程二、gcc/g如何完成三、error: for loop initial declarations are only allowed in C99 mode 的解决方法四、预处理五、编译六、汇编七、链接八、数据库8.1 动态库8.2 静态库8.3 动/静态链接的优缺点 结尾 一、编译过程 预处理&#xff08;头文件的展开、宏替…

计算机网络12——IM聊天系统——项目分析和架构搭建

1、IM——聊天系统主要功能 &#xff08;1&#xff09;注册 根据&#xff1a;昵称&#xff0c;手机号&#xff0c;密码 &#xff08;2&#xff09;登录 根据&#xff1a;手机号&#xff0c;密码 &#xff08;3&#xff09;添加好友 根据&#xff1a;昵称 &#xff08;4&…

【IR】Counterfactual Explainer on Graphs

图神经网络的反事实解释&#xff1a;最新文章略读 Survey [ 5 ] ^{[5]} [5]CFAD [ 1 ] ^{[1]} [1]CAF [ 3 ] ^{[3]} [3]GCFExplainer [ 2 ] ^{[2]} [2]CFE [ 4 ] ^{[4]} [4]RCExplainer [ 6 ] ^{[6]} [6]CF-GNNExplainer [ 7 ] ^{[7]} [7]Ref Survey [ 5 ] ^{[5]} [5] NeurIP…