最近做了一个widget,功能跟android自带的Picture frame功能相似,唯一不同的是,Picture frame是从SDCard中取得图片,而我做的这个widget所找的图片是从Google Picasa上面找,具体操作不是写本文的目的。在这里,我就对widget的用法作一个总结,算是自己记的笔记吧。大家可以与我交流:leehong2005@163.com,由于代码太多,想要代码的可以给我发邮件。

运行效果图:

Android widget hostId如何获取 widgetsmith安卓教程_android

图1. 选择 Picture Frame

Android widget hostId如何获取 widgetsmith安卓教程_android_02

图2. 从SDCard中找图片,它会一个一个地显示出来。

Android widget hostId如何获取 widgetsmith安卓教程_xml_03

图3. 选择要剪切的图片部分

Android widget hostId如何获取 widgetsmith安卓教程_ide_04

图4. 点击 Save 按钮后的界面

Android widget hostId如何获取 widgetsmith安卓教程_xml_05

图5. 在上一界面上,点击 Save 按钮,就会把选择的图片加入到widget中。

Android widget hostId如何获取 widgetsmith安卓教程_xml_06

图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>