---
# System prepended metadata

title: Android Studio 學習筆記 - 課程
tags: [CMoney7th-戰鬥營學習筆記, Android]

---

# Android Studio 學習筆記 - 課程

## 序
* 主要閱讀資料-：[android developer官方文件](https://developer.android.com/guide/topics/ui/declaring-layout?hl=zh-tw#write)
* 次要學習資料：
    * [HKT線上教室【從零開始學 Kotlin 程式設計】Android Kotlin](http://tw-hkt.blogspot.com/2018/11/kotlin_57.html)、
* [Android Studio快捷鍵](https://developer.android.com/studio/intro/keyboard-shortcuts?hl=en)
* 使用的後端資料庫：Node.js + MongoDB(mongoose套件)
*測試伺服器名稱(androidproj)*
* [範例Code](https://www.notion.so/Android-code-0abf76b0d303451c9f0bcc3995bd1ee7)

## 一、基本重要觀念 - Activity LifeCircle (生命週期)
![](https://i.imgur.com/ENu39Lx.png)

1. lifecycle：每個階段觸發條件達成時就呼叫一次，每次打開，就是一個activity（ex:google Map . spotify...)。
2. create： 創建一次就不會創建的資料，ex:地圖元件。
3. start: 將每次activity開始時，所需要設定的初始值寫在這，也可以寫在create。
4. resume: 每次回到這個畫面都要做的事情，ex:更新api刷新畫面、重新定位。
5. create&start&resume，在activity被開啟時，就會一次跑完，然後停留在running的狀態。 
6. pause: 當有其他的activity被打開時，就會先把原本的暫停然後放到stack，所以按下返回會回到畫面，並跑一次resume。
7. stop：如果在暫停一段時間後（通常都滿快的-pause的事情跑完後），就會到stop此時就會停留在手機要開啟分頁才會點即看到的地方），++**如果此時點開會回到resume**++，除非是經過navigates才會到restart -> 再到start，但這一段比較複雜點通常不會執行到。
8. Destroy：除非到手機開啟分頁時，把該程式滑掉，才會被destroy。
9. 記憶體要控管好，才不會閃退，另外pause、stop狀態時如果有碰到記憶體不夠時就會自動釋放掉，所以點開他的時候才會重新create->start->rusume。

---
## 二、專案基本知識
1. manifests->AndroidManifest.xml 整個專案的主幹，設定檔～
```xml=
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.kotlintest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.KotlinTest">
        <!-- label 標題名稱； theme就像遊戲的選單模組的theme一樣，系統會套用 -->
        <activity android:name=".MainActivity"> <!-- 如果有新設的activity要在這裡設定，不然系統不會管 -->
            <intent-filter>  <!-- intent非常重要，所有android的跳轉功能（ex: 開啟app），都是這裡的功能 -->
                <action android:name="android.intent.action.MAIN" />  <!-- 標示這一個是主Activity -->

                <category android:name="android.intent.category.LAUNCHER" /> <!-- 設定首頁的概念，第一個要跑進來的activity -->
            </intent-filter>
        </activity>
    </application>

</manifest>
```

2. java 資料夾：撰寫activity 程式邏輯的地方，實際執行的地方。
3. res 資料夾：所有的資源庫都放這，有內建的R可用（會跑一遍resource資料，然後像是幫你建好path(但是是常數-int）)，所以打R就可以呼叫，在.要的資料，ex: `R.id.btnTest`、`(R.layout.activity_main`
    ★檔名一定要全小寫，_區隔，不然會抱錯，包含圖片
    1. drawable：放圖檔、向量檔。
    2. layout：放所有的畫面（Activity、元件..）
    3. mipmap：放icon
    4. values：設定整個系統的資料、幾乎都是enum（ex:Global）
    5. 之後用到動畫、選單等等，會在新增。
    6. 裡面的檔名命名規則：全小寫、 '_' 區隔、倒裝（先寫功能，在寫元件名稱 ex:activity_main。）
    7. layout並非執行的地方，是資料給java讀檔後寫出(像是地圖產生器&地圖編輯器合併起來的概念）。
    8. values為了後面的彈性方便，所以會把String、Color等等都變成變數，放在這裡，這樣在之後如果要發展多國語系等等時，只需要在這邊設定，全部的地方都會跟著被變動。
4. Gradle： 就像npm，是套件管理工具。
    1. android defaultConfig裡面會放可以支援的java version 從幾到幾，以及該程式的版本號。
        1.versionCode：實際的版本號(內部)。
        2.versionName：要顯示給外面看的版本號(外部)。
    2. dependencies：要安裝導入的套件。

---
## 三、基本知識
1. 開發流程：
    1. res-layout刻出畫面。
    2. java-activity撰寫程式邏輯。
    3. 中間有需要的資源就在res放對應的資料。
2. 原則上所以程式的String都會用變數替代，然後該變數放到res-values裡面，以利後續程式做不同語言時可以使用。
3. androidx是為了解決相容性的問題而產生的套件，基本上都建議使用他，以免版本不同，而導致程式有問題。
4. layout是容器（就像java的JFrame、JPanle）。
5. Android是利用XML ++（標記式語言，可以依照使用者需求來決定解析的方式）++ 來決定UI佈局的設定，並且在編譯(Compile)時，系統會將所有XML版面配置檔案(Layout)編入View資源，再利用setContentView來載入XML資源並產生實際畫面。
6. IDE的資料夾顯示：Compact Middle Package 的設定可以隱藏空的資料夾。
7. 要創建新的activity，要用new->activity->empty activity，才會自動幫你建好layout以及放入manifest裡面。
8. ★網路連線會放到IO(工作用執行緒）的執行緒去跑，不會放在UI(主執行緒) ，不然會阻塞，嚴重可能會直接閃退，只要在主執行緒上處理網路連線，會直接報錯。
9. android studio 用 Log.d印資訊，可以處裡的東西比較多。

### 執行緒
1. 重要觀念，要特別注意及處理：
    1. Race Codition 資源競爭 兩條以上執行緒同時對一個資源做事，導致值異常，此時可以用java Synchronized -把值鎖住，這段時間外面不可以拿到他的值；鎖的方式有分很多種。
    2. Dead Lock 死鎖：用Synchronized鎖起來後，也可能會因為互相鎖住，導致無限卡死，此時就稱為Dead Lock，通常單例都會鎖同步。
2. 因為兩個執行緒是平行(異步)的，每次兩邊都是跑一點點（是電腦指令而非程式碼）：ex: i+=1 是好幾個電腦指令做動的 EX: 先加i放到暫存其，有個Atomit.. 可以把這些指令包成一個原子，也就是是同一個指令
3. ★ Andriod 有兩個執行緒：
    * UI 主執行緒：畫面相關的，絕對不要在IO裡面做修正修改，不然一定會壞，UI要盡量降低運算的複雜度，這樣畫面才不會卡頓，手機的卡頓，就會直接閃退。
    * IO執行緒（副執行緒-工作處理用）(可能有多個)-利用Callback來讓主執行續得到資料。
4. ★IO執行緒可以用`runOnUiThread(()->{})`，把資料傳到主執行緒上，主執行緒在依照收到的資料做事情，要注意送資料不一定會先做，所以先把要跑的邏輯跑完後在送出資料。
5. 只要不在UI跑得都算是背景執行，要像跳通知那樣是程序，跨程序溝通要用作業系統才能處理(C++ 或比較用到底層的寫法才會用到）(IPC) 。
6. 不要用Looper.prepare() 事情Looper.loop()，因為執行續不會被關閉，除非是要像網路連線那樣，程式打開中都要使用，有點類似背景執行，但還不是LINE跳通知(是process程序），。
7. Android 有提供機制輔助你在兩個執行續先傳遞資料，內建處理的，不然實際實現很麻煩。

補充資料：[Synchronized用法-java](https://www.jackforfun.com/java-synchronized)

---
## 四、撰寫畫面（layout）
1. 刻畫面主要為LinearLayout（線性布局）、ConstrainLayout：
    1. Linear： 利用水平、垂直排版物件，可以linear接一個linear以此達到像表格的形狀(稱為嵌套)。
        * 適合格局方正的工具型App。
        * 可以直接編輯code，更快速。
    2. Constrain：利用的是與其他物件或螢幕邊框的相對位置（比率）對齊物件，要設定物件上下左右依靠的物件(要相對的目標）。
        * 建議用拖曳的，code太複雜不會比較快。
    3. 實務上大多會先用Linear完成大部分排版後，再套到Constrain細部調整，已完成自適應大小。
    4. 目前預設容器都是ConstrainLayout。
2. 畫面設定大小的三種方式：
    1. 100dp => 依照螢幕解析度寫死大小
    2. match_parent => 依照父容器的大小決定
    3. wrap_content => 依照容器內元素的大小決
        * 若最外層的容器設為match_parent即為手機的寬/高大小
        * 若最內層的原件設為wrap_content即為元件所需要的範圍大小
3. 常用的元件及縮寫表：

    | 名稱     | 縮寫   | 備註 |
    | -------- | ------ | ---- |
    | [TextView](https://developer.android.com/reference/kotlin/android/widget/TextView?hl=en#top_of_page) | tvName | 1.顯示文字所使用的元件<br>2.text屬性為設定顯示出的文字(set或get)<br>3.正常開發時不會有textview留著，因為在連線時會被看到。 |
    | [EditText](https://developer.android.com/reference/kotlin/android/widget/EditText?hl=en)  | etName       | 1.輸入框的元件<br>2.text屬性為讀取到的文字(set或get)，但格式不是String要自己轉。<br>3.`.isNullorEmpty()`判斷是否有輸入值。<br>4.[自定義邊框、圓角](https://www.itread01.com/content/1546117761.html)    |
    | [Button](https://developer.android.com/reference/kotlin/android/widget/Button?hl=en)       | btnName       |1.按鈕的元件<br>2.基本上邏輯寫在按鈕，也就是點擊後觸發。      |
    
4. 白色畫面是外部顯示的畫面，藍色畫面是元件的實際大小

---
### 撰寫畫面-LinearLayout
1. 更改第一層的Layout方式：到code，把`androidx.constraintlayout.widget.ConstraintLayout` 改成LinearLayout。
2. 第一層LinearLayout：所有元件都會在這裡面，寬高通常都會設成match_parent。
通常除了第一層LinearLayout外的元件寬高都會設wrap_content（依照實際內容）。
3. LinearLayout會由起始位置開始擺放其中的元件，可以在接LinearLayout當子元件（不一樣的方向），設計時以橫跨螢幕的最長邊為基準作為方向判定的方式。
    * 垂直 => 由上往下照順序擺放元件
    * 水平 => 由左至右照順序擺放元件 
4. 其中Padding與Margin皆有all/top/bottom/left/right五種屬性，當all被設定時，其餘屬性會失去效果，如果需要針對不同邊有不同間距，需要個別進行設定。
5. LinearLayout（orientation)分為Vertical與Horizontal兩種：
    * Vertical中的子元件layout_gravity中center_vertical將會無效(垂直的位置由LinearLayout決定)
    * Horizontal亦然，子元件layout_gravity中center_horizontal將會無效。
6. 常用的設定表格及其說明：
    | 名稱         | 用途               |
    | ------------- | ------------------------ |
    | orientation        | 決定LinearLayout垂直(vertical)/水平排版(horizontal)的屬性 |
    | layout_width<br>layout_height     |  設定寬跟高的大小       |
    | padding(內間距) |  1.此元件對元件內部的間距<br>2.通常用於要改變按鈕、內容大小，會影響元件wrap_content的大小|
    | layout_margin(外間距)   | 1.此元件對元件外部的間距<br>2.通常用於要有間格距離時，不會改變內容大小 |
    | gravity (內重心)<br> layout_gravity(外重心) | 內重心：將其子元件設到其重心位置，基本上寬高跟父類時使用。<br>外重心：將其重心位置對應父元件的相對位置，基本上寬高跟內容時使用。                               |
    | hint  |  該欄位提示文字(輸入框)，不會影響輸入。       |
    | text  |  該欄位顯示文字，可以後面用set去設定，要注意如果是輸入框會影響到輸入的值。 |
    | text.size  |  該欄位顯示文字大小。 |
    | id  | 這個元件的變數名稱(縮寫+功能名)，後面可用R.id.這個名稱找到他，要注意要是被導入的layout，其id名稱才有效。（因為findViewById 是吃layout裡的view，所以要在該layout內的id才有效。  |
    | inputType  | ★editText使用，用來決定輸入時要跳出什麼鍵盤EX:`Editor.Info.TYPE_CLASS_NUMBER`開啟數字鍵盤  |
    | weightSum <br> layout_weight (分配比重)  | 1.子元件與父元件(LinearLayout)的分配，將這段距離（剩餘的空間）按weightSum切分，看其比重占多少。<br>2.weightSum只有LinearLayout有，layout_weight都有。<br>3.比重可以有小數點。<br>4.如果沒設weightSum，預設就是weight的總和，ex:有三個元件weight設1，就會剛好分三等份。<br>5.要注意比重分配並不代表間距會自動隔開，還是要靠margin去調整。<br>6.layout_weight設定後，有設定layout_weight的子元件，需把對應方向的大小設定為0dp（vertical->height；Horizontal->width)，不可以將對應方向大小刪除，將會造成錯誤 |

補充資料：[XML屬性大全](https://developer.android.com/reference/kotlin/android/widget/TextView?hl=en#xml-attributes)

---
## 五、撰寫程式邏輯- Activity & intent
1. override 要設定的地方（ex:onCreate、onResume） 
2. 創建該activity的元件實體（ex:TextView、Button）
3. 對元件編寫程式邏輯（ex:btn按下後要做什麼事情、設定tv顯示的文字）
```kotlin=
class MainActivity : AppCompatActivity() {
    //1.override 要設定的地方
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_bmi)      //設定畫面要顯示什麼，xml這時候才被抓取出來使用

        // Log.d("test","Button Login Clicked");// Debug印資訊使用-測試用
    
    //2.創建該activity的元件實體
        var etHeight = findViewById<EditText>(R.id.etHeight)
        var etWeight = findViewById<EditText>(R.id.etWeight)
        var btnBmi = findViewById<Button>(R.id.btnBmi)
        var tvBmi = findViewById<TextView>(R.id.tvBmi)

    //3.對元件編寫程式邏輯ex:設定按下button後的邏輯及textview顯示的文字。
        btnBmi.setOnClickListener( View.OnClickListener {
            var height = etHeight.text.toString().toDouble() / 100.0
            var weight = etWeight.text.toString().toDouble()
            var ans = String.format("%.2f", (weight / (height * height)))
            tvBmi.text = "BMI: " + ans
        })
    }
}
```

### Intent（跳轉頁面） - 廣播系統
1. 先創建另一個activity->layout可將code改過去，記得要修正`tools:context=".LoginActivity">`。
2. manifests看哪個要設成首頁、起始頁，把intent 放進去`<activity><intent-filter>...</activity>`。
3. 在要移轉的地方，撰寫：
    1. 宣告要跳轉的頁面，放入（目前的activity實體, 要跳轉的頁面.class），這邊還是要看之後要跳轉什麼決定放什麼參數。 ex: `intent(Context packageContext, class<?> cls)`
        * activity實體：lambda 可以只打this，如果是匿名內部類就要打類名.this。
        * 要跳轉的activity.class：是用反射的方式幫你把class new出來。
    2. `startActivity(intent)`：執行跳轉的動作。
    3. `finish()`：再執行完跳轉後，把自己關掉->這裡只是告知系統我可以被關掉了，並不是真的自己結束，會等系統GC，如果沒有設定，那就代表剛打開的頁面，按下上一頁時會回到這裡（因為是stack）。 
    ```java=
        //1.宣告要跳轉的頁面
         Intent intent = new Intent(LoginActivity.this, MainActivity.class);
            //目前這個intent 代筆著從login跳到main。
            
        //2.執行跳轉的動作。
            this.startActivity(intent); //跳轉到main
            
        //3.//把登入頁面關掉，但是要等到系統把它刪掉，他只是告知系統我沒用了
            finish(); 
    ```
4. startActionMode: 是用來處理有需要背景控制的時候使用，可以設定要不要關掉前一個頁面等等的細節。
5. 跳轉頁面過去後會觸發該activity的onCreate()。

補充資料：[跳轉頁面帶值](https://www.itread01.com/p/1368293.html)

---
## 六、撰寫網路連線功能
1. 使用第三方套件OkHttp，核心在request。
2. 環境建置：
    1. 用gradle 安裝OkHttp(Releases) ->放到deoendencies->會跳出小大象，案同步(Sync Now）及會安裝。`implementation("com.squareup.okhttp3:okhttp:4.9.0")`
    2. 開啟網路權限：manifests->在manifest及application之間加入`<uses-permission android:name="android.permission.INTERNET" />
`、`<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />`
3. Request裡面的get.post，代表的是postman的get.post。
4. Client.newCall的enqueue - 非同步，異步，所以一定有Callback 才能知道要幹嘛； execute -同步，結束後在下一行，**基本上都要用enqueue，不然會錯誤**。
5. 使用方式：
    1. ++創建request實體：設定url->get或post->build()。++
    2. 如果是get要走params，就在url直接加/:變數名稱/value即可；post看下方4.5。
    3. ++運行request(記得要用enqueue)及撰寫其callback func（實現OkHTTP的Callback介面)++`client.newCall(request).enqueue(new Callback()...)`
        1. `@NotNull`代表該值不能是null，不然就會報錯。
        2. onFailure：如果連線失敗時，會把call傳回來，所以可以重試或做其他處理。
        3. onResponce：成功時，會回傳結果回來，**`response.body().string()`：接收後端(Server)給的資料，但這個function一次請求就只能夠呼叫一次，第二次時就會報錯，所以一定要用一個變數存取下來。**
        4. 如果是json格式，要再利用JSONObject轉成json物件後處理。
    4. 如果是post -JSON版：
        1. 先設置JSON的常數(MediaType JSON)`MediaType.get("application/json; charset=utf-8")`。
        2. 利用java內建的JSONObject類取得json物件，輸入JSON內容後（put），會幫你轉成JSON物件。
        3. 製作RequestBody，利用`create(jsonObject.toString,JSON)`，將json物件包裝成MediaType。
        4. 放入post裡面。
        5. JSON要用try...catch。
    5. 如果是post - form-data版：
        1. 不用設常數
        2. 製作RequestBody，利用`RequestBody requestBody = new FormBody.Builder().(jsonObject.toString,JSON)`，用add將資料包成form-data。
        4. 放入post裡面。
        5. 不用try..catch
    6. 如何選擇，就是看後端SERVER如何收取資料(Body的資料格式）。
 6. ★基本上會開一類NetworkController，做成單例+橋接器模式：利用三個Interface + 一個中介層 + 一個靜態類別(實現Callback(OkHTTP的)但不一定，要看狀況決定。
  ![](https://i.imgur.com/XfgIFEX.png)
7. NetworkController總結-請依照實際所需製作，格式不同、傳入、出的參數、interface做的事情都可能也不同：
    1. 先做好導入OKHTTP的前置作業-皆在OKHTTP官方文件：
        1. manifests要加入uses-permission(OKHTTP)
        2. build.gradle要先導入OKHTTP
        3. 設定常數（網路連線的Header）
    2. 製作單例(因為enqueue，如果不用就不會在同個queue裡面)：
        1. 擁有靜態自己
        2. 創建子
        3. instance
    3. 製作interface*3(傳失敗、成功、完成的function)分開做是為了可以寫成lambda。
    4. 製作橋接器`CallbackAdapter`（用來將自己的Callback包裝成OKHTTP可以接受的Callback）：
        1. 實現OKHttp的Callback。
        2. 擁有失敗、成功、完成的interface-到時候會由中介層傳入這個方法。
        3. 寫好三階段要做的事情（這裡是指固定情況都必須要跑的事情，ex:送出錯誤資訊，response時再次確認自己伺服器上的錯誤代碼，是不是真的正確，因為OKHttp的response抓得很鬆-基本上只是看有沒有連成功。
        4. 寫時要注意都要判斷是否null不然會程式掛掉。
    5. 製作中介層`CallbackMiddle` - 實際上設定、呼叫方法的地方：
        1. 擁有Request、失敗、成功、完成的interface。
        2. Request為創建時擁有-設定（networkController）各項需求時會創建中介層。
        3. 給外面（main)設定失敗、成功、完成方法，每個設定都回CALL自己（builder概念）
        4. 最後使用他，`call.enqueue()`，會在括號內放入`new CallbackAdapter(三個interface)`，因為要先用橋接器幫他轉換成OKHTTP可以接受的Callback。
    6. 撰寫API的內容：
        1. 設定body的內容（如果是get不用設）：`RequestBody x =  FormBody.Builder()`用add放入內容，最後build()。
        2. 設定request（路徑、token、資料-原本postman在做的事情），用Request的builder，url放網址，post(requestBody)/get，最後builder()。
        3. 回傳給中介層。
    7. 整個呼叫流程：
        1. 在main呼叫.apiName後完全通過中介層操作。
        2. 所以只需在NetworkController裡撰寫api(postman的設定）即可。
    8. EX:須兼容JSONObject及JSONArray時，將onResponseInterface，改回傳出String，在外面撰寫時在決定他要被New什麼資料格式，然後做事情。
    9. 最大重點就是要彈性的使用，一切都可能可以改變，要理解哪邊是為了要達到什麼功用而製作的。 
8. 最小限制String 但也可能要直接傳Response
9. 傳入參數request，從OKHttp角度思考，在包裝一層，為了要吻合所有可能的api，因為為了符合我們自己的規範，才會製作networkcontroller。

 
補充資料：[OkHTTP](https://square.github.io/okhttp/)、[範例code-App-java-NetworkController](https://www.notion.so/Android-code-0abf76b0d303451c9f0bcc3995bd1ee7)、[網路連線權限](https://developer.android.com/training/basics/network-ops/connecting?hl=en)

---
### 橋接器設計模式 Adapter Pattern (Design Pattern) 
1. 目的：是將A類別轉為B類別，使其可以給B類別用：建立一個可以被重複使用的類別，不管哪種input實現對應的轉接方法即可使用。
2. 使用時機：需要使用現有的類別但不符合系統所需時。
3. 實現方式一：
    1. Adapter繼承A類並且實現轉換目標的方法。
    2. 要轉換的目標只能是介面。
    3. 不用A類別直接實現介面，是怕破壞A類別本身的結構。
4. 實現方式二(較優)：
    1. Adapter持有A類別，在實現目標方法。
    2. 目標可以是介面或類別，彈性較高。
    3. 可以隱藏A類別原有方法，避免被破壞封裝性。 
5. 實現方式三：
    1. 將Adapter做成抽象類別，並利用泛型來限制轉換輸入的類別範圍（抽象類別+泛型+實現目標方法介面-不寫實現，給繼承人寫）。
    2. 使用者另創一類繼承Adapter並實現目標的方法。 

---
## 七、RecycleView
### 序-第一階段
1. 用來取代ListView所使用的套件，ListView不會回收View所以會很耗資源，因此資料有多長（顯示過），就會有多長。
2. 而RecycleView則會在上下各預留1~2個，如果完全超過畫面，就會把格子拿到上或下，更改裡面的資訊，在顯示，因此總長度就是畫面可顯示的長度+預留的個數，很省資源。
3. RecycleView本身是沒有放大縮小的功能，通常也不會有。
4. 示意圖：
    ![](https://i.imgur.com/JfdgGcI.png)
5. 後端要處理的分頁API，不管是下拉更多，或是按下一頁都是同樣概念。
6. 當使用RecyclerView，必須指定LayoutManager來指定顯示的模式，有三種Manager：
    1. LinearLayoutManager(橫向/縱向的列表)
    2. GridLayoutManager(方格式的列表)
    3. StaggeredGridLayoutManager(瀑布流式的列表)-格子有大有小。
7. LinearLayout建構子有三種：
    1. 預設：Context
    2. 小更動：放入Context,方向(預設是垂直Vertical），boolean reverseLayout(是否要新資料放在上面，預設是false)
    3. 自定義：99%不會用到。
    4. Context 是 Activity的父類，跟使用者有交互的意思，使用上放this代表自己即可。
8. 自己寫的東西建議不要存context 因為很容易導致有指向，會沒辦法GC
9. 實現方法：
    1. 先開立處理的類別（ProductListAdapter)繼承RecyclerView.Adapter<RecyclerView.ViewHolder>抽象類別，並實現其方法。
    2. 在要放入RecycleView的Activity layout放入recycleview
    3. 開立新的layour(item_list_product)，改成LinearLayout，並設置上間距（這樣資料才會有間格），在放入一個textview（當作資料顯示區-格子樣式），也就是這個textview會把recycleview的每個item替換成這個格式。
    4. 在類別中加入要做的資料庫，可以是任何集合。
    5. onCreateViewHolder： 在畫面還沒超過前會跑的內容，主要功能是創建自己刻好的實體。
    6. onBindViewHolder：畫面更新時要做的事情，在畫面滑動時、創建時、自己呼叫時會觸發（`                ProductListAdapter.this.notifyDataSetChanged();//更新全部`、`ProductListAdapter.this.notifyItemChanged(getAdapterPosition());//單一個更新，會有內建動畫`。）
    7. 創建自己的ViewHolder繼承RecycleView.ViewHolder，
    8. 在要使用的地方，先設定LayoutManager的格式`LayoutManager lay = new 三種選一個(this)`，在對Layout(LinearLayout) `setLayoutManager()`。
    9. 在創建ProductListAdapter，並設定adapter。
    10. 如果要在不滑動的情況下更新畫面，可以把textview設定點擊事件，在用notify去更新數值（ArrayList裡的）。
    11. 要開立該類別的Class，可以先在一開始放空的ArrayList進去Adapter後，最後在設定，設定完成後跑一次更新（`adapter.notifyDataSetChanged`）-ArrayList 及 Adapter的型態都應該是該class，而不是JSONObject等等 。
10. ViewHolder：是用來包裝View使用的，會有內建的方法。
11. ViewHolder會設成泛型是為了讓下方創建時可以不用多型向上，然後再更新時又要向下強轉型，可能會有問題，所以將工具設成泛型就不用強轉了。
12. viewholder可以拿到位置。
13. inflate viewGroup = view的集合，inflate的viewGroup就是他的爸爸。

### 兩種ViewHolder
1. CommandAdapter、IType、BaseViewHolder、Factory
2. CardView 裡面只能放一個LinearLayout


補充資料：[context](https://spicyboyd.blogspot.com/2018/04/appcontext.html)、[LayoutInflater](https://spicyboyd.blogspot.com/2018/03/app-layoutinflater.html)

---
## 八、Dialog（彈出視窗）
1. popwindows 可自定義較高（ex:教學提示），dialogs則是會彈出在畫面中央，時間、日期選擇器（還是可以自定義，但會偏向用dialogsFragment）。
2. Button ： 預設有確定、取消、其他，最多只能增加三顆，會預設右下角。
    * Positive ：確認按鈕
    * NegativeButton：取消按鈕-預設會關閉視窗，不要的話就要自己傳view。
    * Neutral：自定義功能按鈕。
3. dismiss預設是true（案其他地方就會關閉 
4. 可以自定義setView，設定完後要：
    * show（先創建再顯示），每次返回時就會重新創建，較耗效能。
    * create，先創建，之後要顯示時在該實體.show()即可<- 這個時候會判別是否已存在，有存在直接顯示(dialog的方法）
）
5. view可以傳id，但只能用於靜態資料-沒辦法設定行為事件，傳view才可以。
6. 有AlertDialog，比較多自行設定的內容以及
    * TimePickerDialog 選擇時間
    * DatePickerDialog 選擇日期
    * 以上都可以設預設的時間、日期，目前會自動記憶，如果要清除就是讓他每次重建。
    * 裡面都有個set的方法，如果有需要預設其他資訊、事件，就可以在此設定（也可以是空）。
7. 有用AlertDialog，inflate的viewgroup一定要設null，因為alertdialog會幫忙綁定，重複綁定會錯誤。
8. 關鍵字：Dialog、Dialogs、AlertDialog、AlertDialog.Builder、TimePickerDialog、DatePickerDialog

補充資料：[AlertDialog.Builder設定](https://developer.android.com/reference/android/app/AlertDialog.Builder)

---
## 九、SharedPreference與Intent_Bundle
1. SharedPreference 記錄使用者資訊用，紀錄較小的資訊（大約1.多MB），ex: 使用者密碼，如果是陣列盡量就用其他方式（如讀寫檔、資料庫）。 
2. ★會在本地存資料，也就是在關閉app之後在打開時，都還是會保留資料!!
3. 創建是用`getSharedPreference(String(Key), MODE_PRIVATE)`。
4. 使用時一定要用editor才能修改資料，裡面就像HashMap，所以同樣的key值會被覆寫。
5. 寫完後一定要`commit`或`apply`，差別在：
    * commit: 在主執行緒做，因此可能延遲，但可以確保資料被正確存入、讀取。 ex:login
    * apply： 在副執行緒做，速度也不會太久，因此常用在登出功能及無須立即使用該值的時候。
6. 要取資料，先取得該Key的實體-跟2創建時同名，之後在用get資料型態(Key)拿取資料，像是JSONObject拿資料，ex:`getString("ACC")`。


### Intent & Bundle
1. Intent可以用來在畫面間傳遞資訊-APP關閉資料就不會再了。
2. 利用Bundle包裝資料後`bundle.putString("TOKEN", token)`，
放到Intent傳給下一個畫面（本身還是可以傳很多型態）`.putExtras(bundle)`。
3. Bundle可以put很多類型，一樣是個Map，如果是要放已經包裝好的類（較複雜的資料），那就要讓類實現Parcelabe 或 Serializable，差別在於：
    * Parcelable：較複雜、需實現方法，但可自動產生，可以呼叫後，**填入資訊（要有資料怎麼存，跟怎麼轉回來的方法）**、但效能較好，且可以傳Array。
    * Serializable：簡單、只要實現即可無須新增方法，但會怕寫檔，且效能較差。 
4. 就算只有一個資料也是可以用Bundle包。
5. 收取上個場景傳到的資料，像是JSONObject拿資料：
    1. 用Bundle(或你傳的型態)接收 
`Bundle bundle = getIntent().getExtras()`
    2. 再把資料取出：
`String token = bundle.getString("TOKEN")` 

---
## 補充知識JSON
1. 每間公司的資料格式都不一樣，要先確認格式，有無在用物件包裝，是否是陣列等等。


補充資料：[JSONPlaceholder(測試api資料庫)](https://jsonplaceholder.typicode.com/)



###### tags: `Android`、`CMoney7th-戰鬥營學習筆記`