AppWidget开发实例讲解(一)

        最近因为项目的原因学习了一下Widget的开发,Widget开发的中文资料,网上的确不是很多,原版的只有那么几份,大家都是抄来抄去的。由于本人接触android仅有半月之久,所以在不了解android的很多机制下,开发APPWidget的开发实在是令人头皮发麻的事情,建议如果是初学android又急于要上手写急于桌面组件的widget程序童鞋,多看看android的activity,service,intent,BroadcastReceiver等这些东东。比如widget中AppWidgetProvider这个关键点东东就是派生自BroadcastReceiver,这样或许能少走一点弯路。

不说其他的了,直接进入正题这个工程主要是为了实现两个功能:USB设备的卸载装载和任务管理器。

1.预实现功能

如图所示:


最首先我们来看看我们的工程目录结构:


2.桌面组件的布局文件和配置文件

从图中可以看出我们使用了三个ImageButton控件,由文件my_app_widget.xml布局而成,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
	android:layout_marginLeft="50dp">
	<ImageButton
	  xmlns:android="http://schemas.android.com/apk/res/android"
	  android:id="@+id/SD_ImgView"
	  android:layout_width="wrap_content"
	  android:layout_height="wrap_content"
	  android:clickable="true">
	</ImageButton>
	<ImageButton
	  xmlns:android="http://schemas.android.com/apk/res/android"
	  android:id="@+id/Task_ImgView"
	  android:layout_width="wrap_content"
	  android:layout_height="wrap_content"
	  android:src="@drawable/taskmanager"
	  android:clickable="true">
	</ImageButton>
		<ImageButton
	  xmlns:android="http://schemas.android.com/apk/res/android"
	  android:id="@+id/USB_ImgView"
	  android:layout_width="wrap_content"
	  android:layout_height="wrap_content"
	  android:clickable="true">
	</ImageButton>
</LinearLayout

我们还可以调整图标的位置和按钮的大小,这些我们就不讲述了。再直接看widget的部署文件,我这里取名也是my_app_widget.xml但是该文件放在src目录的XML文件中,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
  xmlns:android="http://schemas.android.com/apk/res/android"
  	android:minWidth="250dp" 
  	android:minHeight="140dp"
	android:updatePeriodMillis="86400000" 
	android:initialLayout="@layout/my_app_widget"
	>
</appwidget-provider>

上面的这些属性简单介绍下,appwidget-provider:是定义这个xml文件是桌面组件的属性的配置文件

minWidth:是定义这个桌面组件需要显示的最小宽度
minHeight:是定义这个桌面组件需要显示的最小高度
updatePeriodMillis:这个是定义桌面组件刷新的时间,单位是毫秒,定义很大意思是基本不刷新,如果是一些实时性有要求的,那么这个就可以定义成几秒刷新一次,具体根据自己的需求
initialLayout:这个是桌面组件的布局文件的路径,这里意思是该文件在src的layout目录的my_app_widget文件为布局文件(也就是上面那个列出来的源码文件)

3.AppWidgetProvider类的编写

好,完成以上两项以后,也就是我们这个项目的显示部分做完了,那么如何让显示东西能产生点击动作呢,对于桌面组件widget来讲,最重要最关键的AppWidgetProvider这个类,首先我们新建一个AppWidgetProvider类取名EquipmentTaskOperationProvider,然后继承其中的onupdate这个回调函数,代码如下:

EquipmentTaskOperationProvider.java

package com.roadrover.lyf.equipmentTask;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import com.roadrover.lyf.equipmentTask.Configuration.ConfigurationException;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;
import android.widget.RemoteViews;

public class EquipmentTaskOperationProvider extends AppWidgetProvider {
	private static final String SD_CLICK_ACTION = "com.roadrover.lyf.equipmentTask.widgetAppProvider.action.sd.click";
	private static final String USB_CLICK_ACTION = "com.roadrover.lyf.equipmentTask.widgetAppProvider.action.usb.click";
	private static RemoteViews rv;// 更新单元
	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		super.onReceive(context, intent);
		Log.d("myDebug", intent.getAction());
		
	}
	@Override
	public void onUpdate(Context context, AppWidgetManager appWidgetManager,
			int[] appWidgetIds) {
		// TODO Auto-generated method stub
		// super.onUpdate(context, appWidgetManager, appWidgetIds);
		sharedPreferences=context.getSharedPreferences("setting", Context.MODE_PRIVATE);
		final int N = appWidgetIds.length;
		for (int i = 0; i < N; i++) {
			int appWidgetId = appWidgetIds[i];
			Log.d("myDebug", "onUpdate is called!");
			updateAppWidget(context, appWidgetManager, appWidgetId);
		}
	}

	/** Called when the activity is first created. */
	public static void updateAppWidget(Context context,
			AppWidgetManager appWidgeManger, int appWidgetId) {
		Log.d("myDebug", "updateAppWidget is called!");
		if (rv == null) {//第一次创建该组件时实例化更新单元
			rv = new RemoteViews(context.getPackageName(),
					R.layout.my_app_widget);
			//initEquipmentListStatus(appWidgetId, appWidgeManger);
		}
		Intent taskClick = new Intent(context, TasksManager.class);
		Intent sdClick = new Intent(SD_CLICK_ACTION);
		Intent usbClick = new Intent(USB_CLICK_ACTION);
		PendingIntent pendingIntent1;
		PendingIntent pendingIntent2;
		PendingIntent pendingIntent3;
		pendingIntent2 = PendingIntent.getService(context, 0, usbClick, 0);
		pendingIntent1 = PendingIntent.getService(context, 0, sdClick, 0);
		pendingIntent3 = PendingIntent.getActivity(context, 0, taskClick, 0);
		rv.setOnClickPendingIntent(R.id.SD_ImgView, pendingIntent1);
		rv.setOnClickPendingIntent(R.id.Task_ImgView, pendingIntent3);
		rv.setOnClickPendingIntent(R.id.USB_ImgView, pendingIntent2);
		appWidgeManger.updateAppWidget(appWidgetId, rv);
	}
}

这里比较关键的代码我用紫色的字体标记起来,从上面给出的设计效果图来看,我们需要实现上面三个按键的点击事件。首先这里我要讲下为什么我们主要的方法是在onupdate中实现的,因为桌面组件第一次创建首先会调用onenable然后onupdate然后onrecieve,如果有多个该组件被创建了,那么第二次onenable就不会调用了,而只会调用onupdate,所以我们所有的初始化工作在onupdate中进行,代码中有usb点击、sd卡点击和查看已运行的程序点击三个事件,在这里我们使用一个service来处理sd卡和usb设备的点击事件,而使用activity来处理任务管理器事件。查文档可知,通过widget启动servcie可以使用PendingIntent.getService方法,(如果通信方式我们选择不将消息加入到目标servcie的intent-filter中,我们选择PendingIntent.getBroadCast方法),而启动activity则使用getActivity方法,最后通过setOnClickPendingIntent方式进行事件绑定。

4、布局文件与AppWidgetProvider关联

那么如何将xml布局文件与AppWidgetProvider关联起来呢,我们需要在AndroidManifest中加入receiver
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="com.roadrover.lyf.equipmentTask" android:versionCode="1"
	android:versionName="1.0">
	<application android:icon="@drawable/log" android:label="@string/app_name" android:theme="@android:style/Theme.Translucent">
		<receiver android:name=".EquipmentTaskOperationProvider">
			<meta-data android:name="android.appwidget.provider"
				android:resource="@xml/my_app_widget" />
			<intent-filter>
				<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
			</intent-filter>
		</receiver>
		<activity android:name=".TasksManager"></activity>
		<service android:name=".EpOperaService">
			<intent-filter>
				<action
					android:name="com.roadrover.lyf.equipmentTask.widgetAppProvider.action.sd.click" />
				<action
					android:name="com.roadrover.lyf.equipmentTask.widgetAppProvider.action.usb.click" />
			</intent-filter>
		</service>
		<activity android:name=".EpTaskConf">
		</activity>
	</application>
	<uses-sdk android:minSdkVersion="8" />
	<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
</manifest> 
        如源码所示,紫色部分的receiver定义了一个APPWIDET_UPDATE的过滤器,这个就是说当桌面组件创建时发出receiver对象会收到update消息,也就是说EquipmentTaskOperationProvider会调用onupdate回调函数。
        那么按钮如何触发的消息如何传递呢,我们可以看到manifest文件中有加入了.EpOperaService这个Service,而且加入了两个action一个是sd卡click消息另外一个是usb设备click消息,(注意这里的两个消息必须与AppWidgetProvider中定义的两个消息一致)如此当按钮发送了消息之后,便将消息发送至EpOperaService中,我们便可以在service中intent对象中取得传递过来的消息。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值