一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Flutter瀑布流仿寫原生的復用機制詳解

Flutter瀑布流仿寫原生的復用機制詳解

2021-11-01 10:51頭疼腦脹的代碼搬運工 Java教程

這篇文章主要給大家介紹了關于Flutter瀑布流仿寫原生的復用機制的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用flutter具有一定的參考學習價值,需要的朋友可以參考下

廢話開篇:

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仿寫復用機制內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://juejin.cn/post/6989900968842231838

延伸 · 閱讀

精彩推薦
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
主站蜘蛛池模板: 国内体内she精视频免费 | 久久综合老色鬼网站 | 91寡妇天天综合久久影院 | 好大夫在线个人空间 | 国产久草在线 | 九九热在线视频观看这里只有精品 | 亚洲国产情侣偷自在线二页 | 国产高清好大好夹受不了了 | 天天有好逼 | 成人网视频免费播放 | 啪啪国产视频 | 武侠古典久久亚洲精品 | 亚州vs欧州vs日 | 欧美成人免费观看国产 | 午夜在线观看免费完整直播网 | 国产免费久久精品 | 久久精品AV一区二区无码 | 久久久久国产一级毛片高清片 | 男人操美女视频 | 嫩草影院地址一地址二 | 男人都懂www深夜免费网站 | 大肥臀风间由美 中文字幕 大东北chinesexxxx露脸 | 免费看美女被靠到爽 | 久久这里只有精品国产精品99 | 草莓影音| 香蕉视频在线观看网址 | 热穴高校| 久久成人伊人欧洲精品AV | 国产日韩欧美精品在线 | 亚洲男人天堂久久 | naruto hentai玖辛奈| 精品国产人妻国语 | 国语视频高清在线观看 | 亚洲区精品久久一区二区三区 | 欧美又黄又激烈真实床戏 | 久久热在线视频精品店 | 日本福利视频网站 | 色交视频 | 日韩精品一区二区三区中文版 | 韩剧消失的眼角膜免费完整版 | 顶级尤物极品女神福利视频 |