重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
动图录制所以图片有闪动,实际效果正常。
创新互联服务项目包括衡南网站建设、衡南网站制作、衡南网页制作以及衡南网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,衡南网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到衡南省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!
1.首先需要在目录在创建assets文件夹,用于存放资源文件。(loading.gif)
2.修改pubspec.yaml
注:亮度调节和音量调节gif无法体现,功能是ok的,其次默认Icon锁的close和open实在难以分辨。
环境:Flutter 2.8.1 channel stable ;Dart 2.15.1
需要音频播放器的看这里: Flutter音乐播放器
重点说下这个工具类,因为视频播放,涉及到状态改变有很多,笔者刚开始选择使用 InheritedWidget 来在众多的widget之间共享数据。但是总感觉这样有点繁琐,且不很优雅!
这里非广告,如果是使用 GetX 就很简单了,笔者也使用了 GetX 进行封装了,一泻千里的赶脚!,但是笔者还是那句话:刚开始接触Flutter的开发者不是很建议使用 GetX ,可以先熟悉下Flutter状态管理的基础原理再行使用。而且为了尽量简洁,还是不引入其他的第三方了。
我们选择对第三方插件进行封装的目的不外乎这几个:
于是笔者就写了一个工具类 VideoPlayerUtils ,专门且只用来处理播放器的所有业务。包括暂停、播放、跳转、调节音量、调节亮度、切换视频等操作。在所有的widget中不会引用关于 video_player 或其他第三方插件的任何信息, VideoPlayerUtils 负责widget与播放器之间的所有操作交互。后续优化迭代或更换播放器插件时,只需针对这个工具类进行修改,对所有widget不会有任何的影响,大大的解耦合了。
其中 VideoPlayerState :
提供以上的公共属性,可以通过 VideoPlayerUtils 来获取对应的值,使用 get 只读,使外界不会误修改这些属性,以保证数值的安全性。开发者可根据自身需要自行添加属性。
提供以上方法来处理播放器的所有业务。同样的开发者可根据自身需要自行添加或修改。
重点说下这个方法,是整个业务的核心方法,控制视频的播放或暂停。开发者只要遇到播放或暂停是均可调用此方法,具体是播放或暂停,内部根据传入的 url 自行判断,开发者不需要关心。
切换新视频也是使用此方法,传入的 url 与上次不一致,自动切换新视频。笔者可根据 statusListener 来监听播放状态的改变,以此处理自身逻辑。
这个也需要提下,视频播放器在播放新视频时会异步初始化,一般我们的操作是在 initState() 初始化,成功后再 setState() 。这里笔者遇到一个让人蛋疼的问题:
我们看 video_player 的使用:
VideoPlayer(controller) :widget中已经持有了controller。本来笔者封装的目的就是为了让widget与controller的之间解耦合。但此时的笔者。。。。
放弃不是不可能放弃的,这辈子都不会放弃的!
于是笔者取了巧,写了一个初始化监听器 initializedListener ,包换2个参数: bool,Widget ,初始化是否成功;其中widget为初始化成功返回需要展示的播放器UI,失败默认返回 const SizedBox() 。
到这里就可以简单使用了:
没看错,视频播放就是这么简单。
如果有更多的业务功能,笔者也按照自己的需求写了一套,同样的开发者可根据自身需要自行添加或修改。
VideoPlayerGestures 主要是处理手势的,比如快进、快退等跳转播放;左侧上下滑动调节亮度;右侧上下滑动调节音量;单击是否开启沉浸式播放,所有widget的隐藏与显示;双击播放、暂停等。
哦,还有 PercentageWidget 也放到这个文件下了,就是这玩意:
因为显示的百分比与手势相关,随着手势移动而更新。开发者可自行处理。
笔者处出于简单考虑,就按照整个UI的位置命名了。瞅一眼就知道是啥玩意。
同样的开发者可根据自身需要自行添加或修改。
就是这玩意:
同样的开发者可根据自身需要自行添加或修改。话说这个锁的 Icon 的open和close是真的难分辨!
就是这玩意:
同样的开发者可根据自身需要自行添加或修改。
这玩意是自定义的,别问,问就是跟产品干一架落了下风
主要就是自定义这玩意:
同样的开发者可根据自身需要自定义。
注:这里没有添加缓冲的进度,开发可查看 video_player 中的源码 VideoProgressIndicator ,按业务自行定义。
这玩意就是整合以上的widget,再考虑下全屏的安全区域,没啥东西。开发者可自行处理!
具体的实现监听器的思路, 看这里 。
自此一个漂亮的Flutter视频播放器就已经结束了。如果您觉得对您有些许帮助的话,欢迎 Star !
Image(图片组件)是显示图像的组件,一个显示图片的widget,支持图像格式:JPEG,PNG,GIF,动画GIF,WebP,动画WebP,BMP和WBM
构造方法
Image: 从ImageProvider获取数据
Image.network: 加载网络图片。
Image.asset: 加载本地图片文件。
new Image.file: 加载本地图片文件(File文件)图片。
new Image.memory: 加载Uint8List资源图片(byte数组)图片。
常用属性
方式一:CircleAvatar
CircleAvatar可以实现圆形头像,也可以添加一个子Widget:
在图片上加一个文本
方式二:ClipOval
ClipOval也可以实现圆角头像,而且通常是在只有头像时使用
方式三:Container+BoxDecoration
方法一:ClipRRect
方法二:Container + BoxDecoration
补充知识点
Icon字体图标和图片图标的区别 ?
Colors.red 是一个MaterialColor对象,为什么可以使用[](Colors.red[10])来设置颜色 ?
MaterialColor 继承于ColorSwatch,ColorSwatch中有[] 运算符重载;
本文将从三个方面介绍Android 图形系统。
图形系统提供绘图和图形处理支持。
Android 框架提供了各种用于 2D 和 3D 图形渲染的 API、图片解码库,以及各种Driver支持。
• 绘图API:2D引擎 Skia,3D引擎 OpenGL ES,RenderScript,OpenCV和Vulkan。
• 图片解码库:jpg,png,gif等。
应用开发者可通过三种方式将图像绘制到屏幕:
• Canvas : 2D图形API,Android View树实际的绘制者。
• OpenGL ES : 嵌入式设备的OpenGL 三维图形API子集。
• Vulkan :跨平台的2D和3D绘图引擎,Android 7.0后支持,NDK。
整个图形系统架构是一个生产者和消费者模式,五层依次介绍:
2D绘制:Canvas api / view 的子类 (button ,list)/自定义view
3D绘制:应用直接使用OpenGL 接口绘制图形(PixelFlinger对应的是openGl 1.0 ,GUP driver 对应的是2.0和3.0)
所有情况下的绘图都渲染到一个包含 GraphicBuffer的Surface上,当一块 Surface 显示在屏幕上时,就是用户所看到的窗口。
• Canvas:画布,2D图形API,Android View树实际的渲染者。
• Skia绘制:Android4.0之前默认使用,主线程通过CPU完成绘图指令操作,在复杂场景下单帧容易超过16ms导致卡顿。
WindowManagerService(WMS)窗口管理服务,管理系统中所有的窗口。
• 管理window (view的容器)
• Window与surface对应,一块显示区域。添加一个window,就是 WMS 为其分配一块 Surface 的过程。
Google 在Android source官网提示:
这里就对这些控件进行简单介绍:
Surface : Handle onto a raw buffer that is being managed by the screen compositor.
Surface 对应一块屏幕缓冲区。生产者是: SurfaceTexture、MediaRecorder 等,消费者是: OpenGL、MediaPlayer 或 CameraDevice等。每个window对应一个Surface。Canvas或OpenGL ES等最终都渲染到Surface上。
• Flutter在Android平台上也是直接渲染到Surface。例如:一个Activity/Dialog都是一个Surface,它承载了上层的图形数据,与SurfaceFlinger侧的Layer相对应。
Canvas(画布)实现由 Skia 图形库提供。为了确保两个客户端不会同时更新某个缓冲区,使用以下命令处理画布锁:
使用双缓冲机制,有自己的 surface,View只是一个透明的占位符,Surface可以在后台线程中绘制。双缓冲机制提高渲染效率,独立线程
绘制,提升流畅性。适合一些场景:需要界面迅速更新、UI绘制时间长、对帧率要求较高的情况。
提供访问和控制Surface 相关的方法 。通过SurfaceView的getHolder()函数可以获取SurfaceHolder对象,Surface 就在SurfaceHolder对象内。
addCallback(SurfaceHolder.Callbackcallback) /Canvas lockCanvas() /unlockCanvasAndPost(Canvascanvas)
SurfaceTexture: Surface 和 OpenGL ES (GLES) 纹理(Texture)的组合。将图像流转为 OpenGL 外部纹理。
TextureView:持有 SurfaceTexture,将图像处理为 OpenGL 纹理更新到 HardwareLayer。
GLSurfaceView:加入 EGL 管理,自带 GL 上下文和 GL 渲染线程
这些View通常涉及到Android音视频相关,需要高效的渲染能力。如下面的SurfaceTexture在camera中的应用。
简称Buffer, 一个Buffer包含一帧图像,Buffer由gralloc分配和回收。Buffer 属性包含:width, height, format, usage等
BufferQueue 的引入是为了解决显示和性能问题。
• Surface属于APP进程,Layer属于系统进程,如果它们之间只用一个Buffer,会存在显示和性能问题。
• 一些Buffer用于绘制,一些Buffer用于显示,双方处理完之后,交换一下Buffer,提高效率。
• BufferQueue中包含多个Buffer对象。
Android图形系统包含了两对生产者和消费者模型,它们都通过BufferQueue进行连接:
1.Canvas和OpenGL ES生产图形数据,SurfaceFlinger消费图形数据。
2.SurfaceFlinger合成所有图层的图形数据,Display显示合成结果。
code:frameworks/native/services/surfaceflinger
• Surface表示APP进程的一个窗口,承载了窗口的图形数据。
• SurfaceFlinger是系统进程合成所有窗口的系统服务,负责合成所有Surface提供的图形数据,然后送显到屏幕。
• SurfaceFlinger既是上层应用的消费者,又是Display的生产者,起到了承上启下的作用。
数据流:
合成示意图:
在介绍Vsync机制之前先介绍两个重要概念:
屏幕刷新率:屏幕每秒钟可以刷新多少次。60HZ刷新率,16.7ms刷新一次。(120HZ/8.3ms),硬件指标。
GPU 绘制帧率:GPU 每秒能够合成绘制多少帧。
软件层触发 View 绘制的时机是随机的,当下一次屏幕刷新时,屏幕从 Frame Buffer 中拿到的数据还是“帧1”的数据,导致“丢帧”。
每隔 16ms 硬件层发出 vsync 信号,应用层接收到此信号后会触发UI 的渲染流程,同时 vsync 信号也会触发 SurfaceFlinger 读取Buffer 中的数据,进行合成显示到屏幕上。
总结:Vsync机制将 CPU 和 GPU 的开始时间与屏幕刷新强行拖拽到同一起跑线
Android提供的Graphics流程相对比较复杂对其进行具象后的流程如下两张图所示:
flutter开发中,图片的引用是必不可少的,所以为了提高效率和精准度,我们需要对不同分辨率的手机使用相对应的切图图片,本章介绍如何进行 图片分辨率适配 和 图片批量拓展处理 。
flutter中会首先根据系统的devicePixelRatio(每一个逻辑像素包含多少个原始像素,可以通过MediaQueryData.devicePixelRatio来得到)来找对应倍数的文件夹下的图片,如果没有对应倍数,找最接近的。
所以在flutter项目中,我们需要构建对应的倍数像素文件夹
之后再pubspec.yaml中,配置assets文件后就可以使用了(如使用"assets/images/jay.png",会自动适配该像素下最接近的jay图片)。
使用flutter-img-sync插件批量化处理,具体操作如下
目前还不能处理gif、webp等格式的图片,而且如果和上边介绍的不同像素比适配方案一起使用的话,由于进行了精准定位,所以指定图片后就不能进行像素适配,这是目前还存在的较大问题,所以目前两者方案只能暂时取一使用。