为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: 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返回。返回该值的方法是通过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上的存在差异

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值