【原创不易,转载请注明出处:https://blog.youkuaiyun.com/email_jade/article/details/86715600】
二零一八年的最后一篇博客,写完博客就收拾东西回家了,想想就有点兴奋。
这次要实现的是一个可以拖动和缩放的播放器,文字总是苍白的,直接看效果吧:
功能分解:
要实现拖动,那么必须保存播放器的坐标,在拖动的时候不停的更新坐标值即可。
要实现缩放,那么要保存播放器的长和宽,这里,我遇到的需求是在缩放的时候保持播放器的宽高比不变,因此,还要保存初始的宽高比。
无论是拖动还是缩放,都有可能出现播放器超出边界的情况,因此,要对边界进行处理。对于左上部分,只要x>=0&&y>=0就是没有超出范围,对于右下部分,只要播放器的w+x <= 边界的w && 播放器的h + y <= 边界的h,那么也是在边界之内。 因此,我们要求取边界的w和h,在flutter求某个控件的宽高用的是:
RenderBox renderObject = _globalKey.currentContext.findRenderObject();
处理好边界问题,那么就成功了一大半了,对于手势控制的缩放功能,在GestureDetector中直接有对应的Function去监听,对应onScaleStart: onScaleUpdate: onScaleEnd: ,分别代表缩放开始,缩放中,缩放结束。三个函数的参数值ScaleStartDetails、ScaleUpdateDetails、ScaleEndDetails中都有我们缩放的比例,以及缩放中心的坐标。对于拖动,直接使用ScaleUPdateDetails的左边进行计算即可。
视频播放器,选择的是官方的播放器video_player,视频播放部分比较简单,直接参考官方的example就可以了。当然如果要支持rtsp请看我的另一篇博客:https://blog.youkuaiyun.com/email_jade/article/details/86650561
好了,到目前为止感觉没有太多要注意的地方,不过考虑到这部分相对于其他使用频率低,还是放在进阶部分。
贴上一些关键部分的代码:
边界背景:
import 'package:flutter/material.dart';
import 'package:flutter_view/widget/video_view.dart';
class Background extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return BackgroundState();
}
}
class BackgroundState extends State<Background>{
static const double DEFAULT_WIDTH = 160;
static const double DEFAULT_HEIGHT = 90;
GlobalKey _globalKey = new GlobalKey();
//视频的参数,宽,高,比例
double _width;
double _height;
double _x;
double _y;
double _ratio;
//临时变量
double _tmpW;
double _tmpH;
Offset _lastOffset;
//背景的宽高
double _bgW;
double _bgH;
@override
void initState() {
super.initState();
//一些参数的初始化
_width = DEFAULT_WIDTH;
_height = DEFAULT_HEIGHT;
_x = _width;
_y = _height;
_ratio = _width / _height;
_tmpW = _width;
_tmpH = _height;
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Container(key:_globalKey, color: Colors.blueGrey, width: double.infinity, height: double.infinity,),
Positioned(child: GestureDetector(child:CustomView(width: _width, height: _height, url: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",), onScaleEnd: scaleEnd, onScaleStart: scaleStart, onScaleUpdate: scaleUpdate,), left: _x, top: _y,),
],
);
}
//计算背景的宽高
void getBgInfo(){
RenderBox renderObject = _globalKey.currentContext.findRenderObject();
_bgW = renderObject.paintBounds.size.width;
_bgH = renderObject.paintBounds.size.height;
}
//开始缩放
void scaleStart(ScaleStartDetails details){
_tmpW = _width;
_tmpH = _height;
_lastOffset = details.focalPoint;
getBgInfo();
}
//缩放更新
void scaleUpdate(ScaleUpdateDetails details){
setState(() {
_width = _tmpW*details.scale;
_height = _tmpH*details.scale;
_x += (details.focalPoint.dx - _lastOffset.dx) ;
_y += (details.focalPoint.dy - _lastOffset.dy);
//边界判定,保持宽高比
if(_width > _bgW){
_width = _bgW;
_height = _width / _ratio;
}
if(_height > _bgH){
_height = _bgH;
_width = _height * _ratio;
}
if(_x < 0){
_x = 0;
}
if(_y < 0){
_y = 0;
}
if(_x > _bgW-_width){
_x = _bgW-_width;
}
if(_y > _bgH-_height){
_y = _bgH-_height;
}
_lastOffset = details.focalPoint;
});
}
//缩放结束
void scaleEnd(ScaleEndDetails details){
_tmpW = _width;
_tmpH = _height;
//边界判定,保持宽高比
if(_width < DEFAULT_WIDTH){
_width = DEFAULT_WIDTH;
_height = _width / _ratio;
}
if(_height < DEFAULT_HEIGHT){
_height = DEFAULT_HEIGHT;
_width = _height * _ratio;
}
}
}
播放器:
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
class CustomView extends StatefulWidget{
final double width;
final double height;
final String url;
const CustomView({@required this.width, @required this.height, @required this.url});
@override
State<StatefulWidget> createState() {
return CustomViewState();
}
}
class CustomViewState extends State<CustomView>{
VideoPlayerController _controller;
@override
void initState() {
super.initState();
_controller = VideoPlayerController.network(
widget.url)
..initialize().then((_) {
setState(() {});
_controller.setLooping(true);
_controller.play();
});
}
@override
Widget build(BuildContext context) {
return Container(
width: widget.width,
height: widget.height,
child: _controller.value.initialized ? VideoPlayer(_controller) : Container(color: Colors.black,),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
完整代码见:
https://github.com/jadennn/flutter_view
flutter很好,路还很长,让我们一起奋斗前行!