怎么做一个类似微信的聊天app
如何制作微信对话生成器_20200507102923
如何制作微信对话生成器
随着移动互联网的发展,越来越多人逐渐使用微信作为通讯社交工具了,也有一些人在微信上做起了微商生意,相信我们平时在朋友圈也看到不少微商发的交易截图,聊天截图。
实际上微商发的截图并不一定是真实的哦,现在小编就来揭秘微信对话截图是怎样生成的。
首先需要一个在线生成的网站,百度上有很多免费的。
我们打开网站,在首页里点击“微信对话生成器”功能。
左边“外观设置”“对话设置”是进行内容编辑的,右边的预览效果图是实时查看效果。
先进行“外观设置”,编辑信号,电量等手机顶部栏的信息,然后修改聊天标题,未读消息。
接着点击“对话设置”,开始制作对话。
最上面的是“我”在发内容,先输入内容,然后点击下面依次对应的添加按钮,在看看右边预览效果显示的。
往下滑是对方发对话,也是同样的操作。
最后点击“生成图片”就可以了。
微信聊天功能APP的实现
(4)手指左滑取消 ........................................................................................................... 44 (5)手指右滑取消 ........................................................................................................... 45 (6)手指上滑取消 ........................................................................................................... 46 (7)手指下滑取消 ........................................................................................................... 47 (8)消息展示界面 ........................................................................................................... 49 (9)录音文件存储 ........................................................................................................... 49 4 软件开发总结 ..................................................................................................................... 50 (1)心得致谢 ................................................................................................................... 50 (2)参考资料 ................................................................................................................... 50
开发像微信这样的APP的难点和重点
腾讯开发微信花的成本应该超过10个亿,真的技术难度很大,如果交给一个全栈工程师去做,可能要做1万年。
下面详细的讲解和分析为什么成本这么高,都高在哪里。
本人之前就是做APP软件外包行业的,前后外包的APP开发上线过上百款,涉及领域从电商,教育,金融,物联网,医疗都有。
去年开始转型,开始真正的开发了一款属于自己公司的互联网产品,期间真正的体验了一把给自己做产品跟做外包的巨大差别,也能足够说明为什么腾讯开发微信成本如此之高。
腾讯的微信开发成本高,大致可以分为这几种原因,以下会一一详细说明:功能的反复修改造成成本庞大,研发的过程中可能会不断的推翻之前的想法。
不同手机不同操作系统版本号,手机型号兼容性问题调试庞大用户基数情况下的高并发问题的处理有些功能不是你想开发就开发(比如消息推送),还要跟第三方手机厂商洽谈才能做,简单来说可能要给第三方厂商钱。
1. 功能的反复修改造成成本庞大,研发的过程中可能会不断的推翻之前的想法。
我以前做外包公司的时候,大部分客户APP项目的开发成本都在50万以内,以二三十万之间的价格比较多,以最基本的注册登录功能为例。
一般这种二三十万开发的注册登录安卓,IOS,加上后台的开发总耗时一般在2到3个工作日(3个工程师一起协同办公的情况下),包括写代码和调试bug的时间。
遇到繁琐或者客户要求比较高的,可能反复改动下来总耗时一般不超过一周。
而我们真正在给自己做一个APP作为自己的互联网产品去运营的时候,我们光一个注册登录,就改了不下100次,前后总耗时保守估计超过50个工作日,花在光一个注册登录上的工程师的工资成本保守估计超过10万。
那么是什么原因造成了我们给自己做APP产品的时候,要改100次以上,前后耗时要50个工作日,同时光一个简单的注册登录工资成本要花十几万呢?给大家简单看一下注册登录页面,说说这里面的门道。
乍一看之下我们的注册登录页面功能,支持手机短信注册登录,邮件注册登录,微信授权登录,国际短信注册,仿佛也没什么复杂的。
iOS即时通讯+仿微信聊天框架+源码
iOS即时通讯+仿微信聊天框架+源码更新:2017年8⽉1⽇实在是抱歉,git上的Demo这么久,有问题⾃⼰没有发现!肯定给⼤家造成过不⽅便,抱歉!git上Demo刚重新上传,要有需要的可以去下载,要有问题可以直接找我QQ联系我!最近在总结Socket⽅⾯的知识,等⽂章写完会发不来,这篇⽂章是去年什么四五⽉份写的吧,那是⽔平也是有限,希望接下来能总结的⽐这篇好⼀点!!在Demo中,XMPP接收发送消息这⼀块的代码我暂时是删除了,仿照微信的聊天框架是在的,你要想通过XMPP⾃⼰在项⽬中试试,你需要的也就是在Demo中集成XMPP,然后写⼀下它的链接和接收/发送⽅法,其实也很简单!等我补上去我也会在这⾥第⼀时间通知!和这个相关的Socket的系列⽂章也是刚开始写,有需要的可以看看,系列⽂章:以下正⽂:即时通讯(IM)在iOS这⽚江湖⾥⾯已经算是⼀个⽼者了,我这⼩旋风也是在很早以前巡⼭的时候,就知道有即时通讯这个妖怪,以前也多多少少接触过⼀些,在造App的时候⽤过,怎么可以⽤“造”这个词,说着说着就感觉要跑题了,脑海中怎么波涛翻滚的样⼦。
不好,才开头了两句,不能乱开车!说回正题,我知道的即时通讯有⽤还有实现的,当然也有现在也有⼤部分接⼊环信等等三⽅的。
有些东西掩盖不住的,可能可能许多同⾏和我⼀样,感觉你知道的越来越多,就越会觉得⾃⼰像是个⽂盲,其实不懂的真的还有好多好多,还有那么多等着你去学习!这时候果断就要引⼊⼏句有哲学含义的话,安慰⼀下⾃⼰。
⽐如“你⼀定要努⼒,但千万别着急”。
这句是简书主编的⼀本书的书名,顺便也推荐给⼤家去看看,陶冶⼀下情操!应该很多同⾏也都知道很多三⽅即时通讯,知道它们是怎么⽤的,要是还清楚⽹络协议底层的知识,其实可以挺让我羡慕的,毕竟不是计算机专业的,懂得不多,也知道还有很多和我⼀样,算是⼀个半路出家的孩⼦,我还是建议有时间的话去了解学习⼀下这些最底层,最基础的东西。
我也在不断的学习尝试和总结中,相信这些底层的基础的是你⾛向⼤神路,必不可少的,恩,对,必不可少!这篇⽂章我们就说说怎样⾃⼰搭建⼀个仿模仿微信的聊天框架和怎样搭建Openfire服务器来实现⼀个模仿微信的聊天。
Android仿微信课程设计
Android仿微信课程设计一、课程目标知识目标:1. 学生理解Android应用开发的基本概念,掌握应用界面设计、布局管理和组件使用等基础知识。
2. 学生了解微信应用的基本功能,并能够运用所学知识实现类似微信的通讯功能。
3. 学生掌握SQLite数据库的使用,实现用户信息存储和消息记录存储。
技能目标:1. 学生能够运用Android Studio进行界面布局设计,实现美观、易用的用户界面。
2. 学生能够编写Java代码,实现微信类应用的即时通讯功能,包括文字、图片等消息发送与接收。
3. 学生能够通过SQLite数据库管理用户信息和聊天记录,实现数据的增、删、改、查操作。
情感态度价值观目标:1. 培养学生团队协作精神,学会与他人共同分析问题、解决问题,提高沟通与协作能力。
2. 培养学生勇于创新、积极探索的学习态度,激发学生对移动应用开发的兴趣。
3. 增强学生的版权意识,尊重他人知识产权,养成良好的编程习惯。
课程性质:本课程为实践性较强的课程,结合当前热门的移动应用开发技术,以实际项目为驱动,引导学生掌握Android应用开发的基本知识和技能。
学生特点:学生已具备一定的Java编程基础,对移动应用开发有较高的兴趣,喜欢动手实践。
教学要求:注重理论与实践相结合,强调动手实践,培养学生解决实际问题的能力。
在教学过程中,关注学生的个体差异,提供个性化指导,确保学生能够达到课程目标。
通过课程学习,使学生能够独立完成一个类似微信的Android 应用开发,提高其就业竞争力。
二、教学内容1. Android应用开发基础:介绍Android系统架构、开发环境搭建、应用程序组件等基础知识,使学生了解Android应用开发的基本概念和流程。
- 教材章节:第1章 Android开发概述,第2章 Android开发环境搭建,第3章 Android应用程序组件。
- 内容列举:Android系统架构、Android Studio安装与配置、创建第一个Android应用、四大组件介绍。
交互设计面试题汇总
交互设计面试题汇总一、交互设计面试题汇总1. 请简单介绍一下交互设计的概念。
嘿呀,交互设计呢,就是设计人和产品、系统或者环境之间交互的这么一个事儿。
就像是在人和东西之间搭个桥,让大家互动起来超级顺溜,让人用起来觉得特别舒服,就这么个感觉啦。
2. 在交互设计中,用户体验为什么这么重要呢?哎呀,你想啊,要是用户体验不好,谁还想用这个产品呀。
就好比你去一家店,店员态度超差,东西再好你可能也不想去第二次了。
在交互设计里也是一样的道理,好的用户体验能让用户开开心心地用产品,还会推荐给别人呢。
3. 说说你知道的交互设计原则有哪些?那可不少呢。
有可视性原则,就是让用户能看到东西咋操作的,不能藏着掖着。
还有反馈原则,用户做个操作,得给人家个回应,不然用户还以为没按对呢。
再就是一致性原则啦,操作啥的得保持一致,不能一会儿这样一会儿那样,会把用户搞晕的。
4. 如何进行交互设计的用户调研呢?这就很有趣啦。
可以先去观察用户咋用类似产品的,看看他们的习惯。
还能搞问卷调查,问用户喜欢啥不喜欢啥。
再就是做用户访谈,跟用户面对面聊聊,听听他们心里的想法。
5. 请列举一些常见的交互设计工具。
Sketch是个很流行的工具哦,用来做界面设计超方便。
Axure 也很棒,能做出交互原型呢。
Adobe XD也是好多设计师爱用的,功能很强大。
6. 讲述一次你参与的交互设计项目经历。
我之前参加过一个手机APP的交互设计项目。
最开始的时候呢,我们先研究了好多类似的APP,看看人家的优缺点。
然后就开始画草图,把我们想要的交互流程画出来。
接着用Axure做了个原型,拿给一些用户去试用。
用户反馈说有个功能的入口不好找,我们就赶紧调整了一下。
最后这个APP上线后,用户反馈还不错呢。
7. 如果要设计一个电商APP的购物流程,你会怎么做?首先我会让用户很容易找到搜索框,这样能快速找到想要的东西。
然后商品列表页要展示关键信息,像图片、价格、名字啥的。
点进商品详情页,图片要高清,描述要详细,还有购买按钮要很显眼。
用C语言写聊天工具,类似于QQ
和小 四是 知道 了的。但是 main 函数 的参 数是 可以 省略的 ,而 WinMain 是不 可以 省的 。这里 也要 对 VC 6.0 的编 译模 式 改下 看下图
依次 是“工程”→“设置”→“连接”,在“工程选项”里把 console 改为 windows 就可以了。如果认真学了汇编, 或是 手写 命令 编译 连接过 C 程序 ,就会 知道 这是 干什 么的。Console 是控 制台 的意 思,以前 我们 用 mian 函数 写的 程序 都是 以控 制台 模式 连接的 ,所 以很 少会 有界面 的。 现在 我们 要写有 界面 的程 序, 所以要 选 Windows(窗 口) 模式 了。
也许你听说过 printf()函数是在 stdio.h 中预定义的,但是你见过其定义的形式没有,没有且看下图
其定义形式,就如图中所示,也许你并不懂前面那些东西是什么,不用担心,以后我会慢慢解释给大家的。函数是先定义才能使 用的, stdio.h 中定义 printf 函数,所以我们在引用了 stdio.h 头文件后就可以在程序中调用 printf 函数了。
下面就该罗嗦一段了,由于大家以前并没有写过什么窗口程序,写的都是命令行下的,我们知道在命令行下的程序都有一个主函 数 main,这个函数也就是程序的入口函数。我们现在用 VC 6.0 来写,而且要写窗口类程序,VC 6.0 给我们提供了一个专门用作 窗口类程序的入口函数 WinMain() 这个函数原型是这样的
uniapp模仿微信实现聊天界面的示例代码
friendName: "xpq", msg: [{
"sendName": "xpq", "receviceName": "゛时光い", "sendText": { "address": "湖南省岳阳市湘阴县新世纪大道", "latitude": 28.68925, "longitude": 112.90917, "name": "湘阴县政府(新世纪大道北)", }, "createTime": "2022-01-06 12:40:12", "updateTime": null, "chatmState": 1, "TextType": 3 }, { "sendName": "゛时光い", "receviceName": "xpq", "sendText": { "voice": "时光匆匆流过", "time": 2 //秒 }, "createTime": "2022-01-06 12:22:12", "updateTime": null, "chatmState": 1, "TextType": 2 }, { "sendName": "xpq", "receviceName": "゛时光い", "sendText": { "voice": "谢谢你", "time": 60 //秒 }, "createTime": "2022-01-06 12:00:12", "updateTime": null, "chatmState": 1, "TextType": 2 }, { "sendName": "゛时光い", "receviceName": "xpq", "sendText": "这是第九条未读消息", "createTime": "2022-01-03 12:22:12", "updateTime": null,
uni-app全栈仿微信开源项目系列(一)
uni-app全栈仿微信开源项⽬系列(⼀)叮咚项⽬参考⽂档 v1.0项⽬技术栈:前端:uni-app + nvue 实现原⽣页⾯渲染、同时兼容多端。
后端:Egg.js + MySQL + Redis 实现后端API服务。
不使⽤第三⽅组件库,⾃⼰写⼀套。
NVUE需要注意的点在NVUE中引⼊字体图标需要参考Weex的引⼊规则NVUE模式下的页⾯默认是Flex布局iconfont图标应该放在text标签中包裹,不能直接使⽤view标签包裹NVUE中的屏幕都是以750像素为基准text组件换⾏问题,text组件中的内容如果有换⾏,显⽰的效果也会换⾏Weex是从上到下进⾏渲染的,如果你的元素有定位之类的脱离⽂档流的需求,元素最好按顺序写,否则z-index可能也救不了你。
⽬前仅 iOS ⽀持 box-shadow 属性,Android 暂不⽀持,可以使⽤图⽚代替。
每个元素只⽀持设置⼀个阴影效果,不⽀持多个阴影同时作⽤于⼀个元素。
uni-app中 vue 和 nvue 的区别:uni-app是逻辑和渲染分离的。
渲染层,在app端提供了两套排版引擎:⼩程序⽅式的webview渲染,和weex⽅式的原⽣渲染。
两种渲染引擎可以⾃⼰根据需要选。
vue⽂件⾛的webview渲染,nvue⾛的原⽣渲染。
组件和js写法是⼀样的,css不⼀样,原⽣排版的能⽤的css必须是flex布局,这是web的css的⼦集。
当然什么界⾯都可以⽤flex布出来。
不懂flex布局就⾃⼰学。
⼀般情况下⽤vue就可以了。
如果是app且有部分场景vue页⾯的性能不满⾜你的需求时,这个页⾯可以改⽤nvue页⾯。
如果app⾥同时存在同名的vue和nvue页⾯,在app端会优先执⾏nvue页⾯,⽽其他端仍然优先vue页⾯。
区别和适⽤场景这⽂档⾥写的很清楚:记录⼀些踩过的坑1.环境搭建和项⽬创建需要安装的插件:内置浏览器App真机运⾏uni-app App调试less编译scss/sass编译stylus编译es6编译创建项⽬:项⽬类型为:uni-app,使⽤默认模版。
uni-app实战仿微信app开发
uni-app实战仿微信app开发⽬录章节1:项⽬介绍试看章节2:环境搭建和项⽬创建课时2视频创建项⽬和开启原⽣渲染08:35章节3:全局配置课时3视频引⼊全局样式(⼀)08:11课时4视频引⼊全局样式(⼆)08:15课时5视频引⼊⾃定义图标库(⼀)07:51课时6视频引⼊⾃定义图标库(⼆)全局引⼊02:56课时7视频引⼊⾃定义图标库(三)兼容多端05:28课时8视频配置tabbar底部导航09:21课时9视频配置globalStyle03:00章节4:聊天列表页开发课时10视频头部导航组件开发12:43课时11视频图标按钮组件封装05:11课时12视频封装头部导航组件(⼀)06:46课时13视频封装头部导航组件(⼆)08:25课时14视频开发聊天列表组件(⼀)11:32课时15视频开发聊天列表组件(⼆)09:04课时16视频封装头像组件04:34课时17视频badge组件开发06:23课时18视频封装badge组件05:39课时19视频封装聊天列表组件08:52课时20视频封装全局mixin02:54课时21视频开发弹出层组件(⼀)基础架构04:36课时22视频开发弹出层组件(⼆)弹出关闭功能15:03课时23视频开发弹出层组件(三)09:08课时24视频开发弹出层组件(四)⾃定义位置08:34课时25视频开发弹出层组件(五)长按弹出菜单(⼀)06:34课时26视频开发弹出层组件(六)长按弹出菜单(⼆)10:56课时27视频开发弹出层组件(七)长按弹出菜单(三)11:23课时28视频开发弹出层组件(⼋)动画特效09:19课时29视频开发导航弹出扩展菜单(⼀)16:24课时30视频开发导航弹出扩展菜单(⼆)02:17课时31视频删除当前会话07:05课时32视频置顶,取消置顶当前会话11:18章节5:通讯录页开发课时33视频通讯录列表组件开发08:27课时34视频封装公共列表组件07:41课时35视频完善通讯录列表06:47章节6:发现页开发课时36视频优化公共列表组件08:55课时37视频完善发现页07:57章节7:个⼈中⼼开发课时38视频优化⾃定义导航栏功能05:59课时39视频强化全局列表组件10:38课时40视频完善个⼈中⼼页04:49章节8:聊天页开发课时41视频聊天页导航开发09:21课时42视频聊天页底部输⼊框开发09:51课时43视频开发聊天内容区域05:08课时44视频聊天⽓泡组件开发(⼀)07:47课时45视频聊天⽓泡组件开发(⼆)08:08课时46视频优化图标显⽰问题07:21课时47视频封装聊天⽓泡组件(⼀)12:03课时48视频封装聊天⽓泡组件(⼆)06:29课时49视频聊天时间⼈性化显⽰10:31课时50视频长按弹出操作菜单(⼀)11:01课时51视频长按弹出操作菜单(⼆)03:58课时52视频长按弹出操作菜单(三)06:32课时53视频消息撤回功能实现(⼀)07:25课时54视频消息撤回功能实现(⼆)动画效果05:22课时55视频解决键盘顶起窗⼝问题09:43课时56视频滚动到底部功能实现07:31课时57视频发送⽂字功能实现13:02课时58视频底部操作菜单开发(⼀)09:23课时59视频底部菜单开发(⼆)- 菜单列表(⼀)07:28课时60视频底部菜单开发(三)- 菜单列表(⼆)08:53课时61视频底部菜单开发(四)- 键盘切换(⼀)08:38课时62视频底部菜单开发(五)- 键盘切换(⼆)16:46课时63视频底部菜单开发(六)- 表情包(⼀)13:33课时64视频底部菜单开发(七)- 表情包(⼆)07:40课时65视频发送表情包功能(⼀)09:04课时66视频发送表情包功能(⼆)07:59课时67视频发送图⽚功能08:00课时68视频预览图⽚保存相册功能09:42课时69视频图⽚加载组件(⼀)12:05课时70视频图⽚加载组件(⼆)06:32课时71视频封装图⽚加载组件10:43课时72视频语⾳播放功能(⼀)08:57课时73视频语⾳播放功能(⼆)05:41课时74视频多语⾳播放切换-引⼊vuex09:16课时75视频多语⾳播放切换-⾃定义全局事件$on15:06课时76视频多语⾳播放切换-⾃定义全局事件$emit09:36课时77视频多语⾳播放切换-⾃定义全局事件$off05:21课时78视频多语⾳播放切换-实现⾳频切换04:12课时79视频修改action名称防⽌冲突04:12课时80视频实现语⾳播放动画效果08:44课时81视频短视频消息功能(⼀)15:20课时82视频短视频消息功能(⼆)07:57课时83视频短视频消息功能(三)06:04课时84视频处理⾳频时间和⽓泡长短07:54课时85视频⾳频录制和⽂本输⼊切换08:59课时86视频录制状态动画显⽰(⼀)09:19课时87视频录制状态动画显⽰(⼆)10:52课时88视频发送录⾳功能08:05课时89视频计算⾳频时长07:24课时90视频全局录⾳管理器(⼀)10:07课时91视频全局录⾳管理器(⼆)06:15课时92视频视频录制发送视频08:55章节9:聊天信息设置页开发课时93视频聊天信息设置页(⼀)12:38课时94视频聊天信息设置页(⼆)11:05课时95视频聊天信息设置页(三)08:58章节10:好友设置页开发课时96视频个⼈设置页开发(⼀)15:37课时97视频个⼈设置页开发(⼆)03:46课时98视频个⼈设置页开发(三)操作菜单10:11章节11:设置备注和标签页课时99视频设置备注页(⼀)11:44课时100视频设置备注页(⼆)09:35课时101视频设置标签页(⼀)-标签列表11:02课时102视频设置标签页(⼆)-添加标签04:56课时103视频设置标签页(三)-删除和快捷添加05:17课时104视频设置标签页(四)-保存12:19章节12:设置朋友圈和动态权限课时105视频设置朋友圈动态权限10:18章节13:星标朋友和加⼊移出⿊名单课时106视频设为星标朋友09:22课时107视频加⼊和移出⿊名单07:15章节14:通讯录索引列表功能课时108视频通讯录索引列表功能(⼀)13:10课时109视频通讯录索引列表功能(⼆)10:56课时110视频通讯录索引列表功能(三)08:45章节15:推荐名⽚功能课时111视频推荐名⽚(⼀)11:03课时112视频推荐名⽚(⼆)- 多选功能12:52课时113视频推荐名⽚(三)-限制选中数量06:55课时114视频推荐名⽚(四)-实时搜索11:20课时115视频推荐名⽚(五)-提⽰框15:01课时116视频推荐名⽚(六)-提⽰框09:40章节16:⽤户投诉开发课时117视频⽤户投诉开发09:23章节17:朋友圈开发课时118视频朋友圈开发 - 渐变导航(上)11:56课时119视频朋友圈开发 - 渐变导航(下)11:39课时120视频朋友圈列表 - 单图多图11:40课时121视频朋友圈列表 - 组件封装13:31课时122视频朋友圈列表 - 视频02:19课时123视频朋友圈列表 - 点赞评论列表11:24课时124视频朋友圈列表 - 评论(⼀)-输⼊框15:04课时125视频朋友圈列表 - 评论(⼆)-表情包10:11章节18:个⼈资料设置开发课时126视频个⼈资料设置开发(⼀)08:33课时127视频个⼈资料设置开发(⼆)10:58章节19:⼆维码名⽚页开发课时128视频⼆维码名⽚页开发06:12章节20:收藏列表开发课时129视频收藏列表开发06:21章节21:公共搜索页开发课时130视频公共搜索页开发18:21章节22:发布朋友圈页⾯开发课时131视频发布朋友圈页⾯-⽂字11:43课时132视频发布朋友圈页⾯-图⽂(⼀)06:45课时133视频发布朋友圈页⾯-图⽂(⼆)09:07课时134视频发布朋友圈页⾯-图⽂(三)09:37课时135视频发布朋友圈页⾯-视频09:45章节23:登录注册页课时136视频登录页⾯开发15:31课时137视频注册页⾯开发03:52课时138视频启动时登录页到⾸页过渡优化03:51章节24:egg.js基础课课时139视频创建egg.js项⽬07:30课时140视频写第⼀个api接⼝08:42课时141视频router路由传参(⼀)11:30课时142视频router路由传参(⼆)11:22课时143视频资源路由和路由分组11:03课时144视频数据库迁移(⼀)11:06课时145视频数据库迁移(⼆)07:57课时146视频sequelize模型-新增14:59课时147视频sequelize模型-批量新增和修改器08:53课时148视频sequelize模型-查询单个10:39课时149视频sequelize模型-查询多个和获取器07:55课时150视频sequelize模型-where操作符08:26课时151视频sequelize模型-字段限制排序分页12:35课时152视频sequelize模型-修改和限制字段11:09课时153视频sequelize模型-删除和批量删除07:45课时154视频错误和异常统⼀处理11:59课时155视频中间件配置08:11课时156视频参数验证10:33章节25:后端api开发和前后端交互课时157视频创建项⽬和基础配置07:45课时158视频全局抛出异常处理11:59课时159视频封装api返回格式扩展08:31课时160视频sequelize数据库和迁移配置08:06课时161视频⽤户表设计和迁移10:08课时162视频注册功能实现10:14课时163视频参数验证功能实现(⼀)08:59课时164视频参数验证功能实现(⼆)05:09课时165视频crypto 数据加密05:45课时166视频⽤户登录功能14:56课时167视频jwt 加密鉴权08:35课时168视频redis 缓存插件和封装09:23课时169视频全局权限验证中间件实现(⼀)11:31课时170视频全局权限验证中间件实现(⼆)08:14课时171视频退出登录功能03:35课时172视频搜索⽤户功能06:31课时173视频好友表和好友申请表设计07:38课时174视频申请添加好友功能(⼀)13:34课时175视频申请添加好友功能(⼆)07:48课时176视频获取好友申请列表(⼀)08:31课时177视频获取好友申请列表(⼆)13:03课时178视频处理好友申请(⼀)10:30课时179视频处理好友申请(⼆)11:38课时180视频获取通讯录列表(⼀)06:54课时181视频获取通讯录列表(⼆)06:35课时182视频查看好友资料功能实现07:53课时183视频移⼊移除⿊名单功能09:00课时184视频设置取消星标好友05:40课时185视频设置朋友圈权限功能05:35课时186视频举报投诉好友或群组功能(⼀)11:12课时187视频举报投诉好友或群组功能(⼆)05:21课时188视频设置备注和标签功能(⼀)07:25课时189视频设置备注和标签功能(⼆)16:55课时190视频设置备注和标签功能(三)15:44课时191视频安装websocket插件03:14课时192视频连接websocket和权限验证08:36课时193视频兼容H5端处理05:41课时194视频配置H5端跨域问题02:59课时195视频登录注册功能实现(⼀)14:21课时196视频登录注册功能实现(⼆)11:58课时197视频部署聊天调试环境13:04课时198视频退出登录功能实现12:13课时199视频全局mixin权限验证实现07:53课时200视频初始化登录状态08:51课时201视频搜索⽤户功能实现06:56课时202视频查看⽤户资料功能(⼀)10:19课时203视频查看⽤户资料功能(⼆)09:34课时204视频查看⽤户资料功能(三)05:50课时205视频修复处理好友申请api接⼝01:24课时206视频添加好友功能实现17:05课时207视频待处理好友申请数量提⽰13:47课时208视频待处理好友申请列表(⼀)15:28课时209视频待处理好友申请列表(⼆)16:40课时210视频处理好友申请功能08:24课时211视频通讯录列表功能实现10:49课时212视频完善查看个⼈资料页05:52课时213视频设置星标好友和设置⿊名单功能10:08课时214视频设置朋友圈权限功能实现09:38课时215视频设置备注和标签功能实现08:52课时216视频举报投诉好友功能实现06:57课时217视频完善⼏个⼩问题06:23课时218视频websocket连接权限验证和强制下线13:35课时219视频后端⽤户上线和下线深度剖析09:08课时220视频聊天类chat.js封装(⼀)14:08课时221视频聊天类chat.js封装(⼆)07:49课时222视频聊天类chat.js封装(三)10:16课时223视频聊天类封装(四)-创建销毁聊天对象12:54课时224视频发送消息接⼝开发-单聊(⼀)13:36课时225视频发送消息接⼝开发-单聊(⼆)12:32课时226视频聊天类封装(五)-发送消息12:35课时227视频聊天类封装(六)-组织发送消息格式16:59课时228视频聊天类封装(七)-完善发送消息状态08:24课时229视频聊天类封装(⼋)-添加聊天记录18:03课时230视频聊天类封装(九)-更新指定聊天记录07:18课时231视频聊天类封装(⼗)-更新会话列表(⼀)19:59课时232视频聊天类封装(⼗⼀)-更新会话列表(⼆)18:18课时233视频聊天类封装(⼗三)-断线重连提⽰05:08课时234视频渲染和监听聊天会话列表15:51课时235视频聊天类封装(⼗四)-处理接收消息15:10课时236视频初始化总未读数10:57课时237视频聊天类封装(⼗五)-读取会话功能10:41课时238视频聊天页实时接收信息功能实现07:54课时239视频获取当前会话详细资料13:20课时240视频创建群聊功能(⼀)10:54课时241视频创建群聊功能(⼆)13:08课时242视频创建群聊功能(三)14:13课时243视频创建群聊功能(四)14:05课时244视频创建群聊功能(五)08:51课时245视频群聊发送和接收消息18:59课时246视频群昵称显⽰13:19课时247视频获取离线消息08:44课时248视频置于底部兼容H5端06:39课时249视频群聊列表分页(⼀)10:06课时250视频群聊列表分页(⼆)14:25课时251视频获取群聊相关信息(⼀)12:30课时252视频获取群聊相关信息(⼆)07:45课时253视频修改群名称功能(⼀)16:30课时254视频修改群名称功能(⼆)09:34课时255视频群公告推送功能(⼀)07:35课时256视频群公告推送功能(⼆)08:42课时257视频修改我在本群的昵称(⼀)11:11课时258视频修改我在本群的昵称(⼆)07:04课时259视频退出和解散群聊(⼀)11:05课时260视频退出和解散群聊(⼆)06:28课时261视频删除指定会话功能05:00课时262视频群资料设置功能12:28课时263视频群⼆维码⽣成(⼀)10:36课时264视频群⼆维码⽣成(⼆)07:00课时265视频⽣成个⼈⼆维码名⽚05:51课时266视频清空聊天记录功能05:51课时267视频发送表情包功能06:10课时268视频上传⽂件和oss配置14:32课时269视频上传⽂件请求封装16:02课时270视频发送图⽚功能10:02课时271视频发送短视频功能07:41课时272视频发送语⾳功能16:56课时273视频撤回消息功能(⼀)15:06课时274视频撤回消息功能(⼆)14:29课时275视频撤回消息功能(三)15:55课时276视频撤回消息功能(四)08:35课时277视频转发功能实现(⼀)12:13课时278视频转发功能实现(⼆)13:42课时279视频转发功能实现(三)18:00课时280视频转发功能实现(四)03:39课时281视频复制功能实现03:18课时282视频删除指定消息记录18:09课时283视频收藏相关接⼝开发12:08课时284视频创建收藏和收藏列表18:13课时285视频删除收藏功能02:58课时286视频发送收藏功能11:05课时287视频发送名⽚功能(⼀)06:11课时288视频发送名⽚功能(⼆)15:10课时289视频好友申请实时通知04:32课时290视频同意添加好友实时通知08:05课时291视频修改⽤户个⼈资料(⼀)04:54课时292视频修改⽤户个⼈资料(⼆)08:17课时293视频删除好友功能12:16课时294视频朋友圈相关数据表迁移17:58课时295视频发布朋友圈api开发(⼀)17:30课时296视频发布朋友圈api开发(⼆)08:38课时297视频发布朋友圈api开发(三)11:57课时298视频点赞朋友圈api开发(⼀)10:27课时299视频点赞朋友圈api开发(⼆)06:49课时300视频评论朋友圈api开发(⼀)08:21课时301视频评论朋友圈api开发(⼆)03:07课时302视频朋友圈列表api开发25:11课时303视频我的朋友圈列表api开发06:36课时304视频朋友圈列表分页功能实现17:37课时305视频我的朋友圈分页列表实现02:48课时306视频朋友圈点赞功能07:46课时307视频修复删除好友朋友圈记录问题06:53课时308视频朋友圈动态实时通知功能19:55课时309视频读取朋友圈动态功能07:34课时310视频朋友圈评论功能(⼀)13:26课时311视频朋友圈评论功能(⼆)02:09课时312视频朋友圈评论表情包功能02:33课时313视频发布朋友圈-批量上传图⽚11:06课时314视频发布朋友圈-上传短视频08:17课时315视频发布朋友圈-实时通知10:22课时316视频提醒谁看功能14:53课时317视频朋友圈查看权限20:42课时318视频初始化会话列表功能08:58课时319视频断线⾃动重连处理07:21课时320视频Token错误时⾃动退出04:59课时321视频查看聊天记录功能10:02课时322视频将某⼈踢出群聊(⼀)09:45课时323视频将某⼈踢出群聊(⼆)13:45课时324视频将某⼈踢出群聊(三)04:59课时325视频邀请加⼊群聊(⼀)05:50课时326视频邀请加⼊群聊(⼆)10:03课时327视频部分⼩细节优化04:43课时328视频标签列表和标签⽤户列表22:17课时329视频⼩程序端兼容处理19:15课时330视频app端兼容处理(⼀)21:22课时331视频app端兼容处理(⼆)11:47课时332视频添加背景提⽰⾳(⼀)08:19课时333视频添加背景提⽰⾳(⼆)01:25课时334视频转发名⽚功能11:23课时335视频查看好友朋友圈完善(⼀)13:41课时336视频查看好友朋友圈完善(⼆)15:54课时337视频查看好友朋友圈完善(三)10:47课时338视频查看好友朋友圈完善(四)12:16课时339视频查看好友朋友圈完善(五)12:11课时340视频iOS端兼容处理21:05课时341视频多进程实现(⼀)17:59课时342视频多进程实现(⼆)14:18课时343视频修复弹框问题04:43课时344视频扫⼀扫添加群聊功能(⼀)13:34课时345视频扫⼀扫加⼊群聊功能(⼆)16:44课时346视频扫⼀扫加⼊群聊功能(三)07:24课时347视频扫⼀扫添加好友功能08:31课时348视频修复⼀部分⼩bug13:08章节26:打包上线课时349视频部署前环境搭建14:58课时350视频后端部署上线16:35课时351视频部署H5端11:40。
代码制作微信小程序下载什么软件好
代码制作微信小程序下载什么软件好在如今移动互联网时代,微信小程序的出现为我们提供了一个强大的工具,使得我们可以快速开发和发布应用程序。
但是要制作一个优秀的微信小程序,选择合适的开发工具是至关重要的。
本文将介绍几款常用的软件,帮助您选择适合自己的微信小程序开发工具。
1. 小程序开发者工具微信官方提供的小程序开发者工具是小程序开发的首选工具。
该工具集成了代码编辑、代码预览、调试、上传等功能,能够帮助开发者高效地开发和调试小程序。
同时,小程序开发者工具支持实时预览和模拟器调试,方便开发者即时查看效果和调试代码。
2. Visual Studio Code作为一款轻量级、功能强大的开源代码编辑器,Visual Studio Code(简称VS Code)在小程序开发中也备受开发者喜爱。
通过安装相应的插件,开发者可以实现小程序代码的编写、调试和上传功能。
VS Code支持丰富的代码提示、代码补全功能,使得开发小程序更加便捷高效。
3. HBuilderXHBuilderX是一款专为移动端开发而设计的IDE工具,支持多种前端开发框架,包括微信小程序。
HBuilderX集成了代码编辑、调试、模拟器调试等功能,能够帮助开发者快速开发和调试小程序。
此外,HBuilderX还提供了丰富的插件和模板,丰富了小程序开发的可能性。
4. Sublime TextSublime Text是一款轻量级、快速响应的代码编辑器,也是许多开发者喜爱的工具之一。
通过安装相应的插件和配置,开发者可以在Sublime Text中编写、调试和上传微信小程序代码。
Sublime Text支持多种主题和插件,能够满足不同开发者的需求。
5. WebStorm作为一款专业的JavaScript IDE,WebStorm在前端开发领域广受好评。
WebStorm提供了强大的代码分析、智能代码补全、实时错误检查等功能,使得开发者可以更轻松地进行小程序开发。
同时,WebStorm还支持多种版本控制系统,使得团队协作更加便捷。
Vue3.0网页版聊天Vue3.x+ElementPlus仿微信QQ界面vue3聊天实例
Vue3.0⽹页版聊天Vue3.x+ElementPlus仿微信QQ界⾯vue3聊天实例⼀、项⽬简介基于vue3.x+vuex+vue-router+element-plus+v3layer+v3scroll等技术构建的仿微信web桌⾯端聊天实战项⽬Vue3-Webchat。
基本上实现发送消息+emoj表情、图⽚/视频查看、链接预览、粘贴截图/拖拽发送图⽚、红包/朋友圈等功能。
⼆、使⽤技术编码器:Vscode技术框架:Vue3.0.5+Vuex4+VueRouter@4UI组件库:Element-Plus (饿了么桌⾯端vue3组件库)弹窗组件:V3Layer(基于vue3.x⾃定义对话框组件)滚动条组件:V3Scroll(基于vue3.x⾃定义虚拟美化滚动条组件)字体图标:阿⾥iconfont图标库三、项⽬结构⽬录◆⼀览效果◆ vue3.x封装⾃定义弹窗组件为了整体效果⼀致性,项⽬中⽤到的所有弹窗功能均是⾃定义组件v3layer来实现。
V3Layer 基于vue3.0开发的pc端⾃定义弹窗组件,⽀持拖拽(⾃定义拖拽区)、缩放、最⼤化、全屏、置顶弹框等功能。
由于之前有过⼀篇详细的介绍分享,感兴趣的话可以去看下哈。
其实v3layer弹窗是在原先的vue2版本中演变⽽来,专门为vue3项⽬⽽开发的,并且在功能及效果上和v2版的保持⼀致。
◆ vue3.x⾃定义美化模拟滚动条为了使得项⽬中页⾯滚动条更加精致,这⾥采⽤了⾃定义模拟滚动条vscroll组件来替代原⽣滚动条。
V3Scroll 基于vue3.0开发的⼩巧模拟滚动条组件。
⽀持⾃定义滚动条⼤⼩、颜⾊、层级及⾃动隐藏等功能。
并且⽀持实时监测DOM尺⼨改变来动态更新滚动条。
◆ vue3.x聊天主⾯板项⽬整体分为右上按钮、侧边栏、中间区、主体内容区三个模块。
<div :class="['vui__wrapper', store.state.isWinMaximize&&'maximize']"><div class="vui__board flexbox"><div class="flex1 flexbox"><!-- 顶部按钮(最⼤、最⼩、关闭) --><WinBar v-if="!route.meta.hideWinBar"/><!-- 侧边栏 --><SideBar v-if="!route.meta.hideSideBar" class="nt__sidebar flexbox flex-col"/><!-- 中间栏 --><Middle v-show="!route.meta.hideMiddle"/><!-- 主内容区 --><router-view class="nt__mainbox flex1 flexbox flex-col"></router-view></div></div></div>◆引⼊|注册公共组件// 引⼊饿了么vue3组件库import ElementPlus from 'element-plus'import 'element-plus/lib/theme-chalk/index.css'// 引⼊vue3.x弹窗组件import V3Layer from '../components/v3layer'// 引⼊vue3.x滚动条组件import V3Scroll from '@components/v3scroll'// 引⼊公共组件import WinBar from '../layouts/winbar.vue'import SideBar from '../layouts/sidebar'import Middle from '../layouts/middle'import Utils from './utils'const Plugins = app => {e(ElementPlus)e(V3Layer)e(V3Scroll)// 注册公共组件ponent('WinBar', WinBar)ponent('SideBar', SideBar)ponent('Middle', Middle)app.provide('utils', Utils)}项⽬中背景整体采⽤虚化⽑玻璃效果。
网络聊天工具的制作技术
本技术提供网络聊天工具,包括在线聊天软件系统,包括以下步骤:(1)安装为手机上app 应用小程序或安装为功能键、快捷键来快速启动,采用会员上网注册,请求者于视频监控范围内遇到想结识的目标人物,向系统发出请求信号,(2)系统截取当时视频资料,或者对人物图像标记、打上编号代码,(3)请求者下载、观看视频、寻找确认,或者将目标人物的编号代码发送回系统,或者按照说明指引对目标人物进行标记,或者自行标记,将标记后的视频、视频截图发送回系统,(4)系统中心用人工智能识别后将目标人物信息发送给请求者,(5)请求者通过系统向目标人物发起聊天信息、对话。
实现陌生人之间从相遇到认识。
技术要求1.一种网络聊天工具,包括提供具有互联网在线聊天功能的软件系统,其特征在于包括以下步骤:(1)所述网络聊天工具在手机上安装为app应用小程序、一个功能键、快捷键来快速启动应用,或者在其他设备上安装为一个功能键、快捷键来快速启动应用,采用会员上网注册形式,在注册时,需要上传本人头像、提供个人基本信息,或者采用会员身份证实名注册登录,会员处于有录像视频监控的范围内相遇其他会员后,会员利用手机或者电脑设备上网,登录系统,向系统中心发出请求信号,或者直接按下上述功能键、快捷键,一键申请向系统发出请求信号,该会员简称请求者,其他会员简称目标人物,发出的请求信号中或者包含有请求者当时的位置定位信息,或者给系统另外发出请求者当时的位置定位信息,另外上述其他会员包括相遇时还没有注册为会员但是以后才注册的会员;(2)系统中心与实时录像视频监控的监控平台中心设立数据互动、共享机制,截取有请求者图像的前一段时间的视频资料,系统、监控平台中心或者依据收到的请求者的位置定位信息快速锁定对应位置的监控探头设备,截取上述监控探头设备前一段时间的视频资料,利用人工智能脸部特征自动识别功能或者步态行为特征自动识别功能,或者在截取的视频资料中对除请求者之外的人物图像进行编号代码标记,制作成为提供给请求者可下载或者在线观看的视频资料;(3)请求者下载、观看视频,寻找确认目标人物的图像,或者将目标人物对应的编号代码发送回系统;或者按照系统说明指引下进行目标人物的标记,或者自行随意进行目标人物的标记,然后把自己在目标人物做了标记的视频、视频截图发送回系统;(4)系统中心解读请求者发送回的反馈信息,将编号代码标记或带有标记的视频、视频截图,与监控中心数据互动,共享数据,进行人工智能搜寻比对编号代码标记人物或者被标记的人物图像,利用人工智能脸部特征自动识别功能或者步态行为特征自动识别功能,锁定目标人物,对应其身份信息,将目标人物的头像或注册信息发送反馈给请求者;(5)请求者收到系统中心反馈的信息,向目标人物发起聊天信息、对话,目标人物会收到请求者的打招呼请求,目标人物上网,登录系统,或者登录手机上线注册的app上,可以查看请求者的头像、基本信息,或者点击回应,进入系统聊天模式,或者点击拒绝,系统会回应给请求者拒绝信息;上述以后才注册的会员,在注册成功、登录系统时,系统会检索此会员注册之前有没有成为过会员请求者的目标人物,有的话,系统会把所有相关针对此会员而发出过请求信号的请求者头像、基本信息发送给此会员,如果目标人物不是会员,系统也会提示请求者此目标人物不是会员。
仿微信聊天App开发制作经验分享
仿微信聊天App开发制作经验分享APICloud生态今天hi~早上好!今天从官方论坛搬运来一篇开发者仿微信App的经验分享,想要源代码的同学,可以去帖子里问问作者。
作者:川哥哥帖子链接:/bbs/thread-172273-1-1.html之前我一直用融云的接口做即时通信,自己也摸索了一段时间觉得融云做的挺好的。
可是接口是收费的还有点小贵,就放弃融云了决心自己搭建一个后台。
在网上查资料,去学校图书馆借书发现居然还有《30天,App开发从0到1》这本书,官方有点牛批额。
最终发现Workerman这个框架可以做即时通信,而且还容易上手。
装好了后发现Workerman基础框架只能做服务推送,不能实现客户端之间相互通信。
然后继续找资料发现GatewayWorker可以实现通信,接口都封装好了文档也详细。
自己把GatewayWorker环境弄好了后做了几个简单测试,发现客户端之间确实可以相互通信,还可以群聊。
然后就开始设计数据库以及完善各种业务逻辑(业务逻辑代码只能写在Events.php里面)。
服务器也是买的最便宜的1G的内存1M的带宽凑合着用(百度云服务器46元买的),服务端还没做压力测试不知道在线用户多了会不会崩!手机端经过多次测试发现原生HTML5 WebSocket以及平台的其他网络通信模块通信体验很差,最终选用webSocket v1.1.2模块和服务器进行通信很稳定。
目前app主要的功能:单聊、群聊、好友、二维码识别、消息数据本地存储、消息请求验证、意见反馈、消息收藏、微信登陆、截图。
消息类型:语音、文字、表情、图片、位置。
平台用到的模块:webSocket、aMap、NVTabBar、db、fs、UIChatBox、FNScanner、wxPlus、trans、imageFilter、trans。
App开发制作零零散散花了半年的时间,总体来讲前端功能模块设计要难些,很多功能比较麻烦。
后端比较简单最主要就是要把数据库表设计好,App图标和名字都是临时想的。
【精】微信对话生成器
【精】微信对话生成器
转自于福利吧
微信对话生成器。
偶然看到这个软件,很有意思,装B娱乐用的,想试试做个奔波儿灞那样的对话吗?试试这个吧。
特放上来共享,大家也一起娱乐一下!
~~~~~~~~~~~~~~~~~~
1、一键制作微信对话截图,和真实交易记录100%一致。
2、任意设置对话日期时间、对象(昵称)、内容(文本、表情、微信收款)
3、可选择运营商、网络、QQ、360图标、头像,电量可调,支持滚屏,可多屏截图
4、操作简单:左边输入数据,右边直接显示结果图,软件自带高清截图功能,不失真
~~~~~~~~~~~~~~~~~~~
1:搞了很久,软件是网络验证的,不能做注册机,只有强行跳转爆破。
软件启动之后不需要注册,直接进入主界面即可使用。
2:功能没有全部测试,WIN7 64位下可以,XP下没有测试.
3:卡巴斯基、瑞星扫描正常,360是因为软件本身就有冲突,这个没有办法.
4:点个赞你不给也浪费了,给了你也不会少坨肉,大半夜的鼓励
一下吧
~~~~~~~~~~~~~~~~~~~~~ 改變顏色
微信对话生成器爆破版.rar
115网盘礼包码:5lbdzsso1mth
分享者:/group/yaoqingzhuce/。
Android仿微信语音聊天界面设计
Android仿微信语⾳聊天界⾯设计有段时间没有看视频了,昨天晚上抽了点空时间,⼜看了下鸿洋⼤神的视频教程,⼜抽时间写了个学习记录。
代码和⽼师讲的基本⼀样,⽹上也有很多相同的博客。
我只是在AndroidStudio环境下写的。
—-主界⾯代码——public class MainActivity extends Activity {private ListView mListView;private ArrayAdapter<Recorder> mAdapter;private List<Recorder> mDatas = new ArrayList<Recorder>();private AudioRecorderButton mAudioRecorderButton;private View animView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_main);mListView = (ListView) findViewById(R.id.id_listview);mAudioRecorderButton = (AudioRecorderButton) findViewById(R.id.id_recorder_button);mAudioRecorderButton.setFinishRecorderCallBack(new AudioRecorderButton.AudioFinishRecorderCallBack() {public void onFinish(float seconds, String filePath) {Recorder recorder = new Recorder(seconds, filePath);mDatas.add(recorder);//更新数据mAdapter.notifyDataSetChanged();//设置位置mListView.setSelection(mDatas.size() - 1);}});mAdapter = new RecoderAdapter(this, mDatas);mListView.setAdapter(mAdapter);//listView的item点击事件mListView.setOnItemClickListener(new OnItemClickListener() {public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {// 声⾳播放动画if (animView != null) {animView.setBackgroundResource(R.drawable.adj);animView = null;}animView = view.findViewById(R.id.id_recoder_anim);animView.setBackgroundResource(R.drawable.play_anim);AnimationDrawable animation = (AnimationDrawable) animView.getBackground();animation.start();// 播放录⾳MediaPlayerManager.playSound(mDatas.get(position).filePath, new MediaPlayer.OnCompletionListener() {public void onCompletion(MediaPlayer mp) {//播放完成后修改图⽚animView.setBackgroundResource(R.drawable.adj);}});}});}@Overrideprotected void onPause() {super.onPause();MediaPlayerManager.pause();}@Overrideprotected void onResume() {super.onResume();MediaPlayerManager.resume();}@Overrideprotected void onDestroy() {super.onDestroy();MediaPlayerManager.release();}—⾃定义Button——-/*** @param* @author ldm* @description ⾃定义Button* @time 2016/6/25 9:26*/public class AudioRecorderButton extends Button {// 按钮正常状态(默认状态)private static final int STATE_NORMAL = 1;//正在录⾳状态private static final int STATE_RECORDING = 2;//录⾳取消状态private static final int STATE_CANCEL = 3;//记录当前状态private int mCurrentState = STATE_NORMAL;//是否开始录⾳标志private boolean isRecording = false;//判断在Button上滑动距离,以判断是否取消private static final int DISTANCE_Y_CANCEL = 50;//对话框管理⼯具类private DialogManager mDialogManager;//录⾳管理⼯具类private AudioManager mAudioManager;//记录录⾳时间private float mTime;// 是否触发longClickprivate boolean mReady;//录⾳准备private static final int MSG_AUDIO_PREPARED = 0x110;//⾳量发⽣改变private static final int MSG_VOICE_CHANGED = 0x111;//取消提⽰对话框private static final int MSG_DIALOG_DIMISS = 0x112;/*** @description 获取⾳量⼤⼩的线程* @author ldm* @time 2016/6/25 9:30* @param*/private Runnable mGetVoiceLevelRunnable = new Runnable() {public void run() {while (isRecording) {//判断正在录⾳try {Thread.sleep(100);mTime += 0.1f;//录⾳时间计算mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);//每0.1秒发送消息 } catch (InterruptedException e) {e.printStackTrace();}}}};private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_AUDIO_PREPARED://显⽰对话框mDialogManager.showRecordingDialog();isRecording = true;// 开启⼀个线程计算录⾳时间new Thread(mGetVoiceLevelRunnable).start();break;case MSG_VOICE_CHANGED://更新声⾳mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7));break;case MSG_DIALOG_DIMISS://取消对话框mDialogManager.dimissDialog();break;}super.handleMessage(msg);}};public AudioRecorderButton(Context context, AttributeSet attrs) {super(context, attrs);mDialogManager = new DialogManager(context);//录⾳⽂件存放地址String dir = Environment.getExternalStorageDirectory() + "/ldm_voice";mAudioManager = AudioManager.getInstance(dir);mAudioManager.setOnAudioStateListener(new AudioManager.AudioStateListener() { public void wellPrepared() {mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);}});// 由于这个类是button所以在构造⽅法中添加监听事件setOnLongClickListener(new OnLongClickListener() {public boolean onLongClick(View v) {mReady = true;mAudioManager.prepareAudio();return false;}});}public AudioRecorderButton(Context context) {this(context, null);}/*** @description 录⾳完成后的回调* @author ldm* @time 2016/6/25 11:18* @param*/public interface AudioFinishRecorderCallBack {void onFinish(float seconds, String filePath);}private AudioFinishRecorderCallBack finishRecorderCallBack;public void setFinishRecorderCallBack(AudioFinishRecorderCallBack listener) {finishRecorderCallBack = listener;}/*** @param* @description 处理Button的OnTouchEvent事件* @author ldm* @time 2016/6/25 9:35*/@Overridepublic boolean onTouchEvent(MotionEvent event) {//获取TouchEvent状态int action = event.getAction();// 获得x轴坐标int x = (int) event.getX();// 获得y轴坐标int y = (int) event.getY();switch (action) {case MotionEvent.ACTION_DOWN://⼿指按下changeState(STATE_RECORDING);break;case MotionEvent.ACTION_MOVE://⼿指移动if (isRecording) {//根据x,y的坐标判断是否需要取消if (wantToCancle(x, y)) {changeState(STATE_CANCEL);} else {changeState(STATE_RECORDING);}}break;case MotionEvent.ACTION_UP://⼿指放开if (!mReady) {reset();return super.onTouchEvent(event);}if (!isRecording || mTime < 0.6f) {//如果时间少于0.6s,则提⽰录⾳过短mDialogManager.tooShort();mAudioManager.cancel();// 延迟显⽰对话框mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1000);} else if (mCurrentState == STATE_RECORDING) {//如果状态为正在录⾳,则结束录制mDialogManager.dimissDialog();mAudioManager.release();if (finishRecorderCallBack != null) {finishRecorderCallBack.onFinish(mTime, mAudioManager.getCurrentFilePath()); }} else if (mCurrentState == STATE_CANCEL) { // 想要取消mDialogManager.dimissDialog();mAudioManager.cancel();}reset();break;}return super.onTouchEvent(event);}/*** 恢复状态及标志位*/private void reset() {isRecording = false;mTime = 0;mReady = false;changeState(STATE_NORMAL);}private boolean wantToCancle(int x, int y) {// 超过按钮的宽度if (x < 0 || x > getWidth()) {return true;}// 超过按钮的⾼度if (y < -DISTANCE_Y_CANCEL || y > getHeight() + DISTANCE_Y_CANCEL) {return true;}return false;}/*** @param* @description 根据状态改变Button显⽰* @author ldm* @time 2016/6/25 9:36*/private void changeState(int state) {if (mCurrentState != state) {mCurrentState = state;switch (state) {case STATE_NORMAL:setBackgroundResource(R.drawable.btn_recorder_normal);setText(R.string.str_recorder_normal);break;case STATE_RECORDING:setBackgroundResource(R.drawable.btn_recorder_recording);setText(R.string.str_recorder_recording);if (isRecording) {mDialogManager.recording();}break;case STATE_CANCEL:setBackgroundResource(R.drawable.btn_recorder_recording);mDialogManager.wantToCancel();setText(R.string.str_recorder_want_cancel);break;}}}}—-对话框管理⼯具类——/*** @description 对话框管理⼯具类* @author ldm* @time 2016/6/25 11:53* @param*/public class DialogManager {//弹出对话框private Dialog mDialog;//录⾳图标private ImageView mIcon;//⾳量显⽰图标private ImageView mVoice;//对话框上提⽰⽂字private TextView mLable;//上下⽂对象private Context mContext;public DialogManager(Context context) {this.mContext = context;}/*** @param* @description 显⽰对话框* @author ldm* @time 2016/6/25 9:56*/public void showRecordingDialog() {//根据指定sytle实例化DialogmDialog = new Dialog(mContext, R.style.AudioDialog);LayoutInflater inflater = LayoutInflater.from(mContext);View view = inflater.inflate(yout.dialog_recorder, null);mDialog.setContentView(view);mIcon = (ImageView) view.findViewById(R.id.id_recorder_dialog_icon); mVoice = (ImageView) view.findViewById(R.id.id_recorder_dialog_voice); mLable = (TextView) view.findViewById(R.id.id_recorder_dialog_label); mDialog.show();}/*** @param* @description 正在录⾳状态的对话框* @author ldm* @time 2016/6/25 10:08*/public void recording() {if (mDialog != null && mDialog.isShowing()) {mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.VISIBLE);mLable.setVisibility(View.VISIBLE);mIcon.setImageResource(R.drawable.recorder);mLable.setText("⼿指上滑,取消发送");}}/*** @param* @description 取消录⾳状态对话框* @author ldm* @time 2016/6/25 10:08*/public void wantToCancel() {if (mDialog != null && mDialog.isShowing()) {mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.GONE);mLable.setVisibility(View.VISIBLE);mIcon.setImageResource(R.drawable.cancel);mLable.setText("松开⼿指,取消发送");}}/*** @param* @description时间过短提⽰的对话框* @author ldm* @time 2016/6/25 10:09*/public void tooShort() {if (mDialog != null && mDialog.isShowing()) { //显⽰状态mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.GONE);mLable.setVisibility(View.VISIBLE);mIcon.setImageResource(R.drawable.voice_to_short);mLable.setText("录⾳时间过短");}}/*** @param* @description* @author ldm* @time 2016/6/25 取消(关闭)对话框*/public void dimissDialog() {if (mDialog != null && mDialog.isShowing()) { //显⽰状态mDialog.dismiss();mDialog = null;}}// 显⽰更新⾳量级别的对话框public void updateVoiceLevel(int level) {if (mDialog != null && mDialog.isShowing()) { //显⽰状态mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.VISIBLE);mLable.setVisibility(View.VISIBLE);//设置图⽚的id,我们放在drawable中的声⾳图⽚是以v+数字格式的int resId = mContext.getResources().getIdentifier("v" + level, "drawable", mContext.getPackageName()); mVoice.setImageResource(resId);}}}—-声⾳播放⼯具类——/*** @param* @author ldm* @description 播放声⾳⼯具类* @time 2016/6/25 11:29*/public class MediaPlayerManager {//播放⾳频API类:MediaPlayerprivate static MediaPlayer mMediaPlayer;//是否暂停private static boolean isPause;/*** @param* filePath:⽂件路径* onCompletionListener:播放完成监听* @description 播放声⾳* @author ldm* @time 2016/6/25 11:30*/public static void playSound(String filePath, MediaPlayer.OnCompletionListener onCompletionListener) { if (mMediaPlayer == null) {mMediaPlayer = new MediaPlayer();//设置⼀个error监听器mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {public boolean onError(MediaPlayer arg0, int arg1, int arg2) {mMediaPlayer.reset();return false;}});} else {mMediaPlayer.reset();}try {mMediaPlayer.setAudioStreamType(android.media.AudioManager.STREAM_MUSIC);mMediaPlayer.setOnCompletionListener(onCompletionListener);mMediaPlayer.setDataSource(filePath);mMediaPlayer.prepare();mMediaPlayer.start();} catch (Exception e) {}}/*** @param* @description 暂停播放* @author ldm* @time 2016/6/25 11:31*/public static void pause() {if (mMediaPlayer != null && mMediaPlayer.isPlaying()) { //正在播放的时候mMediaPlayer.pause();isPause = true;}}/*** @param* @description 重新播放* @author ldm* @time 2016/6/25 11:31*/public static void resume() {if (mMediaPlayer != null && isPause) {mMediaPlayer.start();isPause = false;}}/*** @param* @description 释放操作* @author ldm* @time 2016/6/25 11:32*/public static void release() {if (mMediaPlayer != null) {mMediaPlayer.release();mMediaPlayer = null;}}—–录⾳操作⼯具类—–/*** @param* @author ldm* @description 录⾳管理⼯具类* @time 2016/6/25 9:39*/public class AudioManager {//AudioRecord: 主要是实现边录边播(AudioRecord+AudioTrack)以及对⾳频的实时处理。
Android仿微信语音聊天功能
Android仿微信语⾳聊天功能本⽂实例讲述了Android仿微信语⾳聊天功能代码。
分享给⼤家供⼤家参考。
具体如下:项⽬效果如下:具体代码如下:AudioManager.javapackage com.xuliugen.weichat;import java.io.File;import java.io.IOException;import java.util.UUID;import android.media.MediaRecorder;public class AudioManager {private MediaRecorder mMediaRecorder;private String mDir;private String mCurrentFilePath;private static AudioManager mInstance;private boolean isPrepare;private AudioManager(String dir) {mDir = dir;}public static AudioManager getInstance(String dir) {if (mInstance == null) {synchronized (AudioManager.class) {if (mInstance == null) {mInstance = new AudioManager(dir);}}}return mInstance;}/*** 使⽤接⼝⽤于回调*/public interface AudioStateListener {void wellPrepared();}public AudioStateListener mAudioStateListener;/*** 回调⽅法*/public void setOnAudioStateListener(AudioStateListener listener) {mAudioStateListener = listener;}// 去准备public void prepareAudio() {try {isPrepare = false;File dir = new File(mDir);if (!dir.exists()) {dir.mkdirs();}String fileName = generateFileName();File file = new File(dir, fileName);mCurrentFilePath =file.getAbsolutePath();mMediaRecorder = new MediaRecorder();// 设置输出⽂件mMediaRecorder.setOutputFile(dir.getAbsolutePath());// 设置MediaRecorder的⾳频源为麦克风mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置⾳频格式mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR); // 设置⾳频编码mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 准备录⾳mMediaRecorder.prepare();// 开始mMediaRecorder.start();// 准备结束isPrepare = true;if (mAudioStateListener != null) {mAudioStateListener.wellPrepared();}} catch (IllegalStateException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** 随机⽣成⽂件的名称*/private String generateFileName() {return UUID.randomUUID().toString() + ".amr";}public int getVoiceLevel(int maxlevel) {if (isPrepare) {try {// mMediaRecorder.getMaxAmplitude() 1~32767return maxlevel * mMediaRecorder.getMaxAmplitude() / 32768 + 1; } catch (Exception e) {}}return 1;}/*** 释放资源*/public void release() {//mMediaRecorder.stop();mMediaRecorder.reset();mMediaRecorder = null;}/*** 取消录⾳*/public void cancel() {release();if (mCurrentFilePath != null) {File file = new File(mCurrentFilePath);file.delete();mCurrentFilePath = null;}}public String getCurrentFilePath() {return mCurrentFilePath;}}AudioRecorderButton.javapackage com.xuliugen.weichat;import android.content.Context;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.widget.Button;import com.xuliugen.weichat.R;import com.xuliugen.weichat.AudioManager.AudioStateListener; public class AudioRecorderButton extends Button {private static final int STATE_NORMAL = 1;// 默认的状态private static final int STATE_RECORDING = 2;// 正在录⾳private static final int STATE_WANT_TO_CANCEL = 3;// 希望取消 private int mCurrentState = STATE_NORMAL; // 当前的状态private boolean isRecording = false;// 已经开始录⾳private static final int DISTANCE_Y_CANCEL = 50;private DialogManager mDialogManager;private AudioManager mAudioManager;private float mTime;// 是否触发longClickprivate boolean mReady;private static final int MSG_AUDIO_PREPARED = 0x110;private static final int MSG_VOICE_CHANGED = 0x111;private static final int MSG_DIALOG_DIMISS = 0x112;/** 获取⾳量⼤⼩的线程*/private Runnable mGetVoiceLevelRunnable = new Runnable() {public void run() {while (isRecording) {try {Thread.sleep(100);mTime += 0.1f;mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);} catch (InterruptedException e) {e.printStackTrace();}}}};private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_AUDIO_PREPARED:// 显⽰對話框在开始录⾳以后mDialogManager.showRecordingDialog();isRecording = true;// 开启⼀个线程new Thread(mGetVoiceLevelRunnable).start();break;case MSG_VOICE_CHANGED:mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7)); break;case MSG_DIALOG_DIMISS:mDialogManager.dimissDialog();break;}super.handleMessage(msg);}};/*** 以下2个⽅法是构造⽅法*/public AudioRecorderButton(Context context, AttributeSet attrs) {super(context, attrs);mDialogManager = new DialogManager(context);String dir = "/storage/sdcard0/my_weixin";//String dir = Environment.getExternalStorageDirectory()+"/my_weixin"; mAudioManager = AudioManager.getInstance(dir);mAudioManager.setOnAudioStateListener(new AudioStateListener() { public void wellPrepared() {mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);}});// 由于这个类是button所以在构造⽅法中添加监听事件setOnLongClickListener(new OnLongClickListener() {public boolean onLongClick(View v) {mReady = true;mAudioManager.prepareAudio();return false;}});}public AudioRecorderButton(Context context) {this(context, null);}/*** 录⾳完成后的回调*/public interface AudioFinishRecorderListener {void onFinish(float seconds, String filePath);}private AudioFinishRecorderListener audioFinishRecorderListener;public void setAudioFinishRecorderListener(AudioFinishRecorderListener listener) {audioFinishRecorderListener = listener;}/*** 屏幕的触摸事件*/@Overridepublic boolean onTouchEvent(MotionEvent event) {int action = event.getAction();int x = (int) event.getX();// 获得x轴坐标int y = (int) event.getY();// 获得y轴坐标switch (action) {case MotionEvent.ACTION_DOWN:changeState(STATE_RECORDING);break;case MotionEvent.ACTION_MOVE:if (isRecording) {// 如果想要取消,根据x,y的坐标看是否需要取消if (wantToCancle(x, y)) {changeState(STATE_WANT_TO_CANCEL);} else {changeState(STATE_RECORDING);}}break;case MotionEvent.ACTION_UP:if (!mReady) {reset();return super.onTouchEvent(event);}if (!isRecording || mTime < 0.6f) {mDialogManager.tooShort();mAudioManager.cancel();mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1000);// 延迟显⽰对话框 } else if (mCurrentState == STATE_RECORDING) { // 正在录⾳的时候,结束mDialogManager.dimissDialog();mAudioManager.release();if (audioFinishRecorderListener != null) {audioFinishRecorderListener.onFinish(mTime,mAudioManager.getCurrentFilePath()); }} else if (mCurrentState == STATE_WANT_TO_CANCEL) { // 想要取消mDialogManager.dimissDialog();mAudioManager.cancel();}reset();break;}return super.onTouchEvent(event);}/*** 恢复状态及标志位*/private void reset() {isRecording = false;mTime = 0;mReady = false;changeState(STATE_NORMAL);}private boolean wantToCancle(int x, int y) {if (x < 0 || x > getWidth()) { // 超过按钮的宽度return true;}// 超过按钮的⾼度if (y < -DISTANCE_Y_CANCEL || y > getHeight() + DISTANCE_Y_CANCEL) { return true;}return false;}/*** 改变*/private void changeState(int state) {if (mCurrentState != state) {mCurrentState = state;switch (state) {case STATE_NORMAL:setBackgroundResource(R.drawable.btn_recorder_normal);setText(R.string.str_recorder_normal);break;case STATE_RECORDING:setBackgroundResource(R.drawable.btn_recorder_recording);setText(R.string.str_recorder_recording);if (isRecording) {mDialogManager.recording();}break;case STATE_WANT_TO_CANCEL:setBackgroundResource(R.drawable.btn_recorder_recording);setText(R.string.str_recorder_want_cancel);mDialogManager.wantToCancel();break;}}}}DialogManager.javapackage com.xuliugen.weichat;import android.app.AlertDialog;import android.content.Context;import youtInflater;import android.view.View;import android.widget.ImageView;import android.widget.TextView;import com.xuliugen.weichat.R;/*** ⽤于管理Dialog** @author xuliugen**/public class DialogManager {private AlertDialog.Builder builder;private ImageView mIcon;private ImageView mVoice;private TextView mLable;private Context mContext;private AlertDialog dialog;//⽤于取消AlertDialog.Builder/*** 构造⽅法传⼊上下⽂*/public DialogManager(Context context) {this.mContext = context;}// 显⽰录⾳的对话框public void showRecordingDialog() {builder = new AlertDialog.Builder(mContext, R.style.AudioDialog);LayoutInflater inflater = LayoutInflater.from(mContext);View view = inflater.inflate(yout.dialog_recorder,null);mIcon = (ImageView) view.findViewById(R.id.id_recorder_dialog_icon); mVoice = (ImageView) view.findViewById(R.id.id_recorder_dialog_voice); mLable = (TextView) view.findViewById(R.id.id_recorder_dialog_label); builder.setView(view);builder.create();dialog = builder.show();}public void recording(){if(dialog != null && dialog.isShowing()){ //显⽰状态mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.VISIBLE);mLable.setVisibility(View.VISIBLE);mIcon.setImageResource(R.drawable.recorder);mLable.setText("⼿指上滑,取消发送");}}// 显⽰想取消的对话框public void wantToCancel() {if(dialog != null && dialog.isShowing()){ //显⽰状态mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.GONE);mLable.setVisibility(View.VISIBLE);mIcon.setImageResource(R.drawable.cancel);mLable.setText("松开⼿指,取消发送");}}// 显⽰时间过短的对话框public void tooShort() {if(dialog != null && dialog.isShowing()){ //显⽰状态mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.GONE);mLable.setVisibility(View.VISIBLE);mIcon.setImageResource(R.drawable.voice_to_short);mLable.setText("录⾳时间过短");}}// 显⽰取消的对话框public void dimissDialog() {if(dialog != null && dialog.isShowing()){ //显⽰状态dialog.dismiss();dialog = null;}}// 显⽰更新⾳量级别的对话框public void updateVoiceLevel(int level) {if(dialog != null && dialog.isShowing()){ //显⽰状态// mIcon.setVisibility(View.VISIBLE);// mVoice.setVisibility(View.VISIBLE);// mLable.setVisibility(View.VISIBLE);//设置图⽚的idint resId = mContext.getResources().getIdentifier("v"+level, "drawable", mContext.getPackageName()); mVoice.setImageResource(resId);}}}MainActivity.javapackage com.xuliugen.weichat;import java.util.ArrayList;import java.util.List;import com.xuliugen.weichat.AudioRecorderButton.AudioFinishRecorderListener;import android.app.Activity;import android.graphics.drawable.AnimationDrawable;import android.media.MediaPlayer;import android.os.Bundle;import android.view.View;import android.widget.AdapterView;import android.widget.ArrayAdapter;import android.widget.ListView;import android.widget.AdapterView.OnItemClickListener;public class MainActivity extends Activity {private ListView mListView;private ArrayAdapter<Recorder> mAdapter;private List<Recorder> mDatas = new ArrayList<MainActivity.Recorder>();private AudioRecorderButton mAudioRecorderButton;private View animView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_main);mListView = (ListView) findViewById(R.id.id_listview);mAudioRecorderButton = (AudioRecorderButton) findViewById(R.id.id_recorder_button);mAudioRecorderButton.setAudioFinishRecorderListener(new AudioFinishRecorderListener() {public void onFinish(float seconds, String filePath) {Recorder recorder = new Recorder(seconds, filePath);mDatas.add(recorder);mAdapter.notifyDataSetChanged(); //通知更新的内容mListView.setSelection(mDatas.size() - 1); //将lisview设置为最后⼀个}});mAdapter = new RecoderAdapter(this, mDatas);mListView.setAdapter(mAdapter);//listView的item点击事件mListView.setOnItemClickListener(new OnItemClickListener() {public void onItemClick(AdapterView<?> arg0, View view,int position, long id) {// 播放动画(帧动画)if (animView != null) {animView.setBackgroundResource(R.drawable.adj);animView = null;}animView = view.findViewById(R.id.id_recoder_anim);animView.setBackgroundResource(R.drawable.play_anim);AnimationDrawable animation = (AnimationDrawable) animView.getBackground();animation.start();// 播放录⾳MediaManager.playSound(mDatas.get(position).filePath,new MediaPlayer.OnCompletionListener() { public void onCompletion(MediaPlayer mp) {animView.setBackgroundResource(R.drawable.adj);}});}});}@Overrideprotected void onPause() {super.onPause();MediaManager.pause();}@Overrideprotected void onResume() {super.onResume();MediaManager.resume();}@Overrideprotected void onDestroy() {super.onDestroy();MediaManager.release();}class Recorder {float time;String filePath;public Recorder(float time, String filePath) {super();this.time = time;this.filePath = filePath;}public float getTime() {return time;}public void setTime(float time) {this.time = time;}public String getFilePath() {return filePath;}public void setFilePath(String filePath) {this.filePath = filePath;}}}MediaManager.javapackage com.xuliugen.weichat;import android.media.AudioManager;import android.media.MediaPlayer;import android.media.MediaPlayer.OnCompletionListener;import android.media.MediaPlayer.OnErrorListener;public class MediaManager {private static MediaPlayer mMediaPlayer;private static boolean isPause;/*** 播放⾳乐* @param filePath* @param onCompletionListener*/public static void playSound(String filePath,OnCompletionListener onCompletionListener) { if (mMediaPlayer == null) {mMediaPlayer = new MediaPlayer();//设置⼀个error监听器mMediaPlayer.setOnErrorListener(new OnErrorListener() {public boolean onError(MediaPlayer arg0, int arg1, int arg2) {mMediaPlayer.reset();return false;}});} else {mMediaPlayer.reset();}try {mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mMediaPlayer.setOnCompletionListener(onCompletionListener);mMediaPlayer.setDataSource(filePath);mMediaPlayer.prepare();mMediaPlayer.start();} catch (Exception e) {}}/*** 暂停播放*/public static void pause() {if (mMediaPlayer != null && mMediaPlayer.isPlaying()) { //正在播放的时候mMediaPlayer.pause();isPause = true;}}/*** 当前是isPause状态*/public static void resume() {if (mMediaPlayer != null && isPause) {mMediaPlayer.start();isPause = false;}}/*** 释放资源*/public static void release() {if (mMediaPlayer != null) {mMediaPlayer.release();mMediaPlayer = null;}}}RecoderAdapter.javapackage com.xuliugen.weichat;import java.util.List;import android.content.Context;import android.util.DisplayMetrics;import youtInflater;import android.view.View;import android.view.ViewGroup;import android.view.WindowManager;import android.widget.ArrayAdapter;import android.widget.TextView;import com.xuliugen.weichat.MainActivity.Recorder;public class RecoderAdapter extends ArrayAdapter<Recorder> {private Context mContext;private List<Recorder> mDatas;private int mMinItemWidth; //最⼩的item宽度private int mMaxItemWidth; //最⼤的item宽度private LayoutInflater mInflater;public RecoderAdapter(Context context, List<Recorder> datas) {super(context, -1, datas);mContext = context;mDatas = datas;//获取屏幕的宽度WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics();wm.getDefaultDisplay().getMetrics(outMetrics);mMaxItemWidth = (int) (outMetrics.widthPixels * 0.7f);mMinItemWidth = (int) (outMetrics.widthPixels * 0.15f);mInflater = LayoutInflater.from(context);}/*** 定义⼀个ViewHolder*/private class ViewHolder {TextView seconds; // 时间View length; // 长度}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder = null;if (convertView == null) {convertView = mInflater.inflate(yout.item_recoder, parent,false);holder = new ViewHolder();holder.seconds = (TextView) convertView.findViewById(R.id.id_recoder_time);holder.length = convertView.findViewById(R.id.id_recoder_lenght);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}holder.seconds.setText(Math.round(getItem(position).time) + "\"");youtParams lp = holder.length.getLayoutParams();lp.width = (int) (mMinItemWidth + (mMaxItemWidth / 60f)* getItem(position).time);return convertView;}}本⽂已被整理到了《》,欢迎⼤家学习阅读。
Android仿微信录音功能
Android仿微信录⾳功能提要:需求是开发类似微信发语⾳的功能,没有语⾳转⽂字。
⽹上看了⼀些代码,不能拿来直接⽤,部分代码逻辑有问题,所以想把⾃⼰的代码贴出来,仅供参考。
功能:a、设置最⼤录⾳时长和录⾳倒计时(为了⽅便测试,最⼤时长设置为15秒,开始倒计时设置为7秒)b、在录⾳之前检查录⾳和存储权限源码:1、录⾳对话框管理类DialogManager:/*** 功能:录⾳对话框管理类*/public class DialogManager {private AlertDialog.Builder builder;private AlertDialog dialog;private ImageView mIcon;private ImageView mVoice;private TextView mLabel;private Context context;/*** 构造⽅法** @param context Activity级别的Context*/public DialogManager(Context context) {this.context = context;}/*** 显⽰录⾳的对话框*/public void showRecordingDialog() {builder = new AlertDialog.Builder(context, R.style.AudioRecorderDialogStyle);LayoutInflater inflater = LayoutInflater.from(context);View view = inflater.inflate(yout.audio_recorder_dialog, null);mIcon = view.findViewById(R.id.iv_dialog_icon);mVoice = view.findViewById(R.id.iv_dialog_voice);mLabel = view.findViewById(_dialog_label);builder.setView(view);dialog = builder.create();dialog.show();dialog.setCanceledOnTouchOutside(false);}/*** 正在播放时的状态*/public void recording() {if (dialog != null && dialog.isShowing()) { //显⽰状态mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.VISIBLE);mLabel.setVisibility(View.VISIBLE);mIcon.setImageResource(R.drawable.ic_audio_recorder);mVoice.setImageResource(R.drawable.ic_audio_v1);mLabel.setText(R.string.audio_record_dialog_up_to_cancel);}}/*** 显⽰想取消的对话框*/public void wantToCancel() {if (dialog != null && dialog.isShowing()) { //显⽰状态mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.GONE);mLabel.setVisibility(View.VISIBLE);mIcon.setImageResource(R.drawable.ic_audio_cancel);mLabel.setText(R.string.audio_record_dialog_release_to_cancel);}}/*** 显⽰时间过短的对话框*/public void tooShort() {if (dialog != null && dialog.isShowing()) { //显⽰状态mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.GONE);mLabel.setVisibility(View.VISIBLE);mLabel.setText(R.string.audio_record_dialog_too_short);}}// 显⽰取消的对话框public void dismissDialog() {if (dialog != null && dialog.isShowing()) { //显⽰状态dialog.dismiss();dialog = null;}}/*** 显⽰更新⾳量级别的对话框** @param level 1-7*/public void updateVoiceLevel(int level) {if (dialog != null && dialog.isShowing()) { //显⽰状态mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.VISIBLE);mLabel.setVisibility(View.VISIBLE);int resId = context.getResources().getIdentifier("ic_audio_v" + level, "drawable", context.getPackageName()); mVoice.setImageResource(resId);}}public void updateTime(int time) {if (dialog != null && dialog.isShowing()) { //显⽰状态mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.VISIBLE);mLabel.setVisibility(View.VISIBLE);mLabel.setText(time + "s");}}}2、录⾳管理类AudioManager/*** 功能:录⾳管理类*/public class AudioManager {private MediaRecorder mMediaRecorder;private String mDir;private String mCurrentFilePath;private static AudioManager mInstance;private boolean isPrepared;private AudioManager(String dir) {this.mDir = dir;}//单例模式:在这⾥实例化AudioManager并传⼊录⾳⽂件地址public static AudioManager getInstance(String dir) {if (mInstance == null) {synchronized (AudioManager.class) {if (mInstance == null) {mInstance = new AudioManager(dir);}}}return mInstance;}/*** 回调准备完毕*/public interface AudioStateListener {void wellPrepared();}public AudioStateListener mListener;/*** 回调⽅法*/public void setOnAudioStateListener(AudioStateListener listener) {mListener = listener;}/*** 准备*/public void prepareAudio() {try {isPrepared = false;File dir = FileUtils.createNewFile(mDir);String fileName = generateFileName();File file = new File(dir, fileName);mCurrentFilePath = file.getAbsolutePath();Logger.t("AudioManager").i("audio file name :" + mCurrentFilePath);mMediaRecorder = new MediaRecorder();//设置输出⽂件mMediaRecorder.setOutputFile(mCurrentFilePath);//设置MediaRecorder的⾳频源为麦克风mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//设置⾳频格式mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); //设置⾳频的格式为AACmMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//准备录⾳mMediaRecorder.prepare();//开始mMediaRecorder.start();//准备结束isPrepared = true;if (mListener != null) {mListener.wellPrepared();}} catch (Exception e) {e.printStackTrace();}}/*** 随机⽣成⽂件的名称*/private String generateFileName() {return UUID.randomUUID().toString() + ".m4a";}public int getVoiceLevel(int maxLevel) {if (isPrepared) {try {//获得最⼤的振幅getMaxAmplitude() 1-32767return maxLevel * mMediaRecorder.getMaxAmplitude() / 32768 + 1;} catch (Exception e) {}}return 1;}/*** 释放资源*/public void release() {if (mMediaRecorder != null) {mMediaRecorder.stop();mMediaRecorder.release();mMediaRecorder = null;}}public void cancel() {release();if (mCurrentFilePath != null) {File file = new File(mCurrentFilePath);FileUtils.deleteFile(file);mCurrentFilePath = null;}}public String getCurrentFilePath() {return mCurrentFilePath;}}3、⾃定义录⾳按钮AudioRecorderButton/*** 功能:录⾳按钮*/public class AudioRecorderButton extends AppCompatButton {private Context mContext;//取消录⾳Y轴位移private static final int DISTANCE_Y_CANCEL = 80;//录⾳最⼤时长限制private static final int AUDIO_RECORDER_MAX_TIME = 15;//录⾳倒计时时间private static final int AUDIO_RECORDER_COUNT_DOWN = 7;//状态private static final int STATE_NORMAL = 1;// 默认的状态private static final int STATE_RECORDING = 2;// 正在录⾳private static final int STATE_WANT_TO_CANCEL = 3;// 希望取消//当前的状态private int mCurrentState = STATE_NORMAL;//已经开始录⾳private boolean isRecording = false;//是否触发onLongClickprivate boolean mReady;private DialogManager mDialogManager;private AudioManager mAudioManager;private android.media.AudioManager audioManager;public AudioRecorderButton(Context context) {this(context, null);}public AudioRecorderButton(Context context, AttributeSet attrs) {super(context, attrs);this.mContext = context;mDialogManager = new DialogManager(context);audioManager = (android.media.AudioManager) context.getSystemService(Context.AUDIO_SERVICE);String dir = SdUtils.getCustomFolder("Audios");//创建⽂件夹mAudioManager = AudioManager.getInstance(dir);mAudioManager.setOnAudioStateListener(new AudioManager.AudioStateListener() {@Overridepublic void wellPrepared() {mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);}});//按钮长按准备录⾳包括startsetOnLongClickListener(new OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {//先判断有没有录⾳和存储权限,有则开始录⾳,没有就申请权限int hasAudioPermission = ContextCompat.checkSelfPermission(mContext, Manifest.permission.RECORD_AUDIO);int hasStoragePermission = ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE);if (hasAudioPermission == PackageManager.PERMISSION_GRANTED && hasStoragePermission == PackageManager.PERMISSION_GRANTED) { mReady = true;mAudioManager.prepareAudio();} else {RxPermissions permissions = new RxPermissions((FragmentActivity) mContext);Disposable disposable = permissions.request(Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE).subscribe(new Consumer<Boolean>() {@Overridepublic void accept(Boolean granted) {if (!granted) {ToastUtils.showShort("发送语⾳功能需要赋予录⾳和存储权限");}}});}return true;}});}private static final int MSG_AUDIO_PREPARED = 0X110;private static final int MSG_VOICE_CHANGED = 0X111;private static final int MSG_DIALOG_DISMISS = 0X112;private static final int MSG_TIME_OUT = 0x113;private static final int UPDATE_TIME = 0x114;private boolean mThreadFlag = false;//录⾳时长private float mTime;//获取⾳量⼤⼩的Runnableprivate Runnable mGetVoiceLevelRunnable = new Runnable() {@Overridepublic void run() {while (isRecording) {try {Thread.sleep(100);mTime += 0.1f;mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);if (mTime >= AUDIO_RECORDER_MAX_TIME) {//如果时间超过60秒,⾃动结束录⾳while (!mThreadFlag) {//记录已经结束了录⾳,不需要再次结束,以免出现问题mDialogManager.dismissDialog();mAudioManager.release();if (audioFinishRecorderListener != null) {//先回调,再Reset,不然回调中的时间是0audioFinishRecorderListener.onFinish(mTime, mAudioManager.getCurrentFilePath());mHandler.sendEmptyMessage(MSG_TIME_OUT);}mThreadFlag = !mThreadFlag;}isRecording = false;} else if (mTime >= AUDIO_RECORDER_COUNT_DOWN) {mHandler.sendEmptyMessage(UPDATE_TIME);}} catch (InterruptedException e) {e.printStackTrace();}}}};private Handler mHandler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {switch (msg.what) {case MSG_AUDIO_PREPARED:mDialogManager.showRecordingDialog();isRecording = true;new Thread(mGetVoiceLevelRunnable).start();break;case MSG_VOICE_CHANGED:mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7));break;case MSG_DIALOG_DISMISS:mDialogManager.dismissDialog();break;case MSG_TIME_OUT:reset();break;case UPDATE_TIME:int countDown = (int) (AUDIO_RECORDER_MAX_TIME - mTime);mDialogManager.updateTime(countDown);break;}return true;}});/*** 录⾳完成后的回调*/public interface AudioFinishRecorderListener {/*** @param seconds 时长* @param filePath ⽂件*/void onFinish(float seconds, String filePath);}private AudioFinishRecorderListener audioFinishRecorderListener;public void setAudioFinishRecorderListener(AudioFinishRecorderListener listener) {audioFinishRecorderListener = listener;}android.media.AudioManager.OnAudioFocusChangeListener onAudioFocusChangeListener = new android.media.AudioManager.OnAudioFocusChangeListener() {@Overridepublic void onAudioFocusChange(int focusChange) {if (focusChange == android.media.AudioManager.AUDIOFOCUS_LOSS) {audioManager.abandonAudioFocus(onAudioFocusChangeListener);}}};public void myRequestAudioFocus() {audioManager.requestAudioFocus(onAudioFocusChangeListener, android.media.AudioManager.STREAM_MUSIC, android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); }@Overridepublic boolean onTouchEvent(MotionEvent event) {Logger.t("AudioManager").i("x :" + event.getX() + "-Y:" + event.getY());switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mThreadFlag = false;isRecording = true;changeState(STATE_RECORDING);myRequestAudioFocus();break;case MotionEvent.ACTION_MOVE:if (isRecording) {//根据想x,y的坐标,判断是否想要取消if (event.getY() < 0 && Math.abs(event.getY()) > DISTANCE_Y_CANCEL) {changeState(STATE_WANT_TO_CANCEL);} else {changeState(STATE_RECORDING);}}break;case MotionEvent.ACTION_UP://如果longClick 没触发if (!mReady) {reset();return super.onTouchEvent(event);}//触发了onLongClick 没准备好,但是已经prepared已经start//所以消除⽂件夹if (!isRecording || mTime < 1.0f) {mDialogManager.tooShort();mAudioManager.cancel();mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DISMISS, 1000);} else if (mCurrentState == STATE_RECORDING) {//正常录制结束mDialogManager.dismissDialog();mAudioManager.release();if (audioFinishRecorderListener != null) {audioFinishRecorderListener.onFinish(mTime, mAudioManager.getCurrentFilePath());}} else if (mCurrentState == STATE_WANT_TO_CANCEL) {mDialogManager.dismissDialog();mAudioManager.cancel();}reset();audioManager.abandonAudioFocus(onAudioFocusChangeListener);break;}return super.onTouchEvent(event);}/*** 恢复状态标志位*/private void reset() {isRecording = false;mTime = 0;mReady = false;changeState(STATE_NORMAL);}/*** 改变状态*/private void changeState(int state) {if (mCurrentState != state) {mCurrentState = state;switch (state) {case STATE_NORMAL:setText(R.string.audio_record_button_normal);break;case STATE_RECORDING:if (isRecording) {mDialogManager.recording();}setText(R.string.audio_record_button_recording);break;case STATE_WANT_TO_CANCEL:mDialogManager.wantToCancel();setText(R.string.audio_record_button_cancel);break;}}}}4、DialogStyle<!--App Base Theme--><style name="AppThemeParent" parent="Theme.AppCompat.Light.NoActionBar"><!--不显⽰状态栏:22之前--><item name="android:windowNoTitle">true</item><item name="android:windowAnimationStyle">@style/ActivityAnimTheme</item><!--Activity动画--> <item name="actionOverflowMenuStyle">@style/MenuStyle</item><!--toolbar菜单样式--></style><!--Dialog式的Activity--><style name="ActivityDialogStyle" parent="AppThemeParent"><item name="android:windowBackground">@android:color/transparent</item><!-- 浮于Activity之上 --><item name="android:windowIsFloating">true</item><!-- 边框 --><item name="android:windowFrame">@null</item><!-- Dialog以外的区域模糊效果 --><item name="android:backgroundDimEnabled">true</item><!-- 半透明 --><item name="android:windowIsTranslucent">true</item><!-- Dialog进⼊及退出动画 --><item name="android:windowAnimationStyle">@style/ActivityDialogAnimation</item></style><!--Audio Recorder Dialog--><style name="AudioRecorderDialogStyle" parent="ActivityDialogStyle"><!-- Dialog以外的区域模糊效果 --><item name="android:backgroundDimEnabled">false</item></style><!-- Dialog动画:渐⼊渐出--><style name="ActivityDialogAnimation" parent="@android:style/Animation.Dialog"><item name="android:windowEnterAnimation">@anim/fade_in</item><item name="android:windowExitAnimation">@anim/fade_out</item></style>5、DialogLayout<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/audio_recorder_dialog_bg"android:gravity="center"android:orientation="vertical"android:padding="20dp"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"><ImageViewandroid:id="@+id/iv_dialog_icon"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ic_audio_recorder" /><ImageViewandroid:id="@+id/iv_dialog_voice"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ic_audio_v1" /></LinearLayout><TextViewandroid:id="@+id/tv_dialog_label"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="15dp"android:text="@string/audio_record_dialog_up_to_cancel"android:textColor="@color/white"android:textSize="15dp" /></LinearLayout>6、⽤到的字符串<!--AudioRecord--><string name="audio_record_button_normal">按住 说话</string><string name="audio_record_button_recording">松开 结束</string><string name="audio_record_button_cancel">松开⼿指 取消发送</string><string name="audio_record_dialog_up_to_cancel">⼿指上划,取消发送</string><string name="audio_record_dialog_release_to_cancel">松开⼿指,取消发送</string><string name="audio_record_dialog_too_short">录⾳时间过短</string>7、使⽤:按钮的样式不需要写在⾃定义Button中,⽅便使⽤<com.kidney.base_library.view.audioRecorder.AudioRecorderButtonandroid:id="@+id/btn_audio_recorder"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="@string/audio_record_button_normal" />AudioRecorderButton audioRecorderButton = findViewById(R.id.btn_audio_recorder);audioRecorderButton.setAudioFinishRecorderListener(new AudioRecorderButton.AudioFinishRecorderListener() { @Overridepublic void onFinish(float seconds, String filePath) {Logger.i(seconds + "秒:" + filePath);}});以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
随着移动互联网的快速发展,智能手机的快速普及,社交类软件基于对人们互联网社交需求的满足,逐渐成为人们生活、工作不可缺少的一部分。
如今打开手机的应用商店,各式各样的社交类APP让人眼花缭乱,每过一段时间都会有新的社交App崛起也有很多社交类App沉默,产品的快速迭代、入市退市,皆印证着市场的激烈与残酷。
因此,一款社交App要想在如此激烈的竞争环境中脱颖而出,并占有一席之地,就需要创造出留住用户的充分理由。
伴随着全球化的深入发展、国家一带一路的政策,许多人的社交圈从一城一国发展到多国全球,国人对外交往的需求与愿望日益迫切。
在这样的背景下,跨国社交、跨语言社交应运而生。
跨语言社交的障碍不在于你敢不敢说,而是想说却不会说。
如果有一款社交软件在满足用户基础交流的同时,还有更佳优化的拓
展和延伸,那它一定会受到用户的欢迎,尤其会被需要跨语言交流的人们所追捧。
据专业观察机构分析,未来几年移动社交的市场规模依然保持着稳中有增的活力,用户的需求正在由原始的关系维护向个人的自我追求进行过渡和升级,这将成为新的行业引爆点,也是社交类产品的发展趋势。
作为后起之秀,敏锐地把握住了这一点,以“让不同语言的人成为朋友”为愿景,围绕“跨语言社交”这一主打功能,不断发掘和满足用户的痛点,迅速获得了大量用户的亲睐。
以上就是为大家介绍的做一款社交app的知识,想了解更多就来咨询黑帽科技。
黑帽科技是一家集软件定制开发、软件外包、智慧信息化建设的软件开发服务商,黑帽科技拥有成熟的APP定制开发、小程序定制开发、软件项目外包开发平台。
是专业的互联网产品解决方案提供商,可提供互联网产品咨询、网站设计、网站开发、手机应用开发、移动应用开发。
黑帽科技为政府、企业以及团体提供行业解决方案和产品工程解决方案以及相关软件产品、平台及服务。
想要了解更多详情内容请拨打联系电话或登录浙江黑帽科技有限公司官网https:///咨询。