Flex(
direction: Axis.horizontal,
children: [
Expanded(
flex: 1,
child: Container(
height: 80.0,
color: Colors.red,
),
),
Expanded(
flex: 2,
child: Container(
height: 80.0,
color: Colors.green,
),
),
],
),
Padding(
padding: const EdgeInsets.only(top: 20.0),
child: SizedBox(
height: 100.0,
//Flex的三个子widget,在垂直方向按2:1:1来占用100像素的空间
child: Flex(
direction: Axis.vertical,
children: [
Expanded(
flex: 2,
child: Container(
color: Colors.red,
),
),
Spacer(//Spacer的功能是占用指定比例的空间,实际上它只是Expanded的一个包装
flex: 1,
),
Expanded(
flex: 1,
child: Container(
color: Colors.green,
),
),
],
),
),
),
],
);
}
流式布局
流式布局(Liquid)的特点(也叫"Fluid") 是页面元素的宽度按照屏幕分辨率进行适配调整,但整体布局不变。栅栏系统(网格系统),用户标签等。在Flutter中主要有Wrap和Flow两种Widget实现。
Wrap
在介绍Row和Colum时,如果子widget超出屏幕范围,则会报溢出错误,在Flutter中通过Wrap和Flow来支持流式布局,溢出部分则会自动折行。
源码属性解读
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 [],
})
上述有很多属性和Row的相同,其意义其实也是相同的,这里我就不一一介绍了,主要介绍下不同的属性:
- spacing:主轴方向子widget的间距
- runSpacing:纵轴方向的间距
- runAlignment:纵轴方向的对齐方式
示例代码
Wrap(
spacing: 10.0,
direction: Axis.horizontal,
alignment: WrapAlignment.start,
children: [
_card(‘关注’),
_card(‘推荐’),
_card(‘新时代’),
_card(‘小视频’),
_card(‘党媒推荐’),
_card(‘中国新唱将’),
_card(‘历史’),
_card(‘视频’),
_card(‘游戏’),
_card(‘头条号’),
_card(‘数码’),
],
)
Widget _card(String title) {
return Card(child: Text(title),);
}
}
复制代码
运行效果
小结
- 使用Wrap可以很轻松的实现流式布局效果
- Wrap支持设置流式布局是纵向显示或者是横向显示
- 可以使用alignment属性来控制widgets的布局方式
Flow
我们一般很少会使用Flow,因为其过于复杂,需要自己实现子widget的位置转换,在很多场景下首先要考虑的是Wrap是否满足需求。Flow主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。Flow有如下优点:
- 性能好;Flow是一个对child尺寸以及位置调整非常高效的控件,Flow用转换矩阵(transformation matrices)在对child进行位置调整的时候进行了优化:在Flow定位过后,如果child的尺寸或者位置发生了变化,在FlowDelegate中的paintChildren()方法中调用context.paintChild 进行重绘,而context.paintChild在重绘时使用了转换矩阵(transformation matrices),并没有实际调整Widget位置。
- 灵活;由于我们需要自己实现FlowDelegate的paintChildren()方法,所以我们需要自己计算每一个widget的位置,因此,可以自定义布局策略。 缺点:
- 使用复杂.
- 不能自适应子widget大小,必须通过指定父容器大小或实现TestFlowDelegate的getSize返回固定大小。
示例代码
我们对六个色块进行自定义流式布局:
Flow(
delegate: TestFlowDelegate(margin: EdgeInsets.all(10.0)),
children: [
new Container(width: 80.0, height:80.0, color: Colors.red,),
new Container(width: 80.0, height:80.0, color: Colors.green,),
new Container(width: 80.0, height:80.0, color: Colors.blue,),
new Container(width: 80.0, height:80.0, color: Colors.yellow,),
new Container(width: 80.0, height:80.0, color: Colors.brown,),
new Container(width: 80.0, height:80.0, color: Colors.purple,),
],
)
实现TestFlowDelegate:
class TestFlowDelegate extends FlowDelegate {
EdgeInsets margin = EdgeInsets.zero;
TestFlowDelegate({this.margin});
@override
void 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;
}
}
}
getSize(BoxConstraints constraints){
//指定Flow的大小
return Size(double.infinity,200.0);
}
@override
bool shouldRepaint(FlowDelegate oldDelegate) {
return oldDelegate != this;
}
}
运行效果
可以看到我们主要的任务就是实现paintChildren,它的主要任务是确定每个子widget位置。由于Flow不能自适应子widget的大小,我们通过在getSize返回一个固定大小来指定Flow的大小,实现起来还是比较麻烦的。
小结
- 参数简单,不过需要自己定义delegate
- delegate一般是为了实现child的绘制,就是位置的摆放,不同情况需要定义不同的delegate
- 不同的delegate一般会提供实现的几个方法:
- getConstraintsForChild: 设置每个child的布局约束条件,会覆盖已有的方式
- getSize:设置控件的尺寸
- shouldRelayout:表示是否需要重新布局
- 尽可能的用Wrap,毕竟简单
层叠布局
层叠布局和Web中的绝对定位、Android中的Frame布局是相似的,子widget可以根据到父容器四个角的位置来确定本身的位置。绝对定位允许子widget堆叠(按照代码中声明的顺序)。Flutter中使用Stack和Positioned来实现绝对定位,Stack允许子widget堆叠,而Positioned可以给子widget定位(根据Stack的四个角)。
Stack
Stack({
this.alignment = AlignmentDirectional.topStart,
this.textDirection,
this.fit = StackFit.loose,
this.overflow = Overflow.clip,
List children = const [],
})
- alignment:此参数决定如何去对齐没有定位(没有使用Positioned)或部分定位的子widget。所谓部分定位,在这里特指没有在某一个轴上定位:left、right为横轴,top、bottom为纵轴,只要包含某个轴上的一个定位属性就算在该轴上有定位。
- textDirection:和Row、Wrap的textDirection功能一样,都用于决定alignment对齐的参考系即:textDirection的值为TextDirection.ltr,则alignment的start代表左,end代表右;textDirection的值为TextDirection.rtl,则alignment的start代表右,end代表左。
- fit:此参数用于决定没有定位的子widget如何去适应Stack的大小。StackFit.loose表示使用子widget的大小,StackFit.expand表示扩伸到Stack的大小。
- overflow:此属性决定如何显示超出Stack显示空间的子widget,值为Overflow.clip时,超出部分会被剪裁(隐藏),值为Overflow.visible 时则不会。
下面是我用Stack实现的一个简易的loading
class Loading extends StatelessWidget {
/// ProgressIndicator的padding,决定loading的大小
final EdgeInsets padding = EdgeInsets.all(30.0);
/// 文字顶部距菊花的底部的距离
final double margin = 10.0;
/// 圆角
final double cornerRadius = 10.0;
final Widget _child;
final bool _isLoading;
final double opacity;
final Color color;
final String text;
Loading({
Key key,
@required child,
@required isLoading,
this.text,
this.opacity = 0.3,
this.color = Colors.grey,
}) : assert(child != null),
assert(isLoading != null),
_child = child,
_isLoading = isLoading,
super(key: key);
@override
Widget build(BuildContext context) {
List widgetList = List();
widgetList.add(_child);
if (_isLoading) {
final loading = [
Opacity(
opacity: opacity,
child: ModalBarrier(dismissible: false, color: color),
),
_buildProgressIndicator()
];
widgetList.addAll(loading);
}
return Stack(
children: widgetList,
);
}
Widget _buildProgressIndicator() {
return Center(
child: Container(
padding: padding,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CupertinoActivityIndicator(),
Padding(
padding: EdgeInsets.only(top: margin),
child: Text(text ?? ‘加载中…’)),
],
),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(cornerRadius)),
color: Colors.white),
),
);
}
}
显示效果
本控件使用Stack封装,你传入的主视图在最下面一层,背景层在中间,最上面一层为菊花和文字loading,用isLoading控制显示
Positioned
const Positioned({
Key key,
this.left,
this.top,
this.right,
this.bottom,
this.width,
this.height,
@required Widget child,
})
left、top 、right、 bottom分别代表离Stack左、上、右、底四边的距离。width和height用于指定定位元素的宽度和高度,注意,此处的width、height 和其它地方的意义稍微有点区别,此处用于配合left、top 、right、 bottom来定位widget,举个例子,在水平方向时,你只能指定left、right、width三个属性中的两个,如指定left和width后,right会自动算出(left+width),如果同时指定三个属性则会报错,垂直方向同理。
示例代码
//通过ConstrainedBox来确保Stack占满屏幕
ConstrainedBox(
constraints: BoxConstraints.expand(),
child: Stack(
alignment:Alignment.center , //指定未定位或部分定位widget的对齐方式
children: [
Container(child: Text(“Hello world”,style: TextStyle(color: Colors.white)),
color: Colors.red,
),
Positioned(
left: 18.0,
child: Text(“I am Jack”),
),
Positioned(
top: 18.0,
child: Text(“Your friend”),
)
],
),
);
运行效果:
由于第一个子widget Text(“Hello world”)没有指定定位,并且alignment值为Alignment.center,所以,它会居中显示。第二个子widget Text(“I am Jack”)只指定了水平方向的定位(left),所以属于部分定位,即垂直方向上没有定位,那么它在垂直方向对齐方式则会按照alignment指定的对齐方式对齐,即垂直方向居中。对于第三个子widget Text(“Your friend”),和第二个Text原理一样,只不过是水平方向没有定位,则水平方向居中。
参考:
https://juejin.im/post/5c2458d6f265da613a541349
https://my.oschina.net/mchp/blog/3123764
https://www.jianshu.com/p/5c2654422486
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/27ceb52dc4bad90b2354a4320d23ac51.jpeg)
总结
开发是面向对象。我们找工作应该更多是面向面试。哪怕进大厂真的只是去宁螺丝,但你要进去得先学会面试的时候造飞机不是么?
作者13年java转Android开发,在小厂待过,也去过华为,OPPO等,去年四月份进了阿里一直到现在。等大厂待过也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。
这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
相信它会给大家带来很多收获:
资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
络 + 诸多细节**,由于篇幅有限,这里以图片的形式给大家展示一部分。
相信它会给大家带来很多收获:
[外链图片转存中…(img-vZ2fzNCw-1712766412014)]
[外链图片转存中…(img-Bp24e16C-1712766412014)]
资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!