Android App === ### Tutorial 線上課程 - [Google Developer 教學文: Build Android Basics in Kotlin](https://developer.android.com/courses/android-basics-kotlin/course) - [UDACITY Google 教學 - Developing Android Apps with Kotlin (intermediate)](https://www.udacity.com/course/developing-android-apps-with-kotlin--ud9012) ### Kotlin 官網資源 - [Kotlin official site](https://kotlinlang.org/docs/getting-started.html) - [learn Kotlin by example code](https://play.kotlinlang.org/byExample/01_introduction/01_Hello%20world) ### kotlin syntax 一覽 - **String:** 只能用 `"`(雙引號),不能用 `'`(單引號),或是反引號 - **Main:** 一隻 kotlin 程式,一定要有 `fun main()` - **Fun** 傳入參數方式: `<var-name>: <Var-type>` (記得 類型 的首字大寫) ```kotlin= fun Sum (val a: Int, val b: Int): Int { return a + b } ``` - **Class:** ```kotlin= class Dice (val sides: Int) { fun rollDice() { // ... } } // class 繼承、抽象化: abstract class foo { // parent class abstract val <val-name>: Int } class bar : foo() { // child class override val <val-name> // ... } ``` - **Final:** 的用法 (Java 才有的) - [final variable, final class, final method 的差別](https://techvidvan.com/tutorials/java-final-keyword/) - **initialized** 初始宣告變數的方法 - [2 ways to initialize variable in kotlin](https://stackoverflow.com/questions/33849811/property-must-be-initialized-or-be-abstract) - **isNotEmpty()**: [kotlin isNotEmpty 和isNotBlank的區別](https://blog.csdn.net/bobby_fu/article/details/82855505) ### Android 教學 1. **minimun-sdk:** 運行你的程式需要的最小安卓版本 6. 獲取圖庫裡的圖: `R.drawable.<img-name>` 7. `xmlns`: xml namespace 8. `EditText`: 相當於 html 裡面的 \<input> 9. [Activity 的 Launch Mode](https://hit-alibaba.github.io/interview/Android/basic/Android-LaunchMode.html) - standard、singleTop、singleTask、singleInstance 11. [Android 串接 第三方 sdk](https://wiki.vpon.com/zh-tw/android/integration-guide/#manual-sdk) - 把 .aar 放入 `lib` ##### View: 1. 畫面上任何一個物件 (按鈕、文字方塊等) 稱為 `View` 4. 多個 View 集合成一個 `ViewGroup`,如 `ConstraintLayout`, `LinearLayout` ![](https://i.imgur.com/GMXNlrC.png) 4. 獲取視圖上的物件: `findViewById(R.id.<obj-id>)` - `R`: 表示 從 `Resource` 裡面拿的物件 5. 獲取物件的第二種方式: `viewBinding` ![](https://i.imgur.com/fKihW4N.png) ##### Click event listener: ```kotlin= val btn: Button = findViewById(R.id.button) btn.SetOnClickListener{ // do something where btn was clicked... } ``` ##### Constraint: - 一個物件的左邊該如何對齊: `app:layout_ConstraintStart_...` - 右邊: `app:layout_ConstraintEnd_` - 上面: `app:layout_ConstraintTop_` - 下面: `app:layout_ConstraintBottom` - 物件的上面對齊父元素的上面: `layout_ConstraintStart_toStartof="parent"` - 物件的左邊對齊父元素的左邊: `layout_ConstraintStart_toStartof="parent"` - 物件的上面對齊A元素的下面: `layout_ConstraintTop_toBottomof="@id/<A-id>"` - 注意: 如果是阿拉伯文,文字是從右到左。start 就變成右邊、end 變成指左邊 ##### layout_width, layout_height: - `wrap_content`: 內容多大就有多大 - `match_parent`: 延伸到跟父元素貼齊的大小 - 在 `ConstraintLayout` 裡無法使用。若想要100%大填入 `0dp` - 然後填 `constraintStart_toStartof="parent"` - 還要填 `constraintEnd_toEndof="parent"` ##### ViewBinding 1. 先到 `build.gradle (module level)` > `android` 裡面新增: ```kotlin= android { // ...ignore buildFeatures { viewBinding true } } ``` 2. 在 `MainActivity.kt` 裡面修改: ```kotlin= class MainActivity : AppCompatActivity() { // 新增這行 // lateinit: a promise that init a var before using it lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 新增下面兩個 // 作用: access all views in activity_main.xml // access .xml 檔案的方式: Pascal case + Binding // ex: activity_main.xml => ActivityMainBinding // 獲取 @id/text_view 物件: binding.textView binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) } } ``` ```kotlin= // Old way with findViewById() val myButton: Button = findViewById(R.id.my_button) myButton.text = "A button" // Better way with view binding val myButton: Button = binding.myButton myButton.text = "A button" // Best way with view binding and no extra variable binding.myButton.text = "A button" ``` --- ### Activity 的生命週期 ![](https://i.imgur.com/pW2oZ2O.png) - 每一個 activity 都有自己一個生命週期 - 開啟 activity 的第一個進入點是 **onCreate()** - 隨後進入 **onStart()**,負責可視化此 activity 的畫面,及初始化畫面上的所有元件 - **onResume()** 使用者持續使用 ### Thread - Android 手機上有一隻 main-thread (或稱 UI-thread),負責渲染目前手機所運行程式的畫面 - 如果一個很費時的功能運行在 main-thread 上,手機會整個卡住不動,直到此功能結束 (如: 讀寫DB,下載檔案)。因此他們得分開在其他 thread 上執行 (非同步) - Create thread 的方式: ```java= // 第一種 class MyThread extends Thread { @Override public void run () { // do something... } } // 第二種 class MyThread implements Runnable { @Override public void run () { } } ``` - create 完以後要執行 thread 的方式: ```java= new MyThread.start() ``` - thread 運行期間,thread 自己不能改變 UI 元件,若在執行期間想改變 ui 內容,須用 `Handler` 或 `runOnUIThread` - If the current thread is not the UI thread, the action is posted to the event queue of the UI thread. ```java= new Thread(new Runnable() { @Override public void run() { yourActivity.runOnUIThread(new Runnable() { @Override public void run() { } }) } }).start(); ``` - thread 的生命週期 ![](https://i.imgur.com/f4ZXlWt.png) ### MessageQueue - 一個裝載多個 tasks (messages, runnables) 的 queue,由 Main-thread 負責控制 - 負責派遣任務到 MessageQueue 的是 `Looper` - `Looper.myqueue()` 可見目前的 messages 一覽 ### Looper ![](https://i.imgur.com/eLuRORn.png) - 一個 thread 只有一個 Looper,負責管理 MessageQueue - 一個 thread 也只有一個 MessageQueue - Handler 不會只有一個。負責把 Message 或 Runnable 傳入 Queue,等 Queue 把要處理的 Message pop 出來後,又再交給 Handler 負責處理 ### Handler 的應用: - thread 之間原本是無法互相影響的,需要額外 method 介入輔助 - 當開始下載程序 (假設你開了一個thread 叫 DownloadThread),也等於創建了另一個新的 thread (不同於 UI-thread) - 在 Downloadthread 呼叫 `MainActivity.statusTextView.setText` 會產生錯誤。因為 MainActivity 的 View 不是 DownloadThread 創建的 ,而是 ui-thread - 如果你下載完了,要在畫面顯示 "下載完畢" 該怎麼做? 1. 在 ui-thread 開一個 Handler (此 handler 就會屬於 ui-thread 的) 2. 在 DownloadThread 中,下載完畢時呼叫: ```java= UiThreadHandler.post(new Runnable() { @Override public void run() { // display: dl finished! } }) ``` - [以上內容來源](https://codertw.com/android-%E9%96%8B%E7%99%BC/352565/) - [以上資料來源](https://betterprogramming.pub/a-detailed-story-about-handler-thread-looper-message-queue-ac2cd9be0d78) - [create handler in Kotlin](https://stackoverflow.com/a/58747929) --- ### 其他 - [onActivityResult](https://stackoverflow.com/questions/52652998/error-onactivityresult-overrides-nothing) - [intent with value](https://ithelp.ithome.com.tw/articles/10207911) - [setOnClickListener](https://stackoverflow.com/questions/55128123/kotlin-setonclicklistener-for-a-listview) - [Enable Bluetooth](https://stackoverflow.com/questions/9424756/android-bluetooth-enable) - [android version](https://ithelp.ithome.com.tw/articles/10227700?sc=rss.iron) - [when to use Handler and Thread](https://stackoverflow.com/questions/13954611/android-when-should-i-use-a-handler-and-when-should-i-use-a-thread) - [ListView tutorial kotlin](https://www.raywenderlich.com/155-android-listview-tutorial-with-kotlin) - [setEnabled() vs setClickable()](https://stackoverflow.com/questions/15615823/setenabled-vs-setclickable-what-is-the-difference) - [myLooper() vs getMainLooper()](https://stackoverflow.com/questions/34322498/android-mylooper-vs-getmainlooper) ------ ### pull to refresh - [SwipeRefreshLayout 下拉重載](https://mnya.tw/cc/word/1520.html) ### Permission - [How to Request Permissions](https://www.geeksforgeeks.org/android-how-to-request-permissions-in-android-application/) ### Get Location - [Getting Current Location (不會即時更新)](https://www.androidhire.com/current-location-in-android-using-kotlin/) - [location updates in a Service](https://stackoverflow.com/a/15067755) - [How to get continuous location updates](https://howtodoandroid.com/continuous-location-updates-android/) ### Service - [Pass data from Service to Activity](http://android-coding.blogspot.com/2011/11/pass-data-from-service-to-activity.html) - Service 要先去 Manifest 裡面宣告 ### Pass Context / non-Activity class 相關問題 - [如何在 non-Activity class 中使用 getSystemService](https://stackoverflow.com/a/24858651) - 改成: `context.getSystemService` - `context` 要從外面當 parameter pass 進來 - [pass a context as a parameter](https://stackoverflow.com/a/50766894) - 在 MainActivity 實例化的時候 `myClass(this)` - 在 myClass 裡面: ```kotlin= class myClass (context: Context) { val mContext = context fun foo () { // use mContext here } } ``` - [startService from class that does not extend Activity](https://stackoverflow.com/a/13882190) - 想在 non-Activity 中使用 startService - 一樣改成 `context.startService` - 一樣 context 要事先傳入 ### 常見問題 - [點了桌面 icon 後 App 又會再重啟一次](https://stackoverflow.com/a/17546878) ### 地圖 - [mapbox v.9](https://docs.mapbox.com/android/legacy/maps/guides/install/) ### WeakReference 是什麼 - [WeakReference 與多執行緒的 Handler](http://wannadoitnow.blogspot.com/2015/07/android-weakreference-handler.html) ### 類似 interval 的功能: CountDownTimer - [如何使用CountDownTimer](https://givemepass.blogspot.com/2011/12/blog-post_08.html) ### Firebase realtime database - [Read and Write Data](https://firebase.google.com/docs/database/android/read-and-write) - [iterate over firebase data object to list](https://stackoverflow.com/a/45038340) ### Date, Time 用法 - [Kotlin - Convert Unix Timestamp to Date 時間戳記的轉換](http://hulkyang.blogspot.com/2019/06/kotlin-convert-unix-timestamp-to-date.html) - [Get current time and date](https://stackoverflow.com/questions/5369682/how-to-get-current-time-and-date-in-android) ### Activity, AppCompatActivity - [Activity 和 AppCompatActivity 部分區別](https://www.twblogs.net/a/5ca65e4abd9eee59d332f682) ### 在 build.gradle & settings.gradle 使用 gradle.properties 1. 在你的電腦,進入 `C:\Users\user\.gradle` 資料夾內 2. 新增一個檔案 `gradle.properties` 3. 把要新增的變數放進裡面,如: `APP_KEY=abc123` 4. 要使用的話直接呼叫 `APP_KEY` 即可 - [gradle.properties 變數使用詳解](https://www.itread01.com/content/1544422502.html) ### Flutter 連接 third party sdk - [Bridging Between Dart and Native Code with Flutter Channel for Communicate Each Other](https://pahlevikun.medium.com/bridging-between-dart-and-native-code-with-flutter-channel-for-communicate-each-other-7c736929ee42) - [Integrating native third-party SDK in Flutter](https://medium.com/@jessanirahim/integrating-native-third-party-sdk-in-flutter-8aab03afa9da) # Firebase Anonymous Auth with Rules ### 前置須知 1. 要使用 realtime-databse 要先建立規則 (rules) 2. 規則制定一定要包含 auth.uid (使用者帳號) 3. app 端上傳資料時也得包含 auth.uid,所以需要做註冊功能 ### 教學 1. [怎麼寫規則 (rules)](https://www.oxxostudio.tw/articles/201904/firebase-realtime-database-rules.html) 2. [寫 url 裡不包含 uid 的特殊規則](https://stackoverflow.com/a/53153565) 3. 規定新資料得包含 *** `newData.child('account').val() == xxx` 4. [app 端要啟用註冊功能](https://firebase.google.com/docs/auth/android/anonymous-auth) - 之後要獲取用戶資訊,用 `Firebase.auth.currentUser` --- ### Lambda - [lambda function 宣告方式](https://developer.android.com/codelabs/basic-android-kotlin-training-collections?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fandroid-basics-kotlin-unit-3-pathway-1%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fbasic-android-kotlin-training-collections#3) - [一般 function 宣告方式](https://kotlinlang.org/docs/functions.html) ```kotlin= // 1. fun fun triple (x: Int): Int { return 3*x } // 2. lambda val triple: (Int) -> Int = { x: Int -> 3*x } // 3. lambda val triple: (Int) -> Int = { 3*it } ```