赞
踩
先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
child: Text(‘BUTTON’),
);
var raisedButton = RaisedButton(
onPressed: () => print(‘RaisedButton pressed’),
child: Text(‘BUTTON’),
);
return raisedButton;
}
}
通过设置 onPressed 回调,我们可以在按钮被点击的时候得到回调。child 参数用于设置按钮的内容。虽然我们给 child 传递的是 Text,但这不是必需的,它可以接受任意的 Widget,比方说,Image。
注意,由于我们只是在按钮点击的时候打印一个字符串,这里使用 StatelessWidget 是没有问题的。但如果有其他 UI 动作(比如弹出一个 dialog,则必须使用 StatefulWidget)。
它们的区别只是样式不同而已的:
FlatButton:
flat-button
RaiseButton:
raised-button
Flutter 的文本输入框叫 TextField。为了获取用户输入的文本,我们需要给他设置一个 controller。通过这个 controller,就可以拿到文本框里的内容:
class MessageForm extends StatefulWidget {
@override
State createState() {
return _MessageFormState();
}
}
class _MessageFormState extends State {
var editController = TextEditingController();
@override
Widget build(BuildContext context) {
// Row、Expand 都是用于布局的控件,这里可以先忽略它们
return Row(
children: [
Expanded(
child: TextField(
controller: editController,
),
),
RaisedButton(
child: Text(“click”),
onPressed: () => print(‘text inputted: ${editController.text}’),
)
],
);
}
@override
void dispose() {
super.dispose();
// 手动调用 controller 的 dispose 方法以释放资源
editController.dispose();
}
}
在前面的 TextField 例子中,我们只是把用户的输入通过 print 打印出来,这未免也太无趣了。在这一小节,我们要把它显示在 dialog 里。为了弹出一个 dialog,我们需要调用 showDialog 方法并传递一个 builder:
class _MessageFormState extends State {
var editController = TextEditingController();
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: TextField(
controller: editController,
),
),
RaisedButton(
child: Text(“click”),
onPressed: () {
showDialog(
// 第一个 context 是参数名,第二个 context 是 State 的成员变量
context: context,
builder: (_) {
return AlertDialog(
// dialog 的内容
content: Text(editController.text),
// actions 设置 dialog 的按钮
actions: [
FlatButton(
child: Text(‘OK’),
// 用户点击按钮后,关闭弹框
onPressed: () => Navigator.pop(context),
)
],
);
}
);
}
)
],
);
}
@override
void dispose() {
super.dispose();
editController.dispose();
}
}
我们经常说,Flutter 里面所有的东西都是 Widget,所以,布局也是 Widget。
控件 Container 可以让我们设置一个控件的尺寸、背景、margin 等:
class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Text(‘text’),
padding: EdgeInsets.all(8.0),
margin: EdgeInsets.all(4.0),
width: 80.0,
decoration: BoxDecoration(
// 背景色
color: Colors.grey,
// 圆角
borderRadius: BorderRadius.circular(5.0),
),
);
}
}
如果我们只需要 padding,可以使用控件 Padding:
class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(8.0),
child: Text(‘text’),
);
}
}
Center 就跟它的名字一样,把一个控件放在中间:
class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8.0),
margin: EdgeInsets.all(4.0),
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
// 背景色
color: Colors.grey,
// 圆角
borderRadius: BorderRadius.circular(5.0),
),
// 把文本放在 Container 的中间
child: Center(
child: Text(‘text’),
),
);
}
}
我们经常说,Flutter 里面所有的东西都是 Widget,所以,布局也是 Widget。水平布局我们可以使用 Row,竖直布局使用 Column。
class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
// 只有一个子元素的 widget,一般使用 child 参数来设置;Row 可以包含多个子控件,
// 对应的则是 children。
children: [
Text(‘text1’),
Text(‘text2’),
Text(‘text3’),
Text(‘text4’),
],
);
}
}
Column 的使用是一样的:
class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(‘text1’),
Text(‘text2’),
Text(‘text3’),
Text(‘text4’),
],
);
}
}
关于 Expand 控件,我们来看看 TextField 的那个例子:
class MessageForm extends StatefulWidget {
@override
State createState() {
return _MessageFormState();
}
}
class _MessageFormState extends State {
var editController = TextEditingController();
@override
Widget build(BuildContext context) {
return Row(
children: [
// 占满一行里除 RaisedButton 外的所有空间
Expanded(
child: TextField(
controller: editController,
),
),
RaisedButton(
child: Text(“click”),
onPressed: () => print(‘text inputted: ${editController.text}’),
)
],
);
}
@override
void dispose() {
super.dispose();
editController.dispose();
}
}
这里通过使用 Expand,TextField 才能够占满一行里除按钮外的所有空间。此外,当一行/列里有多个 Expand 时,我们还可以通过设置它的 flex 参数,在多个 Expand 之间按比例划分可用空间。
class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
// 占一行的 2/3
flex: 2,
child: RaisedButton(child: Text(‘btn1’),),
),
Expanded(
// 占一行的 1/3
flex: 1,
child: RaisedButton(child: Text(‘btn2’),),
),
],
);
}
}
有些时候,我们可能会希望一个控件叠在另一个控件的上面。于是,Stack 应运而生:
class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(
children: [
Text(‘foobar’),
Text(‘barfoo’),
],
);
}
}
默认情况下,子控件都按 Stack 的左上角对齐,于是,上面的两个文本完全一上一下堆叠在一起。我们还可以通过设置 alignment 参数来改变这个对齐的位置:
class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(
// Aligment 的取值范围为 [-1, 1],Stack 中心为 (0, 0),
// 这里设置为 (-0.5, -0.5) 后,可以让文本对齐到 Container 的 1/4 处
alignment: const Alignment(-0.5, -0.5),
children: [
Container(
width: 200.0,
height: 200.0,
color: Colors.blue,
),
Text(‘foobar’),
],
);
}
}
效果如下:
screenshot-stack
通过组合 Row/Column 和 Stack,已经能够完成绝大部分的布局了,所以 Flutter 里没有相对布局之类的东西。更多的 Flutter 控件,读者可以参考 flutter.io/widgets/。
在这一节里,我们综合前面所学的知识,来实现下面这个界面。
lakes-diagram
把图片 lake 放到项目根目录的 images 文件夹下(如果没有,你需要自己创建一个)
修改 pubspec.yaml,找到下面这个地方,然后把图片加进来
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
修改后如下:
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- images/lake.jpg
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ‘Flutter UI basic 1’,
home: Scaffold(
appBar: AppBar(
title: Text(‘Top Lakes’),
),
body: Image.asset(
‘images/lake.jpg’,
width: 600.0,
height: 240.0,
// cover 类似于 Android 开发中的 centerCrop,其他一些类型,读者可以查看
// https://docs.flutter.io/flutter/painting/BoxFit-class.html
fit: BoxFit.cover,
)
),
);
}
}
如果读者是初学 Flutter,强烈建议在遇到不熟悉的 API 时翻一翻文档,并在文档中找到 demo 所使用的 API。我们的例子不可能覆盖所有的 API,通过这种方式熟悉文档后,读者就可以根据文档实现出自己想要的效果。不妨就从 Image 开始吧,在 docs.flutter.io/flutter/wid… 找出上面我们使用的 Image.asset 构造函数的几个参数的含义,还有 BoxFit 的其他几个枚举值。
在这一小节,我们来实现图片下方的标题区域。
我们直接来看代码:
class _TitleSection extends StatelessWidget {
final String title;
final String subtitle;
final int starCount;
_TitleSection(this.title, this.subtitle, this.starCount);
@override
Widget build(BuildContext context) {
// 为了给 title section 加上 padding,这里我们给内容套一个 Container
return Container(
// 设置上下左右的 padding 都是 32。类似的还有 EdgeInsets.only/symmetric 等
padding: EdgeInsets.all(32.0),
child: Row(
children: [
// 这里为了让标题占满屏幕宽度的剩余空间,用 Expanded 把标题包了起来
Expanded(
// 再次提醒读者,Expanded 只能包含一个子元素,使用的参数名是 child。接下来,
// 为了在竖直方向放两个标题,加入一个 Column。
child: Column(
// Column 是竖直方向的,cross 为交叉的意思,也就是说,这里设置的是水平方向
// 的对齐。在水平方向,我们让文本对齐到 start(读者可以修改为 end 看看效果)
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 聪明的你,这个时候肯定知道为什么突然加入一个 Container 了。
// 跟前面一样,只是为了设置一个 padding
Container(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
title,
style: TextStyle(fontWeight: FontWeight.bold),
),
),
Text(
subtitle,
style: TextStyle(color: Colors.grey[500]),
)
],
),
),
// 这里是 Row 的第二个子元素,下面这两个就没用太多值得说的东西了。
Icon(
Icons.star,
color: Colors.red[500],
),
Text(starCount.toString())
],
),
);
}
}
接下来我们要做的这一部分在布局上所用到的知识,基本知识在上一小节我们都已经学习了。这里唯一的区别在于,三个按钮是水平分布的。
实现如下:
Widget _buildButtonColumn(BuildContext context, IconData icon, String label) {
final color = Theme.of(context).primaryColor;
return Column(
// main axis 跟我们前面提到的 cross axis 相对应,对 Column 来说,指的就是竖直方向。
// 在放置完子控件后,屏幕上可能还会有一些剩余的空间(free space),min 表示尽量少占用
// free space;类似于 Android 的 wrap_content。
// 对应的,还有 MainAxisSize.max
mainAxisSize: MainAxisSize.min,
// 沿着 main axis 居中放置
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: color),
Container(
margin: const EdgeInsets.only(top: 8.0),
child: Text(
label,
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: color,
),
),
)
],
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//…
Widget buttonSection = Container(
child: Row(
// 沿水平方向平均放置
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildButtonColumn(context, Icons.call, ‘CALL’),
_buildButtonColumn(context, Icons.near_me, ‘ROUTE’),
_buildButtonColumn(context, Icons.share, ‘SHARE’),
],
),
);
//…
}
关于 cross/main axis,看看下面这两个图就很清楚了:
MainAxisAlignment 的更多的信息,可以查看 docs.flutter.io/flutter/ren…。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final titleSection = _TitleSection(
‘Oeschinen Lake Campground’, ‘Kandersteg, Switzerland’, 41);
final buttonSection = …;
final textSection = Container(
padding: const EdgeInsets.all(32.0),
child: Text(
‘’’
Lake Oeschinen lies at the foot of the Blüemlisalp in the Bernese Alps. Situated 1,578 meters above sea level, it is one of the larger Alpine Lakes. A gondola ride from Kandersteg, followed by a half-hour walk through pastures and pine forest, leads you to the lake, which warms to 20 degrees Celsius in the summer. Activities enjoyed here include rowing, and riding the summer toboggan run.
‘’',
softWrap: true,
),
);
return MaterialApp(
title: ‘Flutter UI basic 1’,
home: Scaffold(
appBar: AppBar(
title: Text(‘Top Lakes’),
),
// 由于我们的内容可能会超出屏幕的长度,这里把内容都放到 ListView 里。
// 除了这种用法,ListView 也可以像我们在 Android 原生开发中使用 ListView 那样,
// 根据数据动态生成一个个 item。这个我们在下一节再来学习
body: ListView(
children: [
Image.asset(
第一章、 热修复设计
第一节、 AOT/JIT & dexopt 与 dex2oat
第二节、 热修复设计之 CLASS_ISPREVERIFIED 问题
第三节、热修复设计之热修复原理
第四节、Tinker 的集成与使用(自动补丁包生成)
第二章、 插件化框架设计
第一节、 Class 文件与 Dex 文件的结构解读
第二节、 Android 资源加载机制详解
第三节、 四大组件调用原理
第四节、 so 文件加载机制
第五节、 Android 系统服务实现原理
第三章、 组件化框架设计
第一节、阿里巴巴开源路由框——ARouter 原理分析
第二节、APT 编译时期自动生成代码&动态类加载
第三节、 Java SPI 机制
第四节、 AOP&IOC
第五节、 手写组件化架构
第四章、图片加载框架
第一节、图片加载框架选型
第二节、Glide 原理分析
第三节、手写图片加载框架实战
第五章、网络访问框架设计
第一节、网络通信必备基础
第二节、OkHttp 源码解读
第三节、Retrofit 源码解析
第六章、 RXJava 响应式编程框架设计
第一节、链式调用
第二节、 扩展的观察者模式
第三节、事件变换设计
第四节、Scheduler 线程控制
第七章、 IOC 架构设计
第一节、 依赖注入与控制反转
第二节、ButterKnife 原理上篇、中篇、下篇
第三节、Dagger 架构设计核心解密
第八章、 Android 架构组件 Jetpack
第一节、 LiveData 原理
第二节、 Navigation 如何解决 tabLayout 问题
第三节、 ViewModel 如何感知 View 生命周期及内核原理
第四节、 Room 架构方式方法
第五节、 dataBinding 为什么能够支持 MVVM
第六节、 WorkManager 内核揭秘
第七节、 Lifecycles 生命周期
本文包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
agger 架构设计核心解密**
[外链图片转存中…(img-jjHOHq0U-1713472300030)]
第八章、 Android 架构组件 Jetpack
第一节、 LiveData 原理
第二节、 Navigation 如何解决 tabLayout 问题
第三节、 ViewModel 如何感知 View 生命周期及内核原理
第四节、 Room 架构方式方法
第五节、 dataBinding 为什么能够支持 MVVM
第六节、 WorkManager 内核揭秘
第七节、 Lifecycles 生命周期
[外链图片转存中…(img-Cwnndjf2-1713472300030)]
本文包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
[外链图片转存中…(img-rtRvrUAd-1713472300030)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-UFpNgAMT-1713472300031)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。