# 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 (生命週期)

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的)但不一定,要看狀況決定。

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. 示意圖:

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-戰鬥營學習筆記`