这节课是 Android 开发(入门)课程 的第一部分《布局和交互》的第三节课,导师是 Katherine Kuan 和 Kunal Chawla,主要内容是 Android Studio 使用技巧、初识 Java 和 Just Java App。
关键词:Button,android:onClick,Java,Activity,变量 (Variables) 及其作用域,Reformat Code & Rearrange Code
经过前两节课以及一次课程实践,可以知道 Udacity 的纳米学位课程不会像大学的那样,着重要求掌握一门语言的特定语法和概念,Udacity 更倾向于从实用的角度出发,从实际的操作和代码中介绍知识点。更关键的是,Udacity 提供了积极的学习方法和资源,无论是提倡从 Google 搜索或在 stack overflow 等社区寻求帮助,还是建议养成阅读技术文档和技术博客的习惯,都比捧着一本教材死磕来的强得多。同时这也消除了初学者担心培训教学不系统的问题,因为一门语言永远都在更新发展,永远都不可能完全掌握,拥有一套高效有用的方法才是王道。
因此,接下来的学习笔记,除了我提炼出来的知识点外,都会结合实际的代码进行。
Android Studio 使用技巧
右侧实时显示效果预览的 Preview 标签有问题时,临时解决办法:降低 API 版本,按钮为标签内右上角的绿色 Android 小人。
每完成里程碑的代码都运行到手机进行验证,勾选设备选择界面中的 "Use same device for future launches" 可以对同一设备免去设备选择界面的操作(在新版 Android Studio 中去除了此选项)。
布局 (Layout) 代码在默认名 activity_main.xml 文件中编辑,文件在左侧 Project 标签 Android 视图中 app→res→layout 路径下。
活动 (Activity) 代码在默认名 MainActivity.java 文件中编辑,文件在左侧 Project 标签 Android 视图中 app→java→com.example.hsujin.justjava(安装包名)路径下。Activity 代码专注于一类用户可操作的事项,包括交互和布局。例如 Gmail 的 Conversation List Activity 专注于查阅邮件的对话清单活动,Compose Activity 则专注于创建新邮件的写作活动。
编译期错误 (compile time error):能够识别并字体标红,右侧红色方块指示错误位置。
运行期错误 (run time error):App 运行时发生的错误,错误信息记录在 Android 输出日志 (logcat) 中,在下侧 Android Monitor→logcat 标签中选择 "Show only selected application" 可找到红色错误信息,例如课程 2A 展示的如下错误信息。
04-07 21:38:11.792 32516-32516/com.example.android.justjava E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.justjava, PID: 32516
java.lang.IllegalStateException: Could not find method submitOrder(View) in a parent or ancestor Context for android:onClick attribute defined on view class android.support.v7.widget.AppCompatButton with id 'button'
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.resolveMethod(AppCompatViewInflater.java:325)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:284)
at android.view.View.performClick(View.java:4781)
at android.view.View$PerformClick.run(View.java:19874)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)
阅读这段堆栈信息。
# 标识了是哪个 App 的错误信息
Process: com.example.android.justjava, PID: 32516
# 无需全部理解,寻找关键词,定位问题点,再重点排查代码
# 此处的关键词为button、android:onClick、submitOrder
java.lang.IllegalStateException: Could not find method submitOrder(View) in a parent or ancestor Context for android:onClick attribute defined on view class android.support.v7.widget.AppCompatButton with id 'button'
# 随后的信息进一步说明错误
- 定点调试,一行一行地执行代码。如下图所示。
(1)在编辑文件时,点击行数右边的空白区域,可添加任意数量的红色断点 (breakpoint);
(2)打开下侧 Debug→Debugger 标签,关注三个地方。
左侧绿色继续运行按钮可在代码遇到断点暂停运行时恢复;暂停按钮可在代码运行时暂停;红色停止按钮可用来停止调试。
上侧三类带箭头的按钮:Step Over (F8) 运行到下一句代码;Step Into (F7) 跳进这一句代码内部;Step Out (Shift+F8) 跳出这段代码。
变量 (Variables):调试对象,通过控制代码的运行来观察变量的变化。
在设置 (Android Studio→Preferences) 中搜索 Auto Import 找到并勾选 "Add unambiguous imports on the fly" 能在发现未知量时立即添加清晰的 imports,通常是导入 Java 的库或包,例如课程 2A 中需要 import(导入)Java 提供的统一数字格式的包。
code→Reformat Code: 格式化代码,将代码对齐、换行以提高可读性,而不是硬盘等存储设备的数据擦出概念。
code→Rearrange Code: 按 Android 代码规范的风格调整代码的顺序。
这两项操作都不会处理空行,不选中代码时作用对象为当前文件的全部代码。
- 善用 Android Studio 的代码自动完成功能,出现候选项时可以上下键选择,回车键确认。
初识 Java
在写 Java 代码之前,可用伪代码 (Pseudo code) 这种无格式要求的解释代码的易懂语言来整理思路,清晰条理。
Java 注释:单行
//...
;多行/*...*/
。养成写注释的习惯,帮助他人和自己日后理解代码。另外修改代码时使用注释而不是直接删除,在 Android Studio 自动保存的机制下,避免了多次撤销或重做产生的混乱。单句必须以分号
;
结束;多条代码用大括号{...}
包含起来。方法 (method) 起止于
{...}
,可包含多行代码,按顺序执行,操作应先声明后调用。method 可认为是多条代码的集合,方便在其他场合统一引用。变量 (Variables):当多处要用到一个经常变化的值时,使用变量可通过仅改变变量值就实现其他所有值的更新,无需修改每一条代码。变量增加代码的鲁棒性 (Robust)。与 method 相同,变量也应先声明后引用。
变量声明
首先变量声明的格式为
变量类型 变量名 = 初始值;
(1)变量类型:定义变量的数据类型,如 int (integer) 定义变量为整数。
(2)变量名:命名规则可 Google 搜索 "variable names java" 找到 Oracle 的说明文档 。通常变量名不能太长也不能短到一两个字母。若是一个单词则全小写;若是多个单词则用小驼峰命名法。可见变量名区分大小写。在 Android Studio 中声明却未引用的变量名显示为灰色,method 名也同样适用。
(3)=: 赋值符号,而不是等号。这里需要养成右手边阅读习惯,即把右边的值赋予左边的变量。另外,赋值符号两边各有一个空格,这种编程风格可提高代码的可读性。
(4)初始值:赋予变量的数值,通常是字面值 (Literal),即具体给出的一个数。
(5)单句代码以分号结束。
变量赋值
声明变量后,通常会对其进行赋值,例如课程 2A 中展示的
quantity = quantity + 1;
(1)声明变量时才需要指定数据类型,赋值不用。
(2)与变量声明相同,这里也要养成右手边阅读习惯,即把右边的值赋予左边的变量。
(3)Java 可输入算式,支持的运算符可 Google 搜索 "arithmetic operators java" 找到 Oracle 的说明文档 。需要注意的是,作为函数真实值的算式,不会对变量 quantity 赋值,如执行完 display(quantity - 1);
这条代码后 quantity 的值不会改变。
(4)同样地,运算符两边也各有一个空格,可提高代码的可读性。
(5)单句代码以分号结束。
变量作用域
根据变量作用域的分类,变量可分为全局变量和局部变量。变量作用域 (Variable Scope) 可简单地理解为变量所在大括号的作用范围。也就是说,
(1)全局变量在类 (class) 的大括号内,而 class 包括许多 methods,所以全局变量在 methods 之间有效,可以保存数据,在 Android Studio 中为紫色。
(2)局部变量在 method 的大括号内,就只在 method 内部有效,在 Android Studio 中为黑色。
全局变量与局部变量可重名,且互不影响。在 method 内局部变量的优先级高于全局变量,即名称相同的两种变量在 method 内只有局部变量有效,不影响全局变量。
Just Java App
接下来的课程通过制作一个咖啡订购应用 Just Java 来学习 Android。 创建一个新 App 工程,App 名称为 Just Java,域名 android.example.com 以及 API 15: Android 4.0.3 (IceCreamSandwich)。
Fun Facts
Java 这个词来源于印度尼西亚的爪哇岛,因盛产咖啡闻名,所以 Java 与 咖啡 有着千丝万缕的联系,Java 的 logo 就是一杯冒着热气的咖啡。
课程 2A 制作的咖啡订购应用名为 Just Java ,算是 Java 语言和咖啡的双关语吧。
这节课要通过 Java 的变量和 method,用按钮 (Button) 来实现 Just Java App 的交互。
In activity_main.xml
<LinearLayout 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"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
tools:context="com.example.android.justjava.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="quantity"
android:textAllCaps="true" />
<Button
android:layout_width="48dp"
android:layout_height="48dp"
android:onClick="increment"
android:text="+" />
<TextView
android:id="@+id/quantity_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textColor="#000000"
android:textSize="16sp" />
<Button
android:layout_width="48dp"
android:layout_height="48dp"
android:onClick="decrement"
android:text="-" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:text="price"
android:textAllCaps="true" />
<TextView
android:id="@+id/price_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="$0"
android:textColor="#000000"
android:textSize="16sp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:onClick="submitOrder"
android:text="order" />
</LinearLayout>
按照选择、放置、设置 Views 的三步布局 (Layout) 构建方法,可见此处有四个 TextView 和三个 Button。
- 布局选择了垂直分布 (vertical) 的 LinearLayout;
- TextView 的设置有 id、全部字母大写、字体黑色、padding/margin 等;
- Button 第一次使用,"+" 和 "-" 按钮的大小为 48dp x 48dp,内部文字默认全部大写,三个 Button 都通过
android:onClick
设置了按钮按下时的调用 method,分别名为 increment、decrement、submitOrder。
In MainActivity.java
package com.example.android.justjava;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
import java.text.NumberFormat;
/**
* This app displays an order form to order coffee.
*/
public class MainActivity extends ActionBarActivity {
int quantity = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* This method is called when the order button is clicked.
*/
public void submitOrder(View view) {
displayPrice(quantity * 5);
}
/**
* This method is called when the + button is clicked.
*/
public void increment(View view) {
quantity = quantity + 1;
display(quantity);
}
/**
* This method is called when the - button is clicked.
*/
public void decrement(View view) {
quantity = quantity - 1;
display(quantity);
}
/**
* This method displays the given quantity value on the screen.
*/
private void display(int number) {
TextView quantityTextView = (TextView) findViewById(
R.id.quantity_text_view);
quantityTextView.setText("" + number);
}
/**
* This method displays the given quantity value on the screen.
*/
private void displayPrice(int number) {
TextView priceTextView = (TextView) findViewById(R.id.price_text_view);
priceTextView.setText(NumberFormat.getCurrencyInstance().format(number));
}
}
- 在设置中勾选 "Add unambiguous imports on the fly" 会自动添加代码
import java.text.NumberFormat;
- 声明全局变量
int quantity = 0;
- 变量赋值
quantity = quantity + 1;
- 引用其他 method
displayPrice(quantity * 5);
目前 Just Java App 实现了增加和减少数量,以及显示价格的功能,如下图。
更多的代码解释会在接下来的课程出现。