用Android原生实现流式布局【实现例如app的很多标签】需要自定义View继承自ViewGroup,然后代码量也不小。

相关自定义View实现流式布局,请参考这位老兄的博客:

https://blog.csdn.net/u013107751/article/details/81701606

或者慕课网上的android免费课程有一门课是专门实现自定义流式布局的。

而在Flutter中实现此效果却非常简单。


1.布局类Widget的介绍:

布局类Widget都会包含一个或多个子widget,不同的布局类Widget对子widget排版(layout)方式 不同。Element树才是最终的绘制树,Element树是通过widget树来创建的(通 过 Widget.createElement() ),widget其实就是Element的配置数据。

Flutter中,根据 Widget是否需要包含子节点将Widget分为了三类,分别对应三种Element:

布局类widget
Widget 对应的Element 用途
LeafRenderObjectWidget LeafRenderObjectElement Widget树的叶子节点, 用于没有子节点的 widget,通常基础 widget都属于这一类, 如Text、Image。
SingleChildRenderObjectWidget SingleChildRenderObjectElement 包含一个子Widget, 如: ConstrainedBox、 DecoratedBox等
MultiChildRenderObjectWidget MultiChildRenderObjectElement 包含多个子Widget,一 般都有一个children 参数,接受一个Widget 数组。如Row、 Column、Stack等

Flutter中的很多Widget是直接继承自StatelessWidget或StatefulWidget,然后 在 build() 方法中构建真正的RenderObjectWidget,如Text,它其实是继承自StatelessWidget, 然后在 build() 方法中通过RichText来构建其子树,而RichText才是继承自 LeafRenderObjectWidget。所以为了方便叙述,我们也可以直接说Text属于 LeafRenderObjectWidget(其它widget也可以这么描述),这才是本质。StatelessWidget和StatefulWidget就是两个用于组合Widget的基类,它们本身并不关联最终的渲染对象 (RenderObjectWidget)。

布局类Widget就是指直接或间接继承(包含)MultiChildRenderObjectWidget的Widget的 Widget,它们一般都会有一个children属性用于接收子Widget。我们看一下继承关系 Widget > RenderObject > (Leaf/SingleChild/MultiChild)RenderObjectWidget 。RenderObjectWidget类中定义了创建、更新RenderObject的方法,子类必须实现他们,关于 RenderObject我们现在只需要知道它是最终布局、渲染UI界面的对象即可,也就是说,对于布局类 Widget来说,其布局算法都是通过对应的RenderObject对象来实现的.


2.弹性布局Flex:

弹性布局允许子widget按照一定比例来分配父容器空间,弹性布局的概念在其UI系统中也都存在,如 H5中的弹性盒子布局,Android中的FlexboxLayout。Flutter中的弹性布局主要通过Flex和 Expanded来配合实现。

2.1 Flex:

Flex可以沿着水平或垂直方向排列子widget,如果你知道主轴方向,使用Row或Column会方便一些, 因为Row和Column都继承自Flex,参数基本相同,所以能使用Flex的地方一定可以使用Row或 Column。Flex本身功能是很强大的,它也可以和Expanded配合实现弹性布局.

Flex({...@required this.direction, //弹性布局的方向, Row默认为水平方向,Column默认为垂直方向List<Widget> children = const <Widget>[],})

Flex继承自MultiChildRenderObjectWidget,对应的RenderObject为RenderFlex, RenderFlex中实现了其布局算法。

2.2 Expanded:

可以按比例“扩伸”Row、Column和Flex子widget所占用的空间【类似于android布局中的layout: width/height = 0 ,layout:weight=n  (n>0) 然后系统根据 (每个weight的值/所有的weight值相加)=每个组件所占布局的比例。】

 const Expanded({int flex = 1,@required Widget child,})

flex为弹性系数,如果为0或null,则child是没有弹性的,即不会被扩伸占用的空间。如果大于0, 所有的Expanded按照其flex的比例来分割主轴的全部空闲空间。

示例代码如下【Expanded和Flex的配合使用】:

void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter 表单Form',theme: ThemeData(primarySwatch: Colors.blue,),
//      home: FormRouteDemo(),home: FlexLayoutRouteDemo(),);}
}import 'package:flutter/material.dart';class FlexLayoutRouteDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(),body: Column(children: <Widget>[//Flex的2个子widget按1:2来占据水平空间Flex(direction: Axis.horizontal,children: <Widget>[Expanded(flex: 1,child: Container(height: 30.0,color: Colors.red,),),Expanded(flex: 2,child: Container(height: 30.0,color: Colors.green,),),],),Padding(padding: const EdgeInsets.only(top: 20.0),child: SizedBox(height: 100.0,//Flex的3个子widget,在垂直方向按2:1:1 来占用100像素的空间child: Flex(direction: Axis.vertical,children: <Widget>[Expanded(flex: 2,child: Container(height: 30.0,color: Colors.blue,),),Spacer(flex: 1,),Expanded(flex: 1,child: Container(height: 30.0,color: Colors.greenAccent,)),],),),)],),);}
}


3.流式布局:

3.1 Wrap:

在使用Row和Colum时,如果子widget超出屏幕范围,则会报溢出错误,如:

Row(children: <Widget>[Text("xxx"*100)//显示100次“xxx”],);

运行后:

右边溢出部分报错。这是因为Row默认只有一行,如果超出屏幕不会折行。我们把超出屏幕 显示范围会自动折行的布局称为流式布局。Flutter中通过Wrap和Flow来支持流式布局。将上例中的 Row换成Wrap后溢出部分则会自动折行。

Wrap的定义:

Wrap({

. ...

this.direction = Axis.horizontal,

this.alignment = WrapAlignment.start,

this.spacing = 0.0,

this.runAlignment = WrapAlignment.start,

this.runSpacing = 0.0,

this.crossAxisAlignment = WrapCrossAlignment.start,

this.textDirection,

this.verticalDirection = VerticalDirection.down,

List children = const [],

})

Wrap的很多属性在Row(包括Flex和Column)中也有,如direction、 crossAxisAlignment、textDirection、verticalDirection等,这些参数意义是相同的.

参数:

spacing:主轴方向子widget的间距

runSpacing:纵轴方向的间距

runAlignment:纵轴方向的对齐方式

可以认为Wrap和Flex(包括Row和 Column)除了超出显示范围后Wrap会折行外,其它行为基本相同。

示例代码如下【wrap实现流式布局效果】:


void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter 表单Form',theme: ThemeData(primarySwatch: Colors.blue,),
//      home: FormRouteDemo(),
//      home: FlexLayoutRouteDemo(),home: WrapDemo(),);}
}import 'package:flutter/material.dart';class WrapDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(),body: Wrap(/*** 这里区分一下主轴和纵轴的概念:* 当水平方向的时候,其主轴就是水平,纵轴就是垂直。* 当垂直方向的时候,其主轴就是垂直,纵轴就是水平。*/direction: Axis.horizontal,//不设置默认为horizontalalignment: WrapAlignment.center,//沿主轴方向居中spacing: 0.0,//主轴(水平)方向间距runSpacing: 4.0,//纵轴(垂直)方向间距children: <Widget>[new Chip(avatar: new CircleAvatar(backgroundColor: Colors.blue,child: Text('F'),),label: new Text('flutter'),),new Chip(avatar: new CircleAvatar(backgroundColor: Colors.orange,child: Text('F'),),label: new Text('android'),),new Chip(avatar: new CircleAvatar(backgroundColor: Colors.grey,child: Text('F'),),label: new Text('微信小程序'),),new Chip(avatar: new CircleAvatar(backgroundColor: Colors.red,child: Text('F'),),label: new Text('python'),),new Chip(avatar: new CircleAvatar(backgroundColor: Colors.greenAccent,child: Text('F'),),label: new Text('jni与Ndk'),),new Chip(avatar: new CircleAvatar(backgroundColor: Colors.yellow,child: Text('F'),),label: new Text('springboot'),),new Chip(avatar: new CircleAvatar(backgroundColor: Colors.purple,child: Text('F'),),label: new Text('kotlin'),),],),);}}

注意:上述代码中的某些参数不设置也是可以的,因为dart语法中的构造函数是具备默认参数的。这点和kotlin一样完美。

3.2 Flow:

其过于复杂,需要自己实现子widget的位置转换,在很多场景下首先 要考虑的是Wrap是否满足需求。Flow主要用于一些需要自定义布局策略或性能要求较高(如动画中)的 场景。

其优点:

性能好;Flow是一个对child尺寸以及位置调整非常高效的控件,Flow用转换矩阵 (transformation matrices)在对child进行位置调整的时候进行了优化:在Flow定位过 后,如果child的尺寸或者位置发生了变化,在FlowDelegate中的 paintChildren() 方法中 调用 context.paintChild 进行重绘,而 context.paintChild 在重绘时使用了转换矩阵 (transformation matrices),并没有实际调整Widget位置。

灵活;由于我们需要自己实现FlowDelegate的 paintChildren() 方法,所以我们需要自己计 算每一个widget的位置,因此,可以自定义布局策略。

缺点:

使用复杂. 不能自适应子widget大小,必须通过指定父容器大小或实现TestFlowDelegate 的 getSize 返回固定大小。

示例【对六个色块进行自定义流式布局】:

void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter 表单Form',theme: ThemeData(primarySwatch: Colors.blue,),
//      home: FormRouteDemo(),
//      home: FlexLayoutRouteDemo(),
//      home: WrapDemo(),home: FlowDemo(),);}
}import 'package:flutter/material.dart';class FlowDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(),//Flow布局:需要自己实现FlowDelegate的paintChildren()方法。【自定义布局策略】body: Flow(delegate: FlowDelegateDemo(margin: EdgeInsets.all(10.0)),children: <Widget>[new Container(width: 80.0,height: 80.0,color: Colors.grey,),new Container(width: 80.0,height: 80.0,color: Colors.blue,),new Container(width: 80.0,height: 80.0,color: Colors.greenAccent,),new Container(width: 80.0,height: 80.0,color: Colors.yellow,),new Container(width: 80.0,height: 80.0,color: Colors.red,),new Container(width: 80.0,height: 80.0,color: Colors.purple,),],),);}
}//自定义类继承FlowDelegate
class FlowDelegateDemo extends FlowDelegate{EdgeInsets margin = EdgeInsets.zero; //zero是0的意思,在此处是初始化赋值。具体的值由构造函数中传递过来的margin决定/*** 这是一个构造方法,其参数是由new出本对象外部传递过来。* 注意的是,dart语法中,构造函数中参数是可以被{}包围的,包围后就是可选参数,可传可不传。* 当然你也完全可以使用默认参数赋值一个默认值,达到外部不传递参数的效果。*/FlowDelegateDemo({this.margin});@overridevoid paintChildren(FlowPaintingContext context) {var x = margin.left;var y = margin.top;//计算每一个子widget的位置for(int i=0;i<context.childCount;i++){var w = context.getChildSize(i).width+x+margin.right;if(w < context.size.width){context.paintChild(i,transform: new Matrix4.translationValues(x, y, 0.0));x=w+margin.left;}else{x=margin.left;y+=context.getChildSize(i).height+margin.top+margin.bottom;//绘制子widget(有优化)context.paintChild(i,transform: new Matrix4.translationValues(x, y, 0.0));x+=context.getChildSize(i).width+margin.left+margin.right;}}}@overrideSize getSize(BoxConstraints constraints) {
//    return super.getSize(constraints);//指定flow的大小return Size(double.infinity, 200.0);}@overridebool shouldRepaint(FlowDelegate oldDelegate) {return oldDelegate != this;}}


总结:

dart似乎不被看好,且flutter的嵌套恶心。但是其性能和思维的简洁对开发者写起来还是有nice的。

flutter解决地狱回调用类似前端promise语法【then(xx,onError()).catchError()】或者 async await写出具有异步的同步代码.

Koltin的协程和rxjava也是解决地狱回调。

Flutter: 弹性布局Flex(Expanded)、流式布局Wrap、Flow相关推荐

  1. CSS的三大布局方式(流式布局,浮动布局和层布局)

    文章目录 前言 一.标准文档流 二.三种布局方式 1.流式布局 2.浮动布局 (1)字围效果 (2)圣杯布局 3.层布局 定位的分类: (1) 相对定位 position:relative (2) 绝 ...

  2. 移动web现状、viewport视口、二倍图、移动web开发主流方案、布局技术选型(流式布局、flex弹性布局、less+rem+媒体查询布局、混合布局、媒体查询、bootstrap)

    移动端web现状: 移动端常见浏览器:UC浏览器,QQ浏览器,Opera浏览器,百度手机浏览器,360安全浏览器,谷歌浏览器,搜狗手机浏览器,猎豹浏览器及杂牌浏览器.移动端常见的浏览器都是基于webk ...

  3. 01移动端布局基础之流式布局

    技术交流QQ群:1027579432,欢迎你的加入! 1.移动端基础 浏览器现状 PC端常见浏览器:360浏览器.谷歌浏览器.火狐浏览器.QQ浏览器.百度浏览器.搜狗浏览器.IE浏览器等. 移动端常见 ...

  4. android recyclerview流式布局,Android FlexboxLayout流式布局

    Android FlexboxLayout流式布局 FlexBoxLayout是为Android带来了与 CSS Flexible Box Layout(CSS 弹性盒子)相似功能的库. 一:添加依赖 ...

  5. 02移动端布局基础之流式布局项目实战(京东移动端首页)

    技术交流QQ群:1027579432,欢迎你的加入! 1.技术选型 方案:采取单独制作移动页面方案 技术:布局采取流式布局 2.搭建相关文件夹结构 3.设置视口标签以及引入初始化样式 ``` < ...

  6. android 流式布局官方,Android 流式布局

    FlowLayoutManager 这是一个自定义实现流式布局的LayoutManager,配合RecyclerView使用,可实现标签效果:目前的问题是没有实现View的缓存,以后再补吧. 代码示例 ...

  7. html5流式布局,流式布局是什么?流式布局详细介绍

    在PC端进行网页制作时,经常使用固定像素并且内容居中的网页布局,为了适应小屏幕的设备,在移动设备和跨平台(响应式)网页开发过程中,多数使用流式布局,下面我们就对流式布局进行详细介绍. 流式布局是一种等 ...

  8. Swing布局管理器--流式布局管理器

    流式布局管理器 FlowLayout流式布局管理器,是JPanel和JApplet的默认布局管理 FlowLayout会将组件从上到下,从左到右的放置规律逐渐进行定位,直到占据这一行所在的空间,才会向 ...

  9. 4.布局:FlowLayout流式布局(Java swing 入门)

    FlowLayout(流式布局管理器)是 JPanel 和 JApplet 的默认布局管理器.FlowLayout 会将组件按照从上到下.从左到右的放置规律逐行进行定位.与其他布局管理器不同的是,Fl ...

  10. android 热搜词 布局,Android FlowLayout流式布局打造热门标签(高仿抖音热搜)

    需要先学习下面2个内容 1.已经基本给大家介绍了如何自定义ViewGroup,如果你还不了解 2.宽高的计算 一.XML布局 从布局图中可以看到,FlowLayout中包含了很多TextView.难度 ...

最新文章

  1. [cocos2d-x]cocos2d和cocos2d-x的一些通用性
  2. viewpager 无网络的时候滑动异常
  3. windows下安装python
  4. SpringSecurity的csrf防护措施
  5. word 2007 中插入图片无法显示,只能显示底部一部分
  6. linux系统无root权限lua库安装,liunx系统中安装lua以及torch
  7. 蓝桥杯 ALGO-107 算法训练 9-7链表数据求和操作
  8. 场景文字检测OD与字符识别OCR概述
  9. MATLAB添加工具包
  10. 初中计算机位图和矢量图教案,浅析图形图像软件教学中位图与矢量图的区别
  11. 分享一大波高清电子元器件矢量图,速速下载收藏!
  12. html 三色渐变色,CSS3常用的几种颜色渐变模式总结现
  13. gulp-sass 使用报错Error:gulp-sass no longer has a default Sass compiler; please set one yourself
  14. flv格式转换为mp4(ffmpeg)
  15. 在Windows平台上如何安装Python
  16. turtle库使用教程 及 绘制 浪漫樱花 五角星 彩虹玫瑰 谢尔宾斯基三角形 实例
  17. AMD首款5纳米PC处理器锐龙7000亮相,频率首破5GHz大关,单核性能提升15%
  18. 计算机研究生就业方向之考公
  19. 每秒1.28万亿行,最快的分布式关系数据库MemSQL又破记录了!
  20. C# SolidWorks 二次开发 API ---读取零件相关属性

热门文章

  1. IDEA2016 license server 激活
  2. IntelliJ IDEA自动添加空行问题
  3. 【数论定理】卢卡斯定理
  4. 国内各IE内核浏览器所调用的IE版本--转了
  5. Linux系统根目录详解
  6. 消息队列常见的几种使用场景介绍!
  7. 【Matlab语音分析】语音信号分析【含GUI源码 1718期】
  8. MySQL Authentications
  9. [当人工智能遇上安全] 5.基于机器学习算法的主机恶意代码识别研究
  10. 基于STM32单片机的直流电机控制系统加减速正反转设计方案原理图程序