代码方式桌面添加AppWidget

  前段时间做了个桌面的widget,第一次接触AppWidget,遇到了不少问题,今天整理下和大家探讨下.

  至于AppWidget的有些基础知识,我在这里就不多说了,大家有什么不明白,随便百度下,就有很多答案.今天主要说一下我在往桌面上添加时遇到的问题.

  

  如图所示,中间的Recommended就是一个appwidget添加在桌面上.下面我们就来说说添加的流程.

  首先,上面的页面不是手机页面,是阅读设备,用的是安卓系统,类似平板一样的设备.桌面也不是安卓原生的Luncher,所以也不可能像手机那样长按空白选择,然后滑动选择位置就添加上去了,而且还要求开机就默认在桌面上,位置也不能变(大笑我们也没做长按可以移动widget的功能).

  加载流程如下:

  1.获取ID

  2.获取appwidget的ComponentName

  3.绑定bindAppWidgetId

  4.获取AppWidgetHostView(RemoteViews并不是真的view)

  5.添加AppWidgetHostView到父Layput中

  代码如下:

  

private void addRecommendationsWidget() {
        int APPWIDGET_HOST_ID = 1024;
        AppWidgetHost appWidgetHost = new AppWidgetHost(getApplicationContext(), APPWIDGET_HOST_ID);
        int appWidgetId = appWidgetHost.allocateAppWidgetId();
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
        List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
        if (providers == null) {
            Log.e(TAG, "failed to find installed widgets ");
            return;
        }
        final int providerCount = providers.size();
        AppWidgetProviderInfo appWidgetProviderInfo = null;
        for (int i = 0; i < providerCount ; i++) {
            ComponentName provider = providers.get(i).provider;
            if (provider != null && provider.getPackageName().equals("com.xxxx.android.xxxx")) {
                appWidgetProviderInfo = providers.get(i);
                break;
            }
        }
        if (appWidgetProviderInfo == null) {
            Log.e(TAG, "failed to find recommendations widget ");
            return;
        }
        int sdkVersion = Integer.valueOf(android.os.Build.VERSION.SDK);
        if (sdkVersion > 15) {
            final String methodName = "bindAppWidgetIdIfAllowed";
            boolean success = bindAppWidgetId(appWidgetManager, appWidgetId, appWidgetProviderInfo.provider, methodName);
            if (!success) {
                addWidgetPermission(appWidgetManager);
                boolean bindAllowed = bindAppWidgetId(appWidgetManager, appWidgetId, appWidgetProviderInfo.provider, methodName);
                if (!bindAllowed) {
                    Log.e(TAG, " failed to bind widget id : " + appWidgetId);
                    return;
                }
            }
        } else {
            boolean success = bindAppWidgetId(appWidgetManager, appWidgetId, appWidgetProviderInfo.provider, "bindAppWidgetId");
            if (!success) {
                Log.e(TAG, " failed to bind widget id : " + appWidgetId);
                return;
            }
        }
        Log.d(TAG, " successful to bind widget id : " + appWidgetId);
        AppWidgetHostView hostView = appWidgetHost.createView(this, appWidgetId, appWidgetProviderInfo);
        appWidgetHost.startListening();
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, appWidgetProviderInfo.minHeight);
        RelativeLayout widgetLayout = (RelativeLayout) findViewById(R.id.recommendation_widget_layout);
        widgetLayout.addView(hostView, params);
    }

    private void addWidgetPermission(AppWidgetManager appWidgetManager) {
        String methodName = "setBindAppWidgetPermission";
        try {
            Class [] argsClass = new Class[]{String.class, boolean.class};
            Method method = appWidgetManager.getClass().getMethod(methodName, argsClass);
            Object [] args = new Object[]{this.getPackageName(), true};
            try {
                method.invoke(appWidgetManager, args);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    private boolean bindAppWidgetId(AppWidgetManager appWidgetManager, int appWidgetId, ComponentName componentName, String methodName) {
        boolean success = false;
        Class [] argsClass = new Class[]{int.class, ComponentName.class};
        try {
            Method method = appWidgetManager.getClass().getMethod(methodName, argsClass);
            Object [] args = new Object[]{appWidgetId, componentName};
            try {
                method.invoke(appWidgetManager, args);
                success = true;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return success;
    }
  代码比较简单,一看应该就很清除.中间为什么要判断sdk版本呢?因为我发现4.0.4和4.2.2绑定的方法不一样,(我也只验证了这两个版本,其它的你们可以看文档)这里为了兼容,采用反射的方法绑定ID.

  4.0.4绑定ID用AppWidgetManager的bindAppWidgetId()函数,而且需要添加权限:

   <uses-permission android:name="android.permission.BIND_APPWIDGET"/>.

  4.2.2绑定ID用AppWidgetManager的bindAppWidgetIdIfAllowed()函数,同时会返回绑定结果的boolean值,而且需要添加权限:

   <uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>

  权限一定不要忘记,setBindAppWidgetPermission(true)这个函数是用来给添加到桌面上授权,可以避免添加时弹出需要授权的对话框(安卓系统的授权对话框).由于这个是hide API,所以也用反射的方式调用.

  最后一点切记,必须有System权限的app才能用这种方法,也就是安装在system/app下面的应用才可以.

  大家有什么不同的看法可以一起讨论!共同进步!下一篇我会讲一下appwidget滑动翻页的问题!第一次写博客,不足之处,请大家多多指正. 

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
|--Activity不允许横竖屏切换 |--Activity常用小技巧 |--Activity按返回直接回到桌面 |--aidl之结合反射获取应用缓存大小等空间占用 |--aidl调用系统service未公开的方法挂电话 |--aidl调用系统未公开的方法代码示例2 |--android dp和px之间转换 |--android INSTALL_PARSE_FAILED_MANIFEST_MALFORMED |--android root下禁用组件 |--android 判断网络状态 |--android 对话框样式 |--android 开机启动 |--android 挪动dialog的位置 |--android 控制对话框位置 |--android 根据uri获取路径 |--android 模拟器错误 |--android 横竖屏切换 |--android 获取mac地址 |--android 获取sd卡状态 |--android 设置apn |--android 调节屏幕亮度 |--android 资源uri |--android 还原短信 |--android 重启 |--android中anim文件特效 |--app信息menifest获取(如版本号) |--AsyncQueryHandler之异步查询Cursor处理 |--AutoCompleteTextView自动提示的用法 |--BitMap、Drawable、inputStream及byte[] 互转 |--ContentProvider内容提供者定义 |--DatePicker日期控件 |--desktop |--Dialog之位置的挪动与控制 |--Dialog实现无标提栏及自定义风格 |--Dialog风格Activity的作法 |--ExpandableListView(下拉伸缩ListView) |--GridView表格布局的用法 |--httpclient超时 |--info体系 |--Intent启动应用apk安装 |--Intent常用功能 |--IO将输入流转成字节 |--Json读js资源文件 |--layout布局样式之style配置 |--listview 页面 图片加文字 |--ListView之CursorAdapter异步查询框架之短信 |--ListView之动态添加子view |--ListView优化之分页加载 |--ListView优化之动态加载 |--ListView优化之控制getView实现复杂显示 |--ListView优化之标准写法 |--listview老虎机 界面设计 水果机 |--listview页面跳转 数据库交互 事务 dao biz 层 |--Log的收集 |--Manager下的info |--Manager之ActivityManager进程管理 |--Manager之LocationManager |--Manager之PackageManager |--Menu之不同模式下显示不同菜单 |--openGL-ES上绘制文字 |--openGL-ES纹理贴图 |--openGL-ES获取帧率 |--openGL-ES雾化 |--PopupWindow的使用 |--PopupWindow的返回健关闭 |--RadioGroup的用法(里面的成员可以是任何view) |--SD卡之计算剩余空间 |--Spinner下拉菜单组件 |--SplashActivity |--StringUtils工具类的常用方法 |--TabHost一个界面显示多Activity |--TextView单行跑马灯效果 |--TextView虚拟获得焦点 |--uploadServlet |--uri之表示资源resource |--ViewPage的使用 |--view中的tag用法之存储对象 |--view常用属性 |--xml常用属性 |--xml文件的pull解析与序列化写入 |--xml的封装序列化 |--任务循环之只在Activity显示时执行 |--修改文件的最后修改时间 |--偏好设置(回显) |--内存优化之各种方法 |--内容提供者之短信的序列化对象读写 |--内容提供者之短信的获取与写入 |--内容提供者之联系人读写与批量操作 |--内容提供者之获取通话记录 |--内容提供者的定义 |--写入联系人信息 |--利用FinalHttp实现多线程断点续传 |--加密之MD5 |--动画Animation详解 |--动画之view左右抖动 |--动画之移动动画 |--动画之组合动画 |--动画之缩放动画ScaleAnimation |--反序列化对象 |--发送短信 读天气 调音量 |--回调函数的定义 |--图片之BitMap、Drawable、inputStream及byte[] 互转 |--图片之保存图片至SD卡 |--图片之删除40%最近没有被使用的 |--图片之的本地缓存至SD卡 |--图片之网络异步下载图片 |--图片之获取SD卡所有及边界可调及压缩和软引用和内存回收 |--图片的LRU算法内存保存和读取 |--图片的缩放处理(防内存溢出) |--多媒体应用设计图 |--多线程下载 |--多线程下载及断点续传 |--多线程之AsyncTask的用法 |--多线程之线程池ExecutorService |--字体为粗体 |--安卓下的多线程断点上传 |--对话框与进度条结合用法 |--屏幕之Activity全屏 |--屏幕之横竖屏切换 |--屏幕之调节屏幕亮度 |--屏幕相关之Display类获取屏幕尺寸和分辨率 |--屏幕适配之ScrollView |--屏幕适配之像素dp和px之间转换 |--工具类之Log的封装类 |--工具类之不同log打印的封装 |--工具类之开启新的Activity |--布局加载器的获取 |--广播接收者之开机启动 |--广播接收者之获取管理员权限 |--广播接收者代码注册与卸载 |--广播接收者常用广播的获取 |--应用之分享(隐式意图) |--应用之卸载(隐式意图) |--应用之安装(隐式意图) |--应用之完美退出 |--应用之获取名称和图标 |--应用之获得占用内存大小 |--应用之通过包名开启一个应用 |--应用之隐式意图开启设置界面 |--应用启动之检查版本更新及初始化 |--延时任务的工具类 |--异常之UncaughtExceptionHandler全局捕获处理 |--异步任务AsyncTask的用法 |--异步任务的自定义 |--快捷方式增删查 |--手势识别器GestureDetector的用法 |--拍照之调用系统相机并显示及保存 |--拨打电话 |--按健之长按menu事件屏蔽 |--按健监听按返回健回桌面 |--搜索之调用系统Searchable的用法 |--数据库CURD通过execSQL与rawQuery |--数据库SQLiteOpenHelper标准写法 |--数据库复杂多表查询 |--数据库查询之归属地 |--数据库直接CURD |--数据提交无需权限 |--文件之从服务器下载 |--文件之拷贝文件至某个目录 |--文件之指定编码读写文件 |--文件之释放Assets下的文件到应用的File目录 |--文件之随机存储RandomAccessFile |--文件使用时间排序 |--文件复制粘贴 |--文件路径之通过uri获取 |--时间java常用应用 |--时间之handle记时器 |--时间之time的用法得到特定时间的long值 |--时间之之定时任务TimerTask |--时间之倒计时CountDownTimer |--时间之当前时间动态显示 |--时间之自动任务ScheduledExecutorService |--时间之记时器 |--时间日期格式化 |--服务之判断是否处于运行状态 |--服务之定义录音机 |--服务之应用内绑定服务调用方法 |--服务之电话录音 |--服务之看门狗代码示例 |--格式化之DecimalFormat数字格式化 |--桌面快捷方式添加代码示例 |--桌面控件widget的创建方法 |--模拟发短信 指定号码 短信窃听提示 |--消息机制 mesage looper |--滑动之左右滑动的两种定义 |--电话簿读取联系人信息 |--监听 |--监听之CheckBox是否选中监听 |--监听之EditText内容变化监听 |--监听之GridView条目点击监听 |--监听之ListView条目点击事件监听 |--监听之ListView滑动监听 |--监听之单击监听的两种定义 |--监听之双击监听 |--监听之电话状态监听 |--监听之触摸监听 |--短信之根据id删除及查询短信 |--短信发送小demo |--短信的截取 |--系统之SD卡清理 |--系统之获取所有开机启动应用 |--系统之重启实现 |--系统信息之获取SD卡内存信息 |--系统信息之获取动态内存RAM信息 |--系统信息之获取可用内存 |--系统信息之获得mac地址 |--系统信息之获得手机sim卡序列号 |--缓存优化之几种方案lastModified |--缓存优化之本地缓存优化(超过规定值或SD卡容量不够时) |--网络post提交查询请求 |--网络之HttpClient的get和post用法 |--网络之判断网络状态是否可用 |--网络之设置apn |--网络图片查看器 |--网络图片的下载与缓存 |--网络文件的下载与关联进度条 |--联系人之各种查找 |--联系人读写与批量操作 |--联系人读取 |--自定义shape形状颜色渐变资源 |--自定义Toast |--自定义view的属性 |--自定义下拉刷新ListView |--自定义动画资源 |--自定义常用小控件 |--自定义控件之小技巧 |--自定义控件风格和全局背景 |--自定义组合控件 |--自定义配置文件资源 |--自定义颜色状态选择资源 |--自定义颜色资源 |--获取网页的源码 |--读取外部资源的文件3种方式 |--读取手机内的短信 |--通信之Application实现应用全局通信 |--重启adb |--隐式转换 页面 |--震动效果的实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值