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

Android Widget——实现桌面小部件一

程序员文章站 2024-02-27 12:53:39
...

前言

      在Android手机中,我们经常会看到各种桌面小部件,天气、音乐播放器、时间表盘等,这些都是桌面小部件的实例。本篇主要介绍桌面小部件开发的一些最基础的知识。

Widget介绍

      Widget并没有运行在我们App的进程中,而是运行在系统的SystemServer进程中。所以意味着我们也不能像在App中那样操作View控件。为了我们能在远程进程中更新界面,Google专门为我们提供了一个RemoteViews类。RemoteViews仅仅表示的是一个View结构。它可以在远程进程中展示和更新界面。
我们先列出要实现Widget的几个核心步骤:

  • widget页面布局

  • 小部件配置信息

  • 写小部件的实现类

  • 在AndroidManifest.xml中声明小部件

 

一.widget页面布局

这个是小部件的布局文件,和活动布局一样

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textView"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="WIDGET"
        android:textSize="25sp"
        android:textColor="#fff"
        android:background="#09c"
        />
</RelativeLayout>

 
注意事项是:Widget并不支持所有的控件跟布局,而仅仅只是支持Android布局和控件的一个子集
1、支持布局:FrameLayout,LinearLayout,RelativeLayout,GridLayout
2、支持控件:AnalogClock,Button,Chronometer,ImageButton,ImageView,ProgressBar,TextView,ViewFlipper,ListView,GridView,StackView,AdapterViewFlipper
 

二.定义小部件配置信息

配置信息主要是设定小部件的一些属性,比如宽高、缩放模式、更新时间间隔等。我们需要在res/xml目录下新建xml文件,文件名字可以任意取。文件内容如下(可做参考):

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minHeight="40dp"
    android:minWidth="40dp"
    android:updatePeriodMillis="86400000"
    android:initialLayout="@layout/widget"
    android:resizeMode="vertical|horizontal"
    android:widgetCategory="home_screen"
    >
</appwidget-provider>

针对上述文件中的配置信息来做下介绍。

  • minHeight、minWidth 定义Widget的最小高度和最小宽度(Widget可以通过拉伸来调整尺寸大小)。

  • previewImage 定义添加小部件时显示的图标。

  • initialLayout 定义了小部件使用的布局。

  • updatePeriodMillis定义小部件自动更新的周期,单位为毫秒。

  • resizeMode 指定了 widget 的调整尺寸的规则。可取的值有: “horizontal”, “vertical”, “none”。“horizontal"意味着widget可以水平拉伸,“vertical”意味着widget可以竖值拉伸,“none”意味着widget不能拉伸;默认值是"none”。

  • widgetCategory 指定了 widget 能显示的地方:能否显示在 home Screen 或 lock screen 或 两者都可以。它的取值包括:“home_screen” 和 “keyguard”。Android 4.2 引入。

     

三.定义小部件实现类

           Widget的功能均是通过AppWidgetProvider来实现的。它是继承自BroadcastReceiver类,也就是一个广播接收者。上面我们提到过RemoteViews是运行在SystemServer进程中的,再结合此处我们应该可以推测小部件的事件应该是通过广播来实现的。像小部件的添加、删除、更新、启用、禁用等均是在AppWidgetProvider中通过接受广播来完成的。

public class MyAppWidgetProvider extends AppWidgetProvider {

    private void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                                int appWidgetId) {
        CharSequence widgetText = "WIDEGET";
        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
        views.setTextViewText(R.id.textView, 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 onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
    }
    /*
     * 当小部件从备份恢复时调用该方法
     */
    @Override
    public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
        super.onRestored(context, oldWidgetIds, newWidgetIds);
    }

    /*
     * 每删除一次窗口小部件就调用一次
     */
    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        super.onDeleted(context, appWidgetIds);
    }

    /*
     * 当该窗口小部件第一次添加到桌面时调用该方法
     */
    @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 onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
    }

}

 

四.在AndroidManifest.xml中声明小部件

<receiver android:name=".MyAppWidgetProvider">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/wdigetresource" />
        </receiver>

此时最简单的小部件已经设置完成了
Android Widget——实现桌面小部件一
Android Widget——实现桌面小部件一

可以实现的更多功能

  • 在桌面显示个时间,每过一秒刷新一次。

我们可以开启一个服务,刷新操作写在服务中。
首先修改MyAppWidgetProvider类

public class MyAppWidgetProvider extends AppWidgetProvider {
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

        for(int appwidgetId : appWidgetIds){
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget);
            remoteViews.setTextViewText(R.id.TextView,"12345");
            appWidgetManager.updateAppWidget(appwidgetId,remoteViews);
        }
    }
	/*
	第一次创建时开启服务
	*/
    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        context.startService(new Intent(context,TimeService.class) );
    }
    @Override
    /*
	最后一个被删除时关闭服务
	*/
    public void onDisabled(Context context) {
        super.onDisabled(context);
        context.stopService(new Intent(context,TimeService.class));
    }
}

写服务类

public class TimeService extends Service {
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Timer timer;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                updata();
            }
        },0,1000);
    }

    private void updata() {
    //更新的逻辑
        String time = sdf.format(new Date());
        RemoteViews remoteViews = new RemoteViews(getPackageName(),R.layout.widget);
        remoteViews.setTextViewText(R.id.TextView,time);
        AppWidgetManager manager = AppWidgetManager.getInstance(getApplicationContext());
        ComponentName componentName =new ComponentName(getApplicationContext(),MyAppWidgetProvider.class);
        manager.updateAppWidget(componentName,remoteViews);

    }
}

最后注册服务以及配置权限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<service android:name=".TimeService"/>

效果图如下:
Android Widget——实现桌面小部件一