Android 打卡 Day1 - 小目标


昨天忙完工作,脑子突然一热对安卓产生了兴趣,因为听周围的人讨论问题挺有意思,于是乎…今天开整。

先定三个小目标:

  1. Hello World!
  2. Dice Roller
  3. Tip Time

对 Android 了解甚少,今天主要目的是借助两个小 demo 接触一下 Android Studio 和 Android 工程结构。

Hello World!

业界众所周知,万物皆始于Hello World,每一门编程语言都逃不过的定律。但是,Android Studio 新建一个空的 Activity,界面中间就是一个 Hello World!,好吧,这个目标自动完成,哈哈。Next~

Dice Roller

看到几个 Android 教程都是以摇骰子作为案例,随手点开一个视频跟着写了一下:

忽略配色,目前不重要🤓。拖拖拽拽几个控件、随机摇到数字、随机展示图片…初步感受:

  1. 与 Android Studio 相比,Xcode 简直是个呆子,他们的编译器也太好用了;
  2. 对于布局文件,既可以通过图形界面来操作,也可以通过 xml 文件来修改,这一点是目前 iOS 做不到的;
  3. 各控件布局时的约束关系,个人觉得没有 iOS 的 AutoLayout 智能和严谨;
  4. 关于随机展示骰子点数的图片:

假设六张图片对应 1~6 的点数,图片名称一般可能定义为 dice_1、dice_2、dice_3、dice_4、dice_5、dice_6,在 iOS 中的做法可能会是

1
2
3
4
5
//用伪代码代替
num = random [1,6] 作为摇到的点数
imageName = "dice_" + num.toString 拼接图片名称
image = Image.withName(imageName)
imageView.setImage(image)

我看在 Android 中,图片资源会放在工程中的 res -> drawable 文件夹下,通过形如 R.drawable.dice_1 获取图片的 id,R.drawable 就对应 res -> drawable。问了一下安卓同事、谷歌了几个博客很多都说只通过这种方式来做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//先把图片都放到一个集合中
//random 到的数字作为 index,再读取对应的“随机”图片
private fun rollDice() {
...
val num = (1..6).random()
val images = arrayOf(
R.drawable.dice_1,
R.drawable.dice_2,
R.drawable.dice_3,
R.drawable.dice_4,
R.drawable.dice_5,
R.drawable.dice_6
)
diceImageView.setImageResource(images[num-1])
}

那如果有 100 张图片,也要都放到数组中吗?不解,是为了保证图片一定存在??。翻了翻文档+问同学,找到另一个 API,可以通过字符串来拼接图片名称,不过我不清楚这种方式有没有什么隐含的技术问题,如下:

1
2
3
4
5
6
7
8
9
10
11
private fun rollDice() {
...
val num = (1..6).random()
val imageName = "dice_$num"
diceImage.setImageResource(getDrawableResource(this, imageName))
}

private fun getDrawableResource(context: Context, name: String): Int {
val packageName = context.packageName
return context.resources.getIdentifier(name, "drawable", packageName)
}

Tip Time

另一个 demo 是用于计算小费的:

  1. when 语法块还挺优雅:
1
2
3
4
5
val tipPercentage = when(binding.radioGroup.checkedRadioButtonId) {
R.id.amazing_21 -> 0.21
R.id.good_16 -> 0.16
else -> 0.10
}
  1. View binding 视图绑定。对于快速构建简单的页面还是挺方便的。这一点貌似也比 iOS 方便,不需要设置插座变量、拖来拖去的“电线”,但是 Android 中每一个控件都需要手动设置 id?…Ummmmm:

View binding 官方文档

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
// build.gradle(Module:xxx.app) 中打开视图绑定的配置
android {
...
buildFeatures {
viewBinding = true
}
}

// 也可以在某个不想使用视图绑定的页面中忽略它
<LinearLayout
...
tools:viewBindingIgnore="true" >
...
</LinearLayout>

// 在 Activity 中使用视图绑定
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

...
}

// 使用视图绑定来引用视图
binding.calculate.setOnClickListener {
calculateTip()
}

val tipPercentage = when(binding.radioGroup.checkedRadioButtonId) {
R.id.amazing_21 -> 0.21
R.id.good_16 -> 0.16
else -> 0.10
}

在布局文件中指定视图 id 貌似一般是用小写单词并以 _ 下划线隔开,’android:id=”@+id/radio_group” 通过 R 来引用视图是形如 R.id.radio_group,id 还是小写的。但是当通过视图绑定来引用的话,id 会自动变为驼峰的形式,radio_group 被引用时形如 binding.radioGroup。

  1. 说到 RadioGroup,Android 的系统组件也太丰富了吧,都是现成的,iOS 没有…几乎很多组件都需要自己来绘制。还有,提到 Switch 组件,就更大无语了,Android 里的 Switch 还自带文本,iOS 系统提供的 Switch 组件这么多年了连改大小都不行,啊啊啊啊啊,家人们谁懂啊,好想骂人。这样想来 Android 开发者也太幸福了吧。🤡
  2. 在 Android Studio 编辑页面中的变量/参数下面出现下划线,是指该变量为 Reassigned parameter,即该变量可以被多次赋值。val 是只读变量,var 是可变变量。还可以设置下划线的样式,设置的位置 : Editor -> Color Scheme -> Language Defaults -> Identifiers -> Reassigned parameters。

Error

Demo 期间遇到了好多个错误…当然,我一点也看不懂是为什么,希望后面学习深入以后能搞清楚:

❌: 6 issues were found when checking AAR metadata:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
6 issues were found when checking AAR metadata:

1. Dependency 'androidx.appcompat:appcompat-resources:1.6.1' requires libraries and applications that
depend on it to compile against version 33 or later of the
Android APIs.

:app is currently compiled against android-32.

Recommended action: Update this project to use a newer compileSdkVersion
of at least 33, for example 33.

Note that updating a library or application's compileSdkVersion (which
allows newer APIs to be used) can be done separately from updating
targetSdkVersion (which opts the app in to new runtime behavior) and
minSdkVersion (which determines which devices the app can be installed
on).

...

是说选择的 android 版本太低了吗?但是写骰子的时候还是可以的,算小费的时候就不行了…改了一下配置:

1
2
3
4
android {
compileSdk 33
...
}

⚠️: Use SwitchCompat from AppCompat or SwitchMaterial from Material library.

1
Use SwitchCompat from AppCompat or SwitchMaterial from Material library  Use SwitchCompat from AppCompat or SwitchMaterial from Material library  Issue id: UseSwitchCompatOrMaterialXml  Vendor: Android Open Source Project Identifier: androidx.appcompat Feedback: https://issuetracker.google.com/issues/new?component=460343 

参考的这里:

1
2
3
4
5
6
7
8
9
<Switch
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

//改为

<androidx.appcompat.widget.SwitchCompat
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

因为 Switch 对于旧版本的 Android 有不同的外观。则使用 SwitchCompat 可以为所有 Android 版本提供一致的外观。

SwitchCompat 是核心 Switch 控件的完整向后移植版,它将该控件的视觉效果和功能带到了旧版本的平台。与此包中的其他控件不同,SwitchCompat 不会自动用于使用该元素的布局中。相反,您需要在布局中显式使用 <androidx.appcompat.widget.SwitchCompat> 和匹配的属性。

❌: system_process E/JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 108)

不知道。

⚠️: Unknown attribute android:importantForAutofill

不知道。

Demo

仅用于记录学习过程,不具备指导性。🤓

Demo-Github


下一日目标:了解 SDK version、Android version、Gradle 之间的关系、工程结构。