[sixinice 原创翻译]安卓屏幕小配件(挂件)开发资料

52manhua

Member
Licensed User
Longtime User
安卓屏幕小配件(挂件)开发资料

原帖第一部分:https://www.b4x.com/android/forum/threads/10166
原帖第二部分:https://www.b4x.com/android/forum/threads/10356
翻译贴地址: http://www.sixinice.pw/drupal/node/67

第一部分

Basic4android v1.6 以上版本开始支持屏幕小配件开发. 这个材料会告诉你如何构建你自己的屏幕小配件(又称为 应用小配件 或者 '外挂'?).

理解这一点是很重要的:小配件是建立和运行在一个特殊的进程中, 和你原来的程序运行在不同的进程内. 主屏幕的启动管理程序在管理你的小配件应用.
这就意味着,你没办法直接对小配件的控件进行处理. 作为替代,我们使用一种特殊对象 RemoteViews 来间接操纵小配件的控件.

小配件类应用并不支持所有的控件类型. 以下几种控件是可以包含在小控件内的(译者注:Android 3.0 版本似乎开始支持 listview,但是 b4a 尚未提供支持):

- Button (default drawable/默认 或者 drawable 模式)
- Label (ColorDrawable 或者 GradientDrawable/渐变 模式 )
- Panel (ColorDrawable 或者 GradientDrawable/渐变 模式)
- ImageView
- ProgressBar (both modes/全部模式)

所有的控件都仅支持单击事件.

小配件的布局和设置必须在 XML 文件中实现. 在编译过程中 Basic4android 读取被设计器建立的布局文件,生成需要的 XML 文件.

每一个配件都需要一个响应的服务模块与之匹配. 通过这个服务模块,建立小配件和更新配件状态.

建立小配件 - 按步骤如下:

- 建立一个服务模块(1). [注意] 管理小配件的服务模块也是一个标准的服务.
- 在设计器中设计小配件的布局(2). 首先添加一个 Panel 然后把其他控件放到这个 Panel 上.
小配件的布局会以 panel 为基础生成.
- 在小配件服务模块中添加以下代码(3):
B4X:
     Sub Process_Globals
     Dim rv As RemoteViews
     End Sub
   
     Sub Service_Create
     rv = ConfigureHomeWidget("LayoutFile", "rv", 0, "Widget Name")
     End Sub
   
     Sub Service_Start (StartingIntent As Intent)
     If rv.HandleWidgetEvents(StartingIntent) Then Return
     End Sub
   
     Sub rv_RequestUpdate
     rv.UpdateWidget
     End Sub
   
     Sub rv_Disabled
     StopService("")
     End Sub
   
     Sub Service_Destroy
   
     End Sub
- 编译并运行你的应用(4). 在手机主界面中, 长按空白处你就会看到你的小配件应用会显示在列表中(译者注: 在不同手机系统中添加桌面配件的过程可能不同).

ConfigureHomeWidget 是一个特殊关键词. 在运行的时候,通过布局文件建立 RemoteViews 对象,初始化事件. 在编译时,编译器根据它的设置,生成需要的文件.
四个参数分别是: 布局文件, 事件名, 更新间隔和配件名称.
通过事件名设置管理 RequestUpdate 和 Disabled 事件.
小配件能被设置成自动更新. 更新间隔, 以分钟为基本单位, 定义了小配件发生更新的时间. 设置为 0 来禁止自动更新. 更新过于频繁会增加电池的损耗. 最小的更新值是 30 分钟.
配件名 - 出现在桌面管理程序中的配件名称.

因为所有的参数都是要传入编译器的, 只接受字符串和数字格式.

事件:
B4X:
     Sub Service_Start (StartingIntent As Intent)
     If rv.HandleWidgetEvents(StartingIntent) Then Return
     End Sub
上面这段代码检查让配件服务启动的进程的消息 ,返回启动的小配件的信息. 如果配件服务顺利启动就会返回 TRUE.
小配件会产生两个事件. RequestUpdate 事件在小配件进行更新的时候被激活. 在放置小配件,系统重启,自动更新,应用程序更新的时候会被触发.
禁用的事件会在小配件从屏幕上移除的时候发生.

如同前面说过的一样,所有的控件都仅支持单击的方法. (举个例子)为一个按钮添加单击事件:比如 Button1 ,就增加一个子过程 Button1_Click(子过程的名称应该和配置中事件名称保持一致).
举例,如果你想在按下 Button1 的时候打开 main Acttivity,写入如下代码:
B4X:
     Sub Button1_Click
     StartActivity(Main)
     End Sub
这样来修改小配件:
不可能直接修改小配件的控件. 作为替代,我们使用 RemoteView.Set 方法.
如果我们想要修改一个名为 Label1 的标签的文本,我们可以使用以下这段代码:
B4X:
   `rv.SetText("Label1", "This is the new text.")`
其他代码
B4X:
   rv.UpdateWidget
在进行修改之后,我们使用 rv.UpdateWidget 来更新小配件.

一个简单的例子在附件里.
这个例子添加了一个简单的小配件. 虽然如此,只作为演示,无任何实用性.
SS-2011.07.11-12.55.04.png


本材料的第二段: http://www.b4x.com/forum/basi...oid-home-screen-widgets-tutorial-part-ii.html

[注意] 推荐在 Release mode 运行小配件类应用(使用调试模式无法运行小配件类程序).


第二部分

如果没有读过第一部分请向上阅读.
在这里我们来建立一个 "每日格言" 的小配件.

quote_1.png


开始布局. 这个小配件由一个文本标签和一个包含箭头图片的图片控件组成.

设计器截图:
quote_design.png


通过上面的图片,你会发现我们使用了两个 panel. 第一层 panel 叫做 pnlBase 是一个透明的 panel (Alpha=0). 这个基础的 panel 包含另一个 panel,即一个灰色的 panel.
使用透明 panel 的目的是为了和其他控件留出一些间隔. 小配件的尺寸是由基础 panel 所决定的. 没有这个透明层(panel) 配件就不会跟其他配件或者屏幕左端保留间隔.

我们设置的基础 panel 的尺寸是 294x72. 这是一个 4x1 标准的小配件的推荐尺寸.
提示: 在一些情况下,你虽然改变布局, 但是因为这个小配件已经存在,小配件不会被更新. 删除这个小配件然后重新添加就能看到改变了.

现在是程序的逻辑问题.
程序每天从 5 个 Famous Quotes at BrainyQuote 的信息源获取 20 条格言,第一条格言会被显示出来. 每次用户按下小配件按钮时候,会显示下一条格言.
在获得格言的同时,每个信息源的第一条格言被添加到格言列表的开头。. 只有每一个feed的第一条格言是新的时候,我们从新的格言开始。.
使用 HttpUtils 下载信息源,使用 XmlSax 库进行解析. 查看代码获得更多信息.

这个小配件被设置成每 24 小时自动更新. 使用以下这条语句实现:
B4X:
'配置小配件,设成每 24 小时更新一次 (1440 分钟).

  rv = ConfigureHomeWidget("WidgetLayout", "rv", 1440, "Quote of the day")

24 小时后或者第一次添加或者重启后 RequestUpdate 事件被触发.
Code:

   Sub rv_RequestUpdate
    quotes.Clear
    currentQuote = -1
    HttpUtils.DownloadList("Quotes", Array As String("http://feeds.feedburner.com/brainyquote/QUOTEBR", _
     "http://feeds.feedburner.com/brainyquote/QUOTEAR", "http://feeds.feedburner.com/brainyquote/QUOTEFU", _
     "http://feeds.feedburner.com/brainyquote/QUOTELO", "http://feeds.feedburner.com/brainyquote/QUOTENA"))
   End Sub
首先我们清除以前的格言,获得新的格言. [注意]如果手机(设备)正处于睡眠(关屏)状态,很可能会失败(因为很多设备都会在睡眠状态关闭 wifi). 这种情况下,新的格言会在用户按下按钮的时候才获取.
这种情况下,你不能寄希望于自动更新,确保有其他方式在用户操作的时候能获得正确的数据.

暂时保留原来的数据. 在小配件中运行的代码不会总是处于运行状态. 系统随时可能关闭小配件的进程.
所以别把变量作为全局变量存储在这些代码中.
所有的需要存储的变量最后到写入一个文件中.
RandomAccessFile.WriteObject 和 ReadObject 能胜任这样的任务.
每次小配件发送一个请求的时候, Service_Start 就会被触发.
这个过程并没有很多要做的事情:
B4X:
   Sub Service_Start (StartingIntent As Intent)
    If rv.HandleWidgetEvents(StartingIntent) Then Return
   End Sub
这段代码让 RemoteViews激活正确的事件.

但是,我们的进程没有被激活的情况下,Service_Create 就会被触发. Service_Create 是一个重要的入口, 它能帮助我们读取前一个保存的状态.
B4X:
   Sub Service_Create
    '配置小配件,设成每 24 小时更新一次 (1440 分钟).
    rv = ConfigureHomeWidget("WidgetLayout", "rv", 1440, "Quote of the day")
    HttpUtils.CallbackActivity = "WidgetService"
    HttpUtils.CallbackUrlDoneSub = "UrlDone"
    HttpUtils.CallbackJobDoneSub = "JobDone"
    parser.Initialize
     
    '如果保存有前一个状态的就进行载入.
    '这适用于我们的进程被结束的时候,用户刚好按下小配件的情况.
    If File.Exists(File.DirInternalCache, QUOTES_FILE) Then
     raf.Initialize(File.DirInternalCache, QUOTES_FILE, True)
     quotes = raf.ReadObject(0)
     raf.Close
    Else
     quotes.Initialize
    End If
    If File.Exists(File.DirInternalCache, CURRENTQUOTE_FILE) Then
     currentQuote = File.ReadString(File.DirInternalCache, CURRENTQUOTE_FILE)
    End If
   End Sub
这个工程在附件中.
 
Top