生活不止有眼前的枸杞, 还有生发水和保温杯.
这是一篇中英掺杂的学习笔记.
MVC 设计模式
view <–> controller <–> Model(data)
项目结构主要部分
- MainActivity: 应用程序的入口, 当构建和运行程序是, 系统会启动这个 Activity 的实例并且加载布局(可以理解为构造函数吧…).
- Activity_main.xml: 对于每一个生成的 activity 模块, Android Studio 都会自动生成一个 xml 文件, 用来定义这个 Activity 的界面布局( list view, grid view, color, font, etc.).
- manifests –> AndroidManifest.xml: 整个应用的布局以及每个组件的定义, 包括了 Activity, fragment 等等.
- build.gradle: 一个是用于项目的(app)配置, 另一个是用来应用模块的.
- drawable: 组件元素文件夹.
界面结构
- ViewGroup: 其实就是 java 的一个类, 用来控制一个界面内的视图布局.
- View: 从源码可以看出, View 实际上实现了一些界面组件的接口.
1 | public class View |
Activity
应用程序的一个组件.
几乎所有的 Activity 都会创建一个可以和用户进行交互的界面. 像之前提到的, MainActivity 是应用程序的入口, 当用户启动这个程序的时候, 这个组件会生成一个主界面, 例如, 登陆界面等等. 一个程序可以拥有多个 Activity, 而他们之间可以相互调用. 调用的过程和普通程序的 call stack 差不多. 例如, 当 MainActivity 调用了其他 Activity 的之后, MainActivity 会停止, 但是仍然在 stack 中. 当被调用的 Activity 返回的时候, 栈顶元素被弹出并销毁, 之前的栈内元素会被恢复. 要确保”停止”, “生成界面”, “恢复”, “销毁”等功能, 就需要在创建 Activity 的时候实现这些方法. 因为和所有其他组件一样, Activity 也是 Java 中的一个类. 其中 2 个方法是必须要实现的, 生成界面(与用户交互), 和停止(暂停但是不销毁).
- onCreate()
- 相当于构造函数, 调用当前 Activity 的时候调用这个方法, 并且调用 setContentView() 来初始化用户界面.
- onPause()
- 被调用的新的 Activity 覆盖了之前的 Activity(不可见), 那个 Activity 存在栈中, 处于停止状态, 但是没有被销毁.
其他的方法例如,
- startActivity() , 通过调用 Intent 类型的对象来向其他 Activity 传递消息.
- startActivityForResult(), 需要从 Activity 返回某些数据/结果. 结果仍然通过 intent 传递.
- finish() 结束当前 Activity
- finishActivity() 结束之前的某一个 Activity.
Activity 的生命周期
Intent
组件之间的通讯, 包括启动 Activity, 启动 service, 传递 broadcast. 从 Activity 的方法实现上可以看出来, Intent 这个类型的对象可以携带数据.
Intent 是两个 activity 之间的信使,让一个 activity 传递消息/命令给另一个 activity,最常用的就是让一个 app 里的 activity 和另一个 app 里的 activity 进行交流。
Intent 有三个用途:
- start an activity (会使用 UI)
- start a service (后端运行,不使用 UI)1
- deliver a broadcast to other apps
在 1,2 种用途中,我们可以使用:
- explicit intent, intent 在初始化时就规定好了发送方和接收方
e.g. Intent intent = new Intent(this, Hello.class); 发送方是 this,也就是当前 activity,接收方是一个叫 Hello 的 activity - implicit intent,intent 在初始化时没有规定好发送方和接收方,但声明需求。Android 系统中的 activitymanager,会检查其他所有 app 声明文件中的 intent filter,找出可以满足需求的 activity,如果有多个满足条件,就弹出列表供用户选择。
但是,我们可以看出,不管是哪种 intent,都只能执行“打开另一个 activity/service“的能力,是因为接受方只有在 onCreat()这一步的时候,才能接受 intent,从而 start。
而 broadcast 不同,它可以在 activity 的任何一步进入监听状态,它可以让 activity 做各种事情,而不局限于 start。
另外,它可以一对多,同时让多个 activity 响应(响应的实现方式为 extends BroadcastReceiver, 以及在 life cycle function 中写入监听开始(register))。
总结:
普通 intent 只能一对一,并只能执行 start 操作让 activity/service 启动
broadcast 可以一对多,并能让 activity 做各种事情。而且 broadcast 有分级别,系统级别的某些 broadcast 禁止普通 app 进行监听。2
Fragment
3.0 版本中引入, 为了应用在大屏幕设备上. Fragment 依赖于 Activity 存在, 但是拥有自己的生命周期, 拥有独立的输入事件的响应. 一个 Activity 可以包含多个 Fragment, 同一个 fragment 也可以被不同的 activity 调用. 尤其适用于 Single Activity 应用.
可以把 Fragment 看做是可以被重复使用的模块.
- 可以做为 activity 的一部分, 多个 fragment 出现在同一个 activity 里面, 或者一个 fragment 在不同的 activity 出现.
- 处于同一个 Activity 下面的多个 Fragment 可以通过 Activity 进行通讯, 而 Activity 之间需要 intent 或者 broadcast 来进行通讯.
- Activity 运行中可以添加, 删除或者替换 fragment.
- Fragment 拥有自己的生命周期, 会受到所依附的 Activity 的生命周期的影响. 但是 Activity 不会受到 Fragment 的影响.
- Fragment 继承自 Object, 是一个独立的类, 而不是 Activity 的子类. Activity 是 Context 的子类.
和 Activity 一样, 加载时都需要调用一个生成界面的方法,
- Oncreate()
- OnCreateView() 如果不想返回 UI 界面, 可以返回 null.
- OnDestroyView()
- OnDestroy()
可以动态和静态加载一个 Fragment:
- 添加 layout 文件
- 使用 add(), remove(), replace() 等方法动态的添加, 删除, 或者替换等操作.
和 Activity 一样, Fragment 也有一个 stack, 除非被销毁, 否则可以回退到上一个 fragment.
Fragment 通讯
两个 Fragment 不应该直接通讯因为会产生 fragment 之间的 dependency. (例如调用另一个 fragment 产生的 instance)
可以使用观察者模式的设计模式, 利用 polymorphism 的特性来解决这个问题.
- Fragment 里面定义一个 interface.
- 主函数继承这个 Fragment.interface, 并且 override 接口.
- onAttach()方法中传进来的 context 就是所依赖的 Activity (的 source).
- 把传入的 activity 的 instance 强制转换成接口类型(Fragment.interface) context.
- 然后就可以通过这个 reference 调用 Activity 中的其他函数, 进而调用另外一个 Fragment.
Layout 文件
每个组件如何显示的文件.
: 根据子 view 之间的依赖关系而排列, 横向纵向都会进行一次比较. : 包含的所有子 view 都是从上到下顺序排列的.
组件
android 中的每一个组件都是 view 的一个子类. 组件有它自己的属性, 包括 id, layout_width, 和 layout_height 等等. 当我们生成了一个 layout 文件, 并且为这个组件设置了一个 id, 那么 R 文件中就自动为这个组建生成了一个 id, 而我们可以通过这个 id 来对组件进行操作.
ListView:
- 生成一个 ListView 的组件 list_view.xml
1 | <?xml version="1.0" encoding="utf-8"?> |
- 改写 MainActivity 里面的 onCreate(), 在程序被启动的时候直接调用这个 ListView 生成的界面.
1 | public class MainActivity extends AppCompatActivity { |
Adapter: 在 Java 中, Adapter 是一个 interface. 用来把复杂的数据填充在界面上和用户交互.
- 继承了 Adapter 的其他 interface 有 ListAdapter 和 SpinnerAdapter.
- 继承了这两个 interface 的类是 BaseAdapter(抽象类). 实际应用中 BaseAdapter 是最常用的.
- 而继承了 BaseAdapter 的常用的类有, SimpleAdapter, ArrayAdapter
.
ArrayAdapter
用来绑定单一类型数据(例如集合, 数组, 列表).
SimpleAdapter: 用来绑定复杂类型的数据, 只能是特定泛型的集合.
- 创建一个 layout 文件, list_of_items.xml
1 | <?xml version="1.0" encoding="utf-8"?> |
- 改写 MainActivity 里面的 onCreate() 方法, 注释掉之前用 ArrayAdapter 生成的 listview, 例如,
1 | /** |
自定义类
更加常用的形式是自定义一个 Adapter 的子类.
异步加载
PS: (cast) 向下转换.
—