安卓应用开发学习:手机摇一摇功能应用尝试--摇骰子和摇红包
一、引言
前几天,我发布的日志《安卓应用开发学习:查看手机传感器信息》记录了如何查看手机传感器的信息,通过上述的方法,可以看到我的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 进行详细介绍,包括其功能、…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
