Android 學習筆記 - 新增一個按鈕、加上點擊的事件並回饋在畫面上

## 環境設定

- Android Studio 2.1.1
- Android 版本設定在 N preview 
- Activity 則為了簡單，選擇了 "Empty Activity" ，如下圖

<img width="821" alt="Add Empty Activity" src="https://qiita-image-store.s3.amazonaws.com/0/41979/dffb1596-9e37-7306-e1ac-198d64354a83.png">

## 期望達成功能

- 在 Main Activity 的畫面中加上一個按鈕
- 在 Java 檔案中接取按鈕的點擊事件
- 接到點擊事件後在畫面上顯示一個簡單的訊息提示，這裡會使用 `Toast` 這個 UI 元件

## 建立專案

實作開始

開專案時，除了名字自訂之外，其他的像是 activity name 等等的，為了簡化實作過程，先照著預設的設定即可，也就是 "Main Activity"。

## 加上一個按鈕

按鈕要加在 `activity_main.xml` 這個檔案中，雖然專案開起來時預設就會開著了，如果沒找到的話，可以在左邊的目錄循著下面寫的路徑找到：

```
app/res/layout/activity_main.xml
```

### 移除預設的內容

預設會有一個內容是 "Hello World!" 的 TextView 在裡面，內容如下

``` xml:activity_main.xml
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!" />
```

直接把這一段移除

### 加上按鈕標籤

加上以下的標籤

``` xml
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
```

#### Android xml 檔案輸入的方式與基本屬性的含義

先輸入 `<Button` ，就會開始跳出候選清單，接著按下鍵盤 <kbd>Tab</kbd> 鍵，就會自動完成下面這一段程式碼：

``` xml
<Button
    android:layout_width=""
    android:layout_height="" />
```

接著在兩個引號我就都選 `wrap_content` ，字意上是「包覆內容」，意思就是「根據內容」決定該參數的值會是多少。

以這個按鈕的話就是指：

- 按鈕的 **寬度** 就是 **內容** 的 **最大寬度**
- 按鈕的 **高度** 就是 **內容** 的 **最大高度**

這兩個屬性則是 Android UI 元件的必填屬性。

而這兩個屬性的前綴 `android` 意思就是：「這個屬性是 android 自己內建的」，算是有點 namespace (命名空間）的味道吧！

這個屬性還有一個值是 `match_parent` ，意思則是指被設定到的屬性，需要填滿自己所在的父容器。換句話說，如果設定一個 UI 元件的寬度是 `match_parent` 的話，它的寬度就會像是在 css 裡面把畫面寬度這樣設定 `width: 100%;` 。

### 設定按鈕文字

這時候按下 Run, 或是快捷鍵 <kbd>Ctrl</kbd>+<kbd>R</kbd> ，接著選定模擬器或是實體裝置，就可以執行這個 app 了。

這時候就會發現按鈕上面空空的，接著就來設定文字。

在按鈕裡面要設定文字內容，要使用以下這個屬性

```
android:text
```

一開始我寫死，直接設定一個字串，像是這樣：

``` xml
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text = "我的按鈕"
    />
```

這時候 Android Studio 就會抱怨了，畫上底色，說這樣寫不好，會造成以下影響

> 1. 當要建立不同尺寸的畫面時，必須要重新設定這個字串
> 2. 對多國語言設定會有不良影響

於是說要在 string resource 裡面設定會比較好。

要設定這個字串的話，有兩種方式：

1. 直接開啟 `app/res/values/strings.xml` 在裡面新增字串設定
2. 使用 Android Studio 的自動抽離功能 (automatically extract) ，把指定字串加入第一點提到的 `strings.xml` 這個檔案裡

#### 手動加入字串設定

這時候點兩下開啟 `app/res/values/strings.xml` 這個檔案，當開新的專案時只會有一個 app name 的設定：

``` strings.xml
<resources>
    <string name="app_name">ButtonBasic</string>
</resources>
```

`app_name` 設定的字串會跟專案名稱不同而改變

接著在他的下一行加上這段

``` xml
<string name="buttonText">我的按鈕</string>
```

把這個字串的名稱從簡先設定成 `buttonText` ，把內容設定成 `我的按鈕`。這邊設定的 `buttonText` ，可以讓我們用這個 resource ID 在 UI 的 xml 裡面設定之後，顯示出來的文字就會是「我的按鈕」。

##### **更新 UI 的 xml 檔案**

接著回到 `activity_main.xml` ，把剛剛打的字串，換成 `buttonText` 這個 resource ID ：

```
android:text="@string/buttonText"
```

##### **Resource ID**

在 Android 官方的文件裡面是用這個詞來表示這一類參照資源用的 ID 。

##### **`@string`**

這是 Android 在 xml 裡面用來指定 string resource 的 syntax ， 編譯器會幫忙根據 `/` 後面的 id 找到指定的字串後，轉成 `String` 物件後，讓指定的 UI 可以把預期中的字串顯示出來。 (String Resource 的 [官方文件說明](https://developer.android.com/guide/topics/resources/string-resource.html))

#### 使用 Automatically Extract - 自動抽離功能

Automatically extract 提供了一些使用者介面，來快速方便的達成建立 string resource 的這個目的。當建立完 resource ID 後， Android Studio 還會幫忙把原本寫死的字串直接換成 resource ID ，非常的方便。

##### **1. 選取寫死的字串，接著按下快捷鍵 <kbd>Alt</kbd>+<kbd>Enter</kbd>**

如下圖：

<img width="447" alt="選取字串按下 Alt+Enter 鍵" src="https://qiita-image-store.s3.amazonaws.com/0/41979/c01d0b92-28d0-cb71-e3c7-3ad8e00abd10.png">

接著點選 `Extract string resource`

##### **2. 設定 Resource ID**

如下圖：

<img width="310" alt="輸入資源名稱" src="https://qiita-image-store.s3.amazonaws.com/0/41979/d9a39d6c-3e8c-3ec5-9507-3d080c2e0e2e.png">

在這邊除了輸入資源名稱，其他參數沒有調整維持預設，接著點下 OK 

##### **3. 完成**

這時候就會發現原本的字串被替換成使用 string resource 的格式：

<img width="266" alt="スクリーンショット 2016-05-21 0.52.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/41979/526704b0-1fc4-4b37-69e2-9e2d05b95248.png">

這時候如果去在前面一段有提到的 `strings.xml` 查看的話，也會看到這個檔案被新增了一行設定，原本輸入寫死的字串「我的按鈕」，也直接被設定成這個 string resource 的值：

``` xml
<string name="buttonText">我的按鈕</string>
```

目前為止的成果：

<img width="350" alt="完成設定按鈕文字" src="https://qiita-image-store.s3.amazonaws.com/0/41979/517f237a-e2a4-1d99-4540-d983eb298d98.png">


### 設定按鈕的事件

拉回按鈕設定，接著要把 xml 和 Java 連結在一起了。

`Button` 有個屬性是叫 `android:onClick` ，是用來指定當按鈕被按的時候會呼叫對應到的 activity 中的 method 。

新增後，我們先指定我們要呼叫的 method 是 `buttonOnClick` ，這裡的名字是可以任意的，等一下我們就會需要在 MainActivity 這個 class 裡面新增這個 method 。

我們的 `Button` 設定發展到這個樣子：

``` xml
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/buttonText"
    android:onClick="buttonOnClick"
    />
```

這時候會發現 `buttonOnClick` 被上了底色，是因為 Android Studio 有檢查到對應的 activity - 這邊就是指 `MainActivity` 裡面沒有名為 `buttonOnClick` 的 method 。

#### 新增反應點擊事件的 Method

接著開啟 `MainActivity.java` ，位置在左側目錄中 `app/java/[你的專案 module id]/MainActivity`

在原本的 `onCreate` 的下方，新增一個 method ：

``` java

// ... 前略

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    void buttonOnClick(View view) {
    }
}
```

1. 這時候打完 `View` 的時候， Android Studio 會提醒我們需要引入這個 class ，接著只需要按下 <kbd>Alt</kbd>+<kbd>Enter</kbd> 即可自動在檔案的最前面幫忙引入，而不用自己手打出來。
2. 為什麼需要加上 `View view` 這個參數？
  - 因為這個按鈕並沒有透過 `findViewById` 並在這個 class 中用一個變數存起來，但是我們又必須要有辦法存取觸發這個 method 的 Button ，所以才需要這個 view 。

##### **我都知道他是 Button 了，參數類別可以改成 Button 嗎？**

> 不行。

當改成 

``` java
void buttonOnClick(Button button) {
}
```

的時候，

只要按下按鈕， app 就會 crash ，說明他找不到 `buttonOnClick(View)` 這個東西，因此看起來，他只接受參數型態是 `View` 的 method 。

因此，如果要使用這個按鈕的話，可以這樣做，轉型後用一個 local 變數存起來：

``` java
void buttonOnClick(View view) {
    Button button = (Button) view;
}
```

這邊因為不會需要對按鈕做任何操作，因此這個 method 先留空白就好。

這時候重新 Run 之後，點下去不會有任何視覺上的反應。

接下來要在按鈕按下觸發的 method 中，加上一段程式碼，讓 app 可以跳出一個 `Toast` 反饋。

### 新增 Toast 物件

Android 的 Toast 是一個會在畫面下半部出現的一個暫時性質的 UI 元件，他的功能基本上是拿來顯示給使用者簡要的提示訊息。

建立一個 Toast 物件：


``` java
void buttonOnClick(View view) {
    Toast toast = Toast.makeText(this, "按鈕已經被點擊", Toast.LENGTH_SHORT);
}
```

這裡使用了 Toast 的 `makeText` method 來產生我們要顯示的 Toast 物件。它包含了三個參數：

`參數 1` 、這個 Toast 物件要顯示的環境：在這裡就是指我們當下的 Main Activity 物件，所以帶入 `this` 代表是自己這個物件
`參數 2` 、要顯示的字串，這邊寫死一個字串在這邊。由於這個 method 的第二個參數可以接 id 也可以接字串，因此這樣寫 Android Studio 不會畫重點警告。
`參數 3` 、這個 Toast 物件該顯示的時間，**一定要**設定為 `Toast.LENGTH_SHORT` 或是 `Toast.LENGTH_LONG` 的其中一個。

#### 使用 String Resource

就像上一個大部分裡面有提到寫死字串是不好的，因此這裡也來把字串轉換成 string resource 並用 resource ID 取用。

在這邊我把它叫做 `buttonOnClickMessage` ，名字一樣是可以自選。

和 xml 不同的是，在 java 檔案裡面並不是用 `@string/` 作為前綴，而是透過 `R.string.` 再加上 resource ID 來取用 string resource 。

因此，我們建立一個 Toast 物件的程式碼就變成這樣：

``` java
Toast toast = Toast.makeText(this, R.string.buttonOnClickMessage, Toast.LENGTH_SHORT);
```

##### **R 是什麼東西？**

`R` 是一個特殊的 class ， 是在專案被編譯過後，由 `aapt` (Android Asset Packaging Tool) 打包資源後所產生的的一個類別。

每一個資源一定會由兩個部分所組成， **資源的種類** 和 **資源的名稱** (resource type, resource name)。

格式會是如此

``` java
R.[resource type].[resource name]
```

就像目前有用到的 `string` ，每一種不同的 resource ，就會被群組成一個 type ，接著再由 resource name 或是 resource ID 存取想要的資源。

以目前用到的程式碼來說，就是從 aapt 打包出來的 `R` class 取用叫做 `string` 的這個 type 中， name 是 `buttonOnClickMessage` 對應到的資源：

``` java
R.string.buttonOnClickMessage
```

參考： [Accessing Resources: 官方文件](https://developer.android.com/guide/topics/resources/accessing-resources.html)

### 顯示 Toast 物件

顯示這個 Toast 物件很簡單，直接呼叫 `.show()` method 即可：

``` java
void buttonOnClick(View view) {
    Toast toast = Toast.makeText(this, R.string.buttonOnClickMessage, Toast.LENGTH_SHORT);
    toast.show();
}
```

最後這個 `onClick` 事件所呼叫的 method 就如上面的程式碼。

這時候再 Run 或 Debug 一次，點擊按鈕之後，就會看到最下面如預期般的跳出剛剛新增並顯示的 Toast 物件：

<img width="480" alt="最後成果" src="https://qiita-image-store.s3.amazonaws.com/0/41979/ea926dde-47d3-7a70-2758-9d9c788f651c.png">