Android-SL4A-脚本编程高级教程-三-

116 阅读44分钟

Android SL4A 脚本编程高级教程(三)

原文:Pro Android Scripting with SL4A

协议:CC BY-NC-SA 4.0

九、使用 HTML 的 Python GUIs

本章将介绍使用 SL4A 构建基于 CSS、HTML、JavaScript 和 Python 的图形用户界面(GUI)的可用选项。

images 本章将讨论如何使用 CSS、HTML 和 JavaScript 来构建呈现真实世界用户界面的应用。如果你有这些领域的背景知识会很有帮助,但这不是必须的。

本章的主要主题如下:

  • HTML 图形用户界面基础
  • 使用级联样式表(CSS)向 HTML 添加一些格式
  • 用 CSS、HTML、JavaScript 和 Python 创建商业质量的用户界面

这里的基本方法是使用 HTML 和 JavaScript 来构建用户界面(UI ),然后在幕后使用 Python 来处理任何额外的处理。CSS 可以用来使 HTML 字段和字体在外观和一致性方面更加整洁。Python 也可以用来构建 HTML 文件,在没有任何用户界面的情况下显示信息。

HTML 和基本信息显示

在构建应用时,需要简单地向用户显示大量信息的情况并不少见。这可能是一个列表的形式,甚至只是一个连续的文本框。使用 HTML 作为显示机制,两者都很容易得到支持。HTML 文件可以通过编程生成,或者使用任何文本编辑器创建,然后使用webViewShow API 调用启动。

我们先看第一个选项。在这个示例代码中,我们将查询电池的状态,并在一个简单的 HTML 文件中显示您想知道的一切。然后,我们将通过调用webViewShow来启动该文件,我们就完成了。下面是实现它的代码:

`import time

import android

Simple HTML template using python's format string syntax.

template = '''

Battery Status

  • Status: %(status)s
  • Temperature: %(temperature)s
  • Level: %(level)s
  • Plugged In: %(plugged)s
'''

if name == 'main': droid = android.Android()

Wait until we have readings from the battery.

droid.batteryStartMonitoring() result = None while result is None: result = droid.readBatteryData().result time.sleep(0.5)

Write out the HTML with the values from our battery reading.

f = open('/sdcard/sl4a/scripts/battstats.html', 'w') f.write(template % result) f.close()

Show the resulting HTML page.

droid.webViewShow('file:///sdcard/sl4a/scripts/battstats.html')`

这将在scripts目录中创建一个名为battstats.html的文件。如果您想保存这些文件的集合,您只需将当前时间添加到文件名中,每次都会生成一个唯一的文件。图 9-1 显示了代码显示文件时应该看到的内容:

images

***图 9-1。*使用简单的 HTML 文件显示电池状态

这个调用的第二个例子是从第七章中获取我们的 WiFi 扫描仪示例,并使用 HTML 文件方法显示信息。在这种情况下,您可能希望在文件中添加诸如时间和日期戳之类的东西,然后每次都追加到末尾。这样,您将有一个您的设备已经看到的 WiFi 接入点的运行日志。下面是生成该文件的代码:

`import time import android

if name == 'main': droid = android.Android()

Show the HTML page immediately.

droid.webViewShow('file:///sdcard/sl4a/scripts/wifi.html')

Mainloop

while True:

Wait until the scan finishes.

while not droid.wifiStartScan().result: time.sleep(0.25)

Send results to HTML page.

droid.postEvent('show_networks', droid.wifiGetScanResults().result)

time.sleep(1)`

虽然这段代码只是显示当前范围内的 WiFi 接入点,但是您可以创建一个日志文件并将您的结果附加到其中。该文件会随着时间的推移而增长,直到您删除它。将它保存到文件中,然后显示为 HTML 文件的好处是,您可以使用与浏览网页相同的手指动作来滚动文件。图 9-2 显示了结果。

images

***图 9-2。*WiFi 扫描结果

HTML 和 JavaScript

除了基本的信息显示,下一步是添加某种类型的交互性。这就是我们必须将 JavaScript 引入讨论的地方。SL4A 为网页和 Python 之间的通信提供了一种机制。这是通过在网页中使用事件和一些 JavaScript 代码来实现的。JavaScript 代码的唯一真正要求是,在进行任何 API 调用之前,必须用代码var droid = new Android()实例化 Android 对象。完成后,您就可以像从 Python 中一样访问相同的 API facades 了。

下面是一个使用 JavaScript 获取联系人列表并根据数据动态构建网页的示例。这种技术可以用于任何返回您想要显示的数据的 API 调用。HTML 文件看起来是这样的:

`

Contacts

    `

    注意,我在这里做的只是调用contactsGet routine并传入display_name限定符。下面是 Python 代码实际显示 HTML 文件的样子(这段代码唯一做的事情就是加载 HTML 文件,然后退出):

    `import android

    droid = android.Android() droid.webViewShow('file:///sdcard/sl4a/scripts/contacts.html')`

    图 9-3 显示了我们努力的结果。

    images

    ***图 9-3。*联系人列表的基本 HTML 显示

    这个版本很适合简单地显示信息,但是如果你想让用户能够用你展示的东西做一些事情呢?我们可以对 HTML 文件做一点小小的修改,并使用一个基本的表格和一个超链接添加一些交互。下面是 HTML 和 JavaScript 代码:

    `

    Contacts

    `

    JavaScript 代码有两处轻微的修改。首先,我们添加以下内容来创建一个超链接,以打开呼叫对话框:

    function call(number){ droid.phoneDialNumber(number); }

    另一个变化是使用 HTML <tr><td>标记代替简单的列表元素标记来创建一个表格。虽然这种改变非常简单,但它无需编写大量代码就能创建良好的用户交互。图 9-4 显示了结果。

    images

    ***图 9-4。*以表格形式显示联系人的 HTML 格式

    HTML 图形用户界面表单基础

    现在我们将看看使用 CSS、HTML 和 JavaScript 构建 SL4A GUI 的基础知识。一般来说,这个想法是创建一个 HTML 表单,使用 Python 来处理表单生成的事件。例如,你可以在窗体上有一个按钮,当你按下它的时候会发生一些事情。SL4A wiki 给出了一个简单的例子,我把它包含在清单 9-1 : 中

    清单 9-1。 text_to_speech.html

    `

    Text to Speech What would you like to say? `

    清单 9-2。 speakit.py

    `import android

    droid = android.Android() droid.webViewShow('file:///sdcard/sl4a/scripts/text_to_speech.html') while True: result = droid.waitForEvent('say').result droid.ttsSpeak(result['data'])`

    组成这个程序需要两个文件:名为text_to_speech.html的 HTML 文件和我们称之为speakit.py的 Python 启动器(见清单 9-2 )。两者都必须位于设备上的/sdcard/sl4a/scripts目录中。要启动程序,运行 SL4A 文件列表中的speakit.py文件。Python 代码首先使用webViewShow API 调用启动text_to_speech.html文件,然后等待 HTML 页面触发一个事件。该事件在用户触摸“speak”按钮时生成。

    图 9-5 显示了屏幕的样子。

    images

    ***图 9-5。*文本到语音转换演示的简单 HTML 页面

    JavaScript 代码包含在<script> </script>标记中,并使用postEvent API 调用提供到调用 Python 脚本的连接。要启动这个 HTML 表单,需要调用如下的webViewShow API:

    `import android

    droid = android.Android() droid.webViewShow('file:///sdcard/sl4a/scripts/text_to_speech.html') while True: result = droid.waitForEvent('say').result droid.ttsSpeak(result['data'])`

    一旦显示了表单,Python 代码将阻塞并等待触发'say'事件。该事件将返回文本以传递给结果对象数据字段中的ttsSpeak API函数。当用户单击 speak 按钮时,web 页面将实际关闭,一旦控制从ttsSpeak函数返回,Python 代码将退出。

    简单的 HTML 表单

    现在我们准备处理一个稍微复杂一点的有多个输入框和输入类型的问题。该脚本将显示一个屏幕,允许用户设置许多设备首选项设置,包括屏幕亮度和超时、媒体音量、铃声音量和 WiFi 模式。创建这种类型表单的 HTML 非常简单。这是你需要的一切:

    `

    My Settings

    Brightness Level
    Timeout Secs 0 1 2 3 4 5
    Screen Off

    Media Volume
    Ringer Volume

    Airplane Mode
    Wifi On
    `

    这将产生一个类似于图 9-6 的页面。正如你所看到的,这在小屏幕上呈现有一些问题。标题被砍掉了,按钮没有完全放在页面上,水平基准线似乎脱离了页面。虽然您可以对 HTML 进行一些调整以使其看起来更漂亮,但更好的方法是使用 CSS。

    images

    ***图 9-6。*没有 CSS 的基本 HTML 表单

    层叠样式表

    用级联样式表(CSS)格式化 HTML 对创建和呈现一个干净的用户界面大有帮助。使用 CSS,你可以决定页面上所有 HTML 元素的对齐方式、字体、文本流和大小。这对于小屏幕来说非常方便,因为在小屏幕上,您想要精确地指示页面上的每个元素是如何出现的。

    下面是一小段 CSS,我将使用它来帮助美化我们的用户设置页面:

    `

    #body {width:100%;} .container {text-align:center;margin:auto;} .container div {text-align:left;width:75%;} h1 {text-align:center;margin:auto;} hr {width:75%;margin:0px auto;} label {display:block;float:left;width:60%;} .buttons div {text-align:center;margin:auto;} `

    web 表单的主体部分包含许多标准元素,如<div>标签、使用<label><input>标签的输入框,以及使用<label><select>标签的下拉框中的选项列表。前面的 CSS 代码控制标签的宽度和外观以及文本的对齐方式。它还控制按钮、h1hr HTML 标签的格式。你可以用 CSS 做更多的事情,但是我们将在这个例子中停止。剩下的 HTML 看起来和以前一样。

    图 9-7 显示了添加了 CSS 的 HTML 页面的样子。虽然差别不大,但请注意文本框的宽度和元素的整体间距。随意搭配你喜欢的造型。

    images

    ***图 9-7。*添加了 CSS 的 HTML 表单

    speakit.py的例子中,我们看到了使用droid.postEvent() JavaScript 代码通过事件将数据发送回 Python 应用。这将发送一个表示要朗读的短语的字符串值。该表单包含许多元素,这些元素包含必须提取并发送回 Python 代码的信息。有许多方法可以实现这一点,但是我们将简单地使用一个键/值字符串对。

    这可以通过在我们的 JavaScript 中增加几行代码来完成,以便从 HTML 表单中传递更多的信息。它看起来是这样的:

    `

    function post_data(){ var values = [ ['airplane', document.getElementById('airplane_mode').value], ['wifi', document.getElementById('wifi_on').value], ['brightness', document.getElementById('brightness').value], ['volume', document.getElementById('volume').value], ];

    var q = '?'; for (i=0;i<values.length;i++){ var k = values[i][0]; var v = values[i][1]; if (q != '?'){ q = q + '&'; } q = q + k + '=' + v; }

    droid.postEvent('save', q); } `

    下面是这段代码的 HTML 代码:

    `

    My Settings

    Airplane Mode
    WiFi On
    Brightness Level
    Media Volume
    `

    从特定的 HTML 表单元素中提取值的关键是document.getElementById()行。在 Python 端,这些值随后被用来设置手机上的特定设置。Python 代码如下所示:

    `import android import urlparse

    droid = android.Android() droid.webViewShow('file:///sdcard/sl4a/scripts/settings.html') while True: result = droid.waitForEvent('save').result data = urlparse.parse_qs(result['data'][1:])

    droid.toggleAirplaneMode('airplane' in data) droid.toggleWifiState('wifi' in data) droid.setScreenBrightness('screen' in data and 255 or 0)`

    这个例子介绍了另一个名为urlparse的 Python 标准库工具。这个函数将把返回的元素解析成一个数据项列表,作为键/值对。此时剩下要做的就是调用适当的 API 函数来设置值。

    短信合并

    SL4A 主页包括许多示例程序的链接,包括一些演示如何使用webViewShow API 函数的链接。SMS 合并绝对是使用 Python、JavaScript、HTML 和 CSS 的组合所能完成的最完整的例子。这个示例还让我们有机会使用 Eclipse 及其文件管理特性来演示如何构建一个复杂的应用,并最终将其作为 Android 包(.apk文件)分发。那部分实际上会在第十章中涉及。

    为了理解这个示例程序,将它分解成不同的组件以了解每个函数的作用是很重要的。请记住,这个程序是一个样本,而不是一个真正的充分测试和工作的应用。它确实有一些怪癖,甚至暴露了 SL4A 早期版本中的一些 bug。这里的目的是检查代码,看看每个函数做什么,并让您知道使用相同的技术可以构建什么。如果您选择自己运行代码,我将在这一节的最后总结一下需要注意的事项。如果你打开SMSMerge.zip文件,你应该看到类似于图 9-8 的东西。

    images

    ***图 9-8。*SMS merge . zip 文件的内容

    每个目录都包含基于名称的信息。/etc目录包含一个名为SMSSender.conf的文件,它存储了应用的所有配置信息。如果您用文本编辑器打开该文件,您将看到如下内容:

    `[locale] prefix = +60

    [merger] informeveryratio = 10 informevery = 0

    [application] showonlycsvfiles = 0 showonlytextfiles = 1 showhiddendirectories = 0

    [package] version = 1.01`

    这是一个使用标准 Python 编码实践的很好的例子,您可以在为桌面编写的典型开源应用中找到。它基于 Python 标准库ConfigParser模块。要使用它,只需简单地使用import ConfigParser,然后用parser = ConfigParser()实例化它,就可以访问不同的方法。节名完全由程序员决定,应该反映一个有意义的标题。在这种情况下,有四个命名的部分。解析时,它们会变成与节名相关联的键/值对的 Python 字典。下面是一段一段加载config文件的代码:

    `def load( self ):

    Go through all the sections

    sections = {}

    Some sections are meant to be ignored

    for section in self.sections(): if section not in self.ignore: items = self.items(section) options = [] for item in items: options.append( {"name":item[0], "value":item[1], "description": self.descriptions[section][item[0]]}) sections[section] = options return sections`

    在图 9-9 中,您可以看到设置页面上的可用选项,并了解它们如何与config文件中的值相关联。

    images

    ***图 9-9。*设置配置页面

    如果您单击任何选项的标签,您将会看到一个弹出对话框,其中描述了该选项的功能以及可接受的值。图 9-10 显示了你点击showonlycsvfiles线时会看到的内容。它使用一个弹出警告对话框向用户提供关于设置这个特定选项的后果的反馈。

    images

    ***图 9-10。*弹出对话框为 showonlycsvfiles 选项

    让我们回到这一点,谈谈 CSS 文件。在前面的 HTML 示例中,我使用了一个非常简单的 CSS 文件来定义表单在小屏幕上的显示方式。这个应用将 CSS 文件提升到了一个全新的水平。如果在文本编辑器中打开zest.css文件,您将看到不同的部分和用于定义 HTML 元素格式的技术。在文件的顶部,你会看到两个标有bodybutton的部分。代码如下所示:

    body { width:100%; padding: 0; font-size: 14px; background: black; color:white; font-family: Arial; } button{ color:white; background: transparent; border: solid 1px #2986a5; }

    body部分定义了整个 HTML 页面的默认值,而button部分定义了按钮的默认值。对于这个应用,页面顶部有一个由四个图标组成的菜单。这些变化取决于用户交互。每个按钮都有一个白色的底色,一个透明的背景,一个像素的边框,边框上有十六进制的颜色代码#2986a5

    代替简单的按钮,SMS 合并应用使用了一种相当常见的方法,即使用图像文件。每个按钮使用两种图像版本,分别表示选中和未选中。当用户按下按钮时,图像被交换,产生高亮效果。图 9-11 显示了包含不同按钮图像的目录视图。

    images

    ***图 9-11。*用于 UI 元素的图标

    下面是一段 CSS 代码,它定义了菜单在页面顶部的外观:

    div#menu { background-image: url("img/tab-bg.png"); background-repeat: repeat-x; color: white; font-weight: bold; height: 96px; } div#menu div.current { background-image: url("img/tab-bg-current.png"); background-repeat: repeat-x; } div.icon { height: 67px; width: 100%; background-repeat: no-repeat; background-position: top center; }

    这是相应的 HTML 块:

    `

    Setup
    File
    Text
    Merge
    `

    图 9-12 显示了菜单在模拟器中的样子。

    images

    ***图 9-12。*由 CSS、HTML、图片和 JavaScript 构建的菜单

    当您触摸其中一个按钮时,如文件按钮,您将看到一个新的显示,如图图 9-13 所示。该页面的 HTML 代码如下所示:

    `

    File - Load

    Fields (select phone number column):

    File dialect:

    End of line character:

    Quote character:

    Field delimiter:

    File preview:

    `![images](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/a33268c083f44623b833f3be48da1501~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771848299&x-signature=etITq89pND7yhYl4aROPDWjMtJI%3D)

    ***图 9-13。*文件加载屏幕

    文件预览部分由加载的 CSV 文件构建,并使用 Python 代码提供的数据。下面是实际读取文件并将其返回给 JavaScript 的代码:

    `def loadfile(self, data): self.log("Loading file") merger = self.merger filename = data["path"] if filename != "": self.log("Selected filename %s " % filename) try: reader = CSVReader( filename ) except csv.Error, e: return { "error": "Unable to open CSV: %s" % e } fields = reader.getFields() self.log("Found fields: %s" % ''.join(fields)) merger.setFields(fields) rows = reader.getRows() merger.setItems(rows)

    Rows are now dicts, for preview, want them as list of values only

    values = [] for row in rows: values.append( row.values() ) else: self.log("No file name") return {"filename":"","fields":[], "error": ""}

    Success and new file, return all info

    return {"filename":filename, "fields":fields, "delimiter":reader.dialect.delimiter, "quotechar":reader.dialect.quotechar, "lineterminator": reader.dialect.lineterminator, "error": "", "rows":values }`

    “合并”选项卡是真正的操作发生的地方。它获取包含电话号码和消息文本的 CSV 文件,并将其与您手动输入或从文件加载的消息合并,最终广播 SMS 消息。图 9-14 显示了这个屏幕的样子。

    images

    ***图 9-14。*合并发送短信画面

    执行合并的 Python 代码一点也不难阅读。看起来是这样的:

    `def merge(self, data): droid = self.droid merger = self.merger merger.prefix = parser.get( "locale", "prefix" ) merger.setNumberColumn(int(data["phone"])) merger.setTemplate(data["text"]) ret = {"success":False, "error":"", "messages":[]}

    Valid template returns a list of merge fields that are not used by the given template

    missing = merger.validTemplate() if missing.len() == 0: ret["messages"] = merger.merge() ret["success"] = True else: droid.dialogCreateAlert("Incomplete text", "The following merge fields are not being used by the template: %s.\r\n Would you like to edit the template text?" % ",".join(missing)) droid.dialogSetPositiveButtonText("Yes") droid.dialogSetNegativeButtonText("No") droid.dialogShow() resp = droid.dialogGetResponse()

    User wishes to load now

    if resp.result["which"] == "positive" : return {"task":"edittext"} else: ret["messages"] = merger.merge() ret["success"] = True return ret`

    该页面背后还有一些 JavaScript 代码:

    `/*

    • Merge tab button event
    • On Click, checks that CSV is loaded, checks that template text is loaded.
    • Then fires an event to request Python to merge all SMS
    • Receives sms as object {number,message} and displays them in a table */ buttons.merge.addEvent("click",function(){ if(!csvLoaded()){ if(loadCsv()){ buttons.file.fireEvent("click"); buttons.importCSV.fireEvent("click"); } } else { var text=dta.getValue(); if(text == ""){ textNeeded(); buttons.textTab.fireEvent("click"); } else { handler.startLoad("Processing", "Merging") showOne(tabs,divs.mergeTab); phone = getMergeFields().phone; var resp = handler.postAndWait( {"task":"merge", "text":text.replace ("\n","\u000A"), "phone":phone }); if(resp.task=="edittext"){ buttons.textTab.fireEvent("click"); dta.fireEvent("click"); }else{ clearMergedSamples(); var table = divs.mergeTab.getElement("table"); resp.messages.each(function(m){ var clone = templateRow.clone(); clone.getElement("td.phone").setText(m.number); clone.getElement("td.message").setText(m.message); table.adopt(clone); }); } handler.stopLoad(); } } });`
    依赖关系

    每个软件项目都有某种依赖关系。当你选择了一种编码语言,你就做了一个依赖选择。如果您的应用将在特定的操作系统上运行,那么您已经做出了操作系统依赖性的决定。外部库通常提供额外的功能,否则很难编写代码。您付出的代价是打包库和管理任何可能破坏代码的更新所带来的痛苦。本书中的所有示例脚本都依赖于 SL4A 和 Python。

    SMS Merger 的第一个版本使用了一个外部依赖项,以 Open Intents (OI)文件管理器的形式浏览和选择文件。下面是该版本的一段代码,它使用startActivityForResult API 调用启动 OI 文件管理器,然后从返回的映射值中提取文件名:

    `def requestTemplateFromFile( self ): droid = self.droid map = droid.startActivityForResult( "org.openintents.action.PICK_FILE", None, None, {"org.openintents.extra.TITLE":"Choose file containing message", "org.openintents.extra.BUTTON_TEXT":"Choose"}) if map.result is None: self.requestMessage() else: filename = map.result["data"].replace( "file://", "" ) text = open( filename, "r" ) smscontent = text.readline().replace( "\n", "" ) if self.validTemplate( smscontent ) is True: return smscontent else: self.warnInvalidTemplate( smscontent )

    Loop

    return self.requestTemplateFromFile()`

    OI 文件管理器是一个界面简洁的好工具。它提供了一种选择文件并将其返回给调用者的简单方法。图 9-15 显示了它的样子。

    images

    ***图 9-15。*打开意向文件管理器

    使用像 OI 文件管理器这样的外部应用的一个缺点是用户必须完成额外的安装。虽然这对程序员来说没什么大不了的,但这肯定不是您希望一个典型用户做的事情。更好的解决方案是使用 HTML、JavaScript 和 Python。下面是一个创建文件列表的 Python 函数:

    `def listdir(self, data): """ Creates two lists of files and folders in the path found in data

    data -- dict containing path and type (for filtering)

    """ self.log("Loading directory content") base = data["path"] type = data["type"]

    Check in the config whether we want to show only a certain type of content

    showHiddenDirectories = self.parser.getboolean( "application", "showhiddendirectories" )

    if type == "txt": if self.parser.getboolean( "application", "showonlytextfiles" ) is True: filter = ".{0}".format( type ) else: filter = None elif type == "csv": if self.parser.getboolean( "application", "showonlycsvfiles" ) is True: filter = ".{0}".format( type ) else: filter = None else: filter = None

    List all directories and files, then filter

    all = os.listdir(base) files = [] folders = [] for file in all:

    Separate files and folders

    abs = "{0}/{1}".format( base, file ) if os.path.isdir( abs ):

    Are we filtering hidden directories?

    if showHiddenDirectories is True or file[0] != ".": folders.append( str( file ) ) elif os.path.isfile( abs ):

    Are we filtering by type?

    if filter is None or os.path.splitext( file )[1] == filter: files.append( str( file ) )

    Sort alphabetically

    files.sort( key=str.lower ) folders.sort( key=str.lower ) return {"files":files,"folders":folders}`

    图 9-16 显示了一个独立于任何外部应用的 HTML 和 JavaScript 版本。

    images

    图 9-16。 HTML 和 JavaScript 文件浏览器

    创建这个窗口的 JavaScript 代码很长,但是可读性很好。它基本上为importCSV按钮添加了一个事件处理程序,首先调用 Python 代码来实际加载 CSV 文件,然后构建一个表来显示结果。它将 CSV 文件的路径传递给 Python 代码,作为从函数filebrowser返回的结果。用户通过滚动filebrowser窗口并触摸文件来选择要读取的 CSV 文件,这将关闭filebrowser窗口。

    buttons.importCSV.addEvent("click",function(){ // Override the onClose function to use the path of the CSV file filebrowser.onClose = function(a) { if(a){ handler.startLoad("Loading","Loading CSV file"); var resp = handler.postAndWait({"task":"loadfile","path":a}); if(resp.error==""){ // resp.filename will definitely be same as a? if(resp.filename!=""){ clearMergedSamples(); divs.csvFilename.setText( resp.filename ); divs.fields.removeClass("nodisplay").getElement("div").remove(); var newdiv = new Element("div").addClass("col").addClass("width-100"); resp.fields.each(function(r, k){ newdiv.adopt(new Element("div").addClass("col") .adopt(new Element("input",{"type":"radio","name":"iField"}) .addEvent("click",function(){hideAll(valid);})) .adopt(new Element("span").setText(r)) ); }); divs.fields.adopt(newdiv); // Select the first item divs.fields.getElement("input").setProperty("checked",true); // More information about the loaded file $("dialectQuotechar").setText(resp.quotechar); $("dialectDelimiter").setText(resp.delimiter); $("dialectLineterminator").setText(resp.lineterminator); // Preview var t=new Element("table", {"cellpadding":"0","border":"0"}),th=new Element("tr"); resp.fields.each(function(v){ th.adopt(new Element("th").setText(v)); }); t.adopt(th); resp.rows.each(function(v){ var tr=new Element("tr"); v.each(function(w){ tr.adopt(new Element("td").setText(w)); }); t.adopt(tr); }); divs.preview.empty().adopt(t); } }else{ handler.alert("CSV import error",resp.error); } handler.stopLoad(); } filebrowser.close(); } filebrowser.setType("csv").setTitle("Load CSV file" ).show(); });

    让应用的用户了解正在发生的事情总是一个好主意。当 SMS 合并应用第一次启动时,它需要加载配置文件(如果存在的话)。微调对话框完美地告诉用户,应用实际上是在加载一个配置文件,而不仅仅是留下一个可见的空白屏幕。图 9-17 显示了手机短信程序如何使用微调按钮让用户知道正在发生的事情。

    images

    ***图 9-17。*设置配置页面

    当您使用webViewShow API 函数在 HTML/JavaScript 和 Python 代码之间传递数据时,您必须在任一端编写一个事件处理程序来接收数据。SMS Sender 示例利用 JavaScript 和 Python 事件处理程序来完成工作。下面是在 JavaScript 端设置不同事件处理程序的一段代码:

    handler = new UIHandler(); window.addEvent("domready",function(){ var buttons = {"saveconfig":$("bSaveConfig"),"file":$("bFile"), "setup": $("bSetup"),"importText":$("bChooseText"), "textTab":$("bText"),"merge":$("bMerge"), "validate":$("bValidate"),"process":$("bProcess"), "importCSV":$("bCSV"),"closebrowser":$("closeButton")}, divs = {"preview":$("dPreview"),"filebrowser":$("filebrowser"), "browsercontent":$("browserContent"),"fileTab":$("dFile"), "fields":$("dFields"),"csvFilename":$("csvfile"), "setupTab":$("dSetup"),"textTab":$("dText"),"mergeTab":$("dCSVMerged")}, dta=divs.textTab.getElement("textarea"),browserTitle=$("browserTitle"), tabs=[divs.setupTab,divs.mergeTab,divs.textTab,divs.fileTab], tabButtons=[buttons.setup,buttons.merge,buttons.textTab,buttons.file], validSpan=$("wValid"),invalidSpan=$("wInvalid"),valid=[validSpan,invalidSpan], templateRow=$("templateTable").getElement("tr"); tabButtons.each(function(button,k){ var current = "current"; button.addEvent("click",function(){ if(!button.hasClass(current)){ removeClassFromAll(tabButtons,current); button.addClass(current); showOne(tabs,tabs[k]); } }); });

    在 Python 方面,必须有相应的事件处理程序。以下是手机短信程序的处理方式:

    `class SMSSenderHandler(UIHandler): """ Handler class for this particular application. Extends UIHandler """ def init(self): UIHandler.init(self)

    Create the dispatch dictionnary which maps tasks to methods

    self.dispatch = { "loadfile": self.loadfile, "validate": self.validate, "loadfilecontent": self.loadfilecontent, "loadconfig": self.loadconfig, "send": self.send, "merge": self.merge, "listdir":self.listdir, "saveconfig":self.saveconfig }`

    手机短信程序的怪癖和陷阱

    请注意,根据您安装的 SL4A 版本,SMS Sender 示例可能无法运行。我遇到了一些 SLA4 r3 和模拟器不能正确处理事件传递的问题。这在当时是一个已知的错误,并被如此报告。HTML 文件选择器也有一个问题,它似乎不允许您在打开文本或 CSV 文件后打开子目录。也就是说,它展示了一些可以在 Python 和 HTML 代码之间双向通信的方法。

    总结

    本章试图向您展示编写使用 HTML 显示信息并通过webViewShow API 调用与用户交互的脚本的基础。

    以下是本章的要点:

    • HTML 基础知识:你所学到的关于优秀 HTML 的一切都适用于此。您可以使用 HTML 文件和几行代码构建简单的输出。
    • 学习一些 JavaScript :这本书的主题是用 Python 编程,但是要在 HTML 页面中实现交互,你必须写一些 JavaScript。学习这门语言并不困难,尤其是如果你对 C++或 Java 很熟悉的话。网上有很多资源可以帮助你开始。
    • 别忘了设计:对于程序员创建的网页,最大的抱怨之一就是它们看起来不太吸引人。SMS Sender 示例使用了许多良好的设计原则来分隔操作,并将相似的功能组合在一起。因为webViewShow API 函数使用 HTML 来创建用户界面,所以学习一点好的 HTML 页面设计是个好主意。
    • CSS 可以帮助:使用 CSS 实际上也是一个很好的编程实践。它有助于将编码中的一些设计方面分离到一个文件中。CSS 有助于给 HTML 带来一致的外观和感觉,并且非常适合小屏幕。

    十、打包和分发

    本章将介绍使用 Eclipse 和 QR 码打包和分发脚本的方法。

    本章将涵盖以下主题:

    • 使用二维码分发脚本
    • 构建可分发的应用
    • 使用 Eclipse 创建一个.apk文件

    虽然这本书的大部分内容都是关于创建供个人使用的脚本,但是使用 SL4A 构建一个商业 Android 应用还是很有可能的。一旦完成,你需要一种方法来分发你的应用,让其他人也能享受它。这一章将会介绍几种你可以做到的方法。

    二维码

    如果你有一个相对较短的脚本想要分享,快速响应(QR)码是发布你的作品的一个很好的方式。大多数 Android 设备都包含一个原生条形码扫描仪应用(ZXing),SL4A 甚至支持直接将二维码导入编辑器。它也可以从 Android 市场获得。当您启动 SL4A 时,您应该会看到设备上的scripts目录中的文件列表。如果你按下硬件菜单按钮,你会在左上角看到一个添加按钮(见图 10-1 )。

    images

    ***图 10-1。*菜单按钮弹出对话框

    如果您按下添加,您将得到一个菜单,其中包含任何已安装的解释器、Shell 和扫描条形码的文件(参见图 10-2 )。

    images

    ***图 10-2。*添加菜单

    如果你在谷歌上快速搜索 SL4A 二维码,你会发现很多条目,人们在博客或个人网站上使用二维码分享他们的脚本。一个二维码只能编码 4296 个字符的内容,所以你的脚本必须简短。有几个网站可以粘贴文本,并为您创建一个二维码。SL4A 维基参考资料http://zxing.appspot.com/generator。以下是附带的说明:

    1. 打开内容下拉列表,然后选择文本。
    2. 在文本内容的第一行,输入脚本的名称(例如,hello_world.py)。
    3. 在那下面,粘贴脚本内容。
    4. 打开大小下拉列表并选择 l。
    5. 单击生成。
    6. 嵌入生成的条形码图像或与朋友分享。

    图 10-3 显示了使用此处显示的makeToast.py代码从[zxing.appspot.com/generator](http://zxing.appspot.com/generator)生成二维码的结果:

    `import android

    droid = android.Android() name = droid.getInput("Hello!", "What is your name?") droid.makeToast("Hello, %s" % name.result)`images

    ***图 10-3。*二维码生成使用http://zxing.appspot.com

    如果你有一个简短的脚本要分享,并且有一个分享的地方,比如博客或网站,二维码给了你一个很好的选择。

    应用包

    Android 应用通常以扩展名为.apk的单个文件或包分发。Android 包本质上是一个类似于.jar.zip文件的存档文件。每个.apk包含一些必须存在的强制文件,否则应用不会安装。最重要的文件是AndroidManifest.xml.这个文件描述了应用在上下文中所需的资源和权限。根据 Android 文档,除了声明应用的组件之外,清单还做了许多事情:

    • 标识应用需要的任何用户权限,例如对用户联系人的 Internet 访问或读取权限
    • 根据应用使用的 API,声明应用所需的最低 API 级别
    • 声明应用使用或需要的硬件和软件功能,如摄像头、蓝牙服务或多点触摸屏
    • 指定应用需要链接的 API 库(除了 Android 框架 API),例如 Google Maps 库

    创建 Android 项目有很多选择。一种方法是从命令行手动创建一个新项目。它包括使用android命令和一些参数。图 10-4 显示了在 Windows 命令提示符下运行该命令的结果。

    images

    ***图 10-4。*命令行项目创建

    当您使用命令行工具android来构建您的项目时,它会在AndroidManifest.xml文件中正确设置。下面是从的命令行得到的那个文件的样子图 10-4 :

    <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myfirstapp" android:versionCode="1" android:versionName="1.0"> <application android:label="@string/app_name" android:icon="@drawable/icon"> <activity android:name="MyFirstApp" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

    命令行方法创建了一个基本的项目框架,需要做一些调整才能使它像 SL4A 项目一样工作。幸运的是,SL4A 人员已经完成了大部分工作。你需要做的第一件事就是从 SL4A 项目网站(http://android-scripting.googlecode.com/hg/android/script_for_android_template.zip)下载脚本模板文件。图 10-5 显示了script_for_android_template.zip文件中的内容。

    images

    ***图 10-5。*script _ for _ Android _ template . zip 文件内容

    提供的AndroidManifest.xml文件包含您明确授予访问权限的项目或属性列表。SL4A 站点提供的模板文件包含了一个完整的列表,但是大部分条目都被注释掉了。它将如下所示:

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

    每个有效的权限行应该如下所示:

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

    一旦你下载了模板,你就可以开始构建你的可发布项目,也就是所谓的.apk文件。最简单的方法是使用 Eclipse。我将使用虚拟脚本模板带您完成这些步骤。第一步是将模板导入 Eclipse。图 10-6 和 10-7 显示了您需要浏览的两个对话框。

    images

    图 10-6。 Eclipse 项目导入对话框

    当你点击下一步按钮时,你应该会看到一个类似于图 10-7 中的对话框。如果您在选择归档文件选项的同一行单击浏览按钮,您将能够导航到该目录并选择script_for_android_template.zip文件。

    images

    图 10-7。 Eclipse 项目导入对话框:归档文件选择

    在构建项目之前,必须在属性页上进行一项更改。为此,请从“窗口”菜单中打开“首选项”对话框。展开 Java 菜单项,后跟构建路径。此时,你的对话框应该类似于图 10-8 中的对话框。选择“类路径变量”项,然后单击“新建”按钮。这将弹出另一个类似于图 10-9 中的对话框。

    images 注意您可能需要在ScriptForAndroidTemplate下添加一个名为gen的目录。我第一次尝试构建项目时遇到了一个错误,因为这个目录丢失了。script_for_android_template.zip文件的后续版本可能会对此进行更正。

    images

    图 10-8。 Eclipse 项目首选项对话框

    ANDROID_SDK 变量必须指向您的 Android SDK 的安装路径。在我的情况下,这是在我的下载目录下。如果您在 Windows 上使用 SDK 的安装程序可执行文件,您的路径可能类似于C:/Program Files/Android/android-sdk/。最好的办法是单击文件夹按钮并导航到目录。

    images

    ***图 10-9。*新建类路径变量条目对话框

    如果您展开新导入的ScriptForAndroidTemplate,您应该在 Eclipse Pydev Package Explorer 窗口中看到类似于图 10-10 的内容。

    images

    ***图 10-10。*导入模板项目的浏览器视图

    此时,您应该已经准备好构建项目了。首先从项目菜单中运行清理工具来确保旧项目或以前的构建没有任何问题,这是一个不错的主意。我养成了每次都这样做的习惯,只是为了更好地衡量。如果项目构建成功,您应该不会在 Problems 选项卡上看到任何条目(参见图 10-11 )。

    images

    ***图 10-11。*问题和控制台标签应该是空的

    至此,我们已经有了一个准备打包的 Android 应用。这是 Eclipse 真正闪光的地方。在“文件”菜单上,选择“导出”。你应该会看到一个类似图 10-12 的对话框。

    images

    图 10-12。 Eclipse Android 包导出对话框

    点击下一步按钮将弹出一个类似于图 10-13 中的对话框。此对话框让您知道您将要导出当前设置为可调试的项目。在开发过程中,这不是问题,但是在将应用发布给其他人使用之前,您会希望对其进行更改。

    images

    图 10-13。 Eclipse 导出项目检查

    接下来的三个对话框处理应用的签名。每个 Android 应用在安装之前都必须进行数字签名。如果这是您第一次经历这个过程,那么您必须生成一个新的密钥库和一个要使用的密钥。点击图 10-13 中对话框的下一步按钮,将出现图 10-14 中所示的对话框。

    images

    ***图 10-14。*项目密钥库选择对话框

    在这里,您可以选择一个文件来保存您的密钥库和一个密码来保护它。密码必须至少包含六个字符,并且应该是您容易记住的内容。单击“下一步”按钮会将您带到另一个对话框,您将在其中输入生成新密钥的信息。图 10-15 显示了密钥创建对话框。

    images

    ***图 10-15。*密钥创建对话框

    请注意有效性字段。您可以创建一个有效期为任意年数的密钥,从 1 到 99 这样的大数字。最后一个对话框允许你指定.apk文件的位置。

    images

    ***图 10-16。*文件.apk的目的目录

    现在我们已经生成了一个.apk文件,我们可以在模拟器中测试它。有两种方法可以做到这一点:直接从 Eclipse 或者从命令行使用 ADB 工具。我个人更喜欢命令行,但我是相当老派的。要使用 ADB 进行安装,请打开终端窗口,将当前目录更改为您选择作为.apk文件目标的目录,并键入以下内容:

    adb install ScriptForAndroidTemplate.apk

    如果安装成功完成,您应该会在模拟器中看到一个名为虚拟脚本的条目,如图 10-17 中的所示。

    images

    ***图 10-17。*安装了虚拟脚本的模拟器屏幕

    如果你将ScriptForAndroidTemplate.apk文件安装到没有安装 SL4A 的设备上,你会看到一个弹出对话框,如图 10-18 中的所示。

    images

    ***图 10-18。*缺少 Python 解释器提示

    单击 Yes 按钮将引导您完成为 SL4A 安装 Python 解释器的过程。一旦该过程完成,您应该能够通过单击它来运行虚拟脚本应用。如果您碰巧没有正确设置AndroidManifest.xml文件中的所有权限,您会得到类似于图 10-19 中的通知。

    images

    ***图 10-19。*权限通知缺失

    要解决这个问题,您必须手动编辑AndroidManifest.xml文件,或者在 Eclipse 中打开该文件并在那里进行更改。Eclipse 方法要安全和快速得多,所以我们将在这里讨论它。要打开该文件,只需在 Package Explorer 窗口中双击AndroidManifest.xml。你应该会看到一个类似于图 10-20 中的对话框。

    images

    图 10-20。 Eclipse Android 清单权限标签

    从图 10-20 中可以看到,这个AndroidManifest.xml文件中唯一的权限是允许访问互联网。如果你点击添加按钮,你会看到一个类似图 10-21 的对话框。

    images

    ***图 10-21。*创建一个新的 Android 清单权限元素

    我们需要选择使用权限来添加新元素。选择使用权限,然后单击确定按钮。接下来,您需要使用下拉框选择一个权限名称,该下拉框包含所有允许的值供您选择。我们需要标有android.permission.VIBRATE的那个。图 10-22 显示了选择的数值。

    images

    ***图 10-22。*选择 android.permission.VIBRATE

    完成后,您可以单击 Eclipse 主菜单下的小磁盘图标来保存您的更新。现在,您需要返回项目清理和导出过程,以创建一个新的.apk文件。

    打包自己的应用

    既然您已经知道如何使用模板打包应用,我们将使用相同的基本方法来打包我们自己的应用。对于单个 Python 脚本文件来说,这个过程非常简单。首先,在 Eclipse 中右键单击项目,然后从菜单中选择 copy,制作模板的副本。接下来,右键单击 Package Explorer 窗口的空白区域,并从菜单中选择 Paste。这将呈现一个类似图 10-23 的弹出窗口。为您的新项目命名,然后单击 OK。

    images

    图 10-23。 Eclipse 复制项目对话框

    现在到了插入脚本的部分。复制您的脚本并将其粘贴到res/raw目录中。这里最简单的事情就是删除现有的script.py文件,并将你的脚本重命名为script.py。这样,您就不必更改引用script.py的任何其他位置。你还需要重命名默认的包com.dummy.fooforandroid / your_package_name 。您可以使用 Eclipse 重构/重命名工具来完成这项工作。然后需要更新AndroidManifest.xml中的package属性来引用 your_package_name

    此时,您应该能够完成构建和导出过程,为您的脚本创建一个.apk文件。

    蚂蚁建筑

    对于真正的铁杆命令行迷来说,还有 Ant。如果你想走这条路,你需要一个 Mac OS X 或者 Linux 盒子。配置脚本是.sh文件,因此它们必须在这些操作系统的终端上运行。首先,您需要下载并解压缩上一节中使用的相同模板文件。您还需要将ANDROID_SDK变量设置为指向 Android SDK 的根目录。这看起来是这样的:

    unzip -d <path/project_directory> script_for_android_template.zip export ANDROID_SDK=<SDK_root>

    接下来,您需要如下执行configure_package.sh脚本:

    sh configure_package.sh <your_fully_qualified_package_name>

    如果您在模板中配置实际的虚拟包,命令应该是这样的:

    sh configure_package.sh com.dummy.fooforandroid

    此时,您需要将您的 Python 脚本复制到res/raw目录中,并替换现有的script.py文件。同样,如果你把你的剧本重新命名为script.py,会更容易。您将需要手动编辑AndroidManifest.xml来取消注释您的脚本需要的所有权限。实际的构建和运行过程使用了run-tests.sh脚本。要构建您的包,您需要打开一个终端窗口并导航到您的项目目录的根目录。命令ant debug将在项目/bin目录下创建一个名为<*your_project_name*>-debug.apk.apk文件。该文件将使用调试密钥签名,并使用zipalign工具对齐。

    构建发布版本稍微复杂一些。首先,您必须用合适的证书签署您的应用。如果您计划在 Android market 上发布您的应用,您的有效期必须在 2033 年 10 月 22 日之后结束。调试证书使用以下默认值:

    • 密钥库名称:"debug.keystore"
    • 密钥库密码:"android"
    • 按键别名:"androiddebugkey"
    • 密钥密码:"android"
    • CN: "CN=Android Debug,O=Android,C=US"

    您的私有释放密钥必须为所有这些值使用不同的字段。对于私钥,您有两种选择:从证书发行商那里购买一个或者自己创建一个。Java 开发工具包(JDK)附带了一个keytool工具,它将为您生成一个自签名密钥。你还需要 JDK 的jarsigner工具。下面是生成私钥的命令行示例:

    keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA –keysize 2048 -validity 10000

    使用有效的密钥,您可以使用命令ant release构建应用的发布版本。默认情况下,Ant 构建脚本编译应用.apk而不对其进行签名。您必须使用jarsigner工具来实际签署.apk文件。您可以使用以下命令来完成它:

    jarsigner -verbose -keystore my-release-key.keystore my_application.apk alias_name

    验证您的.apk文件是否正确签名是个好主意。您也可以使用jarsigner命令:

    jarsigner -verify my_signed.apk

    如果您想了解更多信息,可以添加-verbose-certs。此时,剩下的就是运行zipalign工具来确保所有未压缩的数据都正确对齐。这个工具实际上是调整最终的包,使所有文件都在 4 字节边界上对齐。这极大地提高了应用加载性能,并减少了正在运行的应用消耗的内存量。下面是运行zipalign的命令行:

    zipalign -v 4 *your_project_name*-unaligned.apk *your_project_name*.apk

    这应该是创建一个完全可发布的 Android 应用所需的最后一步。最后,您可能希望考虑更新您的模板项目,以包含核心 SL4A 可执行文件的最新版本,因为它们会不断更新。为此,您需要下载最新版本的script_for_android_teplate.zip并解压以下文件:

    libs/script.jar libs/armeabi/libcom_googlecode_android_scripting_Exec.so

    将这些文件复制到项目中的相同位置,然后使用 Eclipse 进行刷新清理构建或使用 Ant 进行重建。

    编译 SL4A

    如果您想确保您拥有 SL4A 的绝对最新和最棒的版本,您必须从源代码编译它。如果您正在寻找一个稳定的版本,这可能有点冒险,但是它也可能修复您的应用需要的一个问题。不管怎样,如果你想编译 SL4A,这就是你需要做的。您需要做的第一件事是获得 SL4A 源代码树的副本。你要知道 SL4A 是用 Mercurial 做源代码管理工具的。你可以从它的下载页面([mercurial.selenic.com/downloads](http://mercurial.selenic.com/downloads))获得一份适用于各种 Linux 发行版、Mac OS X 和 Windows 的 Mercurial 客户端。

    出于本章的目的,我将在 Windows 7 64 位机器上使用 TortoiseHg。下载页面提供了许多选项,包括一些不需要管理员权限的选项。我选择了 TortoiseHg 2.0.4 和 Mercurial 1 . 8 . 3–x64 Windows 选项。此选项提供了与 Windows 资源管理器的集成,使得将任何存储库克隆到本地驱动器上的特定位置变得非常简单。一旦您安装了客户端,您将需要克隆源代码树。在 Windows 中,你可以在文件资源管理器中右键单击你想要创建克隆的目录,然后选择 TortoiseHg 和 clone,如图图 10-24 所示。

    images

    ***图 10-24。*创建 SL4A 源树的克隆

    选择克隆选项将启动另一个对话框,您必须在其中指定存储库的源 URL 和本地计算机上的目标位置。我使用的网址如下:

    https://rjmatthews62-android-scripting.googlecode.com/hg/

    实际的官方网址是:

    https://android-scripting.googlecode.com/hg/

    在撰写本文时,这似乎是包含所有补丁和更新的最新位置。图 10-25 显示了您必须输入该 URL 的对话框。

    images

    ***图 10-25。*选择 SL4A 源树位置

    一旦下载了整个树,就需要将其导入 Eclipse。为此,打开 Eclipse 并从 File 菜单中选择 Import。因为文件已经存在于本地磁盘上,所以您必须使用选择根目录选项。单击浏览按钮导航到执行克隆操作的位置。图 10-26 显示了选择克隆目录后的对话框。

    images

    ***图 10-26。*从本地目录导入 Eclipse】

    此时,您不需要克隆的源代码树中的所有项目。您可以通过右键单击每个项目并选择“关闭项目”来删除以下内容:

    • BeanShellForAndroid
    • DocumentationGenerator
    • InterpreterForAndroidTemplate
    • JRubyForAndroid
    • LuaForAndroid
    • PerlForAndroid
    • RhinoForAndroid
    • TclForAndroid

    现在,您应该准备好执行项目Image构建,然后执行项目Image清理Image全部清理。我不得不再次将gen目录添加到许多项目中。一旦这样做了,你应该做一个干净的构建,一切都应该是好的。此时你应该会看到一个类似图 10-27 的月食窗口。

    images

    ***图 10-27。*建造 SL4A 后的月食窗口

    现在我们需要添加我们的模板项目来创建我们的最终应用。为此,右键单击ScriptForAndroidTemplate文件夹并制作副本。然后通过右键单击包资源管理器区域并选择粘贴来粘贴新副本。这将是我们的目标应用。要将这个副本连接到 SL4A 克隆,您需要展开项目并右键单击build.xml文件。选择运行方式,然后选择 Ant 构建。如果需要,您可以在此时重命名您的项目。又一个干净的构建,你应该有一个工作的.apk准备好测试了。

    要测试该应用,要么将一个真实的设备连接到您的工作站,要么直接使用模拟器。在 Eclipse 中,你只需右击模板的副本,选择运行方式,然后选择 Android 应用(见图 10-28 )。

    images

    ***图 10-28。*建造 SL4A 后的月食窗口

    此时,您的应用有了一个.apk文件,SL4A 有了一个.apk文件。如果你分发你的应用.apk文件,会提示用户必须先安装 Python 2.6.2(参见图 10-18 )。

    点睛之笔

    如果你打算将你的剧本公之于众,你需要做一些调整。默认模板包括一个名为res的资源目录。在这个目录中有许多子目录,包含应用使用的各种文件——包括当您浏览设备上的应用时会看到的代表图标的图像,以及将出现在该图标下的名称。要更改名称,您需要编辑values子目录中的strings.xml文件。下面是该文件在默认模板中的样子:

    <?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">Hello World!</string> <string name="app_name">Dummy Script</string> </resources>

    要更改名称,只需更改"app_name">Dummy Script行来反映应用的名称。您可能想要更改的另一件事是应用图标。为此,您可以使用 Android SDK 提供的draw9patch工具。这可以通过简单地输入draw9patch从终端窗口启动。图 10-29 显示了加载了默认 SL4A 脚本标志的draw9patch app。

    images 注意安卓图标默认使用.png格式。术语九补丁指的是一个标准的 PNG 图像,包括一个 1 像素宽的边框。它通常用于图像必须拉伸以适应不同长度的文本标签的按钮。

    images

    ***图 10-29。*载入 SL4A 图标的 Draw9patch 应用

    一旦程序运行,你可以拖拽一个图像并放到打开的窗口中,或者使用文件Image打开 9 补丁选项。当你完成后,在文件菜单上有一个保存 9 补丁选项来保存你的工作。

    蜿蜒向下

    SL4A 为希望开发市场现成应用的有抱负的程序员和希望自动化一些功能以使他们的移动生活更轻松的精明的智能手机用户提供了理想的解决方案。对于精通 Python 的人来说,这是利用他们的编程技能使用任何 Android 设备的绝佳机会,就像使用台式机或笔记本电脑一样。对一些人来说,甚至有可能用基于安卓系统的平板电脑取代笔记本电脑。随着移动设备处理和存储能力的提高,这种可能性只会越来越大。

    SL4A 真正伟大的地方在于它的开源特性。随着该项目越来越广为人知,将有更多的用户转化为更广泛的受众和更大的参与。开发工作的新贡献者增加了重要的新特性,例如使用任何原生 Python 库的能力。对谷歌电视等其他 Android 平台的更新应该也允许 SL4A 在那里运行。Google groups 上有一个相当活跃的论坛,你可以在那里提问并获得帮助。

    试用 SL4A 并没有你想象的那么难。你真的不能做任何对你的设备直接有害的事情,尽管它可能会增加你的数据账单,这取决于你的脚本做什么。最安全的入门方式是使用 Android 模拟器。通读本书中的章节将为您使用 SL4A 让您的 Android 设备做您从未想过可能的事情打下坚实的基础。

    总结

    本章非常详细地描述了如何为 SL4A 脚本构建可分发的包。

    以下是本章的要点:

    • 创建二维码:二维码给你一个快速简单的方法来分发任何人都可以直接加载到 Android 设备上的简短脚本。
    • 构建 .apk 文件:如果你想使用 Android market 发布你的应用,你必须学会如何构建.apk文件。
    • 使用 Eclipse:它使得构建和测试可分发应用的过程变得更加容易。
    • 美化你的应用:如果你想让你的用户真正使用它,你真的需要花些时间为你的应用创建一个图标。