PythonIDE Docs
中文
简体中文

相册拍照与分享

PhotoPicker、CameraPicker、AsyncImage 和 ShareLink。

演示 PhotoPickerCameraPickerAsyncImageShareLink 的媒体采集与分享流。

#预期效果

运行后会出现媒体采集相册,选择照片、拍照和分享入口会把结果写入列表。

#适合场景

  • 头像上传、素材库、图片选择、拍照后分享。
  • 需要同时展示远程封面、本地路径列表和分享入口的媒体页面。

#页面结构

区域结构作用
封面AsyncImage展示远程缩略图和加载/失败占位。
导入PhotoPicker + CameraPicker由系统处理相册选择和拍照。
文件列表ForEach + NavigationLink查看每个媒体路径并进入分享页。

#完整示例

python
import appui
import haptics

state = appui.State(
    cover_url="https://images.unsplash.com/photo-1515879218367-8466d910aaa4?w=900",
    picked=[],
    captured="",
    status="请选择照片或拍照",
)


def on_picked(paths):
    state.picked = list(paths or [])
    state.status = f"已选择 {len(state.picked)} 个文件"
    haptics.notification("success")


def on_captured(path):
    state.captured = path or ""
    state.status = "已拍摄" if path else "未获得照片"
    haptics.notification("success" if path else "warning")


def clear_media():
    state.batch_update(picked=[], captured="", status="已清空")
    haptics.selection()


def media_rows():
    rows = [{"title": "拍摄照片", "path": state.captured}] if state.captured else []
    for index, path in enumerate(state.picked, start=1):
        rows.append({"title": f"相册文件 {index}", "path": path})
    return rows


def media_detail(row):
    return appui.Form(
        [
            appui.Section(
                [
                    appui.LabeledContent("名称", value=row["title"]),
                    appui.Text(row["path"]).font("caption").foreground_color("secondaryLabel"),
                    appui.ShareLink(
                        item=row["path"],
                        subject=row["title"],
                        message="来自 Python IDE MiniApp",
                    ),
                ],
                header="文件",
            )
        ]
    ).navigation_title(row["title"])


def media_row_key(row):
    return row["path"]


def media_row_link(row):
    return appui.NavigationLink(
        destination=media_detail(row),
        label=appui.Label(row["title"], system_image="photo"),
    )


def body():
    rows = media_rows()
    return appui.NavigationStack(
        appui.List(
            [
                appui.Section(
                    [
                        appui.AsyncImage(
                            url=state.cover_url,
                            placeholder=appui.ProgressView("加载封面"),
                            error_view=appui.Image(system_name="photo"),
                            content_mode="fill",
                        )
                        .frame(height=180)
                        .corner_radius(12)
                        .clipped(),
                        appui.LabeledContent("状态", value=state.status),
                    ],
                    header="封面",
                ),
                appui.Section(
                    [
                        appui.PhotoPicker(
                            selection_limit=3,
                            filter="images",
                            on_picked=on_picked,
                            label=appui.Label("选择照片", system_image="photo.on.rectangle"),
                        ),
                        appui.CameraPicker(
                            source="camera",
                            media_type="photo",
                            on_captured=on_captured,
                            label=appui.Label("拍照", system_image="camera.fill"),
                        ),
                        appui.Button("清空", role="destructive", action=clear_media),
                    ],
                    header="导入",
                ),
                appui.Section(
                    [
                        appui.ContentUnavailableView(
                            "暂无媒体",
                            system_image="photo",
                            description="选择照片或拍照后显示",
                        )
                        if not rows
                        else appui.ForEach(
                            rows,
                            row_builder=media_row_link,
                            key=media_row_key,
                        )
                    ],
                    header="媒体文件",
                ),
            ]
        ).navigation_title("媒体采集")
    )


appui.run(body, state=state, presentation="sheet")

#关键技巧

  • 选图用 PhotoPicker,拍照用 CameraPicker,由系统处理权限与选取 UI。
  • 在状态里保存路径字符串,不要保存图像对象或视图实例。
  • 分享文件/链接用 ShareLink;远程缩略图用 AsyncImage

#失败路径

  • 用户取消选择或拍照时,把状态更新为「未获得照片」,不要假设一定有路径。
  • 设备没有相机或权限不可用时,保留相册选择和清空按钮,避免整页不可用。

#相关文档

文档用途
媒体 APIPhotoPickerCameraPickerAsyncImageShareLink
原生能力入口权限和设备能力的处理思路。