AWS 入門

建置 Android 應用程式

使用 AWS Amplify 建立簡單的 Android 應用程式

第一單元︰建立和部署 Android 應用程式

在本單元中,您將建立一個 Android 應用程式,並使用 AWS Amplify 的 Web 託管服務將其部署到雲端。

簡介

AWS Amplify 提供一個 Git 型工作流程,用於針對 Web 和行動應用程式建立、管理、整合和部署無伺服器後端。Amplify CLI 提供一個簡單的文字型使用者界面,以佈建和管理後端服務,例如針對應用程式的使用者身份驗證,或者 REST 或 GraphQL API。Amplify Libraries 讓您能夠輕鬆地將這些後端服務與應用程式中的幾行程式碼整合成在一起。

在本單元中,我們首先將建立一個新的 Android 應用程式,以記錄操作附註。附註由標題、描述和圖片組成。我們將在後續單元中增強此應用程式。

您將學到的內容

  • 建立 Android 應用程式
  • 更新主要檢視以顯示項目清單
  • 建置和測試應用程式

主要概念

Kotlin - Kotlin 之所以被選為本教學的程式設計語言,是因為它可以減少很多未定案程式碼,並且在設計時考慮了類型安全性。

Jetpack - 本教學使用 Android 的 Jetpack,這是一個 Android 程式庫集合,該程式庫整合各種最佳實務,並在 Android 應用程式中提供向後相容性。

 完成時間

10 分鐘

 使用的服務

實作

  • 建立 Android 專案

    啟動 Android Studio,然後從初始螢幕中選取 Start a new Android Studio project (啟動新的 Android Studio 專案)︰

    AndroidAppTutorial_Modiule1_Image1

    Phone and Tablet (手機和平板電腦) 下,選取 Basic Activity (基本活動),然後按一下 Next (下一步)

    AndroidAppTutorial_Modiule1_Image2

    輸入專案的名稱,例如 Android 入門。確保語言為 Kotlin,並且最低 SDK 為 API 26: Android 8.0 (oreo),然後按一下 Finish (完成)

    AndroidAppTutorial_Modiule1_Image3

    現在專案的框架已存在,我們需要 4 個步驟來使基本應用程式執行:

    1. 刪除不需要的類別和檔案並新增外掛程式
    2. 建立類別以儲存我們的資料結構
    3. 建立檢視以在清單中儲存個別附註
    4. 修改 MainActivity 以顯示附註清單
  • 刪除不需要的檔案並新增外掛程式

    在 res/layout 和 java/com.example.androidgettingstarted 下,刪除四個片段檔案 (選取這 4 個檔案,按一下滑鼠右鍵,然後從內容功能表中選取 Delete (刪除)):

    AndroidAppTutorial_Modiule1_Image4

    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 作為名稱。

    AndroidAppTutorial_Modiule1_Image5

    將下面的程式碼貼至剛建立的新檔案 (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 類別。個別儲存格如下所示:

    AndroidAppTutorial_Modiule1_Image6

    若要建立版面配置檔案,在 res/layout 上按一下滑鼠右鍵,然後選取 New -> Layout Resource File (新建 -> 版面配置資源檔案)。鍵入 content_note 作為名稱,並保留所有其他值,因為我們將直接編輯 XML 檔案。

    AndroidAppTutorial_Modiule1_Image7

    開啟新建立檔案的程式碼檢視。

    AndroidAppTutorial_Modiule1_Image8

    在剛建立的新檔案 (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 作為名稱。

    AndroidAppTutorial_Modiule1_Image9

    將下面的程式碼貼至剛建立的新檔案 (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

    AndroidAppTutorial_Modiule1_Image10

    一段時間後,該應用程式將在 Android 模擬器中啟動,並顯示初始空白螢幕。

    AndroidAppTutorial_Modiule1_Image11

    在此階段,沒有可在執行時間呈現的資料。我們將在後續步驟中刪除郵件圖示。

結論

您已成功建立一個 Android 應用程式。您可以隨時開始使用 Amplify!

這個單元對您是否有幫助?

感謝您
請告訴我們您喜歡的部分。
關閉
抱歉,讓您失望
是有內容過時、令人困擾,或不準確嗎? 請提供意見回饋,協助我們改進此教學課程。
關閉

初始化 Amplify