文章目录
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: yinglovezhuzhu@gmail.com
* <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上的存在差异