デスクトップウィジェットを作成する
- AppWidgetProvider クラスの作成 アプリケーション
ウィジェットの更新、有効化、無効化、削除時にデスクトップ ウィジェットがブロードキャストを受信する AppWidgetProvider クラスを作成します。また、AppWidgetProvider は BroadcastReceiver を継承し、特にウィジェットに対して特定のブロードキャスト フィルタリングを実行するため、ウィジェット関連の操作を処理するカスタム AppWidgetProvider クラスを作成する必要があります。
package com.example.widgetdemo
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
const val TAG = "MyWidgetProvider"
class MyWidgetProvider : AppWidgetProvider() {
/**
* 每次收到广播之后就会调用该函数,会在其他方法之前进行回调。一般可以不实现
* 默认的 AppWidgetProvider 实现会过滤所有应用微件广播并视情况调用上述方法
*/
override fun onReceive(context: Context?, intent: Intent?) {
super.onReceive(context, intent)
Log.d(TAG, "invoke onReceive......")
}
/**
* 调用此方法可以按 AppWidgetProviderInfo 中的 updatePeriodMillis 属性定义的时间间隔来更新应用微件
* 当用户添加应用微件时也会调用此方法,所以它应执行基本设置,如定义视图的事件处理脚本以及根据需要启动临时的 Service。
* 不过,如果您已声明配置 Activity,则当用户添加应用微件时不会调用此方法,但会调用它来执行后续更新。
* 由配置 Activity 负责在配置完成后执行首次更新。
*/
override fun onUpdate(context: Context?, appWidgetManager: AppWidgetManager?, appWidgetIds: IntArray?) {
super.onUpdate(context, appWidgetManager, appWidgetIds)
Log.d(TAG, "invoke onUpdate......")
}
/**
* 当首次放置微件时以及每当调整微件的大小时,会调用此方法。您可以使用此回调来根据微件的大小范围显示或隐藏内容
*/
override fun onAppWidgetOptionsChanged(context: Context?, appWidgetManager: AppWidgetManager?, appWidgetId: Int, newOptions: Bundle?) {
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
Log.d(TAG, "invoke onAppWidgetOptionsChanged......")
}
/**
* 每次从应用微件托管应用中删除应用微件时,都会调用此方法。
*/
override fun onDeleted(context: Context?, appWidgetIds: IntArray?) {
super.onDeleted(context, appWidgetIds)
Log.d(TAG, "invoke onDeleted......")
}
/**
* 首次创建应用微件的实例时,会调用此方法。例如,如果用户添加应用微件的两个实例,只有首次添加时会调用此方法。
* 如果您需要打开一个新的数据库或执行只需要对所有应用微件实例执行一次的其他设置,则此方法非常合适。
*/
override fun onEnabled(context: Context?) {
super.onEnabled(context)
Log.d(TAG, "invoke onEnabled......")
}
/**
* 从应用微件托管应用中删除了应用微件的最后一个实例时,会调用此方法。
* 您应使用此方法来清理在 onEnabled(Context) 中完成的所有工作,如删除临时数据库。
*/
override fun onDisabled(context: Context?) {
super.onDisabled(context)
Log.d(TAG, "invoke onDisabled......")
}
override fun onRestored(context: Context?, oldWidgetIds: IntArray?, newWidgetIds: IntArray?) {
super.onRestored(context, oldWidgetIds, newWidgetIds)
Log.d(TAG, "invoke onRestored......")
}
}
- ブロードキャストの登録
AndroidManifest.xml ファイルにレシーバーを登録し、ブロードキャスト受信クラスを上記で定義したクラスに指定します。label フィールドの値はウィジェット一覧に表示できるウィジェットの名前を示し、アクションを「android .appwidget.action.APPWIDGET_UPDATE」として、ウィジェットの構成情報に関連するファイル my_appwidget_info を追加します
<receiver
android:name=".MyWidgetProvider"
android:exported="false"
android:label="我的卡片">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/my_appwidget_info" />
</receiver>
- my_appwidget_info.xml ファイル
InitialLayout: ウィジェットの初期レイアウト
minWidth: ウィジェットの最小幅
minHeight: ウィジェットの最小高さ
reviewImage: アプリケーション コンポーネント リスト内のウィジェットのプレビュー イメージ
ResizeMode: サイズ変更可能かどうか、なし|水平|垂直; 固定サイズ |水平方向|垂直方向
updatePeriodMillis: 更新間隔、更新頻度、単位 MS、電力消費を削減するための公式制限 30 分
widgetCategory: ウィジェットを表示できる場所、通常はメイン インターフェイス
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/layout_my_card_widget"
android:minWidth="80dp"
android:minHeight="40dp"
android:previewImage="@drawable/widget_icon"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="86400000"
android:widgetCategory="home_screen">
</appwidget-provider>
最初のデスクトップ ウィジェットが作成され、その結果は次のようになります。
イベントインタラクション
- ウィジェットの更新
ユーザーが初めてデスクトップにウィジェットを追加するときに onUpdate メソッドが呼び出され、appWidgetIds 属性によって現在の AppWidgetProvider によって管理されるいくつかのウィジェットが識別されるため、ウィジェットはこのメソッドで初めて更新されます。ここでデータを取得し、ウィジェットの関連情報を更新できます。
レイアウト プレビュー:
ユーザーがデスクトップに初めてウィジェットを追加する場合、ネットワークからデータを取得してウィジェットに更新し、2 つのボタンのクリック イベントを設定する必要があるというシナリオをシミュレートします。デモンストレーションなので、スケジュールされたタスクを開始して、時間のかかるネットワーク運用をシミュレートするだけですが、実際の状況はさらに複雑で、さまざまなケースを考慮する必要があります。
override fun onUpdate(context: Context?, appWidgetManager: AppWidgetManager?, appWidgetIds: IntArray?) {
super.onUpdate(context, appWidgetManager, appWidgetIds)
Log.d(TAG, "invoke onUpdate......")
val timer = Timer()
val timerTask: TimerTask = object : TimerTask() {
override fun run() {
//定时任务
appWidgetIds?.forEach {
val pendingIntent: PendingIntent =
Intent(context, SecondActivity::class.java).let {
intent ->
PendingIntent.getActivity(context, 0, intent, 0)
}
val views: RemoteViews =
RemoteViews(context?.packageName, R.layout.layout_my_card_widget).apply {
setTextViewText(R.id.name, "来自Code")//设置小组件TextView的值
//添加LeftButton的点击事件
setOnClickPendingIntent(R.id.btnLeft, getSelfPendingIntent(context, LEFT_BUTTON_CLICK))
//添加RightButton的点击事件
setOnClickPendingIntent(R.id.btnRight, getSelfPendingIntent(context, RIGHT_BUTTON_CLICK))
}
//执行更新
appWidgetManager?.updateAppWidget(appWidgetIds, views)
}
}
}
timer.schedule(timerTask, 1000)
}
ウィジェットとアプリ間の対話はブロードキャストに基づいているため、クリック イベントを定義するときは、インテントをカスタマイズし、アクションを通じてクリック イベントを区別する必要があります。
private const val LEFT_BUTTON_CLICK = "widget_left_button"
private const val RIGHT_BUTTON_CLICK = "widget_right_button"
private fun getSelfPendingIntent(context: Context?, action: String): PendingIntent {
val intent = Intent(context, javaClass)//javaClass = this.class
intent.action = action
return PendingIntent.getBroadcast(context, 0, intent, 0)
}
次のステップでは、ウィジェットをクリックし、イベントを処理するために onReceive メソッドを呼び出します。
override fun onReceive(context: Context?, intent: Intent?) {
super.onReceive(context, intent)
Log.d(TAG, "invoke onReceive......")
intent?.action?.let {
action ->
context?.let {
val manager = AppWidgetManager.getInstance(context)
val remoteViews = RemoteViews(context.packageName, R.layout.layout_my_card_widget)
if (action == LEFT_BUTTON_CLICK) {
//设置一个颜色
remoteViews.setTextColor(R.id.name, Color.parseColor("#FF03DAC5"))
manager.updateAppWidget(ComponentName(context, MyWidgetProvider::class.java), remoteViews)
} else if (action == RIGHT_BUTTON_CLICK) {
//将颜色改回来
remoteViews.setTextColor(R.id.name, Color.parseColor("#FF000000"))
manager.updateAppWidget(ComponentName(context, MyWidgetProvider::class.java), remoteViews)
Toast.makeText(context, "点击右边按钮", Toast.LENGTH_SHORT).show()
}
}
}
}
レイアウトファイルは比較的シンプルなので、ウィジェットの簡単な使い方は完了です。
- 注: ウィジェットのレイアウトは、次のように RemoteView のみをサポートします。
公式ドキュメントを参照してください: Google Development Documentation