Android中实现Launcher功能之二 ----- 添加窗口小部件以及AppWidget的创建详解

简介:

    今天给大家展现的如何创建一个窗口小部件(AppWidget)以及如何在自己的应用程序中添加窗口小部件(AppWidget)。

         本文组织如下:

                       第一部分:创建一个窗口小部件;

                       第二部分:在自己的应用程序中添加窗口小部件(AppWidget)。

                                                                                                                                                      

           整个功能其实实现起来也是很简单的,只望大家能耐着性子点看 。

 

                                                第一部分 、创建一个窗口小部件

 

           网上资料已经许多了 ,在此先给大家推荐几个不错的Blog ,大家可以先去看看:

                                         1、SDK对AppWidget的介绍                                   

                                         2、Android 桌面组件【widget】初探

   

        从中我们知道:

 

           1、每个AppWidget都有一个AppWidgetProviderInfo对象,该对象描述了每个AppWidget的基本数据(meta-data)信息 ,

              其定义在<appwidget-provider>节点信息。

 

     2每个AppWidget都对应一个RemoteViews视图对象,该RemoteViews提供了特定AppWidget的展示(View视图)和操作

              (例如,点击该RemoteViews会跨进程处理一些事情)。

                                 关于RemoteViews类请参考Android中文API(138) —— RemoteViews

 

          3、AppWidgetManager类维护了应用程序中所有的AppWidget,并且为给每个AppWidget特定的Id去标识他们(一般我们

             用 appWidgetId去标识)。通过给定的appWidgetId,AppWidgetManager可以管理对应的AppWidget,例如:更新该

             AppWidgetId的RemoteViews视图,删除该AppWidget对象等 。

 

          4、AppWidgetProvider广播类从来说是一个监听器,系统把对AppWidget的操作(例如,创建和更新等)分发给

             AppWidgetProvider类去处理。

 

 

           对每个AppWidget,我们可以创建多个其多个实例,当然这些实例对应于不同的appWidgetId。 假设存在这么个

       MyAppWidgetProvider广播类,以及对应的MyAppWidgetProviderInfo对象。 那么,则存在如下关系:

 

            MyAppWidgetProvider.class : 代表了由该MyAppWidgetProvider创建的窗口小部件(AppWidget)的类型,一般用

       CompontentName对象形式表示 。 那么存在如下关系:

 

                      

 

          从上图可是,每个appWidget都对应于一个MyAppWidgetProvider类,于是当任何一个appWidgetId发生变化时,我们需要

    同步其他实例,保持同步性。

 

   AppWidgetProviderInfo类补充说明:

                     publicComponentNameconfigure: 一般为一个Activity,表明该Activity复杂需要管理AppWidget的创建操作。

                      public int updatePeriodMillis:用来更新AppWidget,但该属性在SDK1.5已废除

  

  AppWidgetProvider类介绍:

         常用方法:

              onDeleted()   : 当该类型的AppWidget每次被删除时,调用此方法

              onDisabled() : 当该类型的窗口小部件(AppWidget)全被删除时,调用此方法

              onEnabled() : 当第一次创建该类型的AppWidget时,调用此方法

                  onReceive() : 广播接受者方法  , 用来接受广播消息

                  onUpdate()   : 每次创建该类型的AppWidget都会调用此方法 , 通常来说我们需要在该方法里为该AppWidget指定

                        RemoteViews对象。

 

  AppWidgetManager类介绍:

        常用常量:

             ACTION_APPWIDGET_PICK= "android.appwidget.action.APPWIDGET_PICK“

                   说明:列出所有能够创建AppWidget的对象,该对象一般为自定义的AppWidgetProvider广播接受者。  

                      注意:发送该Intent必须添加附加值:EXTRA_APPWIDGET_ID 。

                         EXTRA_APPWIDGET_ID含义:该appWidgetId与我们发送Action为ACTION_APPWIDGET_PICK 后

                    所选择的AppWidget绑定。因此,我们可以通过这个appWidgetId获取该AppWidget的信息了。

 

           ACTION_APPWIDGET_CONFIGURE= "android.appwidget.action.APPWIDGET_CONFIGURE”

                 说明: 如果选择的AppWidget配置了android:configure 属性,需要再次启动性对应的Activity,继而进一步去选择

                       AppWidget。同时发送该Intent必须添加附加值:EXTRA_APPWIDGET_ID,含义同上。

 

     常用方法:

 

           public int[]  getAppWidgetIds(ComponentName provider)

             功能:获取对应ComponentName类型的所有appWidgetId

             参数说明:   provider  通常为 XXXAppProvider.class类型

 

          publicAppWidgetProviderInfo  getAppWidgetInfo(int appWidgetId)  

             功能: 获取特定appWidgetId对应的AppWidgetProviderInfo对象

 

        public staticAppWidgetManager  getInstance(Contextcontext)

             功能: 获取 AppWidgetManager对象   

 

 

       public void updateAppWidget(int appWidgetId, RemoteViews views)

            功能: 以特定的views视图更新appWidgetId的窗口小部件(AppWidget) 。同时会发送ACTION_APPWIDGET_UPDATE广播

 

       public void  updateAppWidget(int[] appWidgetIds,RemoteViews views)

           功能:以特定的views视图更新所有appWidgetIds的窗口小部件(AppWidget),同时发送ACTION_APPWIDGET_UPDATE

                   广播

 

        public void  updateAppWidget(ComponentName provider, RemoteViews views)

          功能: 已特定的views更新组件类型为provider的所有窗口小部件(AppWidget),同时发送ACTION_APPWIDGET_UPDATE

                  广播。

 

示例Demo :

 

       说明:创建一个简单的AppWidget实例,点击按钮后可以更该图片资源显示 ,具体代码在

        截图为:

                                           

                      

     

关于如何创建一个AppWidget的教材,我也不再多说了,大家可以参考上面我提到的两篇重量级博客去学习:

                         

                          1、SDK对AppWidget的介绍

                          2、Android 桌面组件【widget】初探

 

          

                        PS: 具体代码可在后面下载 。

 

   再次强调一点,每个AppWidget都对应与AppWidgetProvider , 我们需要同步更新这些AppWidget对象。

 

 

 

 

                                第二部分:在自己的应用程序中添加窗口小部件(AppWidget)

 

 

          本部分的主要功能是像Launchcer那样添加AppWidget 。 知识点介绍如下:

 

  1、AppWidgetHost 类

            

              功能:对每个应用程序App,该类提供了和AppWidgetService(该AppWidgetService用来管理所有AppWidget,类似于

     NotificationManagerService系统服务管理所有Notifciation,不懂?其实我也不懂,知其大意即可)交互,用来更新、管理

     AppWidget。打个比喻:AppWidgetHost是宿主对象,每个AppWidget都是寄生虫,可以附加在(显示)AppWidgetHost上。

             每个能添加、显示AppWidget的Activity都是一个AppWidgetHost对象,比如Launcher.java(Activity对象),以及我们后面自定

    义的MainActivity.java(Activity对象)。

       

   常用方法为:

         public AppWidgetHost(Context context, int hostId)

              功能:构造一个AppWidgetHost对象

              参数:  hostId   大意是该AppWidgetHost(宿主对象)对应的Id号,一般赋予一整数即可。 

       public int allocateAppWidgetId()

             功能:申请一个新的appWidgetId ,该id会与新创建的AppWidget绑定。

       public void startListening()

            功能:监听所有AppWidget的变化 ,该方法必须在Activity的onCreate()/onStart()调用,否则 AppWidget是不会得到更新的

       public void stopListening()

            功能: 对应于startListening(),即停止对AppWidget的更新监听。可以在Activity的onStop()方法里调用 ,

                   一般无需调用此方法去停止监听。

 

     public final  AppWidgetHostView    createView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget) 

       功能: 根据指定的appWidgetId以及AppWidgetProviderInfo对象去构建一个AppWidgetHostView对象(具体该对象,

          参见下 文)。

 

 

AppWidgetHostView 与 RemoteViews的区别

 

         对每个AppWidget内部而言,都有一个RemoteViews对象,用于视图显示;而对于外部而已,则以AppWidgetHostView

   形式 这个RemoteViews视图。换句话来说就是,AppWidgetHost对象而言,它并不知道RemoteViews存在,而只是知

   RemoteViews 的代言人AppWidgetHostView。

 

 

         好了 ,该说明的都说明了,下面最后给大家补充一下如何利用在自己的应用程序里添加窗口小部件(AppWidget) 。

  也是两步走:

 

          第一步:  发送Action为ACTION_APPWIDGET_PICK的 Intent ,则所有能创建窗口小部件的AppWidgetProvider的广播

                接收者都会显示 ,同时为该新创建的AppWidget分配一个appWidgetId ,该appWidgetId即可唯一标记我们选择的

               AppWidget。

 

         第二步:如果选择的AppWidget对应地AppWidgetProviderInfo对象配置了android:configure属性,则需要在此启动该配置

            属性(一般为一个Activity类) ,然后在完成添加AppWidget的操作 ; 否则,没有配置android:configure属性,就可以添加

           AppWidget的操作。

 

    示例Demo截图:

                                                          

 

 

   主工程流程如下:  

 

[java]  view plain copy print ?
  1. package com.qin.addappwidget;  
  2.   
  3.   
  4. import android.app.Activity;  
  5. import android.appwidget.AppWidgetHost;  
  6. import android.appwidget.AppWidgetHostView;  
  7. import android.appwidget.AppWidgetManager;  
  8. import android.appwidget.AppWidgetProviderInfo;  
  9. import android.content.Intent;  
  10. import android.os.Bundle;  
  11. import android.util.Log;  
  12. import android.view.View;  
  13. import android.widget.Button;  
  14. import android.widget.ImageView;  
  15. import android.widget.LinearLayout;  
  16. import android.widget.TextView;  
  17. import android.widget.Toast;  
  18.   
  19. public class MainActivity extends Activity  
  20. {  
  21.   
  22.     private static String TAG = "AddAppWidget" ;  
  23.       
  24.     private Button btAddShortCut;  
  25.     private LinearLayout linearLayout ;  // 装载Appwidget的父视图  
  26.       
  27.     private static final int MY_REQUEST_APPWIDGET = 1;  
  28.     private static final int MY_CREATE_APPWIDGET = 2;  
  29.       
  30.     private static final int HOST_ID = 1024 ;  
  31.       
  32.     private AppWidgetHost  mAppWidgetHost = null ;  
  33.     AppWidgetManager appWidgetManager = null;  
  34.       
  35.     /** Called when the activity is first created. */  
  36.     @Override  
  37.     public void onCreate(Bundle savedInstanceState)  
  38.     {   
  39.         super.onCreate(savedInstanceState);  
  40.         setContentView(R.layout.main);  
  41.   
  42.         btAddShortCut = (Button) findViewById(R.id.bt_addShortcut);  
  43.   
  44.         linearLayout = (LinearLayout)findViewById(R.id.linearLayout) ;  
  45.         
  46.         //其参数hostid大意是指定该AppWidgetHost 即本Activity的标记Id, 直接设置为一个整数值吧 。  
  47.         mAppWidgetHost = new AppWidgetHost(MainActivity.this, HOST_ID) ;   
  48.           
  49.         //为了保证AppWidget的及时更新 , 必须在Activity的onCreate/onStar方法调用该方法  
  50.         // 当然可以在onStop方法中,调用mAppWidgetHost.stopListenering() 停止AppWidget更新  
  51.         mAppWidgetHost.startListening() ;  
  52.           
  53.         //获得AppWidgetManager对象  
  54.         appWidgetManager = AppWidgetManager.getInstance(MainActivity.this) ;  
  55.           
  56.           
  57.         btAddShortCut.setOnClickListener(new View.OnClickListener()  
  58.         {  
  59.             @Override  
  60.             public void onClick(View v)  
  61.             {  
  62.                  //显示所有能创建AppWidget的列表 发送此 ACTION_APPWIDGET_PICK 的Action  
  63.                  Intent  pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK) ;  
  64.                    
  65.                  //向系统申请一个新的appWidgetId ,该appWidgetId与我们发送Action为ACTION_APPWIDGET_PICK  
  66.                  //  后所选择的AppWidget绑定 。 因此,我们可以通过这个appWidgetId获取该AppWidget的信息了  
  67.                    
  68.                  //为当前所在进程申请一个新的appWidgetId   
  69.                  int newAppWidgetId = mAppWidgetHost.allocateAppWidgetId() ;  
  70.                  Log.i(TAG, "The new allocate appWidgetId is ----> " + newAppWidgetId) ;  
  71.                    
  72.                  //作为Intent附加值 , 该appWidgetId将会与选定的AppWidget绑定                 
  73.                  pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, newAppWidgetId) ;  
  74.                    
  75.                  //选择某项AppWidget后,立即返回,即回调onActivityResult()方法   
  76.                  startActivityForResult(pickIntent , MY_REQUEST_APPWIDGET) ;  
  77.                                     
  78.             }  
  79.         });  
  80.     }  
  81.   
  82.     // 如果  
  83.     protected void onActivityResult(int requestCode, int resultCode, Intent data)  
  84.     {  
  85.         //直接返回,没有选择任何一项 ,例如按Back键  
  86.         if(resultCode == RESULT_CANCELED)  
  87.            return ;  
  88.           
  89.         switch(requestCode){  
  90.             case MY_REQUEST_APPWIDGET :  
  91.                 Log.i(TAG, "MY_REQUEST_APPWIDGET intent info is -----> "+data ) ;  
  92.                 int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID , AppWidgetManager.INVALID_APPWIDGET_ID) ;  
  93.                   
  94.                 Log.i(TAG, "MY_REQUEST_APPWIDGET : appWidgetId is ----> " + appWidgetId) ;  
  95.                   
  96.                 //得到的为有效的id  
  97.                 if(appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID){  
  98.                     //查询指定appWidgetId的 AppWidgetProviderInfo对象 , 即在xml文件配置的<appwidget-provider />节点信息  
  99.                     AppWidgetProviderInfo appWidgetProviderInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) ;  
  100.                       
  101.                     //如果配置了configure属性 , 即android:configure = "" ,需要再次启动该configure指定的类文件,通常为一个Activity  
  102.                     if(appWidgetProviderInfo.configure != null){  
  103.                           
  104.                         Log.i(TAG, "The AppWidgetProviderInfo configure info -----> " + appWidgetProviderInfo.configure ) ;  
  105.                           
  106.                         //配置此Action  
  107.                         Intent intent  = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE) ;  
  108.                         intent.setComponent(appWidgetProviderInfo.configure) ;  
  109.                         intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);  
  110.                           
  111.                           
  112.                         startActivityForResult(intent , MY_CREATE_APPWIDGET) ;                 
  113.                     }  
  114.                     else  //直接创建一个AppWidget  
  115.                         onActivityResult(MY_CREATE_APPWIDGET , RESULT_OK , data) ;  //参数不同,简单回调而已                         
  116.                 }             
  117.                 break ;  
  118.             case  MY_CREATE_APPWIDGET:  
  119.                 completeAddAppWidget(data) ;  
  120.                 break ;  
  121.         }  
  122.           
  123.     }  
  124.      
  125.     //向当前视图添加一个用户选择的  
  126.     private void completeAddAppWidget(Intent data){  
  127.         Bundle extra = data.getExtras() ;  
  128.         int appWidgetId = extra.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID , -1) ;  
  129.         //等同于上面的获取方式  
  130.         //int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID , AppWidgetManager.INVALID_APPWIDGET_ID) ;  
  131.           
  132.         Log.i(TAG, "completeAddAppWidget : appWidgetId is ----> " + appWidgetId) ;  
  133.           
  134.         if(appWidgetId == -1){  
  135.             Toast.makeText(MainActivity.this"添加窗口小部件有误", Toast.LENGTH_SHORT) ;  
  136.             return ;  
  137.         }  
  138.           
  139.         AppWidgetProviderInfo appWidgetProviderInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) ;  
  140.           
  141.         AppWidgetHostView hostView = mAppWidgetHost.createView(MainActivity.this, appWidgetId, appWidgetProviderInfo);  
  142.                   
  143.         //linearLayout.addView(hostView) ;   
  144.           
  145.         int widget_minWidht = appWidgetProviderInfo.minWidth ;  
  146.         int widget_minHeight = appWidgetProviderInfo.minHeight ;  
  147.         //设置长宽  appWidgetProviderInfo 对象的 minWidth 和  minHeight 属性  
  148.         LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(widget_minWidht, widget_minHeight);  
  149.         //添加至LinearLayout父视图中  
  150.         linearLayout.addView(hostView,linearLayoutParams) ;         
  151.     }  
  152. }  

 

         

           最后 ,关于AppWidget点击后触发的相应事件,是通过设置该AppWidget的RemoteViews的某个控件点击事件而触发的。

 




本文转自wanqi博客园博客,原文链接:http://www.cnblogs.com/wanqieddy/archive/2012/05/05/2484533.html如需转载请自行联系原作者


相关文章
|
4月前
|
Android开发
Android Stadio Build 窗口字符串乱码问题
在使用Android Studio过程中,如果遇到Build窗口字符串乱码问题,可以通过编辑`studio.vmoptions`文件添加`-Dfile.encoding=UTF-8`配置并重启Android Studio来解决。
183 1
Android Stadio Build 窗口字符串乱码问题
|
2月前
|
Android开发
Android开发表情emoji功能开发
本文介绍了一种在Android应用中实现emoji表情功能的方法,通过将图片与表情字符对应,实现在`TextView`中的正常显示。示例代码展示了如何使用自定义适配器加载emoji表情,并在编辑框中输入或删除表情。项目包含完整的源码结构,可作为开发参考。视频演示和源码详情见文章内链接。
74 4
Android开发表情emoji功能开发
|
2月前
|
安全 Android开发 iOS开发
Android vs iOS:探索移动操作系统的设计与功能差异###
【10月更文挑战第20天】 本文深入分析了Android和iOS两个主流移动操作系统在设计哲学、用户体验、技术架构等方面的显著差异。通过对比,揭示了这两种系统各自的独特优势与局限性,并探讨了它们如何塑造了我们的数字生活方式。无论你是开发者还是普通用户,理解这些差异都有助于更好地选择和使用你的移动设备。 ###
55 3
|
4月前
|
编解码 测试技术 Android开发
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
本文详细介绍了如何利用CameraX库实现高质量的照片及视频拍摄功能,包括添加依赖、初始化、权限请求、配置预览与捕获等关键步骤。此外,还特别针对不同分辨率和帧率的视频拍摄提供了性能优化策略,确保应用既高效又稳定。
414 1
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
|
3月前
|
API Android开发 数据安全/隐私保护
Android经典实战之窗口和WindowManager
本文介绍了Android开发中“窗口”的基本概念及其重要性。窗口是承载用户界面的基础单位,而`WindowManager`系统服务则负责窗口的创建、更新和移除等操作。了解这些概念有助于开发复杂且用户体验良好的应用。
70 2
|
3月前
|
Android开发 开发者
Android平台无纸化同屏如何实现实时录像功能
Android平台无纸化同屏,如果需要本地录像的话,实现难度不大,只要复用之前开发的录像模块的就可以,对我们来说,同屏采集这块,只是数据源不同而已,如果是自采集的其他数据,我们一样可以编码录像。
|
4月前
|
图形学 Android开发
小功能⭐️Unity调用Android常用事件
小功能⭐️Unity调用Android常用事件
|
6月前
|
Android开发 开发者
Android UI设计中,Theme定义了Activity的视觉风格,包括颜色、字体、窗口样式等,定义在`styles.xml`。
【6月更文挑战第26天】Android UI设计中,Theme定义了Activity的视觉风格,包括颜色、字体、窗口样式等,定义在`styles.xml`。要更改主题,首先在该文件中创建新主题,如`MyAppTheme`,覆盖所需属性。然后,在`AndroidManifest.xml`中应用主题至应用或特定Activity。运行时切换主题可通过重新设置并重启Activity实现,或使用`setTheme`和`recreate()`方法。这允许开发者定制界面并与品牌指南匹配,或提供多主题选项。
94 6
|
6月前
|
Android开发 开发者
Android UI中的Theme定义了Activity的视觉风格,包括颜色、字体、窗口样式等。要更改主题
【6月更文挑战第25天】Android UI中的Theme定义了Activity的视觉风格,包括颜色、字体、窗口样式等。要更改主题,首先在`styles.xml`中定义新主题,如`MyAppTheme`,然后在`AndroidManifest.xml`中设置`android:theme`。可应用于全局或特定Activity。运行时切换主题需重置Activity,如通过`setTheme()`和`recreate()`方法。这允许开发者定制界面以匹配品牌或用户偏好。
61 2
|
XML Android开发 数据格式
Android学习之AppWidget高级效果
接着AppWidget基础学习,今天是一个“进阶版”的小例子,用来检验一下自己的学习效果。于是就做了一个掷骰子的Widget。 方便大家观看,先截图如下: 需要注意的是在drawable文件夹下有几张图片,我是在网上下载的别人的素材,你也可以直接在图片素材下载链接,提取码是d47k。
1083 0