Android Tutorial using Kotlin 第五堂(3)設計小工具元件 - AppWidget by Michael | CodeData
top

Android Tutorial using Kotlin 第五堂(3)設計小工具元件 - AppWidget

分享:

Android Tutorial using Kotlin 第五堂(2)系統通知服務 – Notification << 前情

一般個人電腦或行動裝置的作業系統,可以在桌面上放置應用程式的捷徑,使用明顯的圖示和應用程式的名稱,讓使用者在桌面上直接啟動常用的應用程式。一些使用者經常操作的功能,例如開啟或關閉裝置的網路或藍牙設備,如果可以不用啟動這些設定的應用程式,使用者就可以在桌面上直接操作這些功能,那應該會比較方便一些。

Android平台提供一種特別的元件「AppWidget」,它可以讓使用者在桌面上直接瀏覽資料,或是執行一些簡單的操作。例如在桌面上顯示時間、行事曆或氣候資訊,這種元件通常會把它稱為「小工具」元件。

這一章介紹設計AppWidget元件的作法,它的設計方式跟其它元件很不一樣。完成這一章的工作以後,為記事應用程式加入新的小工具元件。使用者在畫面長按以後,Android會開啟這樣的畫面,選擇「小工具」:

在選擇小工具的畫面,找到為記事應用應用程式設計好的元件,長按以後把它放到畫面指定的位置:

決定小工具的位置以後,元件自動開啟選擇記事的畫面,選擇其中一個記事項目:

畫面上就會放置一個顯示記事標題的小工具元件:

依照同樣的步驟,可以在畫面加入其它顯示記事標題的小工具:

17-1 加入小工具元件

現在開始為記事應用程式加入小工具元件。啟動Android Studio與記事應用程式以後,在「app」目錄上按滑鼠右鍵 -> 選擇「New -> Widget -> AppWidget」,依照下列的說明輸入需要的資訊:

  • Class Name 輸入「ItemAppWidget」。
  • Placement 選擇「Home-screen only」。
  • Resizable(API 12+) 選擇「Not resizable」。
  • Minimum Width (cells) 選擇「4」。
  • Minimum Heigth (cells) 選擇「1」。
  • 勾選「Configuration Screen」。

完成後選擇「Finish」按鈕:

Android Studio會建立許多小工具元件需要的程式碼與設定檔:

  • ItemAppWidget.kt :小工具元件類別。
  • ItemAppWidgetConfigureActivity.kt :小工具設定元件類別,選擇記事項目。
  • res/layout/itemappwidget.xml :小工具元件使用的畫面資源。
  • res/layout/itemappwidget_configure.xml :小工具設定元件使用的畫面資源。
  • res/xml/itemappwidget_info.xml :小工具專用的設定檔。
  • res/drawable-nodpi/exampleappwidgetpreview.png :在小工具選擇畫面顯示的縮圖。
  • AndroidManifest.xml :自動加入小工具元件與小工具設定元件的設定。

17-2 實作小工具設定元件

使用者選擇記事小工具以後,必須先啟動選擇記事項目元件。小工具元件在專用的設定檔可以設定這個功能,開啟「res/xml/item_app_widget_info.xml」,檢視裡面的內容:

<?xml version="1.0" encoding="utf-8"?>
<!--
    android:configure:小工具設定元件類別
    android:initialLayout:小工具元件使用的畫面資源
    android:previewImage:縮圖
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:configure="net.macdidi.atk.ItemAppWidgetConfigureActivity"
    android:initialKeyguardLayout="@layout/item_app_widget"
    android:initialLayout="@layout/item_app_widget"
    android:minHeight="40dp"
    android:minWidth="250dp"
    android:previewImage="@drawable/example_appwidget_preview"
    android:updatePeriodMillis="86400000"
    android:widgetCategory="home_screen"/>

Android Studio會自動產生預設的設定元件與畫面資源,這個設定元件可以重複使用主畫面元件的畫面資源,所以刪除在「res/layout」下的「itemappwidget_configure.xml」。接下來開啟「ItemAppWidgetConfigureActivity.kt」,先清除類別裡面所有的內容,然後加入下面的欄位變數宣告:

package net.macdidi.atk

...

class ItemAppWidgetConfigureActivity : Activity() {

    internal var mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID

    // 選擇小工具使用的記事項目
    private val item_list: ListView by bind(R.id.item_list)
    private val itemDAO: ItemDAO by lazy { ItemDAO(applicationContext) }
    private val items: ArrayList<Item> by lazy { itemDAO.all }
    private val itemAdapter: ItemAdapter
            by lazy { ItemAdapter(this, R.layout.single_item, items) }

    ...

}

同樣在「ItemAppWidgetConfigureActivity.kt」,加入下列的函式宣告:

package net.macdidi.atk

...

class ItemAppWidgetConfigureActivity : Activity() {

    ...

    companion object {

        private val PREFS_NAME = "net.macdidi.atk.ItemAppWidget"
        private val PREF_PREFIX_KEY = "appwidget_"

        // 儲存選擇的記事編號
        fun saveItemPref(context: Context, appWidgetId: Int, id: Long) {
            val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit()
            prefs.putLong(PREF_PREFIX_KEY + appWidgetId, id)
            prefs.commit()
        }

        // 讀取記事編號
        fun loadItemPref(context: Context, appWidgetId: Int): Long {
            val prefs = context.getSharedPreferences(PREFS_NAME, 0)

            return prefs.getLong(PREF_PREFIX_KEY + appWidgetId, 0)
        }

        // 刪除記事編號
        fun deleteItemPref(context: Context, appWidgetId: Int) {
            val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit()
            prefs.remove(PREF_PREFIX_KEY + appWidgetId)
            prefs.commit()
        }

    }

    // 選擇記事項目
    internal var itemListener: AdapterView.OnItemClickListener =
            AdapterView.OnItemClickListener {
                _, _, position, _ ->
        val context = this@ItemAppWidgetConfigureActivity

        // 讀取與儲存選擇的記事物件
        val item = itemAdapter.getItem(position)
        saveItemPref(context, mAppWidgetId, item.id)

        val appWidgetManager = AppWidgetManager.getInstance(context)
        ItemAppWidget.updateAppWidget(
                context, appWidgetManager, mAppWidgetId)
        val resultValue = Intent()
        resultValue.putExtra(
                AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId)
        setResult(Activity.RESULT_OK, resultValue)

        finish()
    }
}

同樣在「ItemAppWidgetConfigureActivity.kt」,依照下面的內容加入「onCreate」函式:

package net.macdidi.atk

...

class ItemAppWidgetConfigureActivity : Activity() {

    ...

    public override fun onCreate(icicle: Bundle?) {
        super.onCreate(icicle)

        setResult(Activity.RESULT_CANCELED)

        // 改為使用應用程式主畫面
        setContentView(R.layout.activity_main)

        // 建立與設定選擇小工具使用的記事項目需要的物件
        item_list.adapter = itemAdapter
        item_list.onItemClickListener = itemListener

        val extras = intent.extras

        if (extras != null) {
            mAppWidgetId = extras.getInt(
                    AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID)
        }

        if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
            finish()
            return
        }
    }

...

}

17-3 實作小工具元件

接下來修改預設的小工具元件類別與畫面資源。先建立小工具使用的背景顏色資源,在「res/drawable」目錄上按滑鼠右鍵,選擇「New -> Drawable resource file」,在「File name」輸入「widgetdrawable」後選擇「OK」,修改widgetdrawable為下面的內容:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <corners
        android:topLeftRadius="20sp"
        android:topRightRadius="20sp"
        android:bottomLeftRadius="20sp"
        android:bottomRightRadius="20sp" />

    <solid android:color="#1E88E5"/>

    <stroke android:color="#1976D2" android:width="3dp"/>

</shape>

開啟「res/layout/item_app_widget.xml」,這是小工具元件使用的畫面資源,決定小工具在畫面上的樣子。依照下面的內容修改它的設定:

<!-- 修改背景 -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/widget_margin"
    android:background="@drawable/widget_drawable">

    <!-- 修改文字、大小、顏色與刪除背景 -->
    <TextView
        android:id="@+id/appwidget_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="@string/app_name"
        android:textColor="#EEEEEE"
        android:textSize="16sp"
        android:textStyle="bold|italic"
        android:layout_margin="8dp"
        android:contentDescription="@string/app_name" />

</RelativeLayout>

開啟小工具元件類別「ItemAppWidget.kt」,找到「onDeleted」函式,依照下面的內容修改程式碼:

package net.macdidi.atk

...

class ItemAppWidget : AppWidgetProvider() {

    ...

    override fun onDeleted(context: Context, appWidgetIds: IntArray) {
        val N = appWidgetIds.size

        for (i in 0 until N) {
            // 刪除小工具已經儲存的記事編號
            ItemAppWidgetConfigureActivity.deleteItemPref(
                    context, appWidgetIds[i])
        }
    }

    ...

}

同樣在「ItemAppWidget.kt」,找到「updateAppWidget」函式,依照下面的內容修改程式碼:

package net.macdidi.atk

...

class ItemAppWidget : AppWidgetProvider() {

    ...

    companion object {

        internal fun updateAppWidget(context: Context,
                                     appWidgetManager: AppWidgetManager,
                                     appWidgetId: Int) {

            // 讀取小工具儲存的記事編號
            val id = ItemAppWidgetConfigureActivity.loadItemPref(
                    context, appWidgetId)
            // 建立小工具畫面元件
            val views = RemoteViews(
                    context.packageName, R.layout.item_app_widget)
            // 讀取指定編號的記事物件
            val itemDAO = ItemDAO(context.applicationContext)
            val item = itemDAO[id]

            // 設定小工具畫面顯示記事標題
            views.setTextViewText(R.id.appwidget_text,
                    item?.title ?: "NA")

            // 點選小工具畫面的記事標題後,啟動記事應用程式
            val intent = Intent(context, MainActivity::class.java)
            val pending = PendingIntent.getActivity(
                    context, 0, intent, 0)
            views.setOnClickPendingIntent(R.id.appwidget_text, pending)

            // 更新小工具
            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
    }

}

17-4 執行小工具與設定元件的設定

最後記得要在應用程式設定檔中,使用「receiver」標籤為小工具元件加入需要的設定。小工具設定元件也需要使用「activity」加入必要的設定。開啟「AndroidManifest.xml」檢視裡面設定的內容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.macdidi.atk">

    ...

    <application ...>
        ...

        <!-- 小工具元件 -->
        <receiver android:name=".ItemAppWidget">
            <!-- 一定要加入這個Action名稱的設定 -->
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <!-- 使用android:resource指定小工具專用設定檔的資源名稱 -->
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/item_app_widget_info" />
        </receiver>

        <!-- 小工具設定元件 -->
        <activity android:name=".ItemAppWidgetConfigureActivity">
            <!-- 一定要加入這個設定 -->
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
            </intent-filter>
        </activity>
    </application>

</manifest>

完成這一章所有的功能了,執行應用程式,使用一開始介紹的方式,加入幾個記事小工具。

相關的檔案都可以在GitHub瀏覽與下載:

https://github.com/macdidi5/Android-Tutorial-Kotlin

後續 >> Android Tutorial using Kotlin 第六堂(1)Material Design – Theme與Transition

分享:
按讚!加入 CodeData Facebook 粉絲群

相關文章

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

熱門論壇文章

熱門技術文章