之前一直做平台管理工作,没有多少机会写代码,但自己又对这方面比较感兴趣,所以平时无聊时也学学,刚开始看的是普通的应用开发,后来投了一家公司的简历,要求我模仿新浪微博的widget写个功能类似的小部件,这玩意没玩过,不懂,上网找了下结果没找到,由于时间问题,上网问题,最后放弃了。。。蛋疼,废话。下面主要介绍一下小部件的用法。

    小部件可以看成是一个微型的view,和activity不同的是activity同时只能显示一个而小部件可以显示多个。

    通过继承AppWidgetProvider来发布自己的小部件,而容纳widget的叫做AppWidgetHost,比如桌面,下面开始小白式开发

    1.新建一个android工程,去掉前面的Create activity勾勾,然后在当前包下新建一个class,继承自AppWidgetProvider,取名为ShowTimer.

    2.修改AndroidManifest.xml,代码如下

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.qzp.widget"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
	<receiver android:label="@string/app_name" android:name=".ShowTimer">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
            </intent-filter>
            <meta-data android:name="android.appwidget.provider"   android:resource="@xml/provider"></meta-data>
        </receiver>
    </application>
    <uses-sdk android:minSdkVersion="8" />

</manifest>

其中用到一个receive组件,这是因为我们的程序是继承AppWidgetProvider而不是activity,而后者又是继承自BroadcastReceive.

<intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
 </intent-filter>因为我们的小部件要定期更新

重要一行

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

其中的xml/provider是我们自己定义的一个xml,用于描述小部件的位置,布局和更新时间等,代码如下

<?xml version="1.0" encoding="UTF-8"?>
<appwidget-provider
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:initialLayout="@layout/main"
  android:minWidth="146dip" android:minHeight="146dip" android:updatePeriodMillis="5">
</appwidget-provider>

其中的布局main就是一个普通的布局文件(小部件使用的布局组件有限制,不是什么都能用,具体google),

android:updatePeriodMillis="5"更新周期,我在模拟器上好像这个周期不管用,不知道为什么

其中main的布局如下,简单的text+button

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView  
	android:id="@+id/text"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text=""
    />
    <Button android:id="@+id/btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="click">
    </Button>
</LinearLayout>

普通的布局文件,不解释

下面就是主代码了,上代码如下:

package com.qzp.widget;

import android.os.Handler;
import android.util.Log;
import android.widget.RemoteViews;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;

public class ShowTimer extends AppWidgetProvider {
	private Thread mthread;
	private Context mContext;
	private AppWidgetManager appWidgetManager;
	private int []appWidgets;
	@Override
	public void onUpdate(Context context, AppWidgetManager appWidgetManager,
			int[] appWidgetIds) {
		// TODO Auto-generated method stub
		mContext = context;
		this.appWidgetManager = appWidgetManager;
		appWidgets = appWidgetIds;
		createThread(mthread);
		
		super.onUpdate(context, appWidgetManager, appWidgetIds);
	}
	private void createThread(Thread mthread) {
		// TODO Auto-generated method stub
		if(mthread==null){
			mthread = new Thread(new Runnable() {
				public void run() {
					// TODO Auto-generated method stub
					while(true){
						try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						RemoteViews updateView = buildUpdate(mContext);
						appWidgetManager.updateAppWidget(appWidgets, updateView);
						data++;
					}
				}
			});
			mthread.start();
		}
	}
	static int data;
	private RemoteViews buildUpdate(Context context) {
		// TODO Auto-generated method stub
		Log.e("qzp", "ddd");
		RemoteViews updateView = null;
		updateView = new RemoteViews(context.getPackageName(), R.layout.main);
		updateView.setTextViewText(R.id.text,String.valueOf(data));
		Intent launchIntent = new Intent();
		launchIntent.setComponent(new ComponentName("com.android.contacts", "com.android.contacts.ContactsListActivity"));
		launchIntent.setAction(Intent.ACTION_MAIN);
		launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
		launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
		PendingIntent intent = PendingIntent.getActivity(context, 0, launchIntent, 0);
		updateView.setOnClickPendingIntent(R.id.btn, intent);
		return updateView;
	}
	
}

其中的onUpdate()函数是主要函数,每次接受更新时这个函数都会调用,不过我自己试了只有在把小部件添加到桌面的时候会调用一次。。

还有几个callback这里就不说了,看名字就知道什么意思。onDelete(Context,int []),onEnable(),onDisable(Context),onReceive(Context,Intent)....

下面简单解释一下代码,int[] appWidgetIds存放了该小部件的实例id,因为一个小部件可以有多个实例。由于xml中定义的更新不管用,所以我自己开了个线程来更新,每隔一秒钟data加1,然后显示出来。

RemoteViews updateView = buildUpdate(mContext);   //更新试图,看下函数代码就明白了
appWidgetManager.updateAppWidget(appWidgets, updateView);

由于widget是间接继承自BroadcastReceive的,并没有显示view的能力,所以显示需要有小部件Host(宿主)来处理。这之间的通讯

是通过RemoteViews来完成的传递的(IPC).

OK了,终于完成处女作了,简单的不能再简单,不过总是一个开始。。。继续开荒!!