最近做了一个widget,功能跟android自带的Picture frame功能相似,唯一不同的是,Picture frame是从SDCard中取得图片,而我做的这个widget所找的图片是从Google Picasa上面找,具体操作不是写本文的目的。在这里,我就对widget的用法作一个总结,算是自己记的笔记吧。大家可以与我交流:leehong2005@163.com,由于代码太多,想要代码的可以给我发邮件。
运行效果图:
图1. 选择 Picture Frame
图2. 从SDCard中找图片,它会一个一个地显示出来。
图3. 选择要剪切的图片部分
图4. 点击 Save 按钮后的界面
图5. 在上一界面上,点击 Save 按钮,就会把选择的图片加入到widget中。
图6. 换屏幕显示方向后,得到的界面。
这个跟android自带的Picture frame功能就是一样的,唯一的就是界面上有些差别,实现上也有些差别,我这个更加简单,系统的那个Picture frame功能很多。
1,widget可以有config,也可以没有,config其实就是一个activity,当运行widget时,就会最先启动这个config。
2,最重要的就是要实现AppWidgetProvider这个类,这个类其实是一个BroadcastReceiver,能接受一些系统发过来的消息等,它有几个方法,在此说明一下:
public void onReceive(Context context, Intent intent)
这个函数里面会调用相应的update, delete, enable, disabled这些方法,你可以在这个方法里处理自定义的消息。
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
当widget要更新时调用,一般在这个函数里设置widget view的数据,如果设置文本值,图片等。
public void onDeleted(Context context, int[] appWidgetIds)
当一个widget删除时,就会调用这个函数。
public void onEnabled(Context context)
如果第一次加widget,就会调用这个函数,如果android系统重启时也会调用这个方法。
public void onDisabled(Context context)
当最后一个widget被删除后,就会调用这个方法,可以在这个函数里进行一些数据清除等。
3,widget一般要每次把数据结存起来,可以存到数据库中,也可以存到share preference里面,因为在android重启后,它要能正确加载数据。
4,widget的处理流程(有config)
1)怎么启动config,也就是说,怎么说这个widget与这个config相关联?
2)config结束后,怎么把数据更新到widget里面?
首先,应该在res/xml新建一个appwidget-provider的.xml文件。这个文件就指定了这个widget的一些属性:
[html] view plain copy
1. <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
2. android:minHeight="146dp"
3. android:minWidth="146dp"
4. android:configure="com.lee.demo.activity.PictureFrameConfig">
5. </appwidget-provider>
这个minHeight和minWidth有一个算法,公式是 cells * 74
- 2。
上面红色标明的就是指定它的config。
其次,在config(是一个activity)里的onCreate里面,先要得到widget的id
[java] view plain copy
1. this.m_appWidgetId = getIntent().getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
2. if (AppWidgetManager.INVALID_APPWIDGET_ID == m_appWidgetId)
3. {
4. setResult(RESULT_CANCELED);
5. finish();
6. }
这个id后在后面用到。
在结束这个config时,要把这个id设置到intent里
[java] view plain copy
1. // 更新widget,说白了就是得到一个RemoteView,把其中的view设置成你要的数据。
2. // ...
3.
4. Intent intent = new Intent();
5. intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, m_appWidgetId);
6. setResult(resultCode, intent);
7. finish();
写得有点麻烦,我直接放代码了吧,如果大家有什么不明白的,可以给我发邮件,我之前写了几个widget,基本上对widget还算明白。
PictureFrameProvider.java
[java] view plain copy
1. public class PictureFrameProvider extends AppWidgetProvider
2. {
3. public static RemoteViews buildUpdateViews(Context context, int appWidgetId)
4. {
5. new PictureDataUtility(context);
6. Bitmap bitmap = helper.getPhoto(appWidgetId);
7. null;
8.
9. if (null != bitmap)
10. {
11. new RemoteViews(context.getPackageName(),
12. R.layout.picture_frame);
13. views.setImageViewBitmap(R.id.photo, bitmap);
14. }
15.
16. helper.close();
17.
18. return views;
19. }
20.
21. public static RemoteViews buildUpdateViews(Context context,
22. int appWidgetId, Bitmap bitmap)
23. {
24. if (null == bitmap)
25. {
26. return null;
27. }
28.
29. new PictureDataUtility(context);
30. helper.setPhoto(appWidgetId, bitmap);
31.
32. new RemoteViews(context.getPackageName(),
33. R.layout.picture_frame);
34. views.setImageViewBitmap(R.id.photo, bitmap);
35. helper.close();
36.
37. return views;
38. }
39.
40. public static void updateWidget(Context context,
41. int appWidgetId, Bitmap bitmap)
42. {
43. RemoteViews views = buildUpdateViews(context, appWidgetId, bitmap);
44. if (null != views)
45. {
46. int[] specificAppWidget = new int[] { appWidgetId };
47. AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
48. appWidgetManager.updateAppWidget(specificAppWidget, views);
49. }
50. }
51.
52. public void onUpdate(Context context,
53. int[] appWidgetIds)
54. {
55. for (int appWidgetId : appWidgetIds)
56. {
57. int[] specificAppWidget = new int[] { appWidgetId };
58. RemoteViews views = buildUpdateViews(context, appWidgetId);
59. appWidgetManager.updateAppWidget(specificAppWidget, views);
60. }
61. }
62. }
这个就是widget所对应的xml定义,它定义了widget长什么样。
[html] view plain copy
1. <?xml version="1.0" encoding="utf-8"?>
2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:layout_width="212px"
4. android:layout_height="148px"
5. android:background="@drawable/appwidget_bg_land">
6. <FrameLayout
7. android:layout_width="fill_parent"
8. android:layout_height="fill_parent"
9. android:paddingLeft="17px"
10. android:paddingTop="16px"
11. android:paddingRight="15px"
12. android:paddingBottom="15px">
13. <ImageView
14. android:id="@+id/photo"
15. android:layout_width="fill_parent"
16. android:layout_height="fill_parent"
17. android:scaleType="centerCrop"
18. android:cropToPadding="true" />
19. </FrameLayout>
20. </FrameLayout>
PictureFrameConfig.java
[java] view plain copy
1. public class PictureFrameConfig extends Activity implements
2. MediaSelectView.IMediaSelectCallback
3. {
4. public static final String RETURN_DATA = "cropped_data";
5. public static final int REQUEST_CROP_PICTURE = 0x02;
6.
7. private int m_appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
8.
9. @Override
10. public void onCreate(Bundle savedInstanceState)
11. {
12. super.onCreate(savedInstanceState);
13.
14. new MediaSelectView(this);
15. mediaView.findMedias();
16. false);
17. this);
18.
19. this.setContentView(mediaView);
20. this.m_appWidgetId = getIntent().getIntExtra(
21. AppWidgetManager.EXTRA_APPWIDGET_ID, m_appWidgetId);
22. if (AppWidgetManager.INVALID_APPWIDGET_ID == m_appWidgetId)
23. {
24. setResult(RESULT_CANCELED);
25. finish();
26. }
27. }
28.
29. @Override
30. public void onCancelClick(View v)
31. {
32. }
33.
34. @Override
35. public void onDoneClick(View v)
36. {
37. }
38.
39. @Override
40. public void onItemClick(int position, View v, ImageInfo data)
41. {
42. new Intent(this, PictureFrameCropper.class);
43. intent.setData(data.imageUri);
44. startActivityForResult(intent, REQUEST_CROP_PICTURE);
45. }
46.
47. @Override
48. protected void onActivityResult(int requestCode, int resultCode, Intent data)
49. {
50. if (REQUEST_CROP_PICTURE == requestCode &&
51. AppWidgetManager.INVALID_APPWIDGET_ID != m_appWidgetId)
52. {
53. resultCode = RESULT_OK;
54. Bitmap bitmap = BitmapUtility.byteArrayToBitmap(
55. data.getByteArrayExtra(RETURN_DATA));
56. this, m_appWidgetId, bitmap);
57. }
58. else
59. {
60. resultCode = RESULT_CANCELED;
61. }
62.
63. new Intent();
64. intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, m_appWidgetId);
65. setResult(resultCode, intent);
66. finish();
67. }
68. }
manifest定义
[html] view plain copy
1. <?xml version="1.0" encoding="utf-8"?>
2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3. package="com.lee.demo.picture" android:versionCode="1"
4. android:versionName="1.0">
5. <application android:icon="@drawable/icon" android:label="@string/app_name"
6. android:theme="@android:style/Theme.Light">
7. <activity android:name="com.lee.demo.activity.PictureFrameConfig"
8. android:label="@string/picture_list_title">
9. <intent-filter>
10. <action android:name="android.intent.action.MAIN" />
11. <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
12. </intent-filter>
13. </activity>
14.
15.
16. <activity android:name="com.lee.demo.activity.PictureFrameCropper"
17. android:label="@string/picture_crop_title">
18. </activity>
19.
20.
21. <receiver android:name="com.lee.demo.provider.PictureFrameProvider">
22. <intent-filter>
23. <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
24. </intent-filter>
25. <meta-data android:name="android.appwidget.provider"
26. android:resource="@xml/picture_widget_provider"/>
27. </receiver>
28. </application>
29. <uses-sdk android:minSdkVersion="7" />
30. </manifest>