Android学习的第三篇文章,跟着google的官方教程来到了lesson2,开始进行Layout的学习。

ViewGroups

view的集合,构成了布局的结构。layout的构建使用有层级的ViewViewGroup对象。layout的结构如下:

layout

LinearLayout

线性布局,UI元素被水平或者垂直摆放。

LinearLayout

  • android:orientation: 布局方向,设置为水平或者垂直布局

    • horizontal: 水平,默认方向
    • vertical:垂直
  • android:layout_gravity: 子控件在父控件上的对齐方式

  • android:gravity: 控件内部的元素的对齐方式,支持多个方向,示例如下,下面的TextView中的text就在整个TextView的居中靠下的方向:

1
2
3
4
5
6
7
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="bottom|center"
        android:text="TEST GRAVITY"
        android:textSize="50sp"/>
  • android:layout_weight: 子控件在父控件中的权重,说白了就是占多大,比如说一个LinearLayout有5个button,每个button的weight都是1,那么其orientation方向上均分5分,每个button占1/5

  • 其它的参数例如margin,padding等等和其它控件都是相同的,不赘述

RelativeLayout

相对布局,以相对位置显示子控件。说白了,就是可以指定子控件相对于其它控件的位置,比如说有两个buttonA和buttonB,如果想要buttonB布局在buttonA的右下方45度的未知,那么直接可以用两行就可以解决了,不需要嵌套多个LinearLayout。

1
2
    android:layout_below="@+id/buttonA"
    android:layout_centerHorizontal="true"
  • alignParentXx: 这个系列的参数都是相较于父控件而言的,相对于父控件停靠在哪个位置,Top, Bottom, left, right

  • alignXx: 这个系列的参数都是与周围的空间边界对其,同样是上下左右。

  • centerXx: 相对于父控件居中

具体的参数直接试一把就知道了是如何相对布局的了,下面是测试代码。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--
        @+id新定义id
        在R类中新定义一个整数常量
        如果常量已存在就引用现有常量
        @id引用id

        在引用id时也可以使用@+id
        但是如果写错的已存在的id就会新创建一个整数常量
        所以为了严谨应该用@id
    -->
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="btn1" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:text="btn2" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="true"
        android:text="btn3" />

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:text="btn4" />

    <Button
        android:id="@+id/button5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="btn5" />

    <Button
        android:id="@+id/button6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:text="btn6" />

    <Button
        android:id="@+id/button7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentBottom="true"
        android:text="btn7" />

    <Button
        android:id="@+id/button8"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="btn8" />

    <Button
        android:id="@+id/button9"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:text="btn9" />

    <Button
        android:id="@+id/button10"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/button4"
        android:layout_below="@id/button1"
        android:layout_alignRight="@id/button2"
        android:layout_toRightOf="@id/button1"
        android:text="btn10" />

    <Button
        android:id="@+id/button11"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/button7"
        android:layout_alignTop="@id/button4"
        android:layout_toLeftOf="@id/button5"
        android:layout_toRightOf="@id/button4"
        android:text="btn11" />

    <Button
        android:id="@+id/button12"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@id/button11"
        android:layout_toLeftOf="@id/button6"
        android:layout_toRightOf="@id/button11"
        android:text="btn12" />
</RelativeLayout>

Activity and Intent

Activity

https://developer.android.com/guide/components/activities/intro-activities

其实Activity就两个作用:

  1. 展示页面内容

  2. 响应用户交互

重点在于Activity的生命周期,因为用户在使用APP期间,Activity并不是一成不变的,需要根据用户的操作来进行不同的变化。Activity会提供很多回调类,这些回调让Activity知道状态正在改变。

在生命周期回调方法中,您可以声明用户离开和再次进入 Activity 时 Activity 的行为方式。例如,如果您正构建流媒体视频播放器,当用户切换至另一应用时,您可能要暂停视频或终止网络连接。当用户返回时,您可以重新连接网络并允许用户从同一位置继续播放视频。换言之,每个回调都支持您执行适合给定状态变更的特定作业。在合适的时间执行正确的作业,并妥善处理转换,这将提升应用的稳健性和性能。

生命周期

简单来说,Activity归属于task管理(每个app一个task),task中使用stack来保存Activity,用户在页面上进行操作切换Activity的时候相当于这些Activity在出栈和入栈,那么既然Activity在不停的变化,那么它整个从产生到变化到销毁的过程就会有个生命周期来定义。对于生命周期,讲解的比较多,直接看文档就行。

https://developer.android.google.cn/guide/components/activities/activity-lifecycle

  • onCreate(): activity init

  • onStart(): app becomes visble on the screem

  • onResume(): app gains the user focus

  • onPause(): focus is lost

  • onStop(): no longerr visible

  • onDestory(): destroy activity

启动模式

在manifest文件中我们添加新的Activity,同样可以为它设置启动模式,android:launchMode=,这个配置用来处理Activity和task的关系,task处理Activity的逻辑

  • standard: 默认模式,系统在启动该 Activity 的任务中创建 Activity 的新实例,并将 intent 传送给该实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例。

  • singleTop:如果当前任务的顶部已存在 Activity 的实例,则系统会通过调用其 onNewIntent() 方法来将 intent 转送给该实例,而不是创建 Activity 的新实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例(但前提是返回堆栈顶部的 Activity 不是该 Activity 的现有实例)。

例如,假设任务的返回堆栈包含根 Activity A 以及 Activity B、C 和位于顶部的 D(堆栈为 A-B-C-D;D 位于顶部)。收到以 D 类型 Activity 为目标的 intent。如果 D 采用默认的 “standard” 启动模式,则会启动该类的新实例,并且堆栈将变为 A-B-C-D-D。但是,如果 D 的启动模式为 “singleTop”,则 D 的现有实例会通过 onNewIntent() 接收 intent,因为它位于堆栈顶部,堆栈仍为 A-B-C-D。但是,如果收到以 B 类型 Activity 为目标的 intent,则会在堆栈中添加 B 的新实例,即使其启动模式为 “singleTop” 也是如此。

  • singleTask: 系统会创建新任务,并实例化新任务的根 Activity。但是,如果另外的任务中已存在该 Activity 的实例,则系统会通过调用其 onNewIntent() 方法将 intent 转送到该现有实例,而不是创建新实例。Activity 一次只能有一个实例存在。

  • singleInstance: 与 “singleTask” 相似,唯一不同的是系统不会将任何其他 Activity 启动到包含该实例的任务中。该 Activity 始终是其任务唯一的成员;由该 Activity 启动的任何 Activity 都会在其他的任务中打开。

Activity跳转

  1. startActivity(): 从一个Activity启动另一个Activity,没有返回结果
1
2
3
4
    bind.buttonGuessWord.setOnClickListener {
        val intent2 = Intent(this, GuessTheWordActivity::class.java)
        startActivity(intent2)
    }
  1. startActivityForResult(): 跳转Activity带结果

Intent

一个Activity的启动伴随着一个intent,一个Intent是一个异步的消息在不同的Activity中传递。

Intent分为显性和隐性的:

  • explicit 显性: 明确知道intent的target,已经知道明确的Activity的class名称。
  • implicit 隐性:通用的,并没有明确的目标。

具体作用

  1. 启动Activity,通过传递intent给startActivity()可以切换Activity。

  2. 启动Service,通过将intent传递给startService()来启动服务执行一次性操作。

  3. 传递广播,通过将intent传递给sendBroadcast()来将广播传递给其它应用。

Services

Service是一种可以在后台执行长时间运行操作而不提供界面的应用组件。需要注意的是Service运行在主线程中,如果要在主线程之外操作,则创建线程。

分类

  1. 前台服务:前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必须显示通知。即使用户停止与应用的交互,前台服务仍会继续运行。

  2. 后台服务:后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。

  3. 绑定服务:当应用组件通过调用 bindService() 绑定到服务时,服务即处于绑定状态。绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

Service的方法

  1. onStartCommand(): 启动服务后会调用该方法并在后台无限期运行,此方法返回整型数,返回的标识如下:

    • START_NOT_STICKY:返回后终止服务
    • START_STICKY:无限期运行
    • START_REDELIVER_INTENT:主动执行后立即恢复的
  2. onBind():另外一个组件想和服务绑定,使用该方法

  3. onCreate():首次创建服务,调用 onStartCommand() 或 onBind() 之前调用,执行初始化

  4. onDestroy():不再使用服务,销毁时使用

Content Provider

多个APP之间共享数据。相当于有一个Provider APP提供数据的操作,其它的APP可以访问Provider存的数据。

整体的数据访问依靠约定的URI,具体如何操作看

https://github.com/linsir6/AndroidNote/blob/master/AndroidNote/Android%E5%9F%BA%E7%A1%80/ContentProvider%E5%AE%9E%E4%BE%8B%E8%AF%A6%E8%A7%A3.md

BroadcastReceiver

主要用来监听系统或者应用发出的广播消息,然后监听该广播消息进行相应的逻辑的处理。

https://github.com/linsir6/AndroidNote/blob/master/AndroidNote/Android%E5%9F%BA%E7%A1%80/BroadcastReceiver%E8%AF%A6%E7%BB%86%E8%A7%A3%E6%9E%90.md