Android小部件Widget开发过程中的坑和问题小结

概述

官方参考

Build an App Widget

效果图

放张效果图,这是我玩的桌面 app 文件夹

AndroidManifest.xml

Receiver

切记里面的字母不要弄错,最好复制粘贴再修改相对应自定义的地方就好,一个字母的错误搞了我一天,吐血

<receiver android:name=".desktop.DesktopWidget">
  <meta-data
    android:name="android.appwidget.provider"
    android:resource="@xml/widget_desktop_options" />
  <intent-filter>
    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
  </intent-filter>
</receiver>

Service

如果小部件中使用到了列表项如 ListView,GridView 等,在绑定数据时需要使用 RemoteViewsService 并提供一个 RemoteViewsFactory 实例来填充数据 而非 Adapter
再提,这里面一定不能敲错字母,特别是那个 permission 也一定要有,不然无法绑定数据

<service
  android:name=".desktop.DesktopViewsService"
  android:permission="android.permission.BIND_REMOTEVIEWS" />

Options

res/xml/

widget_desktop_options.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
  android:minWidth="40dp"
  android:minHeight="40dp"
  android:initialLayout="@layout/widget_desktop"
  android:resizeMode="horizontal|vertical"
  android:widgetCategory="home_screen">
</appwidget-provider>

常用参数

Size

  • 尺寸大小最终以单元格数据来显示,但定义时为 dp
  • 单元格数转换基本工式 size = 70 x cells - 30
  • 如:1格 = 70 x 1 - 30 = 40dp
  • 最小尺寸定义时最好不要超过 4 个单元格就是 250dp

updatePeriodMillis

更新时间毫秒数,即间隔多少时间呼叫一次 onUpdate() 方法

initialLayout

加载布局文件

Using App Widgets with Collections

小部件中使用列表项

官方参考

Google Develper

AppWidgetProvider

public class DesktopWidget extends AppWidgetProvider {

  public String ACTION_START_ACTIVITY = "STARTACTIVITYACTION";

  public DesktopWidget() {
    super();
  }

  //当接收到广播的时候会被调用
  //onUpdate, onEnabled等时都会发送广播
  @Override
  public void onReceive(Context context, Intent intent) {
    super.onReceive(context, intent);
    pub.log("onReceive:" + intent.getAction());
    if (intent.getAction().equals(ACTION_START_ACTIVITY)) {
      String strPackage = intent.getStringExtra("app_package");
      Intent appIntent = context.getPackageManager().getLaunchIntentForPackage(strPackage);
      pub.log(appIntent.getPackage());
      if (appIntent != null) {
        appIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        pub.log("start activity");
        context.startActivity(appIntent);
        pub.log("finish");
      }
    }
  }

  //被添加或者被更新时调用
  @Override
  public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    super.onUpdate(context, appWidgetManager, appWidgetIds);

    // There may be multiple widgets active, so update all of them
    for (int appWidgetId : appWidgetIds) {
      updateAppWidget(context, appWidgetManager, appWidgetId);
    }
  }
  private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
    pub.log("onUpdate:" + appWidgetId);
    // Construct the RemoteViews object
    RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_desktop);
    //设定小部件上 TextView 值
    //views.setTextViewText(R.id.tv_widget_desktop_title, "ZGTools");
    //setRemoteAdapter设定Listivew,Gridview等复杂布局的adapter
    //第一个参数为要绑定的widget的Gridview的id,第二个参数为RemoteViewsService的intent
    Intent intent = new Intent(context, DesktopViewsService.class);
    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
    views.setRemoteAdapter(R.id.gv_app_group, intent);

    //绑定 item 的定点事件
    //Activity方式测试不成功,改用广播的方式进行发送并执行事件
    //此处不能是空的Intent 否则广播无法发送
    //定义Intent事件模板,相当于公用设定,再在fillIntent里面设定具体的 extra 值
    Intent templateIntent = new Intent(context, DesktopWidget.class);
    templateIntent.setAction(ACTION_START_ACTIVITY);
    templateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    templateIntent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, appWidgetId, templateIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    views.setPendingIntentTemplate(R.id.gv_app_group, pendingIntent);

    // Instruct the widget manager to update the widget
    appWidgetManager.updateAppWidget(appWidgetId, views);
  }

  //被添加或者更改大小时调用,在这个方法中可以选择性的根据界面大小显示或隐藏某些控件
  @Override
  public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
    super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
    updateAppWidget(context, appWidgetManager, appWidgetId);
  }
  @Override
  public void onEnabled(Context context) {
    super.onEnabled(context);
    // Enter relevant functionality for when the first widget is created
  }
  @Override
  public void onDisabled(Context context) {
    super.onDisabled(context);
    // Enter relevant functionality for when the last widget is disabled
  }
}

RemoteViewsService

public class DesktopViewsService extends RemoteViewsService {
  @Override
  public RemoteViewsFactory onGetViewFactory(Intent intent) {
    return new DesktopViewsFactory(this, intent);
  }
}

RemoteViewsFactory

类型于 BaseAdapter,重点是 onCreate() 加载数据, getViewAt() 方法返回布局绑定数据

public class DesktopViewsFactory implements RemoteViewsService.RemoteViewsFactory {
  private Context mContext;
  private int mAppWidgetId;
  private List<AppPackage> lstApps = new ArrayList<AppPackage>();

  public DesktopViewsFactory(Context context, Intent intent){
    this.mContext = context;
    this.mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
  }

  @Override
  public void onCreate() {
    DesktopDbHelper dbHelper = new DesktopDbHelper(mContext);
    SQLiteDatabase db = dbHelper.getReadableDatabase();
    String strSql = "Select * from tb_app";
    try {
      Cursor cursor = db.rawQuery(strSql, null);
      if (cursor != null && cursor.getCount() > 0) {
      	lstApps.clear();
        while (cursor.moveToNext()) {
          byte[] blob = cursor.getBlob(cursor.getColumnIndex("app_icon"));
          Bitmap bmpIcon = BitmapFactory.decodeByteArray(blob, 0, blob.length);
          String strLabel = cursor.getString(cursor.getColumnIndex("app_label"));
          String strPackage = cursor.getString(cursor.getColumnIndex("app_package"));
          lstApps.add(new AppPackage(strLabel, strPackage, bmpIcon));
        }
      }
      if (cursor != null) {
        cursor.close();
      }
    } catch (Exception e) {
      db.close();
      e.printStackTrace();
    }
    db.close();
  }

  @Override
  public void onDataSetChanged() {
  	//这里我加了重新加载数据的调用,用于更新小部件的列表内容
		onCreate();
	}
  @Override
  public void onDestroy() {}
  @Override
  public int getCount() {return 0;}

  @Override
  public RemoteViews getViewAt(int i) {
    if (i < 0 || i >= lstApps.size()) return null;
    //Construct a remote views item based on the app widget item XML file
    RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.widget_desktop_item);
    views.setTextViewText(R.id.tv_widget_desktop_label, lstApps.get(i).getAppLabel());
    views.setImageViewBitmap(R.id.iv_widget_desktop_icon, lstApps.get(i).getAppIcon());

    Intent intent = new Intent();
    Bundle bundle = new Bundle();
    bundle.putString("app_package", lstApps.get(i).getAppPackage());
    intent.putExtras(bundle);
    views.setOnClickFillInIntent(R.id.iv_widget_desktop_icon, intent);
    views.setOnClickFillInIntent(R.id.tv_widget_desktop_label, intent);

    return views;
  }

  @Override
  public RemoteViews getLoadingView() { return null;}
  @Override
  public int getViewTypeCount() {return 0;}
  @Override
  public long getItemId(int i) { return 0;}
  @Override
  public boolean hasStableIds() { return false; }
}

DesktopActivity

 //更新widget布局
  private void updateWidget(){
    AppWidgetManager widgetManager = AppWidgetManager.getInstance(this);
    ComponentName componentName = new ComponentName(this, DesktopWidget.class);
    int[] ids = widgetManager.getAppWidgetIds(componentName);
    widgetManager.notifyAppWidgetViewDataChanged(ids, R.id.gv_app_group);
  }

总结

到此这篇关于Android小部件Widget开发过程中的坑和总结的文章就介绍到这了,更多相关Android小部件Widget内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2020-09-30

简单掌握Android Widget桌面小部件的创建步骤

一.Widget设计步骤 需要修改三个XML,一个class: 1.第一个xml是布局XML文件(如:main.xml),是这个widget的.一般来说如果用这个部件显示时间,那就只在这个布局XML中声明一个textview就OK了. 2.第二个xml是widget_provider.xml,主要是用于声明一个appwidget的.其中,Layout就是指定上面那个main.xml. 3.第三个xml是AndroidManifest.xml,注册broadcastReceiver信息. 4.最后

Android之AppWidget(桌面小部件)开发浅析

什么是AppWidget AppWidget 即桌面小部件,也叫桌面控件,就是能直接显示在Android系统桌面上的小程序,先看图: 图中我用黄色箭头指示的即为AppWidget,一些用户使用比较频繁的程序,可以做成AppWidget,这样能方便地使用.典型的程序有时钟.天气.音乐播放器等.AppWidget 是Android 系统应用开发层面的一部分,有着特殊用途,使用得当的化,的确会为app 增色不少,它的工作原理是把一个进程的控件嵌入到别外一个进程的窗口里的一种方法.长按桌面空白处,会出现

Android编程之桌面小部件AppWidgetProvider用法示例

本文实例讲述了Android编程之桌面小部件AppWidgetProvider用法.分享给大家供大家参考,具体如下: /** * 桌面小部件 AppWidget配置 * * @description: * @author ldm * @date 2016-5-16 下午1:57:16 */ public class ExampleAppWidgetConfigure extends Activity { static final String TAG = "ExampleAppWidgetCon

Android开发5:应用程序窗口小部件App Widgets的实现(附demo)

前言 本次主要是实现一个Android应用,实现静态广播.动态广播两种改变 widget内容的方法,即在上篇博文中实验的基础上进行修改,所以此次实验的重点是AppWidget小部件的实现啦~ 首先,我们简单说一下Widget是一个啥玩意~ 应用程序窗口小部件(Widget)是微小的应用程序视图,可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新.你可以通过一个App Widget provider来发布一个Widget.可以容纳其它App Widget的应用程序组件被称为App Widge

Android开发之在程序中时时获取logcat日志信息的方法(附demo源码下载)

本文实例讲述了Android开发之在程序中时时获取logcat日志信息的方法.分享给大家供大家参考,具体如下: 今天分享一个在软件开发中很实用的例子,也是这几天在通宵加班中我使用的一个小例子, 在程序中监听Log信息. 为什么说它实用?原因是Android的开发厂商各种修改之后手机和手机之间以后存在很多差异.比如说魅族M9手机 开发中如果项目中涉及到访问手机系统的地方,例如访问系统短信库,M9手机它会提示一个dialog框 让用户自己去选择 访问还是不访问.这样就给开发适配带来了巨大的麻烦.本来

Android开发系列三之窗口的常用事件

相关阅读: Android开发系列二之窗口Activity的生命周期 Android开发系列一用按钮实现显示时间 设置窗口标题事件和在Activity之间跳转. 新建一个项目,新建两个Activity:MainActivity,TitleActivity ,然后再AnroidManifest.xml 中注册TitleActivity AndroidManifest.xml内容如下 <?xml version="1.0" encoding="utf-8"?>

Android开发中应用程序分享功能实例

本文实例讲述了Android开发中应用程序分享功能.分享给大家供大家参考,具体如下: Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); //设置类型 shareIntent.setType("text/plain"); //设置分享的主题 shareIntent.putExtra("android.intent.extra.SUBJECT", "分享&

Android开发系列二之窗口Activity的生命周期

在上篇文章给大家介绍了android开发系列一之用按钮实现显示时间,感兴趣的朋友可以点击阅读详情. 在Activity从创建到销毁的过程中需要在不同的阶段调用7个生命周期的方法这7个生命周期方法定义如下: protected void onCreate(Bundle savedInstanceState) protected void onStart() protected void onResume() protected void onPause() protected void onSto

Android小挂件(APP Widgets)设计指导

应用小挂件(也叫做窗口小挂件)在android1.5的时候被第一次引出,后来再android3.0和android3.1中得到了极大的发展,他们可以展示一些应用的常用信息或者一些相关的信息到桌面上,标准的Android系统镜像中有很多自带的创口小挂件,例如:闹钟.音乐等 Figure 1. Example app widgets in Android 4.0. 本文将描述怎么去设计小挂件,以便于能很好的与其他挂件搭配的很默契,同时也会介绍一些小技巧. AppWidget 剖析 一个典型的andr

Android开发自学笔记(三):APP布局上

hello,大家好,本文主要介绍如何开始开发一个美观.有情调.人见人爱的Android应用程序,已知我们在市面上有不少布局极其精美,在视觉上让人爱不释手的应用程序,如果让我们着手开发,我们该如何下手? 在移动互联网公司都有这样几个部门–UX/UE及UI,UX有的也称作UE是指做用户体验的是User Experience的缩写,这些人一般都毕业于美术学院专门搞设计,我们上面说到那些精美的程序往往都是由他们的手先设计出效果图,然后交由UI,也称GUI即Graphic User Interface或者

Android消息推送:手把手教你集成小米推送(附demo)

前言 在Android开发中,消息推送功能的使用非常常见. 为了降低开发成本,使用第三方推送是现今较为流行的解决方案. 今天,我将手把手教大家如何在你的应用里集成小米推送 目录 1. 官方Demo解析 首先,我们先对小米官方的推送Demo进行解析. 请先到官网下载官方Demo和SDK说明文档 1.1 Demo概况 目录说明: DemoApplication类 继承自Application类,其作用主要是:设置App的ID & Key.注册推送服务 DemoMessageReceiver类 继承自

Android编程实现仿易信精美弹出框效果【附demo源码下载】

本文实例讲述了Android编程实现仿易信精美弹出框效果.分享给大家供大家参考,具体如下: 截图: 动画效果介绍: 1.点击ActionBar上"+"按钮,菜单从上方弹出(带反弹效果): 2.再次点击"+".点击空白区域或者点击返回键,菜单向上方收起: 3.点击弹出框上的按钮时,该按钮放大,其它按钮缩小,菜单整体渐变退出. 主体代码: 1.Activity. /** * 仿易信动画弹出框 */ public class MainActivity extends Ac

Android编程滑动效果之Gallery+GridView实现图片预览功能(附demo源码下载)

本文实例讲述了Android编程滑动效果之Gallery+GridView实现图片预览功能.分享给大家供大家参考,具体如下: Android系统自带一个GridView和Gallery两个控件,GridView网格显示,Gallery单个浏览,两者结合起来可以真正实现Gallery浏览图片效果. 本示例通过GridView和Gallery两个控件,模仿实现一个完整的仿Gallery图像集的图片浏览效果.效果图如下: 1.GridView 首先,自定义一个GridImageAdapter图片适配器