做完你会得到什么
一个「AI 画图」App:写一句「一只戴宇航员头盔的橘猫,站在月球上,水彩风格」,十几秒后出图;再写「把背景换成晚霞」,它在这张图上接着改。每一版都留着,可以退回上一版;满意了下载保存、分享出去。

图为执行最终 .aia 里的真实积木生成的布局预览,不是真机截图;AI 的回答与照片、生成的图片都是示意。
| 难度 | 用时 | 前置 | 用到的模型 |
|---|---|---|---|
| ★★ | 40 分钟 | 第 1 课、会用列表 | 豆包 Seedream 生图模型(doubao-seedream-4-0-250828) |
准备
| 准备什么 | 说明 |
|---|---|
| 火山方舟 API Key | 到 火山方舟控制台 创建 API Key |
| 开通生图模型 | 火山方舟里的模型要先开通再调用:控制台「开通管理」里找到 Seedream,点开通。没开通会报 ModelNotOpen |
| 豆包组件的属性 | 生图只看 ImageModelName(默认 doubao-seedream-4-0-250828)、ImageSize、ImageWatermark,不用填 ModelName(那是对话和识图用的) |
文生图与图生图
| 文生图 | 图生图 | |
|---|---|---|
| 方法 | GenerateImage(描述) |
GenerateImageFromUrl(描述, 参考图地址) |
| 参考图 | 无 | 一个图片网址(本课用上一版的地址;第 5 课教你把本地照片变成地址) |
| 结果 | GotImage(imageUrl):一个临时图片地址 |
同左 |
imageUrl 可以直接设给图像组件的「图片」属性显示。它是临时链接,过一段时间会失效,要长期保留就下载到手机上。
界面组件
| 组件 | 命名 | 关键属性 / 作用 |
|---|---|---|
| 文本输入框 | 文本输入框_描述 | 允许多行,高 90 |
| 下拉框 | 下拉框_尺寸 | 元素字串:方形 2K,竖版 9:16,横版 16:9 |
| 图像 | 图像_作品 | 高 300,缩放图片以适应 |
| 文本输入框 | 文本输入框_修改 | 提示:接着改,比如:把背景换成晚霞 |
| 按钮 | 按钮_画图、按钮_修改、按钮_上一版、按钮_保存、按钮_分享 | |
| Doubao(拓展) | Doubao1 | 默认属性即可 |
| Web 客户端 | Web客户端1 | 保存响应信息 勾上(下载成文件) |
| 信息分享器 | 信息分享器1 | |
| 计时器 | 计时器1 | 给文件名取时间戳;启用计时 设为假 |
API Key 卡片与第 1 课相同(标签名 doubaoKey)。
分步搭
第 1 步:文生图,尺寸用下拉框选
ImageSize 可以填 1K/2K/4K,也可以填具体像素。竖版、横版用 Seedream 推荐的 2K 像素尺寸。
global 版本 = []
global 序号 = 0
global 正在画 = false
global 已保存 = ""
function 尺寸值(选项) {
return choose(选项 == "竖版 9:16", "1440x2560", choose(选项 == "横版 16:9", "2560x1440", "2K"))
}
when 按钮_画图.Click() {
var 描述 = trim(文本输入框_描述.Text)
if Doubao1.APIKey == "" {
底部提示条1.Show("先在上面填好 API Key 并点保存")
} else if 描述 == "" {
底部提示条1.Show("先写下想画什么")
} else {
Doubao1.ImageSize = 尺寸值(下拉框_尺寸.Selection)
开始画("正在画……")
Doubao1.GenerateImage(描述)
}
}
描述怎么写出好图:主体 + 动作 + 场景 + 风格 + 光线,越具体越好。「一只猫」不如「一只戴宇航员头盔的橘猫,站在月球表面,背后是蓝色的地球,水彩风格,柔和的侧光」。描述不要超过 300 个汉字。
第 2 步:每一版都记下来
用一个列表 版本 存下每次得到的图片地址,序号 指向正在看的那一版。新图来了就追加到列表末尾,并切到最新一版。
when Doubao1.GotImage(imageUrl) {
正在画 = false
listadd(版本, imageUrl)
序号 = length(版本)
显示当前()
}
procedure 显示当前() {
图像_作品.Picture = 版本[序号]
已保存 = ""
标签_状态.Text = join("第 ", 序号, " / ", length(版本), " 版。图片链接有时效,满意的记得下载保存。")
刷新按钮()
}
procedure 开始画(状态) {
正在画 = true
标签_状态.Text = 状态
刷新按钮()
}
中途验证:写一句描述点「开始画」,十几秒后图片出现,状态行「第 1 / 1 版」。
第 3 步:图生图,在当前这一版上接着改
修改时把正在看的那一版的地址当参考图传进去。描述只写「要改什么」就行:「把背景换成晚霞」「加一面小红旗」「改成黑白漫画风」。
when 按钮_修改.Click() {
var 修改 = trim(文本输入框_修改.Text)
if 序号 == 0 {
底部提示条1.Show("先画出第一张")
} else if 修改 == "" {
底部提示条1.Show("写下想怎么改,比如:把背景换成晚霞")
} else {
文本输入框_修改.Text = ""
开始画(join("正在改:", 修改))
Doubao1.GenerateImageFromUrl(修改, 版本[序号])
}
}
when 按钮_上一版.Click() {
if 序号 > 1 {
序号 = 序号 - 1
显示当前()
}
}
退回到第 1 版再改,新结果会追加成第 3 版,第 2 版不会丢。
中途验证:改一次得到第 2 版;点「上一版」回到第 1 版;在第 1 版上再改,得到「第 3 / 3 版」。
第 4 步:按钮状态一处管
哪些按钮能点,取决于「是不是正在画」「有没有作品」「有没有上一版」「有没有下载好的文件」。把这些规则写在一个过程里,任何状态变化后调用它一次。
procedure 刷新按钮() {
按钮_画图.Enabled = not 正在画
按钮_修改.Enabled = not 正在画 and 序号 > 0
按钮_上一版.Enabled = not 正在画 and 序号 > 1
按钮_保存.Enabled = not 正在画 and 序号 > 0
按钮_分享.Enabled = not 正在画 and 已保存 != ""
}
第 5 步:下载保存与分享
Web 客户端勾上「保存响应信息」后,Get() 不再返回文本,而是把内容存成文件,完成时触发获得文件,参数里有文件的完整路径。文件名带上毫秒时间戳,保存多张不会互相覆盖。
when 按钮_保存.Click() {
if 序号 > 0 {
Web客户端1.ResponseFileName = join("ai-painting-", 计时器1.SystemTime(), ".jpg")
Web客户端1.Url = 版本[序号]
标签_状态.Text = "正在下载……"
Web客户端1.Get()
}
}
when Web客户端1.GotFile(url, responseCode, responseType, fileName) {
if responseCode == 200 {
已保存 = fileName
标签_状态.Text = join("已保存到 App 文件夹,可以点「分享」发到相册或聊天:", fileName)
} else {
标签_状态.Text = join("下载失败(", responseCode, "):图片链接可能已经过期,重新画一张吧。")
}
刷新按钮()
}
when 按钮_分享.Click() {
信息分享器1.ShareFile(已保存)
}
文件存在 App 自己的文件夹里,相册里看不到。点「分享」弹出系统分享面板,选「保存到相册」或发给好友。
中途验证:点「下载保存」,状态行出现文件路径,「分享」变成可点;分享到微信能看到这张图。
第 6 步:出错处理
生图有两个特有的错误:模型没开通(ModelNotOpen),以及内容审核没通过(错误码里带 Sensitive,描述或生成的图被判定为不适合的内容)。
when Screen1.ErrorOccurred(component, functionName, errorNumber, message) {
正在画 = false
刷新按钮()
标签_状态.Text = 错误说明(message)
}
function 错误说明(message) {
return choose(containstext(message, "401"), "API Key 不对:检查是否抄全,或到火山方舟重新创建。", choose(containstext(message, "ModelNotOpen"), "生图模型还没开通:到火山方舟「开通管理」里开通 Seedream 后再试。", choose(containstext(message, "Sensitive"), "描述或图片被判定为不适合生成的内容,换个说法试试。", choose(containstext(message, "429"), "请求太频繁了,等几秒再试。", join("出错了:", message)))))
}
这一课的要点
- 生图是异步的:调用后立刻返回,图片地址在
GotImage里拿。 - 图生图要的是网址:本课用上一版的地址,自然形成「一句句改」的效果。
- 结果是临时链接:要保留就用 Web 客户端「保存响应信息」下载成文件。
- 生图和对话用不同的模型属性:生图看
ImageModelName,对话识图看ModelName,互不影响。 - 一张图一般要十几秒到几十秒,生成期间一定要把按钮变灰,否则用户连点几次就是几张图的费用。
常见问题
| 现象 | 原因与处理 |
|---|---|
| 状态行「生图模型还没开通」 | 火山方舟控制台「开通管理」里开通 Seedream 后再试 |
| 状态行「被判定为不适合生成的内容」 | 描述里有敏感词,或生成的画面被审核拦下。换个说法,避免真实人名、暴力等内容 |
| 图片出来了,过几天再打开 App 显示不出来 | 链接过期了。满意的图当场「下载保存」 |
| 下载失败(403 / 404) | 同上,链接已失效,重新生成 |
| 改了几次,图越改越不像原来的 | 图生图每次都会重新画,细节会漂移。一次只改一处,并在修改描述里强调「其余保持不变」 |
| 生成的图片上带水印 | ImageWatermark 为真时会加水印,默认是假 |
| 竖版、横版没生效 | 检查 ImageSize 传的是不是 1440x2560 这种「宽x高」格式,中间是小写字母 x |
平台说明
- 安卓、鸿蒙都能运行。鸿蒙版豆包组件的生图默认模型与安卓一致。
- 业务检查见验证记录:尺寸取值、在当前版本上改、退回后再改追加新版本、下载与分享、链接过期、三类出错等都逐条执行过。
下一课
第 5 课 照片变漫画:图生图要的是网址,可手机里的照片没有网址怎么办?下一课用「图片处理」把照片变成一个 Base64 图片地址,一键把自拍变成动漫、水彩、像素、黏土四种风格。
扫码添加客服咨询