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返回。返回該值的方法是通過Activity
的setResult()
方法返回,傳遞的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上的存在差異