c/c++语言开发共享Flutter瀑布流仿写原生的复用机制详解

目录问题二、ui布局代码分析。废话开篇:ios与android在实现列表界面的时候是有重用机制的,目的就是减少内存开销,用时间换空间。个人感觉flutter并没有特别强调复用,关于listview.b

目录
  • 问题二、ui布局代码分析。

废话开篇:

ios与android在实现列表界面的时候是有重用机制的,目的就是减少内存开销,用时间换空间。个人感觉flutter并没有特别强调复用,关于listview.builder 的“复用”个人感觉应该是销毁跟重建的过程,所以这里用flutter实现了简单的复用机制。代码拙劣,大神勿喷,共同进步

先看复用效果

Flutter瀑布流仿写原生的复用机制详解

复用状态打印

Flutter瀑布流仿写原生的复用机制详解

右侧是简单实现瀑布流界面,里面显示的是一共有39个widget。左侧是控制台打印一共创建的12个widget,所以这里就简单的实现了widget复用。

问题一、实现思路是什么?

这里先简单的说一下实现思路。

  • 在渲染界面前,通过计算得出全部的widget的位置坐标。
  • 首次渲染创建一屏可视瀑布流widget.
  • 监听滑动,判断当前页面滚动方向展示的瀑布流widget,先去缓存池里拿,如果没有就直接创建,添加到组件中进行渲染。如果缓存池里有,修改widget的相对布局位置。

问题二、ui布局代码分析。

Flutter瀑布流仿写原生的复用机制详解

tip: waterfallflow.dart 瀑布流主页面;waterfallflowitem.dart 瀑布流单元item

效果展示:

Flutter瀑布流仿写原生的复用机制详解

waterfallflowitem.dart 瀑布流item文件

  class waterfallflowitem extends statefulwidget{    frame? _frame;    waterfallflowitemstate? _waterfallflowitemstate;      waterfallflowitem({required frame frame}){      _frame = frame;    }      frame getframe(){      return _frame!;    }      void setframe({required frame frame}) {      _frame = frame;      _waterfallflowitemstate!.setframe(frame: frame);    }      @override    state<statefulwidget> createstate() {      _waterfallflowitemstate = new waterfallflowitemstate(frame: _frame!);      return _waterfallflowitemstate!;    }  }    class waterfallflowitemstate extends state<waterfallflowitem> with automatickeepaliveclientmixin {    frame? _frame;      waterfallflowitemstate({required frame frame}){      _frame = frame;    }      void setframe({required frame frame}) {      setstate(() {        _frame = frame;      });    }    @override    widget build(buildcontext context) {      return new positioned(          top: _frame!.top,          left: _frame!.left,          child: gesturedetector(            child: new container(              color: _frame!.index == 12 ? colors.red : color.fromargb(255, 220, 220, 220),              width: _frame!.width,              height: _frame!.heigth,              child: new text(_frame!.index.tostring()),            ),            ontap: (){              },          )      );    }      @override    // todo: implement wantkeepalive    bool get wantkeepalive => true;  }  

waterfallflow.dart 主界面文件

builder 实现

  @override  widget build(buildcontext context) {    return new container(      //去掉scrollview顶部空白间隙      child: mediaquery.removepadding(          context: context,          removetop: true,          child: scrollbar(            //isalwaysshown: true,            //showtrackonhover: true,            //scrollview            child: new singlechildscrollview(              controller: _scrollcontroller,              child: new container(                width: mediaquery.of(context).size.width,                //最大高度                height: _maxheight,                color: colors.white,                child: new stack(                  //帧布局下的瀑布流单元格item集合                  children: _listwidget,                ),              ),            ),          )    ),    );  }  

声明的属性

  //瀑布流间隔  double sep = 5;  //瀑布流宽度  double? _width;  //最大高度  double _maxheight = 0;  //左侧最大高度  double leftheight = 0;  //右侧最大高度  double rightheight = 0;  //主界面高度  double _minecontentheight = 0;  //瀑布流item缓存池  list<waterfallflowitem> _bufferpoolwidget = [];  //当前显示的瀑布流item  list<waterfallflowitem> _listwidget = [];  //当前组渲染frame对象保存  list<frame> _flist = [];  //总frame集合  list<frame> _framelist = [];  //数据源这里只保存高度  list<double> _list = [    100,150,45,11,140,89,212,21,434,545,100,150,45,11,140,89,212,21,434,545,    100,150,45,11,140,89,212,21,434,545,100,150,45,11,140,89,212,21,434,545];  //滑动监听  scrollcontroller _scrollcontroller = new scrollcontroller();  //滑动偏移量  double _scrolloff = 0;  

计算主窗口scrollview 高度

  //获取最大高度,并计算出全部的瀑布流位置  void getmaxheight(){    list<frame> flist = [];    double width = (_width! - sep * 3) / 2.0;    double maxheight = _maxheight;    for(int i = _framelist.length;i < _list.length;i++){      double height = _list[i];      bool isleft = (leftheight <= rightheight);      double left = isleft ? sep : (width + sep * 2);      maxheight = isleft ? leftheight : rightheight;      frame frame = frame(leftp: left, topp: maxheight, widthp: width, heigthp: height,indexp: i);      if(isleft == true) {        leftheight += (height + sep);      } else {        rightheight += (height + sep);      }      flist.add(frame);    }    _maxheight = max(leftheight, rightheight);    _framelist.addall(flist);    //刷新    setstate(() {});  }  

frame 位置信息类

  class frame{    double left = 0;//左    double top = 0;//右    double width = 0;//宽度    double heigth = 0;//高度    int index = 0;//索引    frame({required leftp    ,required topp,      required widthp,      required heigthp,      required indexp}){      left = leftp * 1.0;      top = topp * 1.0;      width = widthp * 1.0;      heigth = heigthp * 1.0;      index = indexp;    }  }  

生成瀑布流widget单元item

  //重用池里生成item  _takereuseflowitem(frame f,dynamic block){    waterfallflowitem? waterfallflowitem;    //是否重用,是,直接修改frame;否,重新渲染。    bool isreuse = false;    //有,从缓存池里取(缓存中的已在结构树里,可以修改帧布局位置)    if(_bufferpoolwidget.length > 0){      waterfallflowitem = _bufferpoolwidget.last;      waterfallflowitem.setframe(frame: f);      _bufferpoolwidget.removelast();      isreuse = true;    }        //没有,直接创建(不缓存中的,需要调用setstate方法渲染)    if(waterfallflowitem == null) {      waterfallflowitem = new waterfallflowitem(frame: f,);      isreuse = false;    }    block(waterfallflowitem,isreuse);  }  

创建首屏全部可视瀑布流widget单元组件

  //渲染瀑布流item  createwaterfallflow(int index){    getmaxheight();    //这里加点延迟,保证获取最大高度完成(不太严谨,大神有好方法请赐教[抱拳])    future.delayed(duration(milliseconds: 100),(){      _minecontentheight = context.size!.height;      for(var i = 0;i < _framelist.length;i++){        frame f = _framelist[i];        //判断可视化逻辑        if(f.top <= _minecontentheight + _scrolloff) {          _takereuseflowitem(f,(waterfallflowitem waterfallflowitem,bool isreuse){            _listwidget.add(waterfallflowitem);          });        }      }      setstate(() {      });    });  }  

滑动过程中进行重用渲染

  //获取上滑状态当前显示的下一个item位置  frame? _getupneedshowframe(){    frame? f;    waterfallflowitem? lastwaterfallflowitem = _listwidget.last;    if(lastwaterfallflowitem.getframe().index + 1 < _framelist.length) {      f = _framelist[lastwaterfallflowitem.getframe().index + 1];    }    return f;  }    //获取下滑状态当前显示的上一个item位置  frame? _getdownneedshowframe(){    frame? f;    waterfallflowitem? lastwaterfallflowitem = _listwidget[0];    if(lastwaterfallflowitem.getframe().index - 1 >= 0) {      f = _framelist[lastwaterfallflowitem.getframe().index - 1];    }    return f;  }    //超出界面可视范围的瀑布流加入缓存池  void addflowitemaddtobufferpool(){      list<waterfallflowitem> list = [];    for(int i = 0; i < _listwidget.length;i++){      waterfallflowitem? waterfallflowitem = _listwidget[i];      frame? frame = waterfallflowitem.getframe();      if((frame.top + frame.heigth) <  _scrolloff || frame.top > _minecontentheight + _scrolloff) {        _bufferpoolwidget.add(waterfallflowitem);        list.add(waterfallflowitem);      }    }    if(list.length != 0) {      for(int i= 0;i < list.length;i++){        waterfallflowitem? waterfallflowitem = list[i];        if(_listwidget.contains(waterfallflowitem)){          _listwidget.remove(waterfallflowitem);        }      }    }      //从缓存池里获取item    //上滑状态    frame? upnextframe = _getupneedshowframe();    if(upnextframe != null) {      //debugprint('我是在复用 ${upnextframe.index} ,${upnextframe.top},${_minecontentheight + _scrolloff}');      if(upnextframe.top <= _minecontentheight + _scrolloff) {        debugprint('我在上滑重置第${upnextframe.index}个frame');        _takereuseflowitem(upnextframe,(waterfallflowitem waterfallflowitem,bool isreuse){          _listwidget.add(waterfallflowitem);          if(!isreuse){            debugprint('我不是复用');            setstate(() {});          } else {            debugprint('我是复用');            waterfallflowitem.setframe(frame: upnextframe);          }        });      }    }      //下滑状态    frame? downnextframe = _getdownneedshowframe();    if(downnextframe != null) {      //debugprint('我是在复用 ${downnextframe.index} ,${downnextframe.top},${_minecontentheight + _scrolloff}');      if(downnextframe.top + downnextframe.heigth > _scrolloff && downnextframe.top + downnextframe.heigth < _minecontentheight + _scrolloff) {        debugprint('我在下滑重置第${downnextframe.index}个frame');        _takereuseflowitem(downnextframe,(waterfallflowitem waterfallflowitem,bool isreuse){          _listwidget.insert(0, waterfallflowitem);          if(!isreuse){            debugprint('我不是复用');            setstate(() {});          } else {            debugprint('我是复用');            waterfallflowitem.setframe(frame: downnextframe);          }        });      }    }  }  

滚动监听

  _scrollcontroller.addlistener(() {    _scrolloff = _scrollcontroller.offset;    //加入缓存池,并进行复用    addflowitemaddtobufferpool();    debugprint('总共:${_listwidget.length + _bufferpoolwidget.length} 个');  });  

基本上flutter的瀑布流复用逻辑就完成了,代码拙劣,里面有些地方需要优化,比如:快速滑动防护,item的内容渲染。flutter对于界面渲染已经很极致了,重写复用有点倒退的赶脚。大神勿喷,互相学习。

总结

到此这篇关于flutter瀑布流仿写原生的复用机制的文章就介绍到这了,更多相关flutter仿写复用机制内容请搜索<计算机技术网(www.ctvol.com)!!>以前的文章或继续浏览下面的相关文章希望大家以后多多支持<计算机技术网(www.ctvol.com)!!>!

需要了解更多c/c++开发分享Flutter瀑布流仿写原生的复用机制详解,都可以关注C/C++技术分享栏目—计算机技术网(www.ctvol.com)!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/678288.html

(0)
上一篇 2021年7月31日
下一篇 2021年7月31日

精彩推荐