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

Discussion in 'Chinese Forum' started by 52manhua, Oct 13, 2017.

  1. 52manhua

    52manhua Member Licensed 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):
    Code:
    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 分钟.
    配件名 - 出现在桌面管理程序中的配件名称.

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

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

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

    一个简单的例子在附件里.
    这个例子添加了一个简单的小配件. 虽然如此,只作为演示,无任何实用性.
    [​IMG]

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

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


    第二部分

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

    [​IMG]

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

    设计器截图:
    [​IMG]

    通过上面的图片,你会发现我们使用了两个 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 小时自动更新. 使用以下这条语句实现:
    Code:
    '配置小配件,设成每 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 就会被触发.
    这个过程并没有很多要做的事情:
    Code:
    Sub Service_Start (StartingIntent As Intent)
        
    If rv.HandleWidgetEvents(StartingIntent) Then Return
       
    End Sub
    这段代码让 RemoteViews激活正确的事件.

    但是,我们的进程没有被激活的情况下,Service_Create 就会被触发. Service_Create 是一个重要的入口, 它能帮助我们读取前一个保存的状态.
    Code:
    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
    这个工程在附件中.
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice