开发者学堂课程【高校精品课-上海交通大学 -互联网应用开发技术:移动端框架 2】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/76/detail/15785
移动端框架
内容简介:
一、Flutter实例
二、Dart语言
一、Flutter实例
这个是创建一个下拉条,还是先创建一个MyAPP,其实就是在代码的基础上不断的添加东西。
Randomwords出来的效果大家就可以看这个,有很多的下拉列表,可以滚动的下拉列表,里面出现的词全部是一对一对随机的词拼在一起,所以就可以看到两次运行的结果并不一样。
Randomwords是干什么的呢,我们先调Randomwordsstate,我们刚才看到的例子我们只返回一对词,一个text,而现在这个build这里就不是只有一对词了,是有很多词。
他的build在说,首先要有一个APPBar来显示Startup Name Generator,body是通过调buildsuggestion得到的,而buildsuggestion返回一个listview。
这个listview就是列表的view。
这个列表不是顶格写的,是有一定距离,其设定了一个padding值,将来这里面的内容都要带一个padding值,而padding底下的内容就是要显示文本框,每一个都是调用buildrow来得到的。
Buildrow是通过suggestion=<wordpai>[]开始为空加入_suggestion,addAll(generateWordPairs()take(10));来实现。
每一行都是一个text,显示刚才的文本,显示biggerfont这种字体,所有的栏,wordpairs都由这种方式构建出来。而另一边这个app的内容就比较简单了,整个内容home,都是randomwords来的,而randomwordds是从randomwordsstate来的,randomwordsstate在build的时候是一个脚手架,就包含了这些信息,上面一个bar下面buildsuggestion,buildsuggestion就是在suggestion里放置了若干个单词对,每一个都用buildrow的方法创建出来,buildrow在显示文本是什么。创建出一个如下图所示
的下拉界面。
如果在刚才的基础上做一些其他的动作,比如增加图标和可交互性该怎么做。就是我们这个例子里面右侧心型的图标是怎么来的。
以及列表框内选中的内容是如何出现的。
前面的内容一样,整个是靠通过randomwords里面有suggestion,以及biggerfont进行一系列创建。不同处在于buildrow,之前是把文字呈现出来就可以,所以是一个text,你指定了字体呈现。这个set定义了一个变量saved,数组集合一开始是空着的,saved就是存我们刚才操作打了红心的那部分。
build中添加了_saved数组,我们就要先看看saved里面包不包含那一对pair,有可能包含有可能不包含,所以就是赋到变量alreadysaved变量里。首先我们要把文字呈现出来,这一部分和之前的代码一样。紧跟着底下说,我们要呈现一个图标,在trailing里面呈现一个图标。该图标是空心还是实心就要判断alreadysaved这个属性。这个pair有没有被我选中,如果选中过,就选择favorite这个红的实心的,如果没有选中,就选择只带边框的心favori_border。然后颜色呢?我们就要看是否被存下来,如果存下来了颜色就是红的,如果没有存下来颜色就是空的。这就是我们在右侧看到的心。
这两部分合起来是他的外表,除此以外还有他的动作,是否在任何一个地方点都可以,所以他是在真个text上响应ontap这个动作,通过对alreadysaved进行一个判断,判断alreadysaved是否已存,若已存则是移除,若未存则把其存进去。
就来回的切换,做这个事情。这就是我们在上面看到的一些交互的东西。剩下的与我们之前看到的没有什么区别,还是一个脚手架,脚手架上有一个appbar,底下就是调用buildsuggestion得到的,最后得到以下的界面。
在这个上面就可以点击,点击后就被存下来。然后我们点击右上角的按钮是,会跳到一个页面,是我们选中的所有东西,点击返回又可以回来,这个动作又是怎么实现的?
这个动作很显然前面的部分和刚刚都一样。只有上图部分与前面所讲有所不同,当你在构建randomwordsstates时他确实还是一个脚手架含有title,然后他有一个action,有一个图标类型的按钮,这个图标就是用的list图标,就是你所看到的列表图标,当按钮被按下,调取pushsaved这个方法。
那pushsaved这个方法是在干什么呢?是导航,导航到一个新的页面,这个页面有什么内容的,就是我把saved,那些存的内容每一项做一个处理,就是map,map就是要对每一个wordpair里的每一个pair在listtile里都生成一个text,来显示内容。
整个pushsaved定义好一个这样的逻辑,在return返回的是这样一个脚手架,就是一个界面,上面呢是我们看到的savedsuggestion。
body就是一个listview,其中就是一个dividetiles,而dividetile就是页面中所看到的分割线,而其中一行行的内容来自于tiles。其主要逻辑就把saved组成了一个个text,而这些text就是将来在list里面的那些数据,而divided被划到listview就显示出所示的导航栏。要注意的是navigata当前上下文里是push进去的,这样这个页面就和前面所讲的栈的方式一样,返回的时候会弹出来,它从哪进去的就弹出到哪,也就是弹到进入页面的地方,这就是我们看到的两个页面之间的跳转。
这就是他写的第一个页面,首先用dart语言写,dart毕竟不是js,尽管两者很像。Dart也强调构建化的开发,最好写的都是一些类,在里面通过一些组合呈现新的页面。这个程序在运行的时候可以在不同的模拟器上跑,达到不同的效果。
刚刚有同学问,是不是安卓和iOS的都要装,首先如果苹果的机器的 话,你的ios的系统没法开发,如果你是苹果机器,我认为两个选择一个就可以,看哪一个用的惯,因为你的机器可能跑不起来两个,如果你机器很好,可以两个都试一下,看一看你的程序在两个模拟器上跑出的结果一样吗。如果你用的是windows或者linux那你肯定要装安卓stidus。
我们通过下图可以能看到安卓和ios的页面风格有点差异,比如左上角的箭头的形状,appbar里文字的对齐方式,但是可以看到它可以很好的适配到两个手机上去。
刚才看到的整体蓝颜色的风格,如果整体上想换一种风格,也是可以的,你去定义这个theme,就是主题。就在“myapp”里找到theme主题中的primarycolor更换其主题颜色。所以它变换风格就是靠这个theme做的。这就是大家看到的Flutter第一个例子。我只是解释这个例子是怎么写的,他如何达到这个效果的。如果在另一个flutterdesig里或者flutterexample工程里才是在详细讲flutter每一个知识点和如何设计。
二、Dart语言
接下来我们看一下dart这个语言,dart语言比较麻烦,它需要你在掌握一门语言,它比较接近于JavaScript,所以说还好。它是一个纯面向对象的语言,所以刚才看到的所写的都是class,从这一点来说,它很接近JAVA。
dart语法还是比较简单的,Dart程序要跑的话,必须有一个main函数,作为整个程序,整个应用的起始点,再定义许多其他的函数去执行。
Main函数一般和C++一样没有什么返回值,可以调用其他函数,比如print函数。变量定义用var,这和我们所学的JavaScript就很像了。
控制流包括if、else if、for、while。
除此以外还可以定义函数,有参数返回值,很明显的写了一个递归,你用递归方式来实现也是可以的。
它和JavaScript很像就是它也带一个箭头,这种函数的语法,它也支持这种语法。他的含义就相当于是参数列表后面是你执行的逻辑。那么整个表达式的返回值就是函数的返回值。所以和JavaScript一样支持箭头的快捷方式。注释有“//”、“///”、“/*”三种方式。
然后import,我们要import一个package里的东西,就是如下图一样import。
然后就是定义类,在定义类的时候dart语言在这里就有一些不一样了,我们先看这个类spaceraft,里面包含name和datetime发射日期,他有一个构造器,很像Java语言,构造器里可以直接说,创建出来参数赋给谁。我们还可以看到底下有一个constructor的构造器它可以把请求forward给缺省的那个构造器。定义一些属性,他是只读的,我们会定义一些这样的属性launchyear,它会返回一个这样的属性。然后还有一个descri函数,相当于我们在这个类里创造了一个可以被调用的成员方法,他有一些信息会打印出来一些值。
Dart类的语法与之前所学的javascrip不太一样,但他本身并没有什么离奇的,它并不脱离成员、构造器、方法这样的结构。你定义好类之后,就可以实地化的去使用,实地化也一样,通过在构造器里传递参数。
和Java语言还有一点不同就是,即便构造器可以给它起一个名字,这跟之前的Java语言不一样,Java语言是不可能有launchyear这样的构造器的,但是dart可以有带这样名字的构造器。他的想法就是说有点像Java里缺省参数一样,他是说,你在掉space raft构造器的时候,只需要传递name给我,然后我会调我自身的构造器,另一个构造器,把name传递给它,而发射日期就是null。他的意思就是说正常构造这个对象的时候你必须有两个参数,但是你只有一个参数,我也不会拒绝你说不让构造,你可以调用unlaunch这个构造器传递参数进来,我的逻辑就是拿着this去调用。而Java里我们不是这样写的,Java里我们会写一个重载的构造器版本,space(name){this(name),}这样写。这个操作完全可以写出上面的那个样子。
然后我刚才说它的类比较复杂的地方就出现在这里,它慢慢就会出现。Dart和Java一样是一个单继承,在extends后面只能有一个类,不像c++后可以有两个类。它是单根继承的,父类只能有一个。比如下图所示的orbiter,轨道上运行的飞行器,就是飞行器的一种是spacecraft的扩展,多出来一个属性高度。它的构造器就是传递进来下图的三个参数后,先调父类的构造器,把name、launchdate传给它。
先构造出父类对象,在调它自己,大概就是这个逻辑。
那这个继承听起来还比较简单,单根继承,但是它有一个属性,单根继承这个事情有时候限制比较大,所以他允许这样一个MIXINS机制。mixins是什么呢,我再定义一个类piloted,需要导航,需要有人驾驶,astronauts=1,就是说开我这种飞行器,必须要有一个宇航员,有一个describecrew这样的一个方法。关键是我们想定义一个pilotedcraft这样的一个类,首先他是一种航天器,但他也是一个piloted,它只能有一个父类,不能有第二个父类,怎么办?于是他就用mixins的机制输withpiloted。
这样做的好处是pilotedcraft它的父类只有一个spacecraft,但是它具备astronauts和describecrew这两个东西。这就是mixins,这是不是就和我们之前讲的继承不太一样,Java里面解决单根继承的方法不能这样做,它只能再去实现一些接口,但接口里面是没有方法体的。如果我想把这边整个方法体的定义继承下来,在Java里是不好做了。c++可以做,我想让pilotedcraft继承piloted里面的成员,包括数据成员和函数成员,但不想piloted是pilotedcraft的父类,通过A:privateB,以私有方式继承。所谓私有继承就是要把B里面的东西拿进来但B不是A的父类。还有像scala语言里也支持这种机制。
在dart里也有接口和抽象类,特别像Java和c++的杂合体。接口当然就是指没有任何的实现,他的抽象类虽然有接口但是没有interface这个概念。抽象类的目的是为了实现模板。和c++的模板不同,比如说我有一个类型在这个方法里,比较的方法,我可以比较整数,也可以比较浮点数,我现在说的模板不是这回事。我现在说的模板是啥?举个例子,买书,有orderservice这个类,这个类里会有一个placeorder下订单的方法。这个订单如何下呢?首先要查库存,对book操作一下,第二个是支付payment,第三个是快递shopping。假设这是三个动作,检查库存和快递是固定的,但payment不太一样,有可能是支付宝有可能是微信。Placeorder要把下订单的流程固定下来,下订单和快递是固定的,我们就把支付或者微信设置成抽象的,其他的都继承place order。在place order里定义了一个抽象的没有实际的方法,让其子类去实现,但是大的流程是确定的,这个流程就是一个模板。Dart也可以定义抽象的,也就是方法体为空的那种方法
Dart语言提供了两个关键字async和await
该代码是要把message参数的内容输出,但是在输出前先要delayed一秒钟,delay结束后,才去打印它。在等待的这一秒钟时,机器会去执行别的代码,而不是机器闲置一秒钟。比如说这有一个界面,当用户点了这个按钮,去执行你这个代码,在等待的这一秒钟,如果有其他按钮你去按,他是没有任何响应的,但因为是异构的,在等他1秒的时候,点他会有响应。看到then你应该不陌生,想到fetch,then获取response等等。
Dart提供了更一种直观的写法如下图,使用printwithdelay但是这个时异步执行的。如何异步执行呢?就是print前需要wait,等待一秒,这个方法结束,执行print。所以说,前面的async相当于告诉这个是在异步执行,后面这个效果是在说等这一秒,因为他是异步的,所以不是阻塞在这里的,可以去干别的,一秒钟到了,再去激活这个线程,让他去打印出这条指令。dart认为这种用法更为直观,两种方法都可以,到底哪个更直观,你有自己的判断。这是dart提供的两个参数。
刚才这个只是举个例子,后面这个有点意义的代码,他就认为这种异步执行的代码更容易阅读。像这里createdescription就是一个异步执行的代码,什么意思,就是我要你这个集合的字符串里遍历每一个字符串,这个字符串就是文件名,我去看看这个文件存不存在,如果不存在,我就等,等看文件上一次修改的时间是什么,如果拿不到这个属性,可能这个文件正在被修改,拿不出来这个属性,那就继续等,等到之后,再执行下面的代码。再往下可以看到创建这个文件,如果文件创建不了,比如线程在做其他事情,或者这个文件被别人创建,那就等。
下面也一样,如果写不进去内容,那就等。所谓等不是说整个线程死在这里,而是其他程序可以去跑了。如果这个程序是多线程那里跑,可能会有人把这个文件创建出来,然后你就可以读到这个文件,做相应的处理。
这个我们不展开讲,因为这个涉及操作系统里进程和线程的概念,之后学习操作系统后再详细讲解。这里面涉及到一个锁的问题,如何上锁,线程是如何释放在这个锁再进行操作的。
作为dart语言的特性,我们要提一下,如果你选择flutter来写你的前端,你可能不会用到它,但在大三学完操做系统再来看,就明白什么意思了。
底下这个产生了一个yield,yield就是要产生一个新的进程。
接下来就是它的异常处理,它和Java和c++一样,当你感觉他有问题时,你把他抛个异常。
比如上图这个,他必须有一个宇航员,如果宇航员为0,那他就必须要抛一个异常说没有宇航员。如果往外抛就要有捕获它,如果不捕获它就一层一层抛,到mian都不能捕获它的话,它就会抛“出来”。你就可以在控制台上看到一堆的异常了。所以他用try和catch语句去捕获它,同样他有一个finally,也就是说无论你是正常结束还是异常结束,都要执行finally。在这里面他要做的事情就是,他在做这里面的操作任何一个都可能抛出一个异常,比如这里其他异常我不管,只捕获io异常,只要有io异常就进入到catch语句块里,把异常对象打印一下。finally是说你正常执行和异常执行都可以调。这里面发现一个问题,你说为什么要抛异常?抛异常这种方式好吗,我为什么不能用其他的方式。比如说这是一个方法叫做m,抛异常这是一种,那如果直接return呢?return什么呢,比如说return一个数字,我就声明一下,这个返回值是int。这里我们return -1,跟抛异常比,哪个好?走到下面进行判断,调m,m的值等于i,判断i的值等不等于-1,我就知道有没有错。其实设置异常的机制,谁说-1可以表示异常呢,比如说我现在从数据库里读出一个字段,这个字段是一个int类型的值,这个表示的是一个人银行存款的余额,他可能欠银行的债,也可能不欠银行的债。那么return返回一个什么样的值能表示他在银行欠款的值为空呢?
注意,是空,不是0,空和0是两个概念。空就是没有东西,你用一个什么值表示空可能没有办法找到这样一个值来描述这个值是空的,那你用throw就可以解决问题吗?throw是说,他的返回结果走了这条路,他和return就是两条路,随便throw一个就可以和return区分开,大家走的是两条途径,所以一定要有异常处理。讲这么多什么意思呢,你们现在写代码也会碰到一样的问题,你说库存为0的时候,不能再买书的时候,是返回一个值在前端代码判断,还是我直接throw一个异常出去,让service去捕获他。throw和return走的是两条路,显然throw会更好。所以在看第三方代码里,定义了一堆异常类型,就是为了和return产生区别。
不要觉得什么东西return一个值回去后靠代码去实现判断,比如这里m的返回值是不是-1,首先-1能不能表示异常都是个疑问。其次m返回-1,靠用户的代码去检查,第一所有的用户都要写这个代码太麻烦,第二用户忘了怎么办,难道这个错就一直错下去了吗,你应该把这种错误扼杀掉,所以说凡是有错,就抛异常。
程序不可能正常执行,只能捕获异常,如果捕获异常不处理就直接抛到外面去了,程序就垮掉了。定义大量自己的异常,比如说没有库存异常,或者用户名不对的异常,通过捕获异常的方式做处理,这种比return会更好,这就是为什么告诉大家有异常这个机制。
相信大家在讲c++的时候理解这个机制,但真正让你去运用,尤其是在web就会不是那么顺手。
有关dart和flutter的内容基本都在墙外,如果要看的话可能会费点劲,搭个梯子。例子大家去网站上看一下。