这是 Android 开发(入门)课程 的第一部分《布局和交互》的第二次课程实践,导师是 Lyla Fujiwara,主要内容是变量练习和 Court Counter App。
变量练习
这节课通过代码找茬来练习变量的使用,可总结出两种类型的错误:编译期错误和逻辑错误。
编译期错误
Android Studio 能够识别并字体标红,右侧红色方块指示错误位置,在声明和初始化变量时常犯。
声明变量可理解为创建一个新变量,如
int total;
初始化变量则为新变量设定一个初始值,如
total = 10;
通常习惯将声明和初始化变量写在一起,如
int total = 10;
这两项操作有严格的格式规范:
数据类型 变量名 = 初始值;
-
数据类型应与系统设定完全相同,如整型 int 不能是 Integer、integer 和 INT,字符串 String 首字母大写。数据类型在 Android Studio 中默认为蓝色。
-
数据类型与变量名之间的空格不能省略,也不允许其他任何字符,如 _ 或 - 。
-
变量名应遵循命名规则,可 Google 搜索 "variable names java" 找到 Oracle 的说明文档 。通常变量名不能太长也不能短到一两个字母;若是一个单词则全小写;若是多个单词则用小驼峰命名法。变量名不能与数据类型相同,也不能有符号和空格。
-
初始值要与数据类型匹配,如字符串的值应在双引号内,不允许出现
int total = “10”;
-
分号结束。
逻辑错误
非 XML 或 Java 语法错误,而是代码功能无法实现的错误,所以 Android Studio 无法识别。可关注以下几点:
-
运算符优先级,可 Google 搜索 "arithmetic operators java" 找到 Oracle 的说明文档 。例如乘除的优先级比加减的高,逻辑与的比逻辑或的高。若无法确定运算符优先级时用小括号分隔,如
// 最先做加法,然后乘法,最后减法 total = 1 * ( 2 + 3 ) - 5;
-
变量仅在赋值时改变值,运算不会。
-
局部变量在 method 内声明,只在 method 内部有效,在 Android Studio 中为黑色。
全局变量在 method 外声明,在 method 之间有效,可以保存数据,在 Android Studio 中为紫色。
若变量不跨 method 使用时用局部变量即可。 -
代码按从上到下的顺序运行,先前执行过的代码不会受后面的影响。
Court Counter App
首先构建布局,按步骤进行:分解 Views→画 Views 层级图→写 XML 草稿→代码实现。
- 分解 Views。下图为应用最终布局图。
这里用到了嵌套ViewGroups,有两对水平对称的 TextView 和三个 Button,每对垂直排列,通过 vertical 的 LinearLayout 实现;中间有一个 View 呈垂直线条状,三者用 horizontal 的 LinearLayout 能实现;底部居中一个 Button,因此根 Views 应为 RelativeLayout。
- 画 Views 层级图,即树状图
- 写XML草稿,清晰条理
<RelativeLayout...>
<LinearLayout...>
<LinearLayout...>
<TextView... />
<TextView... />
<Button.../>
<Button.../>
<Button.../>
</LinearLayout>
<View... />
<LinearLayout...>
<TextView... />
<TextView... />
<Button... />
<Button... />
<Button.../>
</LinearLayout>
</LinearLayout>
<Button... />
</RelativeLayout>
- 代码实现
In activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:context="com.example.android.courtcouter.MainActivity">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:gravity="center_horizontal"
android:padding="16dp"
android:text="Team A"
android:textColor="#616161"
android:textSize="14sp" />
<TextView
android:id="@+id/team_a_score"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:gravity="center_horizontal"
android:paddingBottom="24dp"
android:text="0"
android:textColor="#000000"
android:textSize="56sp" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:onClick="addThreeForTeamA"
android:text="+3 Points" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:onClick="addTwoForTeamA"
android:text="+2 Points" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:onClick="addOneForTeamA"
android:text="Free Throw" />
</LinearLayout>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="16dp"
android:background="@android:color/darker_gray" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:gravity="center_horizontal"
android:padding="16dp"
android:text="Team B"
android:textColor="#616161"
android:textSize="14sp" />
<TextView
android:id="@+id/team_b_score"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:gravity="center_horizontal"
android:paddingBottom="24dp"
android:text="0"
android:textColor="#000000"
android:textSize="56sp" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:onClick="addThreeForTeamB"
android:text="+3 Points" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:onClick="addTwoForTeamB"
android:text="+2 Points" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:onClick="addOneForTeamB"
android:text="Free Throw" />
</LinearLayout>
</LinearLayout>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="8dp"
android:layout_marginLeft="22dp"
android:layout_marginRight="22dp"
android:onClick="resetScore"
android:text="reset" />
</RelativeLayout>
- 写完代码后可在 activity_main.xml 文件的 Design 标签下的 component Tree 验证嵌套 ViewGroups 层次是否正确。
- TextView 中的文字使用了 gravity 属性实现居中对齐,这种方式使得在 LinearLayout 中实现类似 RelativeLayout 中的对齐布局。TextView 的边界较大时效果更明显。
- 两个第二层 LinearLayout 使用了 layout_weight 属性实现水平平分宽度,注意此时双方的初始宽度应为 0dp,否则不为零的一方将多占一部分宽度。
- 中间的垂直线条可用宽度为 1dp 的 Views 来实现。
- 注意内边距 padding 与外边距 margin 的区别。padding 会使 Views 内的内容距离指定位置有间隔,而 margin 会使子 Views 距离父 Views 中的指定位置有间隔,不会改变子 Views。
styles.xml 文件能控制 App 的基本样式,统一修改布局。文件在左侧 Project 标签 Android 视图中 app→res→values 路径下。以下代码将 Court Counter App 的主色调改为橙色。
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light">
<!-- Customize your theme here. -->
<item name="colorPrimary">#FF9800</item>
<item name="android:colorButtonNormal">#FF9800</item>
</style>
</resources>
最后通过 Button 的 android:onClick 属性连接 XML 与 Java,在 Java 中通过 method 实现功能。
In MainActivity.java
package com.example.android.courtcouter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
int scoreTeamA = 0, scoreTeamB = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* Increase the score for Team A by 3 points.
*/
public void addThreeForTeamA(View view) {
scoreTeamA = scoreTeamA + 3;
displayForTeamA(scoreTeamA);
}
/**
* Increase the score for Team A by 2 points.
*/
public void addTwoForTeamA(View view) {
scoreTeamA = scoreTeamA + 2;
displayForTeamA(scoreTeamA);
}
/**
* Increase the score for Team A by 1 points.
*/
public void addOneForTeamA(View view) {
scoreTeamA = scoreTeamA + 1;
displayForTeamA(scoreTeamA);
}
/**
* Increase the score for Team B by 3 points.
*/
public void addThreeForTeamB(View view) {
scoreTeamB = scoreTeamB + 3;
displayForTeamB(scoreTeamB);
}
/**
* Increase the score for Team B by 2 points.
*/
public void addTwoForTeamB(View view) {
scoreTeamB = scoreTeamB + 2;
displayForTeamB(scoreTeamB);
}
/**
* Increase the score for Team B by 1 points.
*/
public void addOneForTeamB(View view) {
scoreTeamB = scoreTeamB + 1;
displayForTeamB(scoreTeamB);
}
/**
* Increase the score for Team B by 1 points.
*/
public void resetScore(View view) {
scoreTeamA = 0;
scoreTeamB = 0;
displayForTeamA(0);
displayForTeamB(0);
}
/**
* Displays the given score for Team A.
*/
public void displayForTeamA(int score) {
TextView scoreView = (TextView) findViewById(R.id.team_a_score);
scoreView.setText(String.valueOf(score));
}
/**
* Displays the given score for Team B.
*/
public void displayForTeamB(int score) {
TextView scoreView = (TextView) findViewById(R.id.team_b_score);
scoreView.setText(String.valueOf(score));
}
}
- 用全局变量保存两队的总分。
- 打开 "Add unambiguous imports on the fly" 功能,在发现未知量时立即添加清晰的 imports,通常导入 Java 的库或包。
- 在 Android Studio 中变量名默认为紫色。
- 养成写注释的习惯。
课程至此,我做了第二个实战项目——计分器应用。这同样是个简单练习,在 Court Counter App 的基础上稍作修改,达到如下图的效果。
这次项目提交上去由导师审阅后,给出了关于 App 资源文件相关的优化建议,如
- 将 sp & dp 等尺寸相关的声明放入 dimens.xml 内,而不是将 Views 的高度和宽度,以及字体的大小硬编码 (hard coding);
- 将字符串变量放入了 strings.xml 内,方便应用内多处引用,在翻译应用时也可以大大减少工作量。
- 将 color 颜色值放入 colors.xml 内,统一管理颜色资源,而不是对颜色硬编码;
- 将样式重复的 TextView、Button、LinearLayout 在 styles.xml 封装调用,使 XML 代码简洁易读。
对 Android App 资源文件的介绍会在以后的课程中出现,在此先有个概念,不多作解释。
网友评论