flutter進階之實現一個能夠拖動縮放的播放器

    二零一八年的最後一篇博客,寫完博客就收拾東西回家了,想想就有點興奮。

    這次要實現的是一個可以拖動和縮放的播放器,文字總是蒼白的,直接看效果吧:

    

    功能分解:

    要實現拖動,那麼必須保存播放器的座標,在拖動的時候不停的更新座標值即可。

    要實現縮放,那麼要保存播放器的長和寬,這裏,我遇到的需求是在縮放的時候保持播放器的寬高比不變,因此,還要保存初始的寬高比。

    無論是拖動還是縮放,都有可能出現播放器超出邊界的情況,因此,要對邊界進行處理。對於左上部分,只要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.csdn.net/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很好,路還很長,讓我們一起奮鬥前行!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章