第一行代码学习日志

课本

书籍资源进入官网下载,PC端进入

第四章

常见控件写法

常见公共属性

1
2
3
4
5
6
7
8
9
10
11
12
控件ID
android:id="@+id/text1"
控件在整个布局中的宽度:match_parent(根据父元素),wrap_content(根据内容),xxdp(类型html的px是一种单位)
android:layout_width="match_parent"
控件在整个布局中的高度:可选项和宽度一样
android:layout_height="wrap_content"
控件本身的垂直对齐方式:可多个值|分隔
android:gravity="center"
控件在整个布局中的垂直对齐方式.若使用线性布局固定了垂直或者水平,那么则只能选择相反方向的值,不然无法生效
android:layout_gravity="center"
控件的可见属性,可选值:visible:显示控件(默认值),invisible:不可见但任然占用屏幕空间,gone:不可见且不占用屏幕空间
android:visibility="gone"

常用控件-文本(TextView)

在安卓中显示文本使用的控件是TextView.若要使用它,在activity的布局文件中添加<TextView/>标签即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--
控件内容
android:text="你好"
控件的文本颜色
android:textColor="#cc66ff"
控件的文字大小,使用sp单位可随系统大小变化而变化
android:textSize="30sp"
-->
<TextView
android:id="@+id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="center"
android:text="你好"
android:textColor="#cc66ff"
android:textSize="30sp"
/>

常用控件-按钮(Button)

在安卓中显示文本使用的控件是Button.若要使用它,在activity的布局文件中添加<Button/>标签即可

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 
文本是否全大写
android:textAllCaps="false"
按钮文本
android:text="按钮" />
-->
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="按钮" />

使用函数式API注册监听事件

1
2
3
4
5
6
7
8
9
10
11
12
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 关键代码开始
var buttn1 = findViewById<Button>(R.id.button1)
buttn1.setOnClickListener{
Toast.makeText(this,"你点击了按钮",Toast.LENGTH_LONG).show()
}
// 关键代码结束
}
}

使用接口实现监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 使Activity也实现View.OnClickListener接口
class MainActivity : AppCompatActivity(), View.OnClickListener{

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var buttn1 = findViewById<Button>(R.id.button1)

// 这里要将事件丢到MainActivity中,用于给方法onClick传递View
buttn1.setOnClickListener(this)
}

// 这里接受所有触发了Click的View
override fun onClick(p0: View?) {
// 先判断View是否为空,然后直接将id传入进行匹配
when(p0?.id){
// 这里可以看成p0.id==R.id.button1
R.id.button1->{
Toast.makeText(this,"你点击了按钮",Toast.LENGTH_LONG).show()
}
}
}
}

常见控件-可编辑文本框(EditText)

EditText 它允许用户在控件里输入和编辑内容,并可以在程序中对这些内容进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--
文本框提示文本
android:hint="这是输入框提示文字"
界面最大显示行数,超出部分隐藏
android:maxLines="2"
-->
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="这是输入框提示文字"
android:maxLines="2"
/>

通过点击按钮来获取EditText文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MainActivity : AppCompatActivity(), View.OnClickListener{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var buttn1 = findViewById<Button>(R.id.button1)
buttn1.setOnClickListener(this)
}

override fun onClick(p0: View?) {
when(p0?.id){
R.id.button1->{
// 关键代码开始
val editText = findViewById<EditText>(R.id.editText)
val inText = editText.text.toString()
Toast.makeText(this,inText,Toast.LENGTH_LONG).show()
// 关键代码结束
}
}
}
}

常见控件-图片(ImageView)

ImageView 是用于在界面上展示图片的一个控件,它可以让我们的程序界面变得更加丰富多彩.图片通常是放在以drawable 开头的目录下的,并且要带上具体的分辨率。现在最主流的手机屏幕分辨率大多是xxhdpi 的,所以我们在res 目录下再新建一个drawable-xxhdpi 目录,然后将事先准备好的两张图片img_1.png 和img_2.png (在随书资源的源码\第4章\UIWidgetTest\app\src\main\res\drawable-xxhdpi目录下)
制到该目录当中。

1
2
3
4
5
6
7
8
9
<!--
图片路径@drawable关键字会根据设备大小自动寻找对应分辨率图片
android:src="@drawable/img_1"
-->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/img_1"
/>

使用代码更改src
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MainActivity : AppCompatActivity(), View.OnClickListener{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var buttn1 = findViewById<Button>(R.id.button1)
buttn1.setOnClickListener(this)
}

override fun onClick(p0: View?) {
when(p0?.id){
R.id.button1->{
// 关键代码开始
val imgView = findViewById<ImageView>(R.id.image)
imgView.setImageResource(R.drawable.img_2)
// 关键代码结束
}
}
}
}

常见控件-进度条(ProgressBar)

Progr essBar 用于在界面上显示一个进度条,表示我们的程序正在加载一些数据。

1
2
3
4
5
<ProgressBar
android:id="@+id/progress_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>

使用代码控制进度条的可见性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MainActivity : AppCompatActivity(), View.OnClickListener{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var buttn1 = findViewById<Button>(R.id.button1)
buttn1.setOnClickListener(this)
}

override fun onClick(p0: View?) {
when(p0?.id){
R.id.button1->{
// 关键代码开始
val progressBar = findViewById<ProgressBar>(R.id.progress_1)
when(progressBar.visibility){
View.VISIBLE-> progressBar.visibility = View.GONE
else -> progressBar.visibility = View.VISIBLE
}
// 关键代码结束
}
}
}
}

此时,这个并不是进度条而是循环圆圈,我们可以给它加上style来变成进度条
1
2
3
4
5
6
7
8
9
10
11
12
13
<!--
进度条样式,可选值有很多。
style="?android:attr/progressBarStyleHorizontal"
进度条范围
android:max="100"
-->
<ProgressBar
android:id="@+id/progress_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal"
android:max="100"
/>

使用代码来更改进度,每次点击按钮加10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MainActivity : AppCompatActivity(), View.OnClickListener{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var buttn1 = findViewById<Button>(R.id.button1)
buttn1.setOnClickListener(this)
}

override fun onClick(p0: View?) {
when(p0?.id){
R.id.button1->{
// 关键代码开始
val progressBar = findViewById<ProgressBar>(R.id.progress_1)
progressBar.progress = progressBar.progress + 10
// 关键代码结束
}
}
}
}

常见控件-消息弹窗(AlertDialog)

AlertDialog 可以在当前界面弹出一个对话框,这个对话框是置顶于所有界面元素之上的,能够
屏蔽其他控件的交互能力,因此AlertDialog 一般用于提示一些非常重要的内容或者警告信息

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
class MainActivity : AppCompatActivity(), View.OnClickListener{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var buttn1 = findViewById<Button>(R.id.button1)
buttn1.setOnClickListener(this)
}

override fun onClick(p0: View?) {
when(p0?.id){
R.id.button1->{
// 关键代码开始
AlertDialog.Builder(this).apply {
setTitle("这是一个弹窗")
setMessage("这是弹窗内容")
// 是否可以用返回键关闭对话框
setCancelable(false)
// 确认事件
setPositiveButton("确认",{dialog,which->})
// 取消事件
setNegativeButton("关闭",{dialog,which->})
}.show()
// 关键代码结束
}
}
}
}

其它控件

到此为止第一行代码三中的全部控件已经讲解完毕,其他的控件前往安卓官网指南->界面->外观和风格进行了解

基本布局

控件和布局的关系

博客第一行代码学习布局和控件的关系

线性布局-LinearLayout

这个布局会将它所包含的控件在线性方向(垂直或水平)上依次排列
基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<!--
排列方式vertical:垂直,horizontal:水平
android:orientation="vertical"
xml规则在作为根元素时使用
xmlns:android="http://schemas.android.com/apk/res/android"
-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
>

</LinearLayout>

宽度平分-layout_weight

给在同一水平方向宽度为0dp的控件加上此属性,会将在这一方向添加此属性的控件layout_weight的值总和N,除以每个控件layout_weight的值M,得到单个宽度.若同一方向有设置固定宽度或者wrap_content的控件,则在计算时只会占用剩余的空间
例如下面的layout_weight总和为5,按钮1和按钮3分别占2/5,按钮2占1/5.

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<!--关键代码开始-->
<Button
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="wrap_content"

android:text="按钮1"
/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="按钮2"
/>
<Button
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="wrap_content"
android:text="按钮3"
/>
<!--关键代码结束-->
</LinearLayout>

相对布局-RelativeLayout

它可以通过相对定位的方式让控件出现在布局的任何位置。也正因为如此,RelativeLayout 中的属性非常多,不过这些属性都是有规律可循的,其实并不难理解和记忆。

根据父标签对齐

下面是一个简单的根据父标签上下左右居中对齐,属性见名知其意就不再赘述

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
<?xml version="1.0" encoding="utf-8"?>
<!--关键代码开始-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="左上角"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="右上角"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="居中"
android:layout_centerInParent="true"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="左下角"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="右下角"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
/>
</RelativeLayout>
<!--关键代码结束-->

博客第一行代码效果相对布局

根据同级标签对齐

下面是一个简单的根据同级标签上下左右居中对齐,属性见名知其意就不再赘述

注意: 被引用的标签一定要在最前面

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
<?xml version="1.0" encoding="utf-8"?>
<!--关键代码开始-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<Button
android:id="@+id/root"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Root"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/root"
android:layout_toLeftOf="@+id/root"
android:text="左上角"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/root"
android:layout_toRightOf="@+id/root"
android:text="右上角"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/root"
android:layout_toLeftOf="@+id/root"
android:text="左下角"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/root"
android:layout_toRightOf="@+id/root"
android:text="右上角"
/>
</RelativeLayout>
<!--关键代码结束-->

博客第一行代码相对定位根据同级标签对齐

约束布局-ConstraintLayout

安卓官方文档

由于ConstraintLayout 的特殊性,很难展示如何通过xml进行操作,所以使用可视化编辑器来对界面进行动态操作

要使用constraintLayout布局,先将根标签修改为如下:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">

</androidx.constraintlayout.widget.ConstraintLayout>

为了更加简单的开发,将不再使用Android Studio的Code模式,将改为Design模式
博客第一行代码切换设计模式

由于无法进行文字描述,请前往哔哩哔哩学习

自定义控件

控件和布局的继承结构
博客第一行代码控件布局和结构的继承

所有布局都是直接或间接继承ViewGroup的.控件其实就是在View 的基础上又添加了各自特有的功能.而ViewGroup则是一种特殊的View ,它可以包含很多子View和子ViewGroup,是一个用于放置控件和布局的容器

引入布局

当一个控件在不同的地方被重复调用,当系统自带的控件并不能满足我们的需求时,可以利用上面的继承结构创建自定义控件以实现控件的复用,就类似于Vue的Component.下面我们就来学习一下创建自定义控件的两种简单方法。先将准备工作做好,创建一个UICustomViews 项目,实现自定义标题栏控件.

  1. res目录下创建图片文件夹drawable-xxhdpi 目录,将配套资源中的源码\第4章\UICustomViews\app\src\main\res\drawable-xxhdpi\复制
  2. layout目录下新建一个title.xml布局
    代码如下:
    title.xml
    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
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/title_bg"
    >
    <Button
    android:id="@+id/titleBack"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_margin="5dp"
    android:background="@drawable/back_bg"
    android:text="返回"
    android:textColor="#fff"/>
    <TextView
    android:id="@+id/textText"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:layout_gravity="center"
    android:gravity="center"
    android:text="标题"
    android:textColor="#fff"
    android:textSize="24sp"/>
    <Button
    android:id="@+id/titleEdit"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_margin="5dp"
    android:background="@drawable/edit_bg"
    android:text="编辑"
    android:textColor="#fff"/>
    </LinearLayout>
  3. 引用title.xml
    若要引用改文件,则在对应xml文件中键入一下代码
    1
    <include layout="@layout/title"/>
    这里以activity_main.xml为例子
    1
    2
    3
    4
    5
    6
    7
    8
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <include layout="@layout/title"/>
    </LinearLayout>
  4. 覆盖原有标题栏
    某一些设备可能存在自带的标题栏在对应activity.kt文件中使用如下代码隐藏掉.
    1
    supportActionBar?.hide()
    以MainActivity为例
    1
    2
    3
    4
    5
    6
    7
    class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    supportActionBar?.hide()
    }
    }
  5. 更改主题
    如果不更改主题可能会出现看不清等意外情况,打开res/values/themes.xml文件.更改parentTheme.AppCompat.Light.DarkActionBar.如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.UIConstomViews" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Primary brand color. -->
    <item name="colorPrimary">@color/purple_500</item>
    <item name="colorPrimaryVariant">@color/purple_700</item>
    <item name="colorOnPrimary">@color/white</item>
    <!-- Secondary brand color. -->
    <item name="colorSecondary">@color/teal_200</item>
    <item name="colorSecondaryVariant">@color/teal_700</item>
    <item name="colorOnSecondary">@color/black</item>
    <!-- Status bar color. -->
    <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
    <!-- Customize your theme here. -->
    </style>
    </resources>
  6. 最终效果
    博客第一行代码自定义布局最终效果

自定义控件

引入布局的技巧确实解决了重复编写布局代码的问题,但无法响应事件.
我们还是需要在每个Activity中为这些控件单独编写一次事件注册的代码,不管是在哪一个Activity中,这个控件的功能都是相同的,这就是称为自定义控件.

  1. 新建TitleLayout.kt文件让它继承LinearLayout
    1
    2
    class TitleLayout(context: Context, attrs: AttributeSet) : LinearLayout(context,attrs){
    }
    这里我们在TitleLayout的主构造函数中声明了ContextAttributeSet这两个参数,在布局中引入TitleLayout 控件时就会调用这个构造函数。
  2. 然后在init结构体中需要对标题栏布局进行动态加载,这就要借助LayoutInflater. 通过LayoutInflater 的from()方法可以构建出一个LayoutInflater对象,然后调用inflate()方法就可以动态加载一个布局文件。inflate()方法接收两个参数:
    • 第一个参数是要加载的布局文件的id,这里我们传入R.layout.title;
    • 第二个参数是给加载好的布局再添加一个父布局,这里我们想要指定为TitleLayout ,于是直接传入this。
      1
      2
      3
      4
      5
      class TitleLayout(context: Context, attrs: AttributeSet) : LinearLayout(context,attrs){
      init {
      LayoutInflater.from(context).inflate(R.layout.title,this)
      }
      }
  3. 引用控件,在这里以activity_main.xml为例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <!--引用方式为全路径-->
    <com.example.uiconstomviews.TitleLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

    </LinearLayout>
  4. 为标题栏的按钮注册点击事件,修改`TitleLayout中的代码


第一行代码学习日志
https://007666.xyz/2022/09/21/第一行代码学习日志/
作者
梦无念
发布于
2022年9月21日
许可协议
CC BY-NC-SA 4.0