像视图数据流转机制、底层渲染方案、视图更新策略等知识,都是构成一个UI框架的根本,看似枯燥,却往往具有最长久的生命力。
因此, 只有把这些最基础的知识弄明白,修好内功,才能触类旁通,由点及面形成自己知识体系,也能在框架之上思考应用层构建视图实现的合理性。
对视图基础有整体印象后,再学习Flutter视图系统所提供的UI控件。作为UI框架,与Android、iOS和React类似,Flutter也提供很多UI控件。而文本、图片和按钮则是这些不同UI框架中构建视图都要用到的最基本控件。
1 文本控件
文本是视图系统中的常见控件,用来显示一段特定样式的字符串,就比如Android里的TextView、iOS中的UILabel。而在Flutter中,文本展示是通过Text控件实现的。
Text支持两种类型文本展示:
- 默认的展示单一样式的文本Text
- 支持多种混合样式的富文本Text.rich
1.1 使用单一样式的文本Text
单一样式文本Text的初始化,要传入需展示的字符串。而这字符串的具体展示效果,受构造函数其他参数控制。这些参数分为:
- 控制整体文本布局的参数,如文本对齐方式textAlign、文本排版方向textDirection,文本显示最大行数maxLines、文本截断规则overflow等都是构造函数中的参数
- 控制文本展示样式的参数,如字体名称fontFamily、字体大小fontSize、文本颜色color、文本阴影shadows等等,这些参数被统一封装到了构造函数中的参数style
展示单一样式的文本Text
居中布局、20号红色粗体展示样式的字符串:
Text( '文本是视图系统中的常见控件,用来显示一段特定样式的字符串,就比如Android里的TextView,或是iOS中的UILabel。', textAlign: TextAlign.center,//居中显示 style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.red),//20号红色粗体展示 );
运行效果如下图所示:
在一段字符串中支持多种混合展示样式
与单一样式的关键区别在于分片,即如何把一段字符串分为几个片段,给每个片段单独设置样式:
- Android中使用SpannableString实现
- iOS中使用NSAttributedString来实现
- Flutter也有类似概念TextSpan
TextSpan定义一个字符串片段该如何控制其展示样式,而将这些有独立展示样式的字符串组装在一起,则能支持混合样式的富文本展示。
分别定义黑色、红色两种展示样式,随后把一段字符串分成4个片段,并设置不同展示样式:
TextStyle blackStyle = TextStyle(fontWeight: FontWeight.normal, fontSize: 20, color: Colors.black); //黑色样式 TextStyle redStyle = TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.red); //红色样式 Text.rich( TextSpan( children: <TextSpan>[ TextSpan(text:'文本是视图系统中常见的控件,它用来显示一段特定样式的字符串,类似', style: redStyle), //第1个片段,红色样式 TextSpan(text:'Android', style: blackStyle), //第1个片段,黑色样式 TextSpan(text:'中的', style:redStyle), //第1个片段,红色样式 TextSpan(text:'TextView', style: blackStyle) //第1个片段,黑色样式 ]), textAlign: TextAlign.center, );
运行效果,如下图所示:
再看Flutter中的图片控件Image。
2 图片
使用Image可让我们向用户展示一张图片。图片显示方式很多,如资源图片、网络图片、文件图片等,图片格式各不相同,在Flutter也有多种方式加载不同形式、支持不同格式图片:
- 加载本地资源图片,如Image.asset(‘images/logo.png’)
- 加载本地(File文件)图片,如Image.file(new File(’/storage/xxx/xxx/test.jpg’))
- 加载网络图片,如Image.network(
'http://xxx/xxx/test.gif'
)
除了根据图片显示方式设置不同图片源,图片构造方法还提供:
- 填充模式fit
- 拉伸模式centerSlice
- 重复模式repeat等属性
可针对图片与目标区域的宽高比差异制定排版模式。
这和Android中ImageView、iOS里的UIImageView的属性都类似。可参考官方文档中的 Image的构造函数 部分,去查看Image控件具体使用方法。
FadeInImage控件
加载网络图片,为提升用户等待体验,会加占位图、加载动画等元素,但默认Image.network构造方法不支持这些高级功能,FadeInImage控件就有用了。
FadeInImage控件提供图片占位功能,并支持在图片加载完成时淡入淡出视觉效果。由于Image支持gif格式,还可将一些炫酷加载动画作占位图。
加载大图片时,将一张loading的gif作为占位图展示给用户:
FadeInImage.assetNetwork( placeholder: 'assets/loading.gif', //gif占位 image: 'https://xxx/xxx/xxx.jpg', fit: BoxFit.cover, //图片拉伸模式 width: 200, height: 200, )
Image控件需根据图片资源异步加载情况,决定显示效果,因此是StatefulWidget。图片加载过程由ImageProvider触发,而ImageProvider表示异步获取图片数据的操作,可从资源、文件和网络等不同渠道获取图片。
- ImageProvider根据_ImageeState中传递的图片配置生成对应的图片缓存key
- 然后去ImageCache查找是否有对应图片缓存:
- 有,通知_ImageState刷新UI
- 没有,启动ImageStream开始异步加载,加载完毕后,更新缓存
- 最后,通知_ImageState刷新UI
图片展示流程:
ImageCache使用LRU缓存更新策略,默认最多存储1000张图片,最大缓存限制100MB,当限定空间存满数据,把最久没有被访问到的图片清除。图片 缓存只会在运行期间生效,也就是只缓存在内存中。要支持缓存到文件系统,可使用 CachedNetworkImage 控件。
CachedNetworkImage使用类似Image,除了支持图片缓存,还提供比FadeInImage更强大的加载过程占位与加载错误占位,支持比用图片占位更灵活的自定义控件占位。
加载图片时,不仅给用户展示占位的转圈loading,还提供错误图兜底,以备图片加载出错:
CachedNetworkImage( imageUrl: "http://xxx/xxx/jpg", placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), )
3 按钮
可响应用户交互事件。Flutter提供三个基本按钮控件:
- FloatingActionButton:圆形按钮,一般在屏幕内容前面,处理界面中最常用、最基础用户动作。计数器示例的“+”悬浮按钮就是FloatingActionButton
- RaisedButton:凸起按钮,默认带灰色背景,被点击后灰色背景会加深
- FlatButton:扁平化按钮,默认透明背景,被点击后会呈现灰色背景
按钮控件使用方法唯一区别只是默认样式不同。
分别定义FloatingActionButton、FlatButton与RaisedButton,功能完全一样,点击时打印文字:
FloatingActionButton(onPressed: () => print('FloatingActionButton pressed'),child: Text('Btn'),); FlatButton(onPressed: () => print('FlatButton pressed'),child: Text('Btn'),); RaisedButton(onPressed: () => print('RaisedButton pressed'),child: Text('Btn'),);
按钮控件:
既然是按钮,因此除了控制基本样式,还要响应用户点击行为。这就对应按钮控件中的两个最重要参数:
- onPressed参数用于设置点击回调,告诉Flutter在按钮被点击时通知我们。若onPressed参数为空,则按钮会处于禁用状态,不响应用户点击
- child参数用于设置按钮内容,告诉Flutter控件应长成啥样,即控制按钮控件的基本样式。child可接收任意Widget,如上面例子中传入的Text,此外还可传入Image等控件
虽可通过child参数控制按钮控件基本样式,但系统默认样式太单调,通常进行控件样式定制。与Text控件类似,按钮控件也提供丰富样式定制功能,如背景颜色color、按钮形状shape、主题颜色colorBrightness等。
以FlatButton为例介绍按钮样式定制:
FlatButton( color: Colors.yellow, //设置背景色为黄色 shape:BeveledRectangleBorder(borderRadius: BorderRadius.circular(20.0)), //设置斜角矩形边框 colorBrightness: Brightness.light, //确保文字按钮为深色 onPressed: () => print('FlatButton pressed'), child: Row(children: <Widget>[Icon(Icons.add), Text("Add")],) );
将一个加号Icon与文本组合,定义按钮基本外观;随后通过shape指定其外形为斜角矩形边框,并将按钮背景色设为黄色。
因为按钮背景颜色是浅色的,为避免按钮文字看不清楚,我们通过设置按钮主题colorBrightness为Brightness.light,保证按钮文字颜色为深色。展示效果:
4 总结
UI控件是构建一个视图的基本元素,而文本、图片和按钮则是其中最经典的控件。
首先,认识支持单一样式和混合样式两种类型文本展示控件Text:
- 通过TextStyle控制字符串的展示样式,其他参数控制文本布局,实现单一样式文本展示
- 通过TextSpan将字符串分割为若干片段,对每个片段单独设置样式后组装,实现支持混合样式富文本展示
支持多种图片源加载方式的图片控件Image。Image内部通过ImageProvider根据缓存状态,触发异步加载流程,通知_ImageState刷新UI。不过,由于图片缓存是内存缓存,因此只在运行期间生效。要支持缓存到文件系统,使用CachedNetworkImage。
最后学习按钮控件。Flutter提供多种按钮控件,使用方法类似。控件初始化的child参数用于设置按钮长什么样,而onPressed参数则用于设置点击回调。与Text类似,按钮内部也有丰富UI定制接口。
UI基本信息表达,Flutter经典控件与原生Android、iOS系统提供的控件无本质区别。但自定义控件样式,Flutter的这些经典控件提供强大简洁扩展能力,快速开发功能复杂、样式丰富页面。
5 FAQ
阅读Flutter SDK中Text、Image、FadeInImage,以及按钮控件FloatingActionButton、FlatButton与RaisedButton的源码,在build函数中找出在内部真正承载其视觉功能的控件。发现什么现象?
在阅读Flutter SDK中Text、Image、FadeInImage、FloatingActionButton、FlatButton和RaisedButton的源码时,可以发现它们的build函数中都有一个内部真正承载其视觉功能的控件。
- 对于Text控件,其内部真正承载其视觉功能的控件为RichText。
- 对于Image控件,其内部真正承载其视觉功能的控件为RawImage。
- 对于FadeInImage控件,其内部真正承载其视觉功能的控件为AnimatedOpacity和RawImage。
- 对于FloatingActionButton控件,其内部真正承载其视觉功能的控件为Material和InkResponse。
- 对于FlatButton控件,其内部真正承载其视觉功能的控件为Material和InkWell。
对于RaisedButton控件,其内部真正承载其视觉功能的控件为Material和InkResponse。
这些控件都是Flutter框架中提供的基础控件,用于实现各种不同的视觉效果。在这些控件的build函数中,会根据不同的属性值来创建这些基础控件,并将它们组合在一起,从而实现所需的视觉效果。
再分享我整理汇总的一些 Java 面试相关资料(亲自验证,严谨科学!别再看网上误导人的垃圾面试题!!!),助你拿到更多 offer!
2023年最新Java学习路线一条龙:
再给大家推荐一个学习 Java 和准备Java 面试的公众号【JavaEdge】(强烈推荐!)