Yappli Tech Blog

株式会社ヤプリの開発メンバーによるブログです。最新の技術情報からチーム・働き方に関するテーマまで、日々の熱い想いを持って発信していきます。

Androidウィジェットを触ってみる

Androidエンジニアの白井です。

Android 12 Beta4がリリースされて正式版リリースが目前と迫った今、Android 12で更新された内容を見ているとウィジェットの改善がありまして、今までなかなか触る機会のなかったウィジェットについてまとめてみました。

ウィジェットとは

昔からある機能なので言わずと知れたものではあるのですが改めて説明文を読んでみます。

ウィジェットは、ホーム画面のカスタマイズに不可欠な要素です。ウィジェットは、アプリの特に重要なデータや機能を「ひと目で」確認できるようにし、そうしたデータや機能にユーザーのホーム画面から直接アクセスできるようにするもの、ということができます。ユーザーはウィジェットをホーム画面のパネルからパネルへ移動したり、(サポートされていれば)好みに合わせてサイズを変更してウィジェットに表示される情報量を調整したりできます。

Android 12における改善点

ウィジェットの改善で確認できますので詳細は割愛しますが改善された内容は大きく以下の4点です。 Android 12のUIは全体的に丸みを帯びているのでウィジェットでも採用されたのかもしれません。 style.xmlやカスタム属性等を追加することで下位互換が可能なのでAndroid 12未満でも同様に見せることが可能です。

  • 角の丸み

   システムパラメータが追加されウィジェットやウィジェット内のViewの角に対して丸みが設定できます

  • テーマカラーの利用

   ボタンや背景にテーマカラーを設定できるようになりウィジェット間の遷移がスムーズになります

  • 設定用Activityと再設定

   ウィジェット配置時に設定用Acitvityが起動できます
   またウィジェットをロングタップすることで再設定も可能になります

  • 利用可能なViewの追加

   単純なButtonだけでなくCheckBox等の複合ボタンが追加されて表現の幅が広がります

ウィジェットのしくみ

ここからはウィジェットをどのようにして実装するのか備忘録も兼ねて自分なりにまとめてみました。

操作

ホーム画面に配置されたウィジェットをユーザーが操作する上で可能な動作

  • タップ
  • 上下スワイプ

※左右スワイプはホーム画面の移動になるので利用ができません。

利用可能なView

ウィジェットには制限があるとはいっても、基本的なViewは利用できるので見せ方に困ることはなさそうに思います。

ViewGroup系
  • FrameLayout
  • LinearLayout
  • RelativeLayout
  • GridLayout
View系 (抜粋)
  • ImageView
  • TextView
  • ListView

サイズ

選択一覧でホーム画面に配置できるウィジェットにはサイズが記載されており、以下の画像の2x4や4x6などがそれに当たります。 これらのサイズはセルという単位で、1x1でホーム画面のアプリアイコン1個分のサイズです。

f:id:dshiraiy:20210831142222p:plain

実装時はdpで指定するため次の式でdpへ変換します。

70 x n - 30

例えば1x1のウィジェットを作成する場合はn=1で計算して40dpx40dpで指定することとなります。

実装

ここからはプログラム寄りの話になります。 以下ではサンプルコードの一部を貼付して簡単な説明を書いていきます。

AndroidManifest.xml
<receiver android:name="ExampleAppWidgetProvider">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/example_appwidget_info" />
</receiver>

ウィジェット本体がBroadcastReceiverのサブクラスであるためAndroidManifest.xmlにreceiverとして登録します。 受け取るBroadcastはシステムからのウィジェットの更新通知です。 meta-dataにはウィジェットであることと、ウィジェットの情報を記載したxmlファイルを指定します。

AppWidgetProviderInfoメタデータ
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:updatePeriodMillis="86400000"
    android:previewImage="@drawable/preview"
    android:initialLayout="@layout/example_appwidget"
    android:configure="com.example.android.ExampleAppWidgetConfigure"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen">
</appwidget-provider>

AndroidManifest.xmlで指定したウィジェット情報xmlの内容となります。 属性は以下の通り。

  • minWidth、minHeight

   サイズ。セルをdpで変換して指定

  • updatePeriodMillis

   更新間隔をミリ秒で指定。AlarmManagerなどで自力で更新かける場合は省略可

  • previewImage

   ウィジェット選択一覧画面に表示する画像を指定。省略した場合はアプリアイコンが使用される

  • initialLayout

   表示するレイアウトxmlを指定

  • configure

   配置直後に起動されるActivityを指定。ウィジェット設定画面がない場合は省略可

  • resizeMode

   ホーム画面に配置されたウィジェットをロングタップすることでサイズ変更する場合に指定

  • widgetCategory

   Android 5未満ではロック画面にも配置できたが現在はホーム画面のみの指定

ウィジェット本体の実装
class ExampleAppWidgetProvider : AppWidgetProvider() {
    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        // Perform this loop procedure for each App Widget that belongs to this provider
        appWidgetIds.forEach { appWidgetId ->
        // Create an Intent to launch ExampleActivity
        val pendingIntent: PendingIntent = Intent(context, ExampleActivity::class.java)
            .let { intent ->
                PendingIntent.getActivity(context, 0, intent, 0)
            }

            // Get the layout for the App Widget and attach an on-click listener
            // to the button
            val views: RemoteViews = RemoteViews(
                context.packageName,
                R.layout.appwidget_provider_layout
            ).apply {
                setOnClickPendingIntent(R.id.button, pendingIntent)
            }

            // Tell the AppWidgetManager to perform an update on the current app widget
            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
    }
}

AndroidManifest.xmlで指定した通知を受け取るとonUpdate()が呼び出されるのでここでウィジェットの更新を実施します。 Viewの更新方法が少し特殊でレイアウトxmlからRemoteViewsを取得してそれに対して更新情報を設定していきます。 通信などが必要で処理時間が長くなる場合はServiceに処理を流すべきでしょう。

最後に

Android 2.1の頃にウィジェットを1度触ってみたことがあったのですが、今ほどドキュメントもなく試行錯誤を随分した記憶があります。 意外と簡単にウィジェット実装はできるのでホーム画面にあると便利なアプリをお持ちであったりアイディアがあれば気軽にやってみるのもよさそうです。