1 可滚动组件
对于列表和长布局的显示溢出问题,可以使用Flutter提供的可滚动组件来处理。
在Flutter中,一个可滚动的组件直接或间接包含一个Scrollable组件,它是可滚动组件的基础组件。
1 2 3 4 5 6
| Scrollable({ this.axisDirection = AxisDirection.down, this.controller, this.physics, @required this.viewportBuilder })
|
Scrollbar是一个Material风格的滚动指示器组件,如果要给可滚动组件添加滚动条,只需将Scrollbar组件作为可滚动组件的父组件使用即可。
如果一个可滚动组件支持Sliver模型,那么该滚动可以将子组件分成多个部分,只有当子组件出现在视口中时才会去构建它。
目前,可滚动组件中的大部分组件都支持基于Sliver的延迟构建模型,如ListView、GridView。
是一个只能包含单一子组件的可滚动组件,其作用类似于iOS的UIScrollView组件或Android的ScrollView组件。
只能应用于内容不会超过屏幕尺寸太多的情况,因为SingleChildScrollView组件目前还不支持基于Sliver的延迟加载,如果视图内容超出屏幕尺寸太多会导致性能问题。
所谓基于Sliver的延迟加载,是Flutter中提出的薄片(Sliver)概念。如果一个可滚动组件支持Sliver,那么该可滚动组件可以将子组件分成多个Sliver,只有当Sliver出现在视图窗口时才会去构建它,从而提高渲染的性能。
SingleChildScrollView组件的构造函数:
1 2 3 4 5 6 7 8 9 10 11
| const SingleChildScrollView({ Key key, this.scrollDirection = Axis.vertical, this.reverse = false, this.padding, bool primary, this.physics, this.controller, this.child, this.dragStrartBehavior = DragStrartBehavior.down, })
|
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import 'package:flutter/material.dart';
void main() => runApp(ScollWidget());
class ScollWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--可滚动组件', home: Scaffold( appBar: AppBar(title: Text('可滚动组件--SingleChildScrollView')), body: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: <Widget>[Text('Hello Flutter ' * 100)] ), ) ) ); } }
|
示例效果:
可以使用Sliver模型实现自定义滚动组件,可以包含多个子组件,而且可以将这些子组件包裹起来实现一致的滚动效果。
CustomScrollView作为容器组件时,子组件不能是ListView、GridView等可滚动组件,会造成滚动冲突。在实际使用过程中,Flutter提供了SliverList、SliverGrid等可滚动组件的Sliver版本。
ListView、GridView自带滚动模型,SliverList、SliverGrid不包含滚动模型,不会造成滚动冲突。
CustomScrollView组件的构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class CustomScrollView extends ScrollView { const CustomScrollView({ Key key, Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollController controller, bool primary, ScrollPhysics physics, bool shrinkWrap = false, Key center, double anchor = 0.0, double cacheExtent, this.slivers = const <Widget>[], int semanticChildCount, DragStartBehavior dragStartBehavior = DragStartBehavior.down, }) }
|
CustomScrollView组件通常被用于实现复杂的滚动效果,并且可以用来实现复杂的动画效果。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| import 'package:flutter/material.dart';
void main() => runApp(ScollWidget());
class ScollWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--可滚动组件', home: Scaffold( appBar: AppBar(title: Text('可滚动组件--CustomScrollView')), body: CustomScrollView( slivers: <Widget>[ SliverAppBar( pinned: true, expandedHeight: 160.0, flexibleSpace: FlexibleSpaceBar( title: Text('CustomScrollView'), background: Image.asset('images/test1.png'), ), ), SliverGrid( gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 200.0, mainAxisSpacing: 10.0, childAspectRatio: 3.0, ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return Card( child: Container( alignment: Alignment.centerLeft, padding: EdgeInsets.all(10), child: Text('grid $index'), ), ); }, childCount: 11, ), ), SliverFixedExtentList( itemExtent: 60.0, delegate: SliverChildListDelegate( List.generate(20, (int index) { return GestureDetector( onTap: () => print('单击$index'), child: Card( child: Container( alignment: Alignment.centerLeft, padding: EdgeInsets.all(15), child: Text('list $index'), ) ) ); }) ) ) ], ) ) ); } }
|
示例效果:
如果需要监听可滚动组件的滚动过程,可以使用ScrollController组件来进行监听。
ScrollController组件的构造函数:
1 2 3 4 5
| ScrollController({ double initialScrollOffset = 0.0, this.keepScrollOffset = true, this.debugLabel, })
|
当keepScrollOffset的属性值为true时,可滚动组件的滚动位置会被存储到PageStorage中,当可滚动组件重新创建时可以使用PageStorage恢复存储的位置。
ScrollController组件还有如下属性和方法:
offset:可滚动组件当前的滚动位置;
jumpTo():用于跳转到指定的位置;
animateTo():跳转到指定位置,跳转时会执行设置的动画。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| import 'package:flutter/material.dart';
void main() => runApp(ScrollControllerPage());
class ScrollControllerPage extends StatefulWidget { @override State<StatefulWidget> createState() { return ScrollControllerPageState(); } }
class ScrollControllerPageState extends State<ScrollControllerPage> { ScrollController controller = new ScrollController(); bool showTopBtn = false; @override void initState() { super.initState(); controller.addListener(() { if(controller.offset < 500 && showTopBtn) { setState(() { showTopBtn = false; }); } else if (controller.offset >= 500 && !showTopBtn) { setState(() { showTopBtn = true; }); } }); } @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--可滚动组件', home: Scaffold( appBar: AppBar(title: Text('可滚动组件--ScrollController')), body: ListView.builder( itemCount: 100, itemExtent: 50.0, controller: controller, itemBuilder: (context, index) { return ListTile(title: Text('列表Item $index')); }, ), floatingActionButton: !showTopBtn ? null : FloatingActionButton( child: Icon(Icons.arrow_upward), onPressed: () { controller.jumpTo(0); } ) ) ); } }
|
示例效果:
在有多个组件嵌套的组件树中,组件树的子组件可以通过发送通知来与父组件进行通信,父组件则可以通过NotificationListener组件来监听自己关注的通知,这种跨组件的通信方式通常被称为事件冒泡。
接收滚动事件的参数类型为ScrollNotification,它提供了一个metrics属性,该属性包含了当前可视窗口和滚动位置等信息。
NotificationListener组件支持的属性如下:
pixels:当前滚动位置;
maxScrollExtent:最大可滚动长度;
extentBefore:距离滚出视图窗口顶部的长度;
extentInside:视图窗口内部长度,大小等于屏幕显示的列表长度;
extentAfter:列表中未滑入视图窗口部分的长度;
atEdge:是否滚动到了可滚动组件的边界。 示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| import 'package:flutter/material.dart';
void main() => runApp(ScrollNotificationPage());
class ScrollNotificationPage extends StatefulWidget { @override State<StatefulWidget> createState() { return ScrollNotificationPageState(); } }
class ScrollNotificationPageState extends State<ScrollNotificationPage> { String _progress = '0%'; @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--可滚动组件', home: Scaffold( appBar: AppBar(title: Text('可滚动组件--NotificationListener')), body: Scrollbar( child: NotificationListener<ScrollNotification>( onNotification: (ScrollNotification notification) { double progress = notification.metrics.pixels / notification.metrics.maxScrollExtent; setState(() { _progress = '${(progress * 100).toInt()}%'; }); return null; }, child: Stack( alignment: Alignment.center, children: <Widget>[ ListView.builder( itemCount: 100, itemExtent: 50.0, itemBuilder: (context, index) { return ListTile(title: Text('标题 $index')); }, ), CircleAvatar( radius: 30.0, child: Text(_progress), backgroundColor: Colors.black54, ) ] ) ) ) ) ); } }
|
示例效果:
NotificationListener组件和ScrollController组件都可以实现列表滚动的监听。NotificationListener组件可以监听可滚动组件的整个组件树,并且监听到的信息更多,ScrollController则只能监听关联的可滚动组件的相关信息。
2 列表组件
2.1 ListView
ListView,即列表组件,作用类似于Android的RecyclerView或ListView。ListView可以沿一个线性方向排布相同或相似的子组件元素,并支持基于Sliver的延迟。
ListView的默认构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class ListView extends BoxScrollView { ListView({ Key key, Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollController controller, bool primary, ScrollPhysics physics, EdgeInsetsGeometry padding, bool shrinkWrap = false, this.itemExtent, bool addAutomaticKeepAlives = true, bool addRepaintBoundaries = true, bool addSemanticIndexes = true, double cacheExtent, List<Widget> children = const <Widget>[], int semanticChildCount, DragStartBehavior dragStartBehavior = DragStartBehavior .down, }) }
|
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import 'package:flutter/material.dart';
void main() => runApp(ListViewWidget());
class ListViewWidget extends StatelessWidget { final _items = List<Widget>.generate(10, (index) => Container( padding: EdgeInsets.all(16.0), child: Text('Item $index') ) ); @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--列表组件', home: Scaffold( appBar: AppBar(title: Text('可滚动组件--列表组件')), body: ListView(children: _items) ) ); } }
|
示例效果:
默认的构造函数适合只含有少量子组件的情况,因为它不支持基于Sliver的延迟加载,当列表的元素较多时,容易出现卡顿现象。
2.2 ListView.builder
使用ListView.builder创建的列表是基于Sliver的延迟加载创建的,渲染性能比较高,适合用于列表元素比较多的情况。
ListView.builder特有的属性:
1)itemBuilder:用于构建列表项的可见子组件构建器,只有索引>= 0且< itemCount时才会被调用;
2)itemCount:列表项的数量,如果为null,则列表为无限列表。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import 'package:flutter/material.dart';
void main() => runApp(ListViewWidget());
class ListViewWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--列表组件', home: Scaffold( appBar: AppBar(title: Text('高级组件--列表组件')), body: ListView.builder( itemCount: 100, itemExtent: 50.0, itemBuilder: (BuildContext context, int index) { return ListTile(title: Text('Item $index')); } ) ) ); } }
|
示例效果:
2.3 ListView.separated
和ListView.builder相比,ListView.separated多了一个separatorBuilder属性,该属性可以在生成的列表项之间添加一条分割线。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import 'package:flutter/material.dart';
void main() => runApp(ListViewWidget());
class ListViewWidget extends StatelessWidget { Widget divider = Divider(color: Colors.grey); @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--列表组件', home: Scaffold( appBar: AppBar(title: Text('高级组件--列表组件')), body: ListView.separated( itemCount: 100, itemBuilder: (BuildContext context, int index) { return ListTile(title: Text('Item $index')); }, separatorBuilder: (BuildContext context, int index) { return divider; }, ) ) ); } }
|
示例效果:
2.4 ListView.custom
ListView.custom适用于自定义列表的场景。其中,childrenDelegate是它的必传参数,需要传入一个实现了SliverChildDelegate抽象类的组件,用来给ListView组件添加列表项。
SliverChildDelegate是一个抽象类,它的实现类有SliverChildListDelegate和SliverChildBuilderDelegate,并且SliverChildDelegate的build()可以对单个子组件进行自定义样式处理。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import 'package:flutter/material.dart';
void main() => runApp(ListViewWidget());
class ListViewWidget extends StatelessWidget { final _items = List<Widget>.generate(100, (i) => Container( padding: EdgeInsets.all(16.0), child: Text('Item $i') ) ); @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--列表组件', home: Scaffold( appBar: AppBar(title: Text('高级组件--列表组件')), body: ListView.custom( childrenDelegate: SliverChildListDelegate(_items), ) ) ); } }
|
示例效果:
如果滚动视图中出现列表嵌套的场景,为了不造成滚动时的冲突,需要对子组件添加禁止滚动属性。
1 2 3 4 5
| ListView.builder( ... physics: NeverScrollableScrollPhysics(), ... )
|
3 网格组件
3.1 GridView基础
GridView是一个可以构建二维网格的列表组件,作用类似于原生Android中的GridView/RecyclerView或者iOS的UICollectionView。
GridView的默认构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| GridView({ Key key, Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollController controller, bool primary, ScrollPhysics physics, bool shrinkWrap = false, EdgeInsetsGeometry padding, @required this.gridDelegate, bool addAutomaticKeepAlives = true, bool addRepaintBoundaries = true, bool addSemanticIndexes = true, double cacheExtent, List<Widget> children = const <Widget>[], int semanticChildCount, })
|
SliverGridDelegate是一个抽象类,是一个控制子元素排列方式的接口,有两个实现类:
1)SliverGridDelegateWithFixedCrossAxisCount:用于列数固定的场景
1 2 3 4 5 6
| SliverGridDelegateWithFixedCrossAxisCount({ @required double crossAxisCount, double mainAxisSpacing = 0.0, double crossAxisSpacing = 0.0, double childAspectRatio = 1.0, })
|
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import 'package:flutter/material.dart';
void main() => runApp(GridWidget());
class GridWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--网格组件', home: Scaffold( appBar: AppBar(title: Text('高级组件--网格组件')), body: GridView( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, childAspectRatio: 1.5 ), children: <Widget>[ Icon(Icons.ac_unit), Icon(Icons.airport_shuttle), Icon(Icons.all_inclusive), Icon(Icons.beach_access), Icon(Icons.cake), Icon(Icons.free_breakfast), ], ) ) ); } }
|
示例效果:
SliverGridDelegateWithFixedCrossAxisCount还可以使用GridView.count进行代替:
1 2 3 4 5 6 7 8 9 10
| ... body: GridView.count( crossAxisCount: 3, childAspectRatio: 1.5, children: <Widget>[ Icon(Icons.ac_unit), ... ], ) ...
|
2)SliverGridDelegateWithMaxCrossAxisExtent:用于子元素有最大宽度限制的场景
1 2 3 4 5 6
| SliverGridDelegateWithMaxCrossAxisExtent({ @required this.maxCrossAxisExtent, this.mainAxisSpacing = 0.0, this.crossAxisSpacing = 0.0, this.childAspectRatio = 1.0, })
|
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import 'package:flutter/material.dart';
void main() => runApp(GridWidget());
class GridWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--网格组件', home: Scaffold( appBar: AppBar(title: Text('高级组件--网格组件')), body: GridView( padding: EdgeInsets.zero, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 120.0, childAspectRatio: 2.0 ), children: <Widget>[ Icon(Icons.ac_unit), Icon(Icons.airport_shuttle), Icon(Icons.all_inclusive), Icon(Icons.beach_access), Icon(Icons.cake), Icon(Icons.free_breakfast), ], ) ) ); } }
|
示例效果:
SliverGridDelegateWithFixedCrossAxisCount还可以使用GridView.extent()代替:
1 2 3 4 5 6 7 8 9 10 11
| ... body: GridView.extent( padding: EdgeInsets.zero, maxCrossAxisExtent: 120.0, childAspectRatio: 2.0, children: <Widget>[ Icon(Icons.ac_unit), ... ], ) ...
|
3.2 GridView构造函数
GridView的构造函数一共有5个:
1)GridView():默认构造函数,适用于元素个数有限的场景,会一次性全部渲染children属性中的子元素组件;
2)GridView.builder():适用于构建大量或无限长的列表,它只会构建那些可见的组件,对于不可见的会动态销毁,减少内存销毁,渲染更高效;必须要传入gridDelegate和itemBuilder属性;
3)GridView.count():SliverGridDelegateWithFixedCrossAxisCount实现类的简写,用于创建横轴数量固定的网格视图;
4)GridView.extent():SliverGridDelegateWithFixedCrossAxisCount实现类的简写,用于创建横轴子元素宽度固定的网格视图;
5)GridView.custom():自定义的网格视图,需要同时传入gridDelegate和childrenDelegate。 示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| import 'package:flutter/material.dart';
void main() => runApp(GridViewWidget());
class ItemViewModel { final String icon; final String title; const ItemViewModel({this.icon, this.title}); }
class GridItem extends StatelessWidget { final ItemViewModel data; GridItem({Key key, this.data}): super(key: key); @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.only(bottom: 5), child: Column( children: <Widget>[ Image.asset(this.data.icon, width: 55, fit: BoxFit.fitWidth), Text(this.data.title) ], ), ); } }
const List<ItemViewModel> list = [ ItemViewModel(title: '微信', icon: 'images/wx.png'), ItemViewModel(title: 'QQ', icon: 'images/qq.png'), ItemViewModel(title: '微信', icon: 'images/wx.png'), ItemViewModel(title: 'QQ', icon: 'images/qq.png'), ItemViewModel(title: '微信', icon: 'images/wx.png'), ItemViewModel(title: 'QQ', icon: 'images/qq.png'), ItemViewModel(title: '微信', icon: 'images/wx.png'), ItemViewModel(title: 'QQ', icon: 'images/qq.png'), ];
class GridViewWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--网格组件', home: Scaffold( appBar: AppBar(title: Text('高级组件--网格组件')), body: GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4), itemCount: list.length, padding: EdgeInsets.symmetric(vertical: 10), itemBuilder: (context, index) { return GridItem(data: list[index]); } ) ) ); } }
|
示例效果:
4 滑动切换组件
PageView是一个滑动视图列表组件,它继承自CustomScrollView,作用类似于Android的ViewPager,可以用它实现视图的左右滑动切换功能。
PageView的构造函数:
1)PageView():默认构造函数,创建一个可滚动列表,适合子组件比较少的场景;
1 2 3 4 5 6 7 8 9 10 11 12
| PageView({ Key key, this.scrollDirection = Axis.horizontal, this.reverse = false, PageController controller, this.physics, this.pageSnapping = true, this.onPageChanged, this.semanticChildCount, List<Widget> children = const <Widget>[], this.dragStartBehavior = DragStartBehavior.down, })
|
2)PageView.builder():创建一个滚动列表,适合子组件比较多的场景,需要指定子组件的数量;
3)PageView.custom():创建一个可滚动的列表,需要自定义子项。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import 'package:flutter/material.dart';
void main() => runApp(PageViewWidget());
const List<String> items = [ 'images/test1.png', 'images/test2.png', ];
class PageViewWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--滑块切换组件', home: Scaffold( appBar: AppBar(title: Text('高级组件--滑块切换组件')), body: PageView.builder( onPageChanged: (index) { print('current page $index'); }, itemCount: items.length, itemBuilder: (context, index) { return Image.asset(items[index]); } ) ) ); } }
|
示例效果:
5 自定义组件
5.1 组合组件
按照从上到下、从左到右的方式去拆解布局结构即可。
5.2 自绘组件
在Flutter中创建自绘组件需要用到CustomPaint和CustomPainter两个类:CustomPaint在绘制阶段提供一个Canvas,即画布;CustomPainter在绘制阶段提供画笔,可配置画笔的颜色、样式和粗细等属性。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| import 'dart:math'; import 'package:flutter/material.dart';
void main() => runApp(PiePageWidget());
class PiePageWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '高级组件--自绘组件', home: Scaffold( appBar: AppBar(title: Text('高级组件--自绘组件')), body: Center( child: CustomPaint( size: Size(300, 300), painter: PiePainter() ) ) ) ); } }
class PiePainter extends CustomPainter { Paint getPaint(Color color) { Paint paint = Paint(); paint.color = color; return paint; } @override void paint(Canvas canvas, Size size) { double wheelSize = min(size.width, size.height) / 2; double nbElem = 6; double radius = (2 * pi) / nbElem; Rect boundingRect = Rect.fromCircle(center: Offset(wheelSize, wheelSize), radius: wheelSize); canvas.drawArc(boundingRect, 0, radius, true, getPaint(Colors.red)); canvas.drawArc(boundingRect, radius, radius, true, getPaint(Colors.black38)); canvas.drawArc(boundingRect, radius * 2, radius, true, getPaint(Colors.green)); canvas.drawArc(boundingRect, radius * 3, radius, true, getPaint(Colors.amber)); canvas.drawArc(boundingRect, radius * 4, radius, true, getPaint(Colors.blue)); canvas.drawArc(boundingRect, radius * 5, radius, true, getPaint(Colors.purple)); } @override bool shouldRepaint(CustomPainter oldDelegate) { return true; } }
|
示例效果:
创建Flutter自绘组件时,可以做以下两点性能优化:
1)尽可能利用好shouldRepaint()的返回值
如果绘制的内容不需要依赖外部状态,返回false即可;如果绘制过程需要依赖外部状态,可以在shouldRepaint()中判断依赖的状态是否改变,如果已改变,则返回true并执行重绘操作,反之则返回false不执行重绘;
2)绘制应尽可能多地进行分层
因为复杂的自绘组件都是由很多功能构成的,如果都写在一个方法中,不利于阅读,而且全部重绘带来的性能开销也很大。分层渲染可以降低视图渲染带来的性能开销。
无论是创建组合组件还是创建自绘组件,首先需要考虑如何将复杂的布局简化,把大问题拆分成若干小问题。