经常看到一些教程教你如何写appwidget,但是,你知道你的appwidget是如何被添加到桌面上的吗?
    
     一般的,如果是做桌面的童鞋,基本上都会让自己的桌面支持appwidget。下面说说如何实现。
    
     首先是得定义一个承载appwidget的容器,系统的Launcher里面是用的CellLayout,实现的很不错。我这里就用一个简单的自定义ViewGroup来搞定,它是以长按的坐标处为要添加的appwidget的起始位置,简单点说就是按到哪儿就添加到哪儿。

Java代码     收藏代码
  1. package  chroya.demo.widget;  
  2.   
  3. import  android.content.Context;  
  4. import  android.view.MotionEvent;  
  5. import  android.view.View;  
  6. import  android.view.ViewGroup;  
  7.   
  8. /**  
  9.  * 承载widget的容器  
  10.  * @author chroya  
  11.  */   
  12. public   class  WidgetLayout  extends  ViewGroup {  
  13.     //存放touch的坐标   
  14.     private   int [] cellInfo =  new   int [ 2 ];  
  15.     private  OnLongClickListener mLongClickListener;   
  16.   
  17.     public  WidgetLayout(Context context) {  
  18.         super (context);  
  19.         mLongClickListener = new  OnLongClickListener() {  
  20.               
  21.             @Override   
  22.             public   boolean  onLongClick(View v) {  
  23.                   
  24.                 return   false ;  
  25.             }  
  26.         };  
  27.     }  
  28.       
  29.     public   void  addInScreen(View child,  int  width,  int  height) {  
  30.         LayoutParams lp = new  LayoutParams(width, height);  
  31.         lp.x = cellInfo[0 ];  
  32.         lp.y = cellInfo[1 ];  
  33.         child.setOnLongClickListener(mLongClickListener);  
  34.         addView(child, lp);  
  35.     }  
  36.       
  37.     @Override   
  38.     protected   void  onMeasure( int  widthMeasureSpec,  int  heightMeasureSpec) {  
  39.         super .onMeasure(widthMeasureSpec, heightMeasureSpec);  
  40.         LayoutParams lp;  
  41.         for ( int  index= 0 ; index<getChildCount(); index++) {  
  42.             lp = (LayoutParams) getChildAt(index).getLayoutParams();  
  43.             getChildAt(index).measure(  
  44.                     MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, lp.width),   
  45.                     MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, lp.height));  
  46.         }  
  47.     }  
  48.       
  49.     @Override   
  50.     public   boolean  dispatchTouchEvent(MotionEvent event) {  
  51.         cellInfo[0 ] = ( int )event.getX();  
  52.         cellInfo[1 ] = ( int )event.getY();  
  53.         return   super .dispatchTouchEvent(event);  
  54.     }  
  55.   
  56.     @Override   
  57.     protected   void  onLayout( boolean  changed,  int  l,  int  t,  int  r,  int  b) {  
  58.         LayoutParams lp;  
  59.         for ( int  index= 0 ; index<getChildCount(); index++) {  
  60.             lp = (LayoutParams) getChildAt(index).getLayoutParams();  
  61.             getChildAt(index).layout(lp.x, lp.y, lp.x+lp.width, lp.y+lp.height);  
  62.         }  
  63.     }  
  64.       
  65.     public   static   class  LayoutParams  extends  ViewGroup.LayoutParams {  
  66.         int  x;  
  67.         int  y;  
  68.   
  69.         public  LayoutParams( int  width,  int  height) {  
  70.             super (width, height);  
  71.         }         
  72.     }  
  73. }  
 


          然后是重点了。还记得系统默认的桌面上,长按的时候出现的上下文菜单吗?里面有好几个选项,选择widget之后,会弹出一个已经安装的widget列表,选择一个widget之后,就会添加到桌面。我们可以把第一步去掉,长按之后,直接弹出已安装的widget列表,这是一个activity,用AppWidgetManager.ACTION_APPWIDGET_PICK这个Intent来启动,必须带上Extras,下面给出代码中有,不详叙。

Java代码     收藏代码
  1. package  chroya.demo.widget;  
  2.   
  3. import   static  android.util.Log.d;  
  4.   
  5. import  java.util.ArrayList;  
  6.   
  7. import  android.app.Activity;  
  8. import  android.appwidget.AppWidgetHost;  
  9. import  android.appwidget.AppWidgetManager;  
  10. import  android.appwidget.AppWidgetProviderInfo;  
  11. import  android.content.ComponentName;  
  12. import  android.content.Intent;  
  13. import  android.os.Bundle;  
  14. import  android.view.View;  
  15. import  android.view.View.OnLongClickListener;  
  16.   
  17. /**  
  18.  * 添加appwidget  
  19.  * @author chroya  
  20.  *  
  21.  */   
  22. public   class  Main  extends  Activity {  
  23.     private  AppWidgetHost mAppWidgetHost;  
  24.     private  AppWidgetManager mAppWidgetManager;  
  25.     private  WidgetLayout layout;      
  26.       
  27.     private   static   final   int  REQUEST_PICK_APPWIDGET =  1 ;  
  28.     private   static   final   int  REQUEST_CREATE_APPWIDGET =  2 ;    
  29.     private   static   final   int  APPWIDGET_HOST_ID =  0x100 ;  
  30.     private   static   final  String EXTRA_CUSTOM_WIDGET =  "custom_widget" ;  
  31.       
  32.     @Override   
  33.     public   void  onCreate(Bundle savedInstanceState) {  
  34.         super .onCreate(savedInstanceState);  
  35.           
  36.         mAppWidgetManager = AppWidgetManager.getInstance(getApplicationContext());  
  37.         mAppWidgetHost = new  AppWidgetHost(getApplicationContext(), APPWIDGET_HOST_ID);  
  38.         //开始监听widget的变化   
  39.         mAppWidgetHost.startListening();  
  40.           
  41.         layout = new  WidgetLayout( this );  
  42.         layout.setOnLongClickListener(new  OnLongClickListener() {  
  43.               
  44.             @Override   
  45.             public   boolean  onLongClick(View v) {  
  46.                   
  47.                 addWidget();  
  48.                 return   false ;  
  49.             }  
  50.         });  
  51.         setContentView(layout);  
  52.     }  
  53.       
  54.     @Override   
  55.     protected   void  onActivityResult( int  requestCode,  int  resultCode, Intent data) {  
  56.         if  (resultCode == RESULT_OK) {  
  57.             switch  (requestCode) {  
  58.             case  REQUEST_PICK_APPWIDGET:  
  59.                 addAppWidget(data);  
  60.                 break ;  
  61.             case  REQUEST_CREATE_APPWIDGET:  
  62.                 completeAddAppWidget(data);  
  63.                 break ;  
  64.             }  
  65.         } else   if  (requestCode == REQUEST_PICK_APPWIDGET &&  
  66.                 resultCode == RESULT_CANCELED && data != null ) {  
  67.             // Clean up the appWidgetId if we canceled   
  68.             int  appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, - 1 );  
  69.             if  (appWidgetId != - 1 ) {  
  70.                 mAppWidgetHost.deleteAppWidgetId(appWidgetId);  
  71.             }  
  72.         }  
  73.     }  
  74.       
  75.     /**  
  76.      * 选中了某个widget之后,根据是否有配置来决定直接添加还是弹出配置activity  
  77.      * @param data  
  78.      */   
  79.     private   void  addAppWidget(Intent data) {  
  80.         int  appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, - 1 );  
  81.   
  82.         String customWidget = data.getStringExtra(EXTRA_CUSTOM_WIDGET);  
  83.         d("addAppWidget" "data:" + customWidget);  
  84.         if  ( "search_widget" .equals(customWidget)) {  
  85.             //这里直接将search_widget删掉了   
  86.             mAppWidgetHost.deleteAppWidgetId(appWidgetId);  
  87.         } else  {  
  88.             AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);  
  89.               
  90.             d("addAppWidget" "configure:" + appWidget.configure);  
  91.             if  (appWidget.configure !=  null ) {  
  92.                 //有配置,弹出配置   
  93.                 Intent intent = new  Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);  
  94.                 intent.setComponent(appWidget.configure);  
  95.                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);  
  96.   
  97.                 startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);  
  98.             } else  {  
  99.                 //没有配置,直接添加   
  100.                 onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);  
  101.             }  
  102.         }  
  103.     }  
  104.       
  105.     /**  
  106.      * 请求添加一个新的widget  
  107.      */   
  108.     private   void  addWidget() {  
  109.         int  appWidgetId = mAppWidgetHost.allocateAppWidgetId();  
  110.   
  111.         Intent pickIntent = new  Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);  
  112.         pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);  
  113.         // add the search widget   
  114.         ArrayList<AppWidgetProviderInfo> customInfo =  
  115.                 new  ArrayList<AppWidgetProviderInfo>();  
  116.         AppWidgetProviderInfo info = new  AppWidgetProviderInfo();  
  117.         info.provider = new  ComponentName(getPackageName(),  "XXX.YYY" );  
  118.         info.label = "Search" ;  
  119.         info.icon = R.drawable.ic_search_widget;  
  120.         customInfo.add(info);  
  121.         pickIntent.putParcelableArrayListExtra(  
  122.                 AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo);  
  123.         ArrayList<Bundle> customExtras = new  ArrayList<Bundle>();  
  124.         Bundle b = new  Bundle();  
  125.         b.putString(EXTRA_CUSTOM_WIDGET, "search_widget" );  
  126.         customExtras.add(b);  
  127.         pickIntent.putParcelableArrayListExtra(  
  128.                 AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras);  
  129.         // start the pick activity   
  130.         startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);  
  131.     }      
  132.       
  133.     /**  
  134.      * 添加widget  
  135.      * @param data  
  136.      */   
  137.     private   void  completeAddAppWidget(Intent data) {  
  138.         Bundle extras = data.getExtras();  
  139.         int  appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, - 1 );  
  140.   
  141.         d("completeAddAppWidget" "dumping extras content=" +extras.toString());  
  142.         d("completeAddAppWidget" "appWidgetId:" + appWidgetId);  
  143.         AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);  
  144.           
  145.         View hostView = mAppWidgetHost.createView(this , appWidgetId, appWidgetInfo);  
  146.           
  147.         layout.addInScreen(hostView, appWidgetInfo.minWidth, appWidgetInfo.minHeight);          
  148.     }  
  149. }  

 

        运行效果如下:


    
     需要注意的几点:
1。 必须调用AppWidgetHost的startListening方法来监听appwidget的状态变化,否则添加上去的appwidget不会更新的。
2。 需要override一个onActivityResult方法,来接收添加appwidget和appwidget的配置activity的返回值。
3。 启动AppWidgetManager.ACTION_APPWIDGET_PICK这个Intent,必须要给列表中加上自己定义的一个选项,否则出错。如本例中是用的Search。

 

         源码见附件。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐