爲AppWidget添加配置Activity

1. 前言

    在構建自己的窗口小部件的時候,爲了更加定製化,會根據用戶的喜好,讓用戶選擇小部件的樣式等,這種情況下,就需要爲窗口小部件添加一個配置頁面了。在用戶添加窗口小部件的時候,會彈出配置頁面,讓用戶根據自己的喜好配置小部件的樣式等等屬性。

2. 實現步驟

更多實現細節及注意事項請參考Google官方介紹:Creating an App Widget Configuration Activity

關於構建AppWidget相關內容參考:爲你的Android應用構建窗口小部件(App Widget)

2.1 新建Activity及佈局文件

    新建一個Activity及佈局文件,並在內部實現相關代碼

  • 新建配置Activity
package com.owen.clockwidget

import android.app.Activity
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Color
import android.os.Bundle
import android.widget.RadioGroup
import android.widget.SeekBar
import kotlinx.android.synthetic.main.activity_config.*
import java.time.Clock

/**
 * 配置Activity
 * <br/>Author:yunying.zhang
 * <br/>Email: [email protected]
 * <br/>Date: 2019/12/17
 */
class ConfigActivity: Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_config)
        val sp = getSharedPreferences("sp_widget_config", Context.MODE_PRIVATE)
        val appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0)

        sp.edit().putInt("${appWidgetId}_bg_color", Color.BLACK).apply()
        sp.edit().putInt("${appWidgetId}_bg_alpha", 100).apply()

        btnOk.setOnClickListener() {
            setResult(RESULT_OK, Intent().also {
                it.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
            })

            ClockWidgetProvider.updateWidget(this)

            finish()
        }

        btnCancel.setOnClickListener() {
            setResult(RESULT_CANCELED)
            finish()
        }

        colorGroup.setOnCheckedChangeListener() { group: RadioGroup, checkedId: Int -> UInt
            when(checkedId) {
                R.id.blue -> {
                    println("選擇了藍色背景")
                    sp.edit().putInt("${appWidgetId}_bg_color", Color.argb(0xff, 0x33, 0xb5, 0xe5)).apply()
                }
                R.id.gray -> {
                    println("選擇了灰色背景")
                    sp.edit().putInt("${appWidgetId}_bg_color", Color.argb(0xff, 0xaa, 0xaa, 0xaa)).apply()
                }
                R.id.green -> {
                    println("選擇了綠色背景")
                    sp.edit().putInt("${appWidgetId}_bg_color", Color.argb(0xff, 0x99, 0xcc, 0x00)).apply()
                }
                R.id.white -> {
                    println("選擇了白色背景")
                    sp.edit().putInt("${appWidgetId}_bg_color", Color.argb(0xff, 0xff, 0xff, 0xff)).apply()
                }
            }
        }

        seekBar.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                tvBgAlpha.text = "背景透明度:${progress}%"
                sp.edit().putInt("${appWidgetId}_bg_alpha", progress).apply()
            }

            override fun onStartTrackingTouch(seekBar: SeekBar?) {
            }

            override fun onStopTrackingTouch(seekBar: SeekBar?) {
            }
        })

        tvBgAlpha.text = "背景透明度:${seekBar.progress}%"
        sp.edit().putInt("${appWidgetId}_bg_alpha", seekBar.progress).apply()

    }
}
  • 佈局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
    android:weightSum="2"
    android:gravity="bottom">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:layout_gravity="bottom"
        android:background="#FFBBBBBB"
        android:padding="10dp">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="選擇字體顏色:"
            android:textColor="@android:color/black"
            android:textSize="18sp"/>
        <RadioGroup
            android:id="@+id/colorGroup"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <RadioButton
                android:id="@+id/blue"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="@android:color/holo_blue_light"
                android:checked="true"
                android:layout_margin="5dp"/>
            <RadioButton
                android:id="@+id/gray"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="@android:color/darker_gray"
                android:layout_margin="5dp"/>
            <RadioButton
                android:id="@+id/white"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="@android:color/white"
                android:layout_margin="5dp"/>
            <RadioButton
                android:id="@+id/green"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="@android:color/holo_green_light"
                android:layout_margin="5dp"/>
        </RadioGroup>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:layout_marginTop="15dp"
            android:orientation="vertical">
            <TextView
                android:id="@+id/tvBgAlpha"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="18sp"
                android:textColor="@android:color/black"
                android:text="背景透明度:100%" />

            <SeekBar
                android:id="@+id/seekBar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="15dp"
                android:progress="100"/>

        </LinearLayout>


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <Button
                android:id="@+id/btnCancel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Cancel"/>
            <View
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_weight="1" />
            <Button
                android:id="@+id/btnOk"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="OK"/>
        </LinearLayout>

    </LinearLayout>
</LinearLayout>
  • AndroidManifest.xml中聲明你的Activity
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.owen.clockwidget">

    <permission android:name="com.owen.clockwidget.ClockBroadcast" />


    <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/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">


        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".ConfigActivity"
            android:theme="@android:style/Theme.Translucent.NoTitleBar">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
            </intent-filter>
        </activity>

        <receiver android:name=".ClockWidgetProvider">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.TIME_SET" />
            </intent-filter>
            <intent-filter>
                <action android:name="com.owen.clockwidget.DaemonDie" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/clock_widget_info" />
        </receiver>

    </application>

</manifest>

2.2 在AppWidgetProviderInfo元數據中添加配置Activity配置

    之前的文章介紹過,AppWidget的元數據是以xml文件形式存儲,只需要在xml中增加android:configure節點配置,因爲這個Activity需要在外部調用,所以Activity名稱必須使用完整類名(即含完整包名的類名)。

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="320dp"
    android:minHeight="40dp"
    android:minResizeWidth="110dp"
    android:minResizeHeight="40dp"
    android:updatePeriodMillis="0"
    android:previewImage="@drawable/ic_gift"
    android:initialLayout="@layout/clock_widget"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen"
    android:configure="com.owen.clockwidget.ConfigActivity">
</appwidget-provider>

2.3 注意事項

  • 配置Activity必須返回AppWidget的ID
        配置Activity由AppWidget主機啓動,AppWidget主機會在啓動Intent中傳入一個AppWidget的ID,當配置完成時,必須將這個AppWidget的ID返回。返回該值的方法是通過ActivitysetResult()方法返回,傳遞的key爲AppWidgetManager.EXTRA_APPWIDGET_ID如果沒有完成這一步,添加AppWidget到主機的時候,總是會失敗,無法添加。

  • 完成配置返回時必須手動更新AppWidget
        當用戶向主機添加AppWidget的時候,會在AppWidget創建時回調onUpdate()方法,當配置Activity啓動或者關閉時,系統不會發送ACTION_APPWIDGET_UPDATE的廣播,因此必須在配置Activity中主動更新AppWidget,從配置Activity中更新AppWidget,可通過獲取一個AppWidgetManager對象,並調用updateAppWidget()接口進行更新,以下是示例代碼(詳細代碼可參考demo源碼: ClockWidget):

fun updateWidget(context: Context?) {
    val appWidgetManager = AppWidgetManager.getInstance(context)

    val componentName = ComponentName(context!!, ClockWidgetProvider::class.java)

    val appWidgetIds = appWidgetManager.getAppWidgetIds(componentName)
    appWidgetIds?.forEach {
        appWidgetManager.updateAppWidget(componentName, buildView(it, context))
    }
}

3. Demo項目源碼

demo項目源碼可在Github上下載:ClockWidget

說明:因demo源碼會不斷更新,當前文章的代碼可能會跟Github上的存在差異

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章