区块链技术博客
www.b2bchain.cn

Android 的微件求职学习资料

本文介绍了Android 的微件求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

前言

我们在 Android 的快捷方式 阐述了如何使用 Android 的快捷方式,快捷方式最多只能显示文字和图标,无法动态的显示更多的信息。而桌面 Launcher 除了显示图标外,其实还可以显示 Widget 微件,也有翻译成小部件,小组件的。看看你的手机,通常都会内置时钟,图库,联系人等应用,定制厂商还会加入天气之类的应用。一般这类信息展示的应用,都会提供微件功能。

要显示应用的微件,通常是长按该应用,点击微件即可显示该应用支持的微件,如下:

Android 的微件

或者是长按桌面(有些深度定制的厂商,是双指内缩的手势)触发桌面页编辑功能,也能看到微件的入口,如下:

Android 的微件

以时钟应用为例,可以将其提供的时间微件放到桌面上,如下:

Android 的微件

并且可以动态的去更新它,显然比快捷方式好多了,事实上他们俩也是完全不一样的概念。

利用好微件,不仅可以将应用的常见功能放到桌面上,还可以增加用户使用应用的频率,常见的微件类型如下:

1. 信息类

比如时钟、天气等应用,用来显示信息的。如上图。

2. 控制类

比如有些系统会增加个快捷方式的集合,如下图:

Android 的微件

3. 混合类

信息和控制可以混合在一起的,比如音乐播放器,不仅可以控制,还可以显示当前播放到哪一首了。

Android 的微件

4. 滚动类

比如新闻列表,或者是图库的图片列表,可以上下滚动的,如下图:

Android 的微件


要理解微件首先要明确一些概念。微件是隶属于应用的,可以理解是应用一部分功能控件,但是它却可以显示在其他应用上,通常是我们的系统桌面,即 Launcher。也就是托管给 Launcher 了,不仅仅是 Launcher,你要是有这方面的需求,也可以自己创建一个去管理和显示其他应用的微件,通常是自定义 Launcher 时需要用到,一般其他应用没有这方面的需求。

也就是说需要有:提供微件的应用托管微件的应用。本文的示例就是我们的应用桌面 Launcher

用户通常会移动微件,设置微件的大小,对于应用来说,一般我们需要以下 3 个方面去处理微件:

  1. 主动更新微件的内容。
  2. 接收处理用户的手势和点击。
  3. 监听微件的增删改查。

有关手势方面,微件最多可以使用点击上下滑动功能。


下面我们开始看看如何创建应用微件。

创建微件

在创建前,我们需要知道当前系统桌面是否支持微件,类似于 在 Android 中使用低功耗蓝牙-客户端 这篇文章中所描述的,使用蓝牙需要判断蓝牙是否可用,使用微件也要先判断 feature,如下在 Manifest 中声明:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"     package="..."     >     <uses-feature android:name="android.software.app_widgets" android:required="false" />     ... </manifest>

在代码中判断:

boolean hasWidgetsFeature = getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);

微件相关的类都在 android.appwidget 包下,数量也较少,先简单介绍下,如下所示:

AppWidgetHost、AppWidgetHostView

托管微件的应用才需要用到,这里不讨论。

AppWidgetManager

微件的管理类,可以更新获取微件,8.0 以后还可以主动请求创建微件。

AppWidgetProvider

微件的提供者,看名字会以为是个 ContentProvider,但它其实是个 BroadcastReceiver,用它可以声明微件的元数据,以及接收用户操作微件时的相关回调。

AppWidgetProviderInfo

微件的元数据类,对应 xml 的 <appwidget-provider> 标签。其中很多字段都是对应该标签下的属性。


大致介绍了相关的类,现在我们来实现一个微件试试。

1. 定义元数据 AppWidgetProviderInfo

元数据的定义是用 xml 配置的,创建 res/xml/appwidget_info.xml,可配置的内容如下:

<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"     android:autoAdvanceViewId="0"     android:configure="com.imyyq.ui.widget.WidgetConfigureActivity"     android:description="@string/widget_des"      android:initialKeyguardLayout="@layout/widget_initial_in_keyguard"     android:initialLayout="@layout/widget_initial"      android:maxResizeHeight="80dp"     android:maxResizeWidth="80dp"     android:minResizeHeight="40dp"     android:minResizeWidth="40dp"     android:minWidth="40dp"     android:minHeight="40dp"      android:previewImage="@android:drawable/ic_btn_speak_now"     android:previewLayout="@layout/widget_preview"      android:resizeMode="horizontal|vertical|none"      android:targetCellHeight="1"     android:targetCellWidth="1"      android:updatePeriodMillis="86400000"     android:widgetCategory="home_screen|keyguard"     android:widgetFeatures="0"     > </appwidget-provider>
  • autoAdvanceViewId
    • 微件布局中子视图的 ID,比如如果你的微件是 StackView,如果设定了此属性,那么在用户没有操作时,托管应用 Launcher 可能会自动间隔去切换 StackView 的下一个卡片。
  • configure
    • 提供配置微件的 activity,Launcher 在添加微件时,会打开它。
  • description
    • 描述微件的说明。
  • initialKeyguardLayout、initialLayout
    • 微件在锁屏和桌面上的初始布局
  • maxResizeHeight、maxResizeWidth、minResizeHeight、minResizeWidth
    • 微件可以调整的最大和最小高宽
  • minWidth、minHeight
    • 微件的最大最小宽高
  • previewImage、previewLayout
    • 预览图和预览布局
  • resizeMode
    • 调整微件大小的规则,可以是水平、垂直,或两个方向,或不可调整
  • targetCellWidth、targetCellHeight
    • 添加到 Launcher 时 微件 的默认宽高,单位是 Launcher 的网格单元为单位。
  • updatePeriodMillis
    • 微件希望的更新频率,毫秒为单位
    • 建议尽可能小,因为此属性并不保证一定准时,另外也会唤醒设备,比较耗电。最好配合 AlarmManager 去使用。
  • widgetCategory
    • 微件可以显示在什么地方,可以是锁屏、桌面。
    • 5.0 以前的 Android 才支持在锁屏上显示微件。5.0 及以上的,只有 home_screen 有效。
  • widgetFeatures
    • 告知微件的托管应用,即 Launcher,微件支持哪些功能

属性在 Android 版本间的支持情况:

  • 9.0 才提供的
    • widgetFeatures
  • 12.0 才提供的
    • targetCellWidth
    • targetCellHeight
    • previewLayout
    • maxResizeWidth
    • maxResizeHeight
    • descriptionRes

其中 Cell 的概念要解释下,在 Launcher 中,通常都是网格的布局,把界面分成一个网格,一个 Cell 就是一个网格,打开布局边界看的清清楚楚,如下图:

Android 的微件

可以看出,图中的设备,是把屏幕宽度分成了 5 个 Cell,每个应用图标和快捷方式占据一个 Cell,而图库的微件,Width 占据了 3 个 Cell 宽,Height 占据了 3 个 Cell 高。

xml 准备好了,如何使用呢?接着看!

2. 注册 AppWidgetProvider 广播

用户在操作微件时,会通过广播的形式告知 APP,比如添加和删除微件。注册如下:

<receiver     android:name=".ui.widget.MyAppWidgetProvider"     android:icon="@android:drawable/ic_delete"     android:exported="true"     >     <intent-filter>         <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />     </intent-filter>     <meta-data         android:name="android.appwidget.provider"         android:resource="@xml/appwidget_info"         /> </receiver>

其中 icon 将会是 AppWidgetProviderInfo 的 icon 字段。
meta-data 声明了此广播对应的微件 xml。action 是必须且值是固定的。

MyAppWidgetProvider 的内容如下:

public class MyAppWidgetProvider extends AppWidgetProvider {     private static final String TAG = "MyAppWidgetProvider";      /**      * 尺寸改变时调用      */     @Override     public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {         Log.i(TAG, "onAppWidgetOptionsChanged: " + appWidgetId);     }      /**      * 当要求应用提供 RemoteViews 时回调。      */     @Override     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {         Log.i(TAG, "onUpdate: " + Arrays.toString(appWidgetIds));     }      /**      * 删除时调用,比 onDisabled 早      */     @Override     public void onDeleted(Context context, int[] appWidgetIds) {         Log.i(TAG, "onDeleted: " + Arrays.toString(appWidgetIds));     }      /**      * 要求应用提供 widget 时就调用了,尽管用户最后可能是 Cancel 的操作      */     @Override     public void onEnabled(Context context) {         Log.i(TAG, "onEnabled: ");     }      /**      * 删除时调用      */     @Override     public void onDisabled(Context context) {         Log.i(TAG, "onDisabled: ");     }      /**      * 从备份中恢复时调用。      * 然后调用 onUpdate      * <p>      * 应该将 AppWidgetManager#OPTION_APPWIDGET_RESTORE_COMPLETED 设置为 true 以指示小部件是否已从提供者端成功恢复。      */     @Override     public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {         Log.i(TAG, "onRestored: old=" + Arrays.toString(oldWidgetIds) + ", new=" + Arrays.toString(newWidgetIds));     } }

注释已经说明了调用的时机。从选择一个微件到完成创建,回调如下:

onEnabled onUpdate

然后删除微件,回调如下:

onDeleted onDisabled

当然用户要是改变微件大小,onAppWidgetOptionsChanged 就会被调用,而且看了源码就知道,其实 AppWidgetProvider 类只是在 onReceive 方法中对以下 Action 进行了判断,拆分成了一个个方法供子类使用:

AppWidgetManager.ACTION_APPWIDGET_UPDATE; AppWidgetManager.ACTION_APPWIDGET_DELETED; AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED; AppWidgetManager.ACTION_APPWIDGET_ENABLED; AppWidgetManager.ACTION_APPWIDGET_DISABLED; AppWidgetManager.ACTION_APPWIDGET_RESTORED;

3. 配置微件的 Activity

configure 配置的 WidgetConfigureActivity 要在 manifest 中注册:

<activity android:name=".ui.widget.WidgetConfigureActivity">     <intent-filter>         <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />     </intent-filter> </activity>

action 也是必须配置的,值也是固定的。

Launcher 在生成该微件前,会先启动此 activity,传入微件 id,在 activity 中可通过 intent 获取:

Intent intent = getIntent(); Bundle extras = intent.getExtras(); if (extras != null) {     appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); }

然后决定好微件的配置后,生成 RemoteViews 去更新微件:

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this); RemoteViews views = new RemoteViews(getPackageName(), R.layout.layout_widget); appWidgetManager.updateAppWidget(appWidgetId, views);

最后将微件 id 回传回去:

Intent resultValue = new Intent(); resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); setResult(RESULT_OK, resultValue); finish();

最好在 activity 启动后,就先 setResult(RESULT_CANCELED),这样用户如果中途取消了,微件也不会生成,即确保操作是正确的。


经过上述配置,用户在添加微件前,应用微件列表就会显示该微件的 previewLayoutpreviewImage,还有 descriptionRes 描述信息。

然后用户长按该微件,拖到桌面上,Launcher 会启动 configure 配置的 activity,该 activity 会对微件进行配置,然后更新微件。

最后大致效果如下:

Android 的微件

当然,如果你不配值 configure 属性,即表示不需要配置微件,那么就不会启动配置 activity 了,会直接生成微件,微件显示 initialLayout 的界面,并回调 onUpdate在回调中你可以更新微件。

微件支持的布局与控件

微件的布局控件是有限制的,并不一定所有的布局控件都支持。

以下是支持的布局和控件,注意:并不支持其子类。

1. 布局

“`
AdapterViewFlipper
FrameLayout
LinearLayout
RelativeLayout

前言

我们在 Android 的快捷方式 阐述了如何使用 Android 的快捷方式,快捷方式最多只能显示文字和图标,无法动态的显示更多的信息。而桌面 Launcher 除了显示图标外,其实还可以显示 Widget 微件,也有翻译成小部件,小组件的。看看你的手机,通常都会内置时钟,图库,联系人等应用,定制厂商还会加入天气之类的应用。一般这类信息展示的应用,都会提供微件功能。

要显示应用的微件,通常是长按该应用,点击微件即可显示该应用支持的微件,如下:

Android 的微件

或者是长按桌面(有些深度定制的厂商,是双指内缩的手势)触发桌面页编辑功能,也能看到微件的入口,如下:

Android 的微件

以时钟应用为例,可以将其提供的时间微件放到桌面上,如下:

Android 的微件

并且可以动态的去更新它,显然比快捷方式好多了,事实上他们俩也是完全不一样的概念。

利用好微件,不仅可以将应用的常见功能放到桌面上,还可以增加用户使用应用的频率,常见的微件类型如下:

1. 信息类

比如时钟、天气等应用,用来显示信息的。如上图。

2. 控制类

比如有些系统会增加个快捷方式的集合,如下图:

Android 的微件

3. 混合类

信息和控制可以混合在一起的,比如音乐播放器,不仅可以控制,还可以显示当前播放到哪一首了。

Android 的微件

4. 滚动类

比如新闻列表,或者是图库的图片列表,可以上下滚动的,如下图:

Android 的微件


要理解微件首先要明确一些概念。微件是隶属于应用的,可以理解是应用一部分功能控件,但是它却可以显示在其他应用上,通常是我们的系统桌面,即 Launcher。也就是托管给 Launcher 了,不仅仅是 Launcher,你要是有这方面的需求,也可以自己创建一个去管理和显示其他应用的微件,通常是自定义 Launcher 时需要用到,一般其他应用没有这方面的需求。

也就是说需要有:提供微件的应用托管微件的应用。本文的示例就是我们的应用桌面 Launcher

用户通常会移动微件,设置微件的大小,对于应用来说,一般我们需要以下 3 个方面去处理微件:

  1. 主动更新微件的内容。
  2. 接收处理用户的手势和点击。
  3. 监听微件的增删改查。

有关手势方面,微件最多可以使用点击上下滑动功能。


下面我们开始看看如何创建应用微件。

创建微件

在创建前,我们需要知道当前系统桌面是否支持微件,类似于 在 Android 中使用低功耗蓝牙-客户端 这篇文章中所描述的,使用蓝牙需要判断蓝牙是否可用,使用微件也要先判断 feature,如下在 Manifest 中声明:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"     package="..."     >     <uses-feature android:name="android.software.app_widgets" android:required="false" />     ... </manifest>

在代码中判断:

boolean hasWidgetsFeature = getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);

微件相关的类都在 android.appwidget 包下,数量也较少,先简单介绍下,如下所示:

AppWidgetHost、AppWidgetHostView

托管微件的应用才需要用到,这里不讨论。

AppWidgetManager

微件的管理类,可以更新获取微件,8.0 以后还可以主动请求创建微件。

AppWidgetProvider

微件的提供者,看名字会以为是个 ContentProvider,但它其实是个 BroadcastReceiver,用它可以声明微件的元数据,以及接收用户操作微件时的相关回调。

AppWidgetProviderInfo

微件的元数据类,对应 xml 的 <appwidget-provider> 标签。其中很多字段都是对应该标签下的属性。


大致介绍了相关的类,现在我们来实现一个微件试试。

1. 定义元数据 AppWidgetProviderInfo

元数据的定义是用 xml 配置的,创建 res/xml/appwidget_info.xml,可配置的内容如下:

<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"     android:autoAdvanceViewId="0"     android:configure="com.imyyq.ui.widget.WidgetConfigureActivity"     android:description="@string/widget_des"      android:initialKeyguardLayout="@layout/widget_initial_in_keyguard"     android:initialLayout="@layout/widget_initial"      android:maxResizeHeight="80dp"     android:maxResizeWidth="80dp"     android:minResizeHeight="40dp"     android:minResizeWidth="40dp"     android:minWidth="40dp"     android:minHeight="40dp"      android:previewImage="@android:drawable/ic_btn_speak_now"     android:previewLayout="@layout/widget_preview"      android:resizeMode="horizontal|vertical|none"      android:targetCellHeight="1"     android:targetCellWidth="1"      android:updatePeriodMillis="86400000"     android:widgetCategory="home_screen|keyguard"     android:widgetFeatures="0"     > </appwidget-provider>
  • autoAdvanceViewId
    • 微件布局中子视图的 ID,比如如果你的微件是 StackView,如果设定了此属性,那么在用户没有操作时,托管应用 Launcher 可能会自动间隔去切换 StackView 的下一个卡片。
  • configure
    • 提供配置微件的 activity,Launcher 在添加微件时,会打开它。
  • description
    • 描述微件的说明。
  • initialKeyguardLayout、initialLayout
    • 微件在锁屏和桌面上的初始布局
  • maxResizeHeight、maxResizeWidth、minResizeHeight、minResizeWidth
    • 微件可以调整的最大和最小高宽
  • minWidth、minHeight
    • 微件的最大最小宽高
  • previewImage、previewLayout
    • 预览图和预览布局
  • resizeMode
    • 调整微件大小的规则,可以是水平、垂直,或两个方向,或不可调整
  • targetCellWidth、targetCellHeight
    • 添加到 Launcher 时 微件 的默认宽高,单位是 Launcher 的网格单元为单位。
  • updatePeriodMillis
    • 微件希望的更新频率,毫秒为单位
    • 建议尽可能小,因为此属性并不保证一定准时,另外也会唤醒设备,比较耗电。最好配合 AlarmManager 去使用。
  • widgetCategory
    • 微件可以显示在什么地方,可以是锁屏、桌面。
    • 5.0 以前的 Android 才支持在锁屏上显示微件。5.0 及以上的,只有 home_screen 有效。
  • widgetFeatures
    • 告知微件的托管应用,即 Launcher,微件支持哪些功能

属性在 Android 版本间的支持情况:

  • 9.0 才提供的
    • widgetFeatures
  • 12.0 才提供的
    • targetCellWidth
    • targetCellHeight
    • previewLayout
    • maxResizeWidth
    • maxResizeHeight
    • descriptionRes

其中 Cell 的概念要解释下,在 Launcher 中,通常都是网格的布局,把界面分成一个网格,一个 Cell 就是一个网格,打开布局边界看的清清楚楚,如下图:

Android 的微件

可以看出,图中的设备,是把屏幕宽度分成了 5 个 Cell,每个应用图标和快捷方式占据一个 Cell,而图库的微件,Width 占据了 3 个 Cell 宽,Height 占据了 3 个 Cell 高。

xml 准备好了,如何使用呢?接着看!

2. 注册 AppWidgetProvider 广播

用户在操作微件时,会通过广播的形式告知 APP,比如添加和删除微件。注册如下:

<receiver     android:name=".ui.widget.MyAppWidgetProvider"     android:icon="@android:drawable/ic_delete"     android:exported="true"     >     <intent-filter>         <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />     </intent-filter>     <meta-data         android:name="android.appwidget.provider"         android:resource="@xml/appwidget_info"         /> </receiver>

其中 icon 将会是 AppWidgetProviderInfo 的 icon 字段。
meta-data 声明了此广播对应的微件 xml。action 是必须且值是固定的。

MyAppWidgetProvider 的内容如下:

public class MyAppWidgetProvider extends AppWidgetProvider {     private static final String TAG = "MyAppWidgetProvider";      /**      * 尺寸改变时调用      */     @Override     public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {         Log.i(TAG, "onAppWidgetOptionsChanged: " + appWidgetId);     }      /**      * 当要求应用提供 RemoteViews 时回调。      */     @Override     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {         Log.i(TAG, "onUpdate: " + Arrays.toString(appWidgetIds));     }      /**      * 删除时调用,比 onDisabled 早      */     @Override     public void onDeleted(Context context, int[] appWidgetIds) {         Log.i(TAG, "onDeleted: " + Arrays.toString(appWidgetIds));     }      /**      * 要求应用提供 widget 时就调用了,尽管用户最后可能是 Cancel 的操作      */     @Override     public void onEnabled(Context context) {         Log.i(TAG, "onEnabled: ");     }      /**      * 删除时调用      */     @Override     public void onDisabled(Context context) {         Log.i(TAG, "onDisabled: ");     }      /**      * 从备份中恢复时调用。      * 然后调用 onUpdate      * <p>      * 应该将 AppWidgetManager#OPTION_APPWIDGET_RESTORE_COMPLETED 设置为 true 以指示小部件是否已从提供者端成功恢复。      */     @Override     public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {         Log.i(TAG, "onRestored: old=" + Arrays.toString(oldWidgetIds) + ", new=" + Arrays.toString(newWidgetIds));     } }

注释已经说明了调用的时机。从选择一个微件到完成创建,回调如下:

onEnabled onUpdate

然后删除微件,回调如下:

onDeleted onDisabled

当然用户要是改变微件大小,onAppWidgetOptionsChanged 就会被调用,而且看了源码就知道,其实 AppWidgetProvider 类只是在 onReceive 方法中对以下 Action 进行了判断,拆分成了一个个方法供子类使用:

AppWidgetManager.ACTION_APPWIDGET_UPDATE; AppWidgetManager.ACTION_APPWIDGET_DELETED; AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED; AppWidgetManager.ACTION_APPWIDGET_ENABLED; AppWidgetManager.ACTION_APPWIDGET_DISABLED; AppWidgetManager.ACTION_APPWIDGET_RESTORED;

3. 配置微件的 Activity

configure 配置的 WidgetConfigureActivity 要在 manifest 中注册:

<activity android:name=".ui.widget.WidgetConfigureActivity">     <intent-filter>         <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />     </intent-filter> </activity>

action 也是必须配置的,值也是固定的。

Launcher 在生成该微件前,会先启动此 activity,传入微件 id,在 activity 中可通过 intent 获取:

Intent intent = getIntent(); Bundle extras = intent.getExtras(); if (extras != null) {     appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); }

然后决定好微件的配置后,生成 RemoteViews 去更新微件:

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this); RemoteViews views = new RemoteViews(getPackageName(), R.layout.layout_widget); appWidgetManager.updateAppWidget(appWidgetId, views);

最后将微件 id 回传回去:

Intent resultValue = new Intent(); resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); setResult(RESULT_OK, resultValue); finish();

最好在 activity 启动后,就先 setResult(RESULT_CANCELED),这样用户如果中途取消了,微件也不会生成,即确保操作是正确的。


经过上述配置,用户在添加微件前,应用微件列表就会显示该微件的 previewLayoutpreviewImage,还有 descriptionRes 描述信息。

然后用户长按该微件,拖到桌面上,Launcher 会启动 configure 配置的 activity,该 activity 会对微件进行配置,然后更新微件。

最后大致效果如下:

Android 的微件

当然,如果你不配值 configure 属性,即表示不需要配置微件,那么就不会启动配置 activity 了,会直接生成微件,微件显示 initialLayout 的界面,并回调 onUpdate在回调中你可以更新微件。

微件支持的布局与控件

微件的布局控件是有限制的,并不一定所有的布局控件都支持。

以下是支持的布局和控件,注意:并不支持其子类。

1. 布局

“`
AdapterViewFlipper
FrameLayout
LinearLayout
RelativeLayout

前言

我们在 Android 的快捷方式 阐述了如何使用 Android 的快捷方式,快捷方式最多只能显示文字和图标,无法动态的显示更多的信息。而桌面 Launcher 除了显示图标外,其实还可以显示 Widget 微件,也有翻译成小部件,小组件的。看看你的手机,通常都会内置时钟,图库,联系人等应用,定制厂商还会加入天气之类的应用。一般这类信息展示的应用,都会提供微件功能。

要显示应用的微件,通常是长按该应用,点击微件即可显示该应用支持的微件,如下:

Android 的微件

或者是长按桌面(有些深度定制的厂商,是双指内缩的手势)触发桌面页编辑功能,也能看到微件的入口,如下:

Android 的微件

以时钟应用为例,可以将其提供的时间微件放到桌面上,如下:

Android 的微件

并且可以动态的去更新它,显然比快捷方式好多了,事实上他们俩也是完全不一样的概念。

利用好微件,不仅可以将应用的常见功能放到桌面上,还可以增加用户使用应用的频率,常见的微件类型如下:

1. 信息类

比如时钟、天气等应用,用来显示信息的。如上图。

2. 控制类

比如有些系统会增加个快捷方式的集合,如下图:

Android 的微件

3. 混合类

信息和控制可以混合在一起的,比如音乐播放器,不仅可以控制,还可以显示当前播放到哪一首了。

Android 的微件

4. 滚动类

比如新闻列表,或者是图库的图片列表,可以上下滚动的,如下图:

Android 的微件


要理解微件首先要明确一些概念。微件是隶属于应用的,可以理解是应用一部分功能控件,但是它却可以显示在其他应用上,通常是我们的系统桌面,即 Launcher。也就是托管给 Launcher 了,不仅仅是 Launcher,你要是有这方面的需求,也可以自己创建一个去管理和显示其他应用的微件,通常是自定义 Launcher 时需要用到,一般其他应用没有这方面的需求。

也就是说需要有:提供微件的应用托管微件的应用。本文的示例就是我们的应用桌面 Launcher

用户通常会移动微件,设置微件的大小,对于应用来说,一般我们需要以下 3 个方面去处理微件:

  1. 主动更新微件的内容。
  2. 接收处理用户的手势和点击。
  3. 监听微件的增删改查。

有关手势方面,微件最多可以使用点击上下滑动功能。


下面我们开始看看如何创建应用微件。

创建微件

在创建前,我们需要知道当前系统桌面是否支持微件,类似于 在 Android 中使用低功耗蓝牙-客户端 这篇文章中所描述的,使用蓝牙需要判断蓝牙是否可用,使用微件也要先判断 feature,如下在 Manifest 中声明:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"     package="..."     >     <uses-feature android:name="android.software.app_widgets" android:required="false" />     ... </manifest>

在代码中判断:

boolean hasWidgetsFeature = getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);

微件相关的类都在 android.appwidget 包下,数量也较少,先简单介绍下,如下所示:

AppWidgetHost、AppWidgetHostView

托管微件的应用才需要用到,这里不讨论。

AppWidgetManager

微件的管理类,可以更新获取微件,8.0 以后还可以主动请求创建微件。

AppWidgetProvider

微件的提供者,看名字会以为是个 ContentProvider,但它其实是个 BroadcastReceiver,用它可以声明微件的元数据,以及接收用户操作微件时的相关回调。

AppWidgetProviderInfo

微件的元数据类,对应 xml 的 <appwidget-provider> 标签。其中很多字段都是对应该标签下的属性。


大致介绍了相关的类,现在我们来实现一个微件试试。

1. 定义元数据 AppWidgetProviderInfo

元数据的定义是用 xml 配置的,创建 res/xml/appwidget_info.xml,可配置的内容如下:

<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"     android:autoAdvanceViewId="0"     android:configure="com.imyyq.ui.widget.WidgetConfigureActivity"     android:description="@string/widget_des"      android:initialKeyguardLayout="@layout/widget_initial_in_keyguard"     android:initialLayout="@layout/widget_initial"      android:maxResizeHeight="80dp"     android:maxResizeWidth="80dp"     android:minResizeHeight="40dp"     android:minResizeWidth="40dp"     android:minWidth="40dp"     android:minHeight="40dp"      android:previewImage="@android:drawable/ic_btn_speak_now"     android:previewLayout="@layout/widget_preview"      android:resizeMode="horizontal|vertical|none"      android:targetCellHeight="1"     android:targetCellWidth="1"      android:updatePeriodMillis="86400000"     android:widgetCategory="home_screen|keyguard"     android:widgetFeatures="0"     > </appwidget-provider>
  • autoAdvanceViewId
    • 微件布局中子视图的 ID,比如如果你的微件是 StackView,如果设定了此属性,那么在用户没有操作时,托管应用 Launcher 可能会自动间隔去切换 StackView 的下一个卡片。
  • configure
    • 提供配置微件的 activity,Launcher 在添加微件时,会打开它。
  • description
    • 描述微件的说明。
  • initialKeyguardLayout、initialLayout
    • 微件在锁屏和桌面上的初始布局
  • maxResizeHeight、maxResizeWidth、minResizeHeight、minResizeWidth
    • 微件可以调整的最大和最小高宽
  • minWidth、minHeight
    • 微件的最大最小宽高
  • previewImage、previewLayout
    • 预览图和预览布局
  • resizeMode
    • 调整微件大小的规则,可以是水平、垂直,或两个方向,或不可调整
  • targetCellWidth、targetCellHeight
    • 添加到 Launcher 时 微件 的默认宽高,单位是 Launcher 的网格单元为单位。
  • updatePeriodMillis
    • 微件希望的更新频率,毫秒为单位
    • 建议尽可能小,因为此属性并不保证一定准时,另外也会唤醒设备,比较耗电。最好配合 AlarmManager 去使用。
  • widgetCategory
    • 微件可以显示在什么地方,可以是锁屏、桌面。
    • 5.0 以前的 Android 才支持在锁屏上显示微件。5.0 及以上的,只有 home_screen 有效。
  • widgetFeatures
    • 告知微件的托管应用,即 Launcher,微件支持哪些功能

属性在 Android 版本间的支持情况:

  • 9.0 才提供的
    • widgetFeatures
  • 12.0 才提供的
    • targetCellWidth
    • targetCellHeight
    • previewLayout
    • maxResizeWidth
    • maxResizeHeight
    • descriptionRes

其中 Cell 的概念要解释下,在 Launcher 中,通常都是网格的布局,把界面分成一个网格,一个 Cell 就是一个网格,打开布局边界看的清清楚楚,如下图:

Android 的微件

可以看出,图中的设备,是把屏幕宽度分成了 5 个 Cell,每个应用图标和快捷方式占据一个 Cell,而图库的微件,Width 占据了 3 个 Cell 宽,Height 占据了 3 个 Cell 高。

xml 准备好了,如何使用呢?接着看!

2. 注册 AppWidgetProvider 广播

用户在操作微件时,会通过广播的形式告知 APP,比如添加和删除微件。注册如下:

<receiver     android:name=".ui.widget.MyAppWidgetProvider"     android:icon="@android:drawable/ic_delete"     android:exported="true"     >     <intent-filter>         <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />     </intent-filter>     <meta-data         android:name="android.appwidget.provider"         android:resource="@xml/appwidget_info"         /> </receiver>

其中 icon 将会是 AppWidgetProviderInfo 的 icon 字段。
meta-data 声明了此广播对应的微件 xml。action 是必须且值是固定的。

MyAppWidgetProvider 的内容如下:

public class MyAppWidgetProvider extends AppWidgetProvider {     private static final String TAG = "MyAppWidgetProvider";      /**      * 尺寸改变时调用      */     @Override     public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {         Log.i(TAG, "onAppWidgetOptionsChanged: " + appWidgetId);     }      /**      * 当要求应用提供 RemoteViews 时回调。      */     @Override     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {         Log.i(TAG, "onUpdate: " + Arrays.toString(appWidgetIds));     }      /**      * 删除时调用,比 onDisabled 早      */     @Override     public void onDeleted(Context context, int[] appWidgetIds) {         Log.i(TAG, "onDeleted: " + Arrays.toString(appWidgetIds));     }      /**      * 要求应用提供 widget 时就调用了,尽管用户最后可能是 Cancel 的操作      */     @Override     public void onEnabled(Context context) {         Log.i(TAG, "onEnabled: ");     }      /**      * 删除时调用      */     @Override     public void onDisabled(Context context) {         Log.i(TAG, "onDisabled: ");     }      /**      * 从备份中恢复时调用。      * 然后调用 onUpdate      * <p>      * 应该将 AppWidgetManager#OPTION_APPWIDGET_RESTORE_COMPLETED 设置为 true 以指示小部件是否已从提供者端成功恢复。      */     @Override     public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {         Log.i(TAG, "onRestored: old=" + Arrays.toString(oldWidgetIds) + ", new=" + Arrays.toString(newWidgetIds));     } }

注释已经说明了调用的时机。从选择一个微件到完成创建,回调如下:

onEnabled onUpdate

然后删除微件,回调如下:

onDeleted onDisabled

当然用户要是改变微件大小,onAppWidgetOptionsChanged 就会被调用,而且看了源码就知道,其实 AppWidgetProvider 类只是在 onReceive 方法中对以下 Action 进行了判断,拆分成了一个个方法供子类使用:

AppWidgetManager.ACTION_APPWIDGET_UPDATE; AppWidgetManager.ACTION_APPWIDGET_DELETED; AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED; AppWidgetManager.ACTION_APPWIDGET_ENABLED; AppWidgetManager.ACTION_APPWIDGET_DISABLED; AppWidgetManager.ACTION_APPWIDGET_RESTORED;

3. 配置微件的 Activity

configure 配置的 WidgetConfigureActivity 要在 manifest 中注册:

<activity android:name=".ui.widget.WidgetConfigureActivity">     <intent-filter>         <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />     </intent-filter> </activity>

action 也是必须配置的,值也是固定的。

Launcher 在生成该微件前,会先启动此 activity,传入微件 id,在 activity 中可通过 intent 获取:

Intent intent = getIntent(); Bundle extras = intent.getExtras(); if (extras != null) {     appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); }

然后决定好微件的配置后,生成 RemoteViews 去更新微件:

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this); RemoteViews views = new RemoteViews(getPackageName(), R.layout.layout_widget); appWidgetManager.updateAppWidget(appWidgetId, views);

最后将微件 id 回传回去:

Intent resultValue = new Intent(); resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); setResult(RESULT_OK, resultValue); finish();

最好在 activity 启动后,就先 setResult(RESULT_CANCELED),这样用户如果中途取消了,微件也不会生成,即确保操作是正确的。


经过上述配置,用户在添加微件前,应用微件列表就会显示该微件的 previewLayoutpreviewImage,还有 descriptionRes 描述信息。

然后用户长按该微件,拖到桌面上,Launcher 会启动 configure 配置的 activity,该 activity 会对微件进行配置,然后更新微件。

最后大致效果如下:

Android 的微件

当然,如果你不配值 configure 属性,即表示不需要配置微件,那么就不会启动配置 activity 了,会直接生成微件,微件显示 initialLayout 的界面,并回调 onUpdate在回调中你可以更新微件。

微件支持的布局与控件

微件的布局控件是有限制的,并不一定所有的布局控件都支持。

以下是支持的布局和控件,注意:并不支持其子类。

1. 布局

“`
AdapterViewFlipper
FrameLayout
LinearLayout
RelativeLayout

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » Android 的微件求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们