搭建網(wǎng)站找什么公司銷售網(wǎng)站排名
作者:xuyisheng
Flutter會(huì)在屏幕上繪制Widget。如果一個(gè)Widget的內(nèi)容需要更新,那就只能重繪了。盡管如此,Flutter同樣會(huì)重新繪制一些Widget,而這些Widget的內(nèi)容仍有部分未被改變。這可能會(huì)影響應(yīng)用程序的執(zhí)行性能,有時(shí)影響會(huì)非常巨大。如果您正在尋找一種方法,來防止不必要的部分重繪,您可以考慮利用RepaintBoundary。
在這篇博客理,我們將探討Flutter中的RepaintBoundary。我們將看到如何實(shí)現(xiàn)RepaintBoundary的演示程序以及如何在您的flutter應(yīng)用程序中使用它。
RepaintBoundary
RepaintBoundary類是Null安全的。首先,你需要了解什么是Flutter中的RepaintBoundary。它是一個(gè)為它的Child設(shè)置不同的展示層級的Widget。這個(gè)Widget為它的Child設(shè)置了一個(gè)不同的展示層級,如果一個(gè)子樹與它周圍的部分相比,會(huì)在意想不到的短時(shí)間內(nèi)重新繪制,Flutter建議你使用RepaintBoundary來進(jìn)一步提高性能。
為什么需要使用RepaintBoundary呢。
Flutter Widget與RenderObjects有關(guān)。一個(gè)RenderObject有一個(gè)叫做paint的函數(shù),它被用來執(zhí)行繪畫過程。盡管如此,無論相關(guān)組件的內(nèi)容是否發(fā)生變化,都可以使用繪制方法。這是因?yàn)?#xff0c;如果其中一個(gè)RenderObjects被設(shè)定為dirty,Flutter可能會(huì)對類似Layer中的其他RenderObjects進(jìn)行重新繪制。當(dāng)一個(gè)RenderObject需要利用RenderObject.markNeedsPaint進(jìn)行重繪的時(shí)候,它就會(huì)建議它最接近的前輩進(jìn)行重繪。祖先也會(huì)對它的前輩做同樣的事情,直到根RenderObject。當(dāng)一個(gè)RenderObject的paint策略被啟動(dòng)時(shí),它在類似層中的所有相關(guān)RenderObjects都將被重新paint。
而有時(shí),當(dāng)一個(gè)RenderObject應(yīng)該被重繪時(shí),類似層中的其他RenderObjects不應(yīng)該被重繪,因?yàn)樗鼈兊睦L制產(chǎn)物保持不變。因此,如果我們只是對某些RenderObjects進(jìn)行重繪,那會(huì)更好。利用RepaintBoundary可以幫助我們在渲染樹上限制markNeedsPaint的生成,在渲染樹下限制paintChild的生成。
RepaintBoundary可以將先前的渲染對象與相關(guān)的渲染對象解耦。通過這種方式,只對內(nèi)容發(fā)生變化的子樹進(jìn)行重繪是可行的。利用RepaintBoundary可以進(jìn)一步提高應(yīng)用程序的執(zhí)行效率,特別是當(dāng)不應(yīng)該被重繪的子樹需要大量的工作來重繪時(shí)。
我們將做一個(gè)簡單的演示程序,背景是利用CustomPainter繪制的,有10000個(gè)橢圓。同時(shí)還有一個(gè)光標(biāo),在客戶接觸到屏幕的最后一個(gè)位置后移動(dòng)。下面是沒有RepaintBoundary的代碼。
示例
在正文中,我們將創(chuàng)建一個(gè)Stack widget。在里面,我們將添加一個(gè)StackFit.expand,并添加兩個(gè)部件:_buildBackground(),和_buildCursor()。我們將定義以下代碼。
Stack(fit: StackFit.expand,children: <Widget>[_buildBackground(),_buildCursor(),],
),
_buildBackground() widget
在_buildBackground()小組件中。我們將返回CustomPaint() widget。在里面,我們將在繪畫器上添加BackgroundColor類。我們將在下面定義。另外,我們將添加isComplex參數(shù)為true,這意味著是否提示這個(gè)圖層的繪畫應(yīng)該被緩存,willChange是false意味著是否應(yīng)該告訴光柵緩存,這個(gè)繪畫在下一幀可能會(huì)改變。
Widget _buildBackground() {return CustomPaint(painter: BackgroundColor(MediaQuery.of(context).size),isComplex: true,willChange: false,);
}
BackgroundColor class
我們將創(chuàng)建一個(gè)BackgroundColor來擴(kuò)展CustomPainter。
import 'dart:math';
import 'package:flutter/material.dart';class BackgroundColor extends CustomPainter {static const List<Color> colors = [Colors.orange,Colors.purple,Colors.blue,Colors.green,Colors.purple,Colors.red,];Size _size;BackgroundColor(this._size);@overridevoid paint(Canvas canvas, Size size) {final Random rand = Random(12345);for (int i = 0; i < 10000; i++) {canvas.drawOval(Rect.fromCenter(center: Offset(rand.nextDouble() * _size.width - 100,rand.nextDouble() * _size.height,),width: rand.nextDouble() * rand.nextInt(150) + 200,height: rand.nextDouble() * rand.nextInt(150) + 200,),Paint()..color = colors[rand.nextInt(colors.length)].withOpacity(0.3));}}@overridebool shouldRepaint(BackgroundColor other) => false;
}
_buildCursor() widget
在這個(gè)Widget,我們將返回Listener Widget。我們將在onPointerDown/Move方法中添加_updateOffset()組件,并添加CustomPaint。在里面,我們將添加一個(gè)Key和CursorPointer類。我們將在下面定義。另外,我們將添加ConstrainedBox()。
Widget _buildCursor() {return Listener(onPointerDown: _updateOffset,onPointerMove: _updateOffset,child: CustomPaint(key: _paintKey,painter: CursorPointer(_offset),child: ConstrainedBox(constraints: BoxConstraints.expand(),),),);
}
CursorPointer class
我們將創(chuàng)建一個(gè)CursorPointer來擴(kuò)展CustomPainter。
import 'package:flutter/material.dart';class CursorPointer extends CustomPainter {final Offset _offset;CursorPointer(this._offset);@overridevoid paint(Canvas canvas, Size size) {canvas.drawCircle(_offset,10.0,new Paint()..color = Colors.green,);}@overridebool shouldRepaint(CursorPointer old) => old._offset != _offset;
}
當(dāng)我們運(yùn)行應(yīng)用程序時(shí),我們應(yīng)該得到下面屏幕的輸出,如屏幕下的視頻。如果你試圖在屏幕上移動(dòng)指針,應(yīng)用程序?qū)⒎浅?#xff0c;因?yàn)樗匦吕L制背景,需要昂貴的計(jì)算。
下面,我們將添加RepaintBoundary。解決上述問題的答案是將CustomPaint部件包裝成RepaintBoundary的子Widget。
Widget _buildBackground() {return RepaintBoundary(child: CustomPaint(painter: BackgroundColor(MediaQuery.of(context).size),isComplex: true,willChange: false,),);
}
當(dāng)我們運(yùn)行應(yīng)用程序時(shí),我們應(yīng)該得到屏幕的輸出,就像屏幕下面的視頻一樣。有了這個(gè)簡單的改變,現(xiàn)在當(dāng)Flutter重繪光標(biāo)時(shí),背景就不需要重繪了。應(yīng)用程序應(yīng)該不再是滯后的了。
整個(gè)代碼如下所示。
import 'package:flutter/material.dart';
import 'package:flutter_repaint_boundary_demo/background_color.dart';
import 'package:flutter_repaint_boundary_demo/cursor_pointer.dart';class HomePage extends StatefulWidget {@overrideState createState() => new _HomePageState();
}class _HomePageState extends State<HomePage> {final GlobalKey _paintKey = new GlobalKey();Offset _offset = Offset.zero;Widget _buildBackground() {return RepaintBoundary(child: CustomPaint(painter: BackgroundColor(MediaQuery.of(context).size),isComplex: true,willChange: false,),);}Widget _buildCursor() {return Listener(onPointerDown: _updateOffset,onPointerMove: _updateOffset,child: CustomPaint(key: _paintKey,painter: CursorPointer(_offset),child: ConstrainedBox(constraints: BoxConstraints.expand(),),),);}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(automaticallyImplyLeading: false,backgroundColor: Colors.cyan,title: const Text('Flutter RepaintBoundary Demo'),),body: Stack(fit: StackFit.expand,children: <Widget>[_buildBackground(),_buildCursor(),],),);}_updateOffset(PointerEvent event) {RenderBox? referenceBox = _paintKey.currentContext?.findRenderObject() as RenderBox;Offset offset = referenceBox.globalToLocal(event.position);setState(() {_offset = offset;});}
}
總結(jié)
在文章中,我解釋了Flutter中RepaintBoundary的基本結(jié)構(gòu);你可以根據(jù)你的選擇來修改這個(gè)代碼。這是我對RepaintBoundary On User Interaction的一個(gè)小的介紹,它在使用Flutter時(shí)是可行的。
Android 學(xué)習(xí)筆錄
Android 性能優(yōu)化篇:https://qr18.cn/FVlo89
Android 車載篇:https://qr18.cn/F05ZCM
Android 逆向安全學(xué)習(xí)筆記:https://qr18.cn/CQ5TcL
Android Framework底層原理篇:https://qr18.cn/AQpN4J
Android 音視頻篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(內(nèi)含Compose):https://qr18.cn/A0gajp
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
OkHttp 源碼解析筆記:https://qr18.cn/Cw0pBD
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知識體:https://qr18.cn/CyxarU
Android 核心筆記:https://qr21.cn/CaZQLo
Android 往年面試題錦:https://qr18.cn/CKV8OZ
2023年最新Android 面試題集:https://qr18.cn/CgxrRy
Android 車載開發(fā)崗位面試習(xí)題:https://qr18.cn/FTlyCJ
音視頻面試題錦:https://qr18.cn/AcV6Ap