首页 > 代码库 > 桌面控件Widget的使用

桌面控件Widget的使用

开发者文档中详细介绍了Widget的使用方法

 file:///D:/Program%20Files%20(x86)/Andriod/android-sdks/docs/guide/topics/appwidgets/index.html

最终效果展示:

image

1. 首先建立AppWidgetProvider的实体类(AppWidgetProvider class implementation)MyWidget

public class
    AppWidgetProvider
    extends BroadcastReceiver

2. AppWidgetProvider 继承 BroadcastReceiver,所以要在清单文件中进行注册(Declaring an App Widget in the Manifest)

<receiver android:name="com.itheima.mobilesafe.receiver.MyWidget" >
    <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>

3. android:resource="@xml/appwidget_info" 为Widget控件的资源描述(Adding the AppWidgetProviderInfo Metadata)

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/process_widget"
    android:minHeight="72dp"
    android:minWidth="294dp"
    android:updatePeriodMillis="1800000" >
</appwidget-provider>

4. android:initialLayout="@layout/process_widget" 为Widget的布局文件(Creating the App Widget Layout)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/widget_bg_portrait"
    android:gravity="center_vertical" >
    <LinearLayout
        android:layout_width="0.0dip"
        android:layout_height="fill_parent"
        android:layout_marginLeft="5.0dip"
        android:layout_weight="1.0"
        android:background="@drawable/widget_bg_portrait_child"
        android:gravity="center_vertical"
        android:orientation="vertical"
        android:paddingBottom="3.0dip"
        android:paddingTop="3.0dip" >
        <TextView
            android:id="@+id/process_count"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10.0dip"
            android:text="正在运行的软件:15个"
            android:textAppearance="@style/widget_text" />
        <ImageView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="1.0dip"
            android:layout_marginTop="1.0dip"
            android:background="@drawable/widget_bg_portrait_child_divider" />
        <TextView
            android:id="@+id/process_memory"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10.0dip"
            android:text="可用内存:391.80MB"
            android:textAppearance="@style/widget_text" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="vertical" >
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_vertical" >
            <ImageView
                android:layout_width="20.0dip"
                android:layout_height="20.0dip"
                android:src="@drawable/ic_launcher" />
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/app_name"
                android:textColor="@color/textColorPrimary" />
        </LinearLayout>
        <Button
            android:id="@+id/btn_clear"
            android:layout_width="90.0dip"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginTop="5.0dip"
            android:background="@drawable/function_greenbutton_selector"
            android:text="一键清理"
            android:textColor="@color/function_greenbutton_textcolor_selector" />
    </LinearLayout>
</LinearLayout>

5. 在服务(UpdateWidgetService)中更新Widget(Using the AppWidgetProvider Class)

服务为四大组件之一,需要在清单文件注册.
<service android:name="com.itheima.mobilesafe.service.UpdateWidgetService" />

服务开启时设置定时更新Widget

@Override
    public void onCreate() {
		
	// 获取Widget的管理器
	awm = AppWidgetManager.getInstance(this);
	startWidgetUpdate();
	super.onCreate();
}
定时5秒更新桌面Widget
/**
 * 定时5秒更新Widget
 */
private void startWidgetUpdate() {
    timer = new Timer();
    task = new TimerTask() {
	@Override
	public void run() {
	    Log.i(TAG, "更新Widgit");
	    // 更新另外一个进程的操作。ipc调用,进程间通讯(inner process communication)
	    RemoteViews views = new RemoteViews(getPackageName(), R.layout.process_widget);
	    // 指定要更新的是哪一个widget
	    ComponentName provider = new ComponentName(getApplicationContext(), MyWidget.class);
	    views.setTextViewText(R.id.process_count, "正在运行进程数:"+SystemInfoUtils.getRunningProcessCount(getApplicationContext())+"");
	    views.setTextViewText(R.id.process_memory, "可用内存:"+Formatter.formatFileSize(getApplicationContext(), SystemInfoUtils.getAvailRam(getApplicationContext())));
	    //设置"一键清理"按钮的点击事件,因为在不同的进程之间,所以采用延期意图
	    //一个延期的意图 发送自定义的广播
	    Intent intent = new Intent();
	    intent.setAction("com.itheima.mobilesafe.killall");
	    //FLAG_UPDATE_CURRENT如果两次点击第一次没有执行,第二次将第一次覆盖
	    PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
	    views.setOnClickPendingIntent(R.id.btn_clear, pendingIntent);
	    awm.updateAppWidget(provider, views);
	}
    };
    timer.schedule(task, 0, 5000);// 每隔5秒钟执行一次。
}
服务关闭时关闭定时器,释放资源
@Override
public void onDestroy() {
	
    //服务停止关闭屏幕广播接受者
    unregisterReceiver(offReceiver);
    unregisterReceiver(onReceiver);
    offReceiver = null;
    onReceiver = null;
		
    super.onDestroy();
}

6. 点击按钮“一键清理”的时候,因为该空间属于桌面程序,所以只能通过延期意图的方式进程间通讯

发送自定义广播intent.setAction("com.itheima.mobilesafe.killall");

7. 编写广播接受者KillAllReceiver,接受广播,执行操作

广播为四大组件之一,在清单文件注册

<receiver android:name="com.itheima.mobilesafe.receiver.KillAllReceiver">
    <intent-filter>
        <action android:name="com.itheima.mobilesafe.killall"/>
    </intent-filter>
</receiver>

当广播到来,杀死所有能杀死的进程

@Override
public void onReceive(Context context, Intent intent) {
    Log.i(TAG, "接收到自定义广播,杀死所有进程");
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> infos = am.getRunningAppProcesses();
    for (RunningAppProcessInfo info : infos) {
	am.killBackgroundProcesses(info.processName);
	Log.i(TAG, info.processName+"被杀死了");
    }
}

8. 那么就带来一个问题,我们在什么时候开启服务(UpdateWidgetService)比较合适?

当然可以程序一运行就开启服务,如果不添加桌面Widget这不就是一种资源浪费?那么来研究下Widget的生命周期

第一个widget被创建
06-25 07:50:00.923: I/System.out(8130): onEnabled     (第一个widget被创建的时候 执行 onenable 适合做widget初始化)
06-25 07:50:00.923: I/System.out(8130): onreceiver
06-25 07:50:00.923: I/System.out(8130): onUpdate     只要有新的widget被创建都会调用update方法。 当时间片到了。
06-25 07:50:00.923: I/System.out(8130): onreceiver

第二个widget被创建 (新的创建)
06-25 07:51:11.605: I/System.out(8130): onUpdate
06-25 07:51:11.605: I/System.out(8130): onreceiver

移除一个widget
06-25 07:51:56.966: I/System.out(8130): onDeleted
06-25 07:51:56.966: I/System.out(8130): onreceiver

最后一个被移除
06-25 07:52:19.936: I/System.out(8130): onDeleted
06-25 07:52:19.936: I/System.out(8130): onreceiver
06-25 07:52:19.936: I/System.out(8130): onDisabled   最后一个widget被移除 适合 应用程序的销毁 反初始化 擦屁股
06-25 07:52:19.936: I/System.out(8130): onreceiver

 
发现第一个创建会执行onEnabled,最后一个移除会执行onDisabled,则可以在onEnable中开启更新服务,在onDisable中关闭更新服务。
@Override
public void onEnabled(Context context) {
    // 创建桌面Widget的时候开启更新服务
    Log.i(TAG, "开启Widget更新服务");
    Intent intent = new Intent(context, UpdateWidgetService.class);
    context.startService(intent);
    super.onEnabled(context);
}
@Override
public void onDisabled(Context context) {
    // 移除所有Widget的时候关闭更新服务
    Log.i(TAG, "关闭Widget更新服务");
    Intent intent = new Intent(context, UpdateWidgetService.class);
    context.stopService(intent);
    super.onDisabled(context);
}

以防服务更新死掉,在定时30分钟更新时再次将服务激活

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    // 设定时间更新Widget时,再次激活服务
    Log.i(TAG, "定时Widget更新服务");
    Intent intent = new Intent(context, UpdateWidgetService.class);
    context.startService(intent);
    super.onUpdate(context, appWidgetManager, appWidgetIds);
}

9. 那么当手机处于待机状态下我们是不需要更新桌面Widget的,则应当监听屏幕锁闭和屏幕开启的广播

在屏幕锁闭的情况下,关闭定时器释放资源,屏幕开启的广播到来开启定时器,定时更新。
在代码内注册广播接受者
//屏幕锁定内部类广播接受者
private class InnerScreenOffReceiver extends BroadcastReceiver {
    private static final String TAG = "InnerScreenOffReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
	Log.i(TAG, "屏幕锁定");
	// 屏幕锁定取消定时器
	if(timer != null && task != null) {
	    timer.cancel();
	    task.cancel();
	    timer = null;
	    task = null;
	}
    }
}
//屏幕锁定内部类广播接受者
private class InnerScreenOnReceiver extends BroadcastReceiver {
    private static final String TAG = "InnerScreenOnReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "屏幕解锁");
        // 屏幕开启建立定时器
        if(timer == null && task == null) {
            startWidgetUpdate();
        }
    }
}
服务开启时注册广播
@Override
public void onCreate() {
    //注册屏幕锁闭广播接受者
    offReceiver = new InnerScreenOffReceiver();
    IntentFilter offFilter = new IntentFilter();
    offFilter.addAction(Intent.ACTION_SCREEN_OFF);
    registerReceiver(offReceiver, offFilter);
    //注册屏幕开启广播接受者
    onReceiver = new InnerScreenOnReceiver();
    IntentFilter onFilter = new IntentFilter();
    onFilter.addAction(Intent.ACTION_SCREEN_ON);
    registerReceiver(onReceiver, onFilter);
	
    // 获取Widget的管理器
    awm = AppWidgetManager.getInstance(this);
    startWidgetUpdate();
    super.onCreate();
}
服务停止时反注册广播
@Override
public void onDestroy() {
    // 服务停止关闭定时操作
    if(timer != null && task != null) {
        timer.cancel();
        task.cancel();
        timer = null;
        task = null;
    }
	
        // 服务停止关闭屏幕广播接受者
        unregisterReceiver(offReceiver);
        unregisterReceiver(onReceiver);
        offReceiver = null;
        onReceiver = null;
        super.onDestroy();
}

自此,桌面Widget设置已完成,下面是UpdateWidgetService用到的得到运行进程数以及得到可用内存的工具类

/**
 * 得到系统内存信息的工具类
 * 
 * @author zwenkai
 * 
 */
public class SystemInfoUtils {
    /**
     * 得到运行的进程总个数
     * 
     * @param context
     * @return 运行进程个数
     */
    public static int getRunningProcessCount(Context context) {
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        return am.getRunningAppProcesses().size();
	}
    /**
     * 得到可用内存数
     * 
     * @param context
     * @return
     */
    public static long getAvailRam(Context context) {
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        MemoryInfo outInfo = new MemoryInfo();
        am.getMemoryInfo(outInfo);
        return outInfo.availMem;
    }
}