欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

App Widgets 详解一 简单使用

程序员文章站 2022-05-30 13:39:09
...

导读:

本文根据谷歌官方文档,加上本人理解汇编而成,如有雷同,说明都是从官方文档学习的.

  • App Widget 小部件系列其他文章链接

App Widgets 详解一 简单使用

App Widgets 详解二 Configuration Activity

App Widgets 详解三 Activity中添加App Widgets

App Widgets 详解四 RemoteViews、RemoteViewsService和RemoteViewsFactory

App Widget 简介

  • App Widgets (微型应用视图),它能够嵌入到其他的应用程序(如系统桌面/其他应用的Activity)并接受定期更新,主要用于展现程序的快捷入口

  • 如果想创建一个AppWidget 需要:

一、 AppWidgetProviderInfo object

描述了App Widget 的mtadata(元数据),如 App Widget 的布局,更新频率和AppWidgetProvider类等.这需要在XML文件中定义.

AppWidgetProviderInfo 官方文档链接

二、 AppWidgetProvider class implementation

AppWidgetProvider 接口基于BroadcastReciver,通过定义这个接口,当 App Widget updata(数据发生改变),enabled(启动),disabled(禁用)和deletd(删除)时,我们将会受到广播

三、 View layout

定义App Widget 的初始化XML布局文件,另外,可以在App Widget 启动前,添加一个Activity用于配置Widget的一些参数


简单使用:

一、在AndroidMainfest.xml清单文件声明我们定义的AppWidgetProvider 类


<receiver android:name="ExampleAppWidgetProvider" >

    <intent-filter>
        <--!指定AppWidgetProvider接受系统的APPWIDGET_UPDATE广播-->
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>

    <--!指定Meta_data名称,使用android.appwidgetb必须确定AppWidgetProviderInfo描述符的数据-->
    <--!指定AppWidgetProviderInfo资源XML文件-->
    <meta-data android:name="android.appwidget.provider"
               android:resource="@xml/example_appwidget_info" />
</receiver>

二、创建AppWidgetProviderInfo XML文件

该XML文件定义 App Widget 的基本属性,在res/xml/目录下创建appwidger-provider 标签的XML文件


<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>

属性说明

属性 说明
minWidth 和 minHeight 定义了AppWidget在桌面显示的最小宽高,单位dp
minResizeWidth 和 minResizeHeight Android 4.0 引入,单位dp,minWidth 和 minHeight相当于窗口小部件的默认大小,minResizeWidth 和 minResizeHeight小部件大小少于多少将模糊或不可见,一般用于ListView和GridView小部件
updatePeriodMillis AppWidget更新频率,单位毫秒,通过调用AppWidgetProvider类的onUpate()实现数据更新,为了节省电量默认建议一小时更新一次,系统默认最低30分钟(低于30会自动设为30)
initialLayout 设置 AppWidget XML布局文件
configure 设置用户添加App Widget前启动的Activity,一般用于配置App widget属性
previewImage Android 3.0引入的,设置Widget预览页的图标,对应appwidget-provider 标签里的android:previewImage
autoAdvanceViewId Android 3.0引入的,指定的子View会自动更新
resizeMode Android 3.1 引入的,设置该属性,用户可以在桌面调整Widget的大小,包括”horizontal”, “vertical”, and “none”.支持使用 “horizontal竖线vertical”
widgetCategory 设置AppWidget能够显示的屏幕,home_scree(主屏),keyguard(锁屏)或者同时
initialKeyguardLayout Android 4.2 引入的,设置App Widget 处于 lockscreen 中的XML布局文件

==注意==

  1. 如果定义的minWidth 和 minHeight不匹配当前屏幕,会自动缩放到合适大小
  2. 如果定义的AppWidght想跨设备、最小宽高不应超过4*4单元格.
  3. 如果设备到了更新时间(updatePeriodMillis)时处于睡眠状态,设备将会被唤醒更新,体验不好,建议要么设置一个按钮让用户手动刷新,要么设置updatePeriodMillis为0,使用AlarmManager设置警报Intent让AppwidgetProvider类接受,将报警类型设置为ELAPSED_REALTIM或RTC,只有设备唤醒时才会发出警报
  4. minResizeHeight属性指定小部件可以调整大小的最小高度.如果该字段大于minHeight,或者resizeMode的取值不包括vertical时,则该字段不起作用;
  5. minResizeWidth属性指定小部件可以调整大小的最小宽度.如果该字段大于minWidth,或者resizeMode的取值不包括horizontal时,则此字段无效
  6. AppWidget只有低于Android 5.0才能锁屏显示,高于5.0只能主屏显示

三、定义 App Widget XML布局文件

由于Widget的布局需要RemoteViews支持,因此不能随便定义或自定义view**

支持的布局:

  • FrameLayout
  • LinearLayout
  • RelativeLayout
  • GridLayout

支持的控件:

  • AnalogClock
  • Button
  • Chronometer
  • ImageButton
  • ImageView
  • ProgressBar
  • TextView
  • ViewFlipper
  • ListView
  • GridView
  • StackView
  • AdapterViewFlipper

以上类的子类都不支持,不过支持ViewStub标签


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#09C"
                android:padding="@dimen/widget_margin">

    <TextView
        android:id="@+id/appwidget_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:layout_margin="8dp"
        android:background="#09C"
        android:contentDescription="@string/appwidget_text"
        android:text="@string/appwidget_text"
        android:textColor="#ffffff"
        android:textSize="24sp"
        android:textStyle="bold|italic"/>

</RelativeLayout>

==注意==

Android 4.0+ ; API>14 AppWidget会与设备桌面的图标边距对齐,但是低版本的AppWidget,要做以下兼容处理


App Widget XML布局文件,根布局设置 android:padding="@dimen/widget_margin"

//res/values/dimens.xml:
<dimen name="widget_margin">8dp</dimen>

//res/values-v14/dimens.xml:
<dimen name="widget_margin">0dp</dimen>

四、定义 AppWidgetProvider 类

几个函数方法:

  • onUpdate()

当widget更新时被执行.(包含首次添加),如果在 AppWidgetProviderInfo 调用android:config,那么当用户首次添加widget时,onUpdate()不会被调用,之后更新widget时,onUpdate才会被调用.

  • onAppWidgetOptionsChanged()

Android 4.1引入的,当 widget 被初次添加 或者 当 widget 的大小被改变时,执行onAppWidgetOptionsChanged().你可以在该函数中,根据 widget 的大小来显示/隐藏某些内容.可以通过 getAppWidgetOptions() 来返回 Bundle 对象以读取 widget 的大小信息,Bundle中包括以下信息

OPTION_APPWIDGET_MIN_WIDTH – 包含 widget 当前宽度的下限,以dp为单位。

OPTION_APPWIDGET_MIN_HEIGHT – 包含 widget 当前高度的下限,以dp为单位。

OPTION_APPWIDGET_MAX_WIDTH – 包含 widget 当前宽度的上限,以dp为单位。

OPTION_APPWIDGET_MAX_HEIGHT – 包含 widget 当前高度的上限,以dp为单位。

  • onDeleted(Context, int[])

当 widget 被删除时被触发.

  • onEnabled(Context)

当第一次创建widget实例时触发.如果用户对同一个widget增加了两次(两个实例),那么onEnabled()只会在第一次添加widget时触发.

  • onDisabled(Context)

当最后一个widget实例被删除时触发.

  • onReceive(Context, Intent)

接收到任意广播时触发,并且会在上述的方法之前被调用.实际上,App Widge中的onUpdate()、onEnabled()、onDisabled()等方法都是在 onReceive()中调用的;是onReceive()对特定事情的响应函数

  • onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds)

    在从备份还原此AppWidget提供程序的实例时,响应ACTION_APPWIDGET_RESTORED广播调用.

    如果您的提供商维护有关其窗口小部件实例的任何持久性数据,请覆盖此方法以将旧的AppWidgetIds重新映射到新值,并更新可能相关的任何其他应用程序状态.

    这个回调函数将立即通过调用onUpdate(Context,AppWidgetManager,int [])来实现,因此您的提供者可以立即生成适用于新恢复的实例集的新RemoteView.


public class ExampleAppWidget extends AppWidgetProvider {

    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                                int appWidgetId) {

        CharSequence widgetText = context.getString(R.string.appwidget_text);
        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);
        views.setTextViewText(R.id.appwidget_text, widgetText);

        // Instruct the widget manager to update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    @Override
    public void onEnabled(Context context) {
        // Enter relevant functionality for when the first widget is created
    }

    @Override
    public void onDisabled(Context context) {
        // Enter relevant functionality for when the last widget is disabled
    }

    @Override
    public void onReceive(Context context, Intent intent) {
       //用于接收指定意图,处理相关需求,可以重写onRecrive(),如我们收到一个toast的动作时,显示一条Toast
    }

}

 AppWidgetProvider只是一个方便类.如果想直接收到App Widget广播,可以自定义BroadcastReceiver或重写onReceive(Context,Intent).

您需要关心的意图如下:

ACTION_APPWIDGET_UPDATE //处理更新

ACTION_APPWIDGET_DELETED // 处理删除

ACTION_APPWIDGET_ENABLED //可用

ACTION_APPWIDGET_DISABLED//不可用

ACTION_APPWIDGET_OPTIONS_CHANGED // 配置改变


给App Widget 设置点击事件

在AppWidgetProvider中最重要的回调是onUpate(),除非使用 configuration Activity ,不然每个 App Widget 添加到主机Host时都会调用onUpdate().

那么如果你的App Widget不需要创建临时文件或数据库,或需要执行清理其他工作,那么其他的逻辑业务基本在onUpate()中实现即可


public class ExampleAppWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;

        // Perform this loop procedure for each App Widget that belongs to this provider
        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];

            // Create an Intent to launch ExampleActivity
            Intent intent = new Intent(context, ExampleActivity.class);
            intent.setAction("TOAST_ACTION");
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

            // Get the layout for the App Widget and attach an on-click listener
            // to the button
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);

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

重写onReceive() ,用于接受意图,处理相关需求


@Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        if (intent.getAction().equals("TOAST_ACTION")) {

            Toast.makeText(context, "Touched view ", Toast.LENGTH_SHORT).show();
        }

    }

==注意==

  1. appWidgetIds是一个ID数组,用于标识此提供程序创建的每个App Widget。 这样,如果用户创建了多个App Widget实例,那么它们都将同时更新。 但是,如果多个App Widget 设置了updatePeriodMillis,那么只会调用第一个App Widget 实例的
  2. 由于AppWidgetProvider继承处于BroadcastReceiver,生命周期非常短,如果需要执行耗时操作会发生ANR异常,因此我们可以在onUpdate()方法中启动Service,然后在Service数据处理,个人建议在onRnable()启动服务,onDisable()关闭Service

Android Studio 快速集成 App Widgets

右键moudle -> New -> Widget -> App Widget

App Widgets 详解一 简单使用

填好参数Finish即可

如果想像系统那样有多个Weight可以选择,只需要多创建一个Widget即可:

App Widgets 详解一 简单使用

总结:

本系列Demo源码

本篇文章到此结束,欢迎关注,后续有补充的会即使更新,有问题也欢迎评论,共同成长