AWS 入門
建置 Android 應用程式
使用 AWS Amplify 建立簡單的 Android 應用程式
第一單元︰建立和部署 Android 應用程式
在本單元中,您將建立一個 Android 應用程式,並使用 AWS Amplify 的 Web 託管服務將其部署到雲端。
簡介
AWS Amplify 提供一個 Git 型工作流程,用於針對 Web 和行動應用程式建立、管理、整合和部署無伺服器後端。Amplify CLI 提供一個簡單的文字型使用者界面,以佈建和管理後端服務,例如針對應用程式的使用者身份驗證,或者 REST 或 GraphQL API。Amplify Libraries 讓您能夠輕鬆地將這些後端服務與應用程式中的幾行程式碼整合成在一起。
在本單元中,我們首先將建立一個新的 Android 應用程式,以記錄操作附註。附註由標題、描述和圖片組成。我們將在後續單元中增強此應用程式。
您將學到的內容
- 建立 Android 應用程式
- 更新主要檢視以顯示項目清單
- 建置和測試應用程式
完成時間
10 分鐘
使用的服務
實作
-
建立 Android 專案
啟動 Android Studio,然後從初始螢幕中選取 Start a new Android Studio project (啟動新的 Android Studio 專案)︰
在 Phone and Tablet (手機和平板電腦) 下,選取 Basic Activity (基本活動),然後按一下 Next (下一步)︰
輸入專案的名稱,例如 Android 入門。確保語言為 Kotlin,並且最低 SDK 為 API 26: Android 8.0 (oreo),然後按一下 Finish (完成)︰
現在專案的框架已存在,我們需要 4 個步驟來使基本應用程式執行:
- 刪除不需要的類別和檔案並新增外掛程式
- 建立類別以儲存我們的資料結構
- 建立檢視以在清單中儲存個別附註
- 修改 MainActivity 以顯示附註清單
-
刪除不需要的檔案並新增外掛程式
在 res/layout 和 java/com.example.androidgettingstarted 下,刪除四個片段檔案 (選取這 4 個檔案,按一下滑鼠右鍵,然後從內容功能表中選取 Delete (刪除)):
在 Gradle Scripts (Gradle 指令碼) 下,開啟 build.gradle (Module:app) ,然後新增 Kotlin Extension 外掛程式。
plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-android-extensions' // <== add this line }
-
建立一個 UserData 資料類別
UserData 類別保存使用者狀態:isSignedIn 標誌和 Notes 值清單。
若要建立新類別,在 java/com.example/androidgettingstarted 上按一下滑鼠右鍵,然後選取 New -> Kotlin file/class。輸入 UserData 作為名稱。
將下面的程式碼貼至剛建立的新檔案 (UserData.kt)
package com.example.androidgettingstarted import android.graphics.Bitmap import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData // a singleton to hold user data (this is a ViewModel pattern, without inheriting from ViewModel) object UserData { private const val TAG = "UserData" // // observable properties // // signed in status private val _isSignedIn = MutableLiveData<Boolean>(false) var isSignedIn: LiveData<Boolean> = _isSignedIn fun setSignedIn(newValue : Boolean) { // use postvalue() to make the assignation on the main (UI) thread _isSignedIn.postValue(newValue) } // the notes private val _notes = MutableLiveData<MutableList<Note>>(mutableListOf()) // please check https://stackoverflow.com/questions/47941537/notify-observer-when-item-is-added-to-list-of-livedata private fun <T> MutableLiveData<T>.notifyObserver() { this.postValue(this.value) } fun notifyObserver() { this._notes.notifyObserver() } fun notes() : LiveData<MutableList<Note>> = _notes fun addNote(n : Note) { val notes = _notes.value if (notes != null) { notes.add(n) _notes.notifyObserver() } else { Log.e(TAG, "addNote : note collection is null !!") } } fun deleteNote(at: Int) : Note? { val note = _notes.value?.removeAt(at) _notes.notifyObserver() return note } fun resetNotes() { this._notes.value?.clear() //used when signing out _notes.notifyObserver() } // a note data class data class Note(val id: String, val name: String, val description: String, var imageName: String? = null) { override fun toString(): String = name // bitmap image var image : Bitmap? = null } }
我們剛新增了什麼?
- UserData 類別負責保存使用者資料,即 isSignedIn 標誌,用於追蹤目前的身份驗證狀態和附註物件清單。
- 這兩個屬性根據 LiveData 發佈/訂閱框架實作。其允許圖形使用者界面 (GUI) 訂閱變更,並做出相應的回應。若要進一步了解有關 LiveData 的資訊,可以閱讀本文件,或關注此簡短的影片教學。為了遵循最佳實務,請將 MutableLiveData 屬性設定為私有,並且僅公開唯讀 LiveData 屬性。若發佈的資料為清單,則需要一些額外的未定案程式碼,以確保在修改清單中的個別元件時通知觀察者。
- 我們還新增了附註資料類別,僅用於保存個別附註的資料。ImageName 和影像使用兩個不同的屬性。影像將在後續單元中處理。
- 針對 UserData 物件實作 singleton 設計模式,因為其允許僅使用 UserData 從應用程式中的任何地方引用。
-
在清單中為個別儲存格新增 GUI
捲動清單中的個別儲存格稱為 RecyclerView,因為若使用者在螢幕上不再可見,當使用者上下捲動檢視時,可以回收檢視。
就像常規檢視一樣,我們建立版面配置 XML 檔案和 Kotlin 類別。個別儲存格如下所示:
若要建立版面配置檔案,在 res/layout 上按一下滑鼠右鍵,然後選取 New -> Layout Resource File (新建 -> 版面配置資源檔案)。鍵入 content_note 作為名稱,並保留所有其他值,因為我們將直接編輯 XML 檔案。
開啟新建立檔案的程式碼檢視。
在剛建立的新檔案 (content_note.xml) 程式碼檢視中,透過貼上以下程式碼來取代產生的程式碼:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:paddingVertical="16dp"> <ImageView android:id="@+id/image" android:layout_width="100dp" android:layout_height="match_parent" android:padding="8dp" android:scaleType="centerCrop" android:src="@drawable/ic_launcher_background" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginLeft="5dp" android:orientation="vertical"> <TextView android:id="@+id/name" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Title" android:textSize="20sp" android:textStyle="bold" /> <TextView android:id="@+id/description" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Title" android:textSize="15sp" /> </LinearLayout> </LinearLayout>
最後,建立 Kotlin 類別︰在 java/com.example/androidgettingstarted 上按一下滑鼠右鍵,然後選取 New -> Kotlin file/class。鍵入 NoteRecyclerViewAdapter 作為名稱。
將下面的程式碼貼至剛建立的新檔案 (NoteRecyclerViewAdapter.kt) 中
package com.example.androidgettingstarted import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView // this is a single cell (row) in the list of Notes class NoteRecyclerViewAdapter( private val values: MutableList<UserData.Note>?) : RecyclerView.Adapter<NoteRecyclerViewAdapter.ViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.content_note, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = values?.get(position) holder.nameView.text = item?.name holder.descriptionView.text = item?.description if (item?.image != null) { holder.imageView.setImageBitmap(item.image) } } override fun getItemCount() = values?.size ?: 0 inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val imageView: ImageView = view.findViewById(R.id.image) val nameView: TextView = view.findViewById(R.id.name) val descriptionView: TextView = view.findViewById(R.id.description) } }
我們剛新增了什麼?
上面的程式碼由- 版面配置 xml 檔案組成,該檔案描述表示附註的儲存格元件的版面配置。其顯示影像、附註標題和附註描述。
- 支援 Kotlin 類別。其在建立時接收附註資料物件,並將個別值指派給其相應的檢視 (影像、標題和描述)
-
更新主要活動
現在我們有了資料類別 (UserData 和附註) 以及個別附註的檢視 (NoteRecyclerViewAdapter),我們來在主要活動上建立附註清單。
在 Android Studio 左側的檔案清單中,開啟 res/layout/content_main.xml,並將程式碼取代為以下內容:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/frameLayout" android:layout_width="match_parent" android:layout_height="match_parent" > <androidx.recyclerview.widget.RecyclerView android:id="@+id/item_list" android:name="com.example.myapplication.ItemListFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="60dp" android:paddingHorizontal="8dp" android:paddingVertical="8dp" app:layoutManager="LinearLayoutManager" tools:context=".MainActivity" tools:listitem="@layout/content_note" /> </FrameLayout>
在 Android Studio 左側的檔案清單中,開啟 java/com.example/androidgettingstarted/MainActivity.kt,然後將程式碼取代為以下內容:
package com.example.androidgettingstarted import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.content_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) // prepare our List view and RecyclerView (cells) setupRecyclerView(item_list) } // recycler view is the list of cells private fun setupRecyclerView(recyclerView: RecyclerView) { // update individual cell when the Note data are modified UserData.notes().observe(this, Observer<MutableList<UserData.Note>> { notes -> Log.d(TAG, "Note observer received ${notes.size} notes") // let's create a RecyclerViewAdapter that manages the individual cells recyclerView.adapter = NoteRecyclerViewAdapter(notes) }) } companion object { private const val TAG = "MainActivity" } }
我們剛新增了什麼?
- 主要版面配置為 RecyclerView,其管理我們之前建立的個別儲存格清單
- 主要活動類別觀察附註清單上的變更,然後建立 NoteRecyclerViewAdapter 以建立個別儲存格。
-
驗證建置相依項
在 Gradle Scripts (Gradle 指令碼) 下,開啟 build.gradle (Module:app),然後驗證產生的相依項是否正確。需要檢查 `libraries versions`。
```gradle dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.2.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.2' implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1' implementation 'androidx.navigation:navigation-ui-ktx:2.3.1' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' }
-
建置和測試
現在,在模擬器中建置並啟動該應用程式。按一下工具列中的 Run (執行) 圖示 ▶️,或鍵入 ^ R。
一段時間後,該應用程式將在 Android 模擬器中啟動,並顯示初始空白螢幕。
在此階段,沒有可在執行時間呈現的資料。我們將在後續步驟中刪除郵件圖示。