当前位置: 首页 > news >正文

Android 实现录音功能

思路:

通过媒体录制器MediaRecorder实现:MediaRecorder是Android自带的音频和视频录制工具,它通过操纵摄像头和麦克风完成媒体录制,既可录制视频,又可单独录制音频。

MediaRecorder常用方法(录音与录像通用):

  • reset:重置录制资源。
  • prepare:准备录制。
  • start:开始录制。
  • stop:结束录制。
  • release:释放录制资源。
  • setOnErrorListener:设置错误监听器。可监听服务器异常和未知错误的事件。需要实现接口MediaRecorder.OnErrorListener的onError方法。
  • setOnInfoListener:设置信息监听器。可监听录制结束事件,包括达到录制时长或达到录制大小。需要实现接口MediaRecorder.OnInfoListener的onInfo方法。
  • setMaxDuration:设置可录制的最大时长,单位毫秒。
  • setMaxFileSize:设置可录制的最大文件大小,单位字节。
  • setOutputFile:设置输出文件的路径。
setOutputFormat:设置媒体输出格式
OutputFormat类的输出格式格式分类扩展名格式说明
AMR_NB音频.amr窄带格式
AMR_WB音频.amr宽带格式
AAC_ADTS音频.aac高级的音频传输流格式
MPEG_4视频.mp4MPEG4格式
THREE_GPP视频.3gp3GP格式

音频录制示例,上代码

一.权限添加

<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /><uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /><uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /><uses-permission android:name="android.permission.RECORD_AUDIO"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>

权限区分版本33及以上和以下

动态权限请求:

使用权限请求库,在app的build.gradle添加

// 权限请求implementation 'com.guolindev.permissionx:permissionx:1.7.1'

 使用音频抖动动效

// https://github.com/xfans/VoiceWaveView
implementation 'com.github.xfans:VoiceWaveView:1.0.2'
/*** 请求视频、音频、图片权限*/private void requestPermission() {ArrayList<String> requestList = new ArrayList<>();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {requestList.add(Manifest.permission.READ_MEDIA_IMAGES);requestList.add(Manifest.permission.RECORD_AUDIO);requestList.add(Manifest.permission.READ_MEDIA_VIDEO);} else {requestList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);requestList.add(Manifest.permission.READ_EXTERNAL_STORAGE);requestList.add(Manifest.permission.RECORD_AUDIO);}PermissionX.init(this).permissions(requestList).onExplainRequestReason((scope, deniedList) -> {scope.showRequestReasonDialog(deniedList, UiUtil.getString(R.string.toast_permission_request), UiUtil.getString(R.string.toast_permission_allow), UiUtil.getString(R.string.toast_permission_deny));}).request((allGranted, grantedList, deniedList) -> {if (allGranted) {showVoiceAddDialog();LogUtil.i(TAG, "所有申请的权限都已通过");} else {LogUtil.i(TAG, "您拒绝了如下权限:" + deniedList);}});}/*** 判断是否有存储、音频权限* @return */public boolean hasStoragePermissions() {boolean isStorage, isAudio, isVideo, isImages;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {isImages = ContextCompat.checkSelfPermission(MyApplication.getInstance(), Manifest.permission.READ_MEDIA_IMAGES)== PackageManager.PERMISSION_GRANTED;isAudio = ContextCompat.checkSelfPermission(MyApplication.getInstance(), Manifest.permission.RECORD_AUDIO)== PackageManager.PERMISSION_GRANTED;isVideo = ContextCompat.checkSelfPermission(MyApplication.getInstance(), Manifest.permission.READ_MEDIA_VIDEO)== PackageManager.PERMISSION_GRANTED;return isImages && isAudio && isVideo;} else {isStorage = ContextCompat.checkSelfPermission(MyApplication.getInstance(), Manifest.permission.WRITE_EXTERNAL_STORAGE)== PackageManager.PERMISSION_GRANTED;isAudio = ContextCompat.checkSelfPermission(MyApplication.getInstance(), Manifest.permission.RECORD_AUDIO)== PackageManager.PERMISSION_GRANTED;return isStorage || isAudio;}}

二.录制代码

package com.calendar.master.gp.dialog;import android.app.AlertDialog;
import android.app.Dialog;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.format.DateFormat;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;import com.calendar.master.gp.R;
import com.calendar.master.gp.bean.NoteVoice;
import com.calendar.master.gp.databinding.DialogAddVoiceBinding;
import com.calendar.master.gp.databinding.DialogEmojiBinding;
import com.calendar.master.gp.fragment.EmojiFragment;
import com.calendar.master.gp.listener.IVoiceSaveListener;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.oooppqqzzz.base.utils.LogUtil;import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;import me.xfans.lib.voicewaveview.VoiceWaveView;/*** 描述:* 作者: shawn* 时间: 2023/4/2716:44*/
public class VoiceAddDialogFragment extends DialogFragment {private static final String TAG = "VoiceAddDialogFragment";private MediaRecorder mMediaRecorder;private String voicePath;private String outputFileName;private File fileVoice;private IVoiceSaveListener iVoiceSaveListener;private DialogAddVoiceBinding mBinding;private int recordingTime = 0; // 记录录音时长(秒)private Handler handler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(@NonNull Message msg) {// 更新UI显示录音时长mBinding.tvVoiceTime.setText(getRecordTime(recordingTime));}};;@Overridepublic void onStart() {super.onStart();setCustomStyle();}@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {if (getDialog() == null) {return super.onCreateView(inflater, container, savedInstanceState);}// 设置宽度为屏宽、靠近屏幕底部。final Window window = getDialog().getWindow();window.setBackgroundDrawableResource(R.color.transparent);window.getDecorView().setPadding(0, 0, 0, 0);WindowManager.LayoutParams wlp = window.getAttributes();wlp.gravity = Gravity.BOTTOM;wlp.width = WindowManager.LayoutParams.MATCH_PARENT;wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;window.setAttributes(wlp);return super.onCreateView(inflater, container, savedInstanceState);}@NonNull@Overridepublic Dialog onCreateDialog(Bundle savedInstanceState) {// 加载布局文件mBinding = DialogAddVoiceBinding.inflate(getLayoutInflater());// 音频抖动控件VoiceWaveView voiceWaveView = mBinding.vwvVoice;voiceWaveView.setDuration(100);voiceWaveView.addHeader(12).addHeader(14).addHeader(26);voiceWaveView.addBody(27).addBody(17).addBody(38).addBody(88).addBody(38).addBody(24).addBody(8).addBody(18).addBody(48).addBody(30).addBody(60).addBody(38);voiceWaveView.addFooter(24).addFooter(14).addFooter(8);mBinding.ivClose.setOnClickListener(v -> {this.dismiss();});mBinding.ivVoiceEdit.setOnClickListener(new View.OnClickListener() {boolean isInput = true;@Overridepublic void onClick(View v) {if (isInput) {isInput = false;voiceWaveView.start();mBinding.ivVoiceEdit.setBackgroundResource(R.mipmap.icon_voice_edit_stop);getPath();startRecord();} else {isInput = true;voiceWaveView.stop();cancelRecord();// 移除定时任务handler.removeCallbacks(timerRunnable);voiceStop(true);}}});mBinding.ivVoiceCancel.setOnClickListener(v -> {voiceStop(false);fileVoice.delete();recordingTime = 0;mBinding.tvVoiceTime.setText("00:00");mBinding.ivVoiceEdit.setBackgroundResource(R.mipmap.icon_voice_edit_start);});mBinding.ivVoiceSave.setOnClickListener(v -> {NoteVoice noteVoice = new NoteVoice();noteVoice.setUrl(voicePath);noteVoice.setTime(getRecordTime(recordingTime));noteVoice.setFileName(outputFileName);if (iVoiceSaveListener != null) {iVoiceSaveListener.save(noteVoice);}dismiss();});return new AlertDialog.Builder(getActivity()).setView(mBinding.getRoot()).create();}private void showAd(int selectIndex) {}private void voiceStop(boolean isStop) {if (isStop) {mBinding.ivVoiceCancel.setVisibility(View.VISIBLE);mBinding.ivVoiceSave.setVisibility(View.VISIBLE);mBinding.ivVoiceEdit.setVisibility(View.GONE);} else {mBinding.ivVoiceCancel.setVisibility(View.GONE);mBinding.ivVoiceSave.setVisibility(View.GONE);mBinding.ivVoiceEdit.setVisibility(View.VISIBLE);}}private void setCustomStyle() {// 设置主题,这里只能通过xml方式设置主题,不能通过Java代码处理,因为这是getWindow还是null,// 而且window的几乎所有属性,都可以通过xml设置setStyle(STYLE_NORMAL, R.style.AddDialogTheme);}/*** 录制前创建一个空文件并获取路径*/private void getPath() {File appDir = new File(Environment.getExternalStorageDirectory() + File.separator + Environment.DIRECTORY_MUSIC, "NoteToVoice");if (!appDir.exists()) {appDir.mkdirs();}outputFileName = System.currentTimeMillis() + ".amr";fileVoice = new File(appDir, outputFileName);if (!fileVoice.exists()) {try {fileVoice.createNewFile();voicePath = fileVoice.getPath();LogUtil.i("shawn", "音频保存路径:" + voicePath);} catch (IOException e) {e.printStackTrace();}}}/*** 开始录音*/private void startRecord() {// 充值录音时长recordingTime = 0;//开始录音操作mMediaRecorder = new MediaRecorder(); // 创建一个媒体录制器mMediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {@Overridepublic void onError(MediaRecorder mr, int what, int extra) {if (mr != null) {mr.reset(); // 重置媒体录制器}}}); // 设置媒体录制器的错误监听器mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {@Overridepublic void onInfo(MediaRecorder mr, int what, int extra) {// 录制达到最大时长,或者达到文件大小限制,都停止录制if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED|| what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {cancelRecord();}}}); // 设置媒体录制器的信息监听器mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频源为麦克风mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB); // 设置媒体的输出格式mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 设置媒体的音频编码器// mMediaRecorder.setAudioSamplingRate(8); // 设置媒体的音频采样率。可选// mMediaRecorder.setAudioChannels(2); // 设置媒体的音频声道数。可选// mMediaRecorder.setAudioEncodingBitRate(1024); // 设置音频每秒录制的字节数。可选mMediaRecorder.setMaxDuration(10 * 1000); // 设置媒体的最大录制时长// mMediaRecorder.setMaxFileSize(1024*1024*10); // 设置媒体的最大文件大小// setMaxFileSize与setMaxDuration设置其一即可mMediaRecorder.setOutputFile(voicePath); // 设置媒体文件的保存路径try {mMediaRecorder.prepare(); // 媒体录制器准备就绪mMediaRecorder.start(); // 媒体录制器开始录制startRecordingTimer();} catch (Exception e) {LogUtil.i("shawn", "录音出错 = " + e.getMessage());e.printStackTrace();}}// 取消录制操作private void cancelRecord() {if (mMediaRecorder != null) {mMediaRecorder.setOnErrorListener(null); // 错误监听器置空mMediaRecorder.setPreviewDisplay(null); // 预览界面置空try {LogUtil.i("shawn", "结束录音");mMediaRecorder.stop(); // 媒体录制器停止录制} catch (Exception e) {LogUtil.i("shawn", "结束录音出错 = " + e.getMessage());e.printStackTrace();}mMediaRecorder.release(); // 媒体录制器释放资源mMediaRecorder = null;}}@Overridepublic void onDestroy() {super.onDestroy();// 移除定时任务handler.removeCallbacks(timerRunnable);}public void setVoiceSaveListener(IVoiceSaveListener iVoiceSaveListener) {this.iVoiceSaveListener = iVoiceSaveListener;}private void startRecordingTimer() {handler.postDelayed(timerRunnable, 1000); // 每隔一秒执行一次}private Runnable timerRunnable = new Runnable() {@Overridepublic void run() {recordingTime++;handler.sendEmptyMessage(0); // 通知UI更新startRecordingTimer(); // 继续下一次定时}};private String getRecordTime(int recordingTime) {StringBuilder stringBuilder = new StringBuilder();if (recordingTime < 10) {stringBuilder.append("00:0").append(recordingTime);} else if (recordingTime < 60) {stringBuilder.append("00:").append(recordingTime);} else {int minutes = recordingTime / 60;int seconds = recordingTime % 60;if (minutes < 10) {stringBuilder.append("0").append(minutes).append(":");} else {stringBuilder.append(minutes).append(":");}if (seconds < 10) {stringBuilder.append("0").append(seconds);} else {stringBuilder.append(seconds);}}return stringBuilder.toString();}
}

设置dialog样式,在style.xml

<!-- 这里的parent必须是Theme.AppCompat.Dialog --><style name="AddDialogTheme" parent="Theme.AppCompat.Dialog"><!-- 这两个属性对于一个常规的Dialog,一般必须设置的--><!-- 这两个属性按照下面的值设置之后,确保了弹窗的实际显示效果,跟你在layout文件中的定义效果是一样的 --><item name="android:windowIsFloating">false</item>
<!--        <item name="android:windowBackground">@drawable/radius_add_dialog_bg</item>--></style>

相关文章:

Android 实现录音功能

思路&#xff1a; 通过媒体录制器MediaRecorder实现&#xff1a;MediaRecorder是Android自带的音频和视频录制工具&#xff0c;它通过操纵摄像头和麦克风完成媒体录制&#xff0c;既可录制视频&#xff0c;又可单独录制音频。 MediaRecorder常用方法(录音与录像通用)&#xf…...

drawio导出矢量图

1.选中要导出的图 2.导出为pdf 3.用adobe打开pdf&#xff0c;另存为eps...

关于angular router-outlet

关于angular router-outlet Angular是一个现代化的前端框架&#xff0c;它提供了很多强大的工具来帮助我们开发出高效的Web应用。其中一个最常用的功能是路由&#xff08;routing&#xff09;系统&#xff0c;它允许我们在不同的URL之间导航并加载不同的组件。而<router-ou…...

设计模式详解-桥接模式

类型&#xff1a;结构型模式 实现原理&#xff1a;将抽象类和实现类分离&#xff0c;使其独立&#xff0c;然后使用接口再将二者连接起来。 意图&#xff1a;将抽象部分与实现部分分离&#xff0c;使它们都可以独立的变化。 主要解决&#xff1a;类变化频繁时用继承可能会出…...

设计模式—— 单一职责原则

文章目录 设计模式的目的设计模式原则单一职责原则基本介绍应用实例单一职责原则注意事项和细节 设计模式的目的 1&#xff0c;代码重用性&#xff08;即&#xff1a;相同功能的代码&#xff0c;不用多次编写&#xff09; 2&#xff0c;可读性&#xff08;即&#xff1a;编程…...

嵌入式系统中如何选择RTC电池?

RTC&#xff08;Real Time Clock&#xff09;是一种用于提供系统时间的独立定时器&#xff0c;它可以在系统断电或低功耗模式下继续运行&#xff0c;只需要一个后备电池作为供电源。在嵌入式系统中&#xff0c;选择合适的RTC电池时非常关键的&#xff0c;它会影响系统时间的准确…...

56 | 国内游戏直播竞品分析

国内游戏直播竞品分析 一、需求分析 当前直播用户群可分为两大类: 主播观众用户需求: 1.主播: 作为直播内容的创造者,主播表现方式和内容很大程度上决定了观众的需求, 其中主播主要只有三点需求: (一) 通过某一手段(如游戏技术、唱歌技巧)获取他人关注,满足虚荣心…...

STM32 CubeMX (第一步Freertos任务管理:创建、删除、挂起、恢复)

STM32 CubeMX Freertos STM32 CubeMX &#xff08;Freertos任务&#xff1a;创建、删除、挂起、恢复&#xff09; STM32 CubeMX Freertos前言一、STM32 CubeMX 配置时钟树配置HAL时基选择TIM1&#xff08;不要选择滴答定时器&#xff1b;滴答定时器留给OS系统做时基&#xff09…...

0101读写分离测试-jdbc-shardingsphere-中间件

文章目录 1 前言2、创建SpringBoot程序2.1、创建项目2.2、添加依赖2.3、生成实体类、service与Mapper1.5、配置读写分离 2、测试2.1、读写分离测试2.2、事务测试2.3、负载均衡测试 结语 1 前言 shardingshpere-jdbc定位为轻量级 Java 框架&#xff0c;在 Java 的 JDBC 层提供的…...

sqlite3将词典导入数据库

使用sqlite3代码实现将词典导入数据库中 #include <head.h> #include <sqlite3.h> #include <strings.h> #include <unistd.h> int main(int argc, const char *argv[]) {sqlite3 *db NULL;if(sqlite3_open("./dict.db",&db) ! SQLITE…...

浏览器 - 事件循环机制详解

目录 1&#xff0c;浏览器进程模型进程线程浏览器的进程和线程1&#xff0c;浏览器进程2&#xff0c;网络进程3&#xff0c;渲染进程 2&#xff0c;渲染主线程事件循环异步同步 JS 为什么会阻塞渲染任务优先级 3&#xff0c;常见面试题1&#xff0c;如何理解 js 的异步2&#x…...

析构函数中不应该抛出异常(摘录)

析构函数中抛出异常时概括性总结 从语法上面讲&#xff0c;析构函数抛出异常是可以的&#xff0c;C并没有禁止析构函数引发异常&#xff0c;但是C不推荐这一做法&#xff0c;从析构函数中抛出异常是及其危险的。 如果析构函数抛出异常&#xff0c;则异常点之后的程序不会执行&a…...

Windows定时任务计划无法显示任务程序界面的问题解决

笔者这两天写了一个python脚本程序&#xff0c;用来自动从公司的主数据系统获取数据&#xff0c;并按格式编制成excel。脚本程序编写一切顺利&#xff0c;运行结果很是完美&#xff0c;笔者很是舒心。但在最后一步&#xff0c;用上班的电脑每天早上定时运行它时&#xff0c;出了…...

【Azure API 管理】APIM如何实现对部分固定IP进行访问次数限制呢?如60秒10次请求

问题描述 使用Azure API Management, 想对一些固定的IP地址进行访问次数的限制&#xff0c;如被限制的IP地址一分钟可以访问10次&#xff0c;而不被限制的IP地址则可以无限访问&#xff1f; ChatGPT 解答 最近ChatGPT爆火&#xff0c;所以也把这个问题让ChatGPT来解答&#x…...

Python学习笔记_进阶篇(二)_django知识(一)

本章简介&#xff1a; Django 简介Django 基本配置Django urlDjango viewDjango 模板语言Django Form Django 简介 Django是一个开放源代码的Web应用框架&#xff0c;由Python写成。采用了MVC的软件设计模式&#xff0c;即模型M&#xff0c;视图V和控制器C。它最初是被开发来…...

【hive】hive中row_number() rank() dense_rank()的用法

hive中row_number() rank() dense_rank()的用法 一、函数说明 主要是配合over()窗口函数来使用的&#xff0c;通过over(partition by order by )来反映统计值的记录。 rank() over()是跳跃排序&#xff0c;有两个第二名时接下来就是第四名(同样是在各个分组内)dense_rank() …...

【云原生】【k8s】Kubernetes+EFK构建日志分析安装部署

目录 EFK安装部署 一、环境准备&#xff08;所有主机&#xff09; 1、主机初始化配置 2、配置主机名并绑定hosts&#xff0c;不同主机名称不同 3、主机配置初始化 4、部署docker环境 二、部署kubernetes集群 1、组件介绍 2、配置阿里云yum源 3、安装kubelet kubeadm …...

计算实数数组中所有元素的绝对值 numpy.fabs()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 计算实数数组中所有元素的绝对值 numpy.fabs() [太阳]选择题 请问关于以下代码表述错误的是&#xff1f; iimport numpy as np a np.array([-1,-3]) b np.array([-1,34j]) print("【显…...

深入浅出Pytorch函数——torch.nn.init.orthogonal_

分类目录&#xff1a;《深入浅出Pytorch函数》总目录 相关文章&#xff1a; 深入浅出Pytorch函数——torch.nn.init.calculate_gain 深入浅出Pytorch函数——torch.nn.init.uniform_ 深入浅出Pytorch函数——torch.nn.init.normal_ 深入浅出Pytorch函数——torch.nn.init.c…...

ORACLE中UNION、UNION ALL、MINUS、INTERSECT学习

1、UNION和UNION ALL的使用与区别 如果我们需要将两个select语句的结果作为一个整体显示出来&#xff0c;我们就需要用到union或者union all关键字。union的作用是将多个结果合并在一起显示出来。 union和union all的区别是union会自动压缩多个结果集合中的重复结果&#xff…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器

拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件&#xff1a; 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...