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