找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 348|回复: 0

[教程] 一个Renpy安卓游戏调用手机相册权限的方案

[复制链接]
发表于 2026-1-10 17:31:12 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 blurred 于 2026-2-10 09:04 编辑

前言:
1.此方法由"我"单独提出,实现,测试
2.本方案在Ren'py-8.3.7能够实现效果,如果使用,请注意引擎版本

3.涉及到修改引擎的代码,所以请在事前把引擎目录下的rapt相关文件备份

3.学习操作门槛:需要一定的java小基础以及了解Android应用的相关特性
(包括但不限于一些基础的apk知识,apk的文件结构以及其rapt各个文件作用和内容)

4.你使用了这个方案来调用相册权限,可以在游戏名单标上“浅唱”,不标也没关系
本帖参考:

一个在Ren'py中调用手机通知和(小米特有)卸载弹窗方案 - 经验教程 - RenPy中文空间
一个Renpy打包安卓游戏的不调用安全输入法的解决方案 - 经验教程 - RenPy中文空间
一个Renpy打包安卓游戏的不调用安全输入法的解决方案 - 经验教程 - RenPy中文空间
一个Renpy安卓打包游戏通过安卓平台审核方案 - 经验教程 - RenPy中文空间
效果预览:
88A6A6B1D39194913EC00DC34E982966.png
压缩包:(懒得看帖子可以一键解压即可食用)(内置食用教程和餐具)
https://www.renpy.cn/forum.php?mod=attachment&aid=MTY5NHxiNjI0OWM3OHwxNzcyMDE2NzE3fDB8
方法思路以及部分代码解释:
在renpy中,我们需要实现安卓扩展功能都一定要改rapt,所以按照惯例,我们打开了PythonSDLActivity.java并且敏锐地发现,关于外部文件引用的代码——一行没有
所以我们需要自己定义一个用于uri转化的方法,这里特别感谢龙叔(@Aaron栩生阿龙 )给出的参考代码
PythonSDLActivity.java
首先导入需要的模块
[RenPy] 纯文本查看 复制代码
import android.content.Intent;
import android.net.Uri;
import android.database.Cursor;
import android.provider.MediaStore;
import java.io.InputStream;
import java.io.OutputStream;

然后我们写几个方法,分别用于:使用请求码(2024)打开相册,Renpy中调用获取图片路径,图片路径和uri转化
打开相册
[RenPy] 纯文本查看 复制代码
// 定义请求码
private static final int PICK_IMAGE_REQUEST = 2024;
// 保存选中的图片路径
public static String lastPickedImagePath = "";

// 供 Ren'Py 调用:打开相册
public void openSystemAlbum() {
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType("image/Q_C_pick/*");
    Log.i("openSystemAlbum", "Opening system album.");
    startActivityForResult(intent, PICK_IMAGE_REQUEST);
}

调用路径
[RenPy] 纯文本查看 复制代码
public String getPickedPath() {
    String path = lastPickedImagePath;
    lastPickedImagePath = ""; 
    Log.i("openSystemAlbum", "getPickedPath returning: " + path);
    return path;
}

图片路径和uri转化
[RenPy] 纯文本查看 复制代码
private String getRealPathFromURI(Uri contentUri) {
    String res = null;
    String[] proj = { MediaStore.Images.Media.DATA };
    Cursor cursor = getContentResolver().query(contentUri, proj, null, null, null);
    if(cursor != null) {
        if(cursor.moveToFirst()) {
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            res = cursor.getString(column_index);
        }
        cursor.close();
    }
    Log.i("openSystemAlbum", "Real path from URI: " + res);
    return res;
}

关于uri转文件路径,这部分修改原有的onActivityResult就行
[RenPy] 纯文本查看 复制代码
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent resultData) {
    if (mStore != null && mStore.onActivityResult(requestCode, resultCode, resultData)) {
        return;
    }

    // 修改 onActivityResult 
    super.onActivityResult(requestCode, resultCode, resultData);

    if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && resultData != null && resultData.getData() != null) {
        Uri uri = resultData.getData();
        // 将 Uri 转换为真实文件路径
        lastPickedImagePath = getRealPathFromURI(uri);
        Log.i("openSystemAlbum", "Selected image path: " + lastPickedImagePath);
    }
    if (mStore != null && mStore.onActivityResult(requestCode, resultCode, resultData)) {
        return;
    }
    // 

    Log.v("python", "onActivityResult(" + requestCode + ", " + resultCode + ", " + resultData.toString() + ")");

    mActivityResultRequestCode = requestCode;
    mActivityResultResultCode = resultCode;
    mActivityResultResultData = resultData;

    super.onActivityResult(requestCode, resultCode, resultData);
}

至此,关于调用的java基层就写完了,剩下的就是调用java中的接口,用rpy中的python函数调用接口即可,我们先写一个类用于管理
在此之前要先导入模块
[Python] 纯文本查看 复制代码
# 导入jnius
    if renpy.android:
        from jnius import autoclass
        import os
        import shutil
        # 标记是否为安卓环境
        is_android = True
    else:
        is_android = False

相册类
[Python] 纯文本查看 复制代码
class AlbumManager(object):    
        def __init__(self):
            if not is_android:
                renpy.notify("当前不是 Android 平台,无法调用相册")
                self.activity = None
                return
            try:
                self.PythonSDLActivity = autoclass('org.renpy.android.PythonSDLActivity')
                self.activity = self.PythonSDLActivity.mActivity
                self.last_saved_filename = ""
            except Exception as e:
                renpy.notify(f"初始化 Java 类失败: {e}")
                self.activity = None
        def open_gallery(self):
            # 调用java中的openSystemAlbum
            self.activity.openSystemAlbum()
        def fetch_and_copy_image(self):
            # 调用java中的getPickedPath 
            source_path = self.activity.getPickedPath()
            renpy.notify(f"Picked image path: {source_path}")
            # 这个notify是调试用的,可以删除
            if not source_path:
                return None
            target_dir = os.path.join(config.gamedir, "images/Q_C_pick")
            # 定义图片路径,这里必须和下面图片变量定义一样
            if not os.path.exists(target_dir):
                os.makedirs(target_dir)
            original_filename = os.path.basename(source_path)
            dest_filename = "picked_" + original_filename
            # 在获取的图片名前加上picked_
            target_path = os.path.join(target_dir, dest_filename)
            try:
                shutil.copy(source_path, target_path)
                self.last_saved_filename = dest_filename
                renpy.notify(f"Image copied to: {target_path}")
                # 这个notify是调试用的,可以删除
                return dest_filename
            except Exception as e:
                renpy.notify("Copy Error: " + str(e))
                # 这个notify是调试用的,可以删除
                return None
        def get_image_name(self):
            return self.last_saved_filename

剩下的就是使用,这里给出一个使用案例
[RenPy] 纯文本查看 复制代码
# 首先先实例化Album
# 然后调用open_gallery()打开相册
# 之后调用fetch_and_copy_image()获取所选图片并复制到游戏目录,返回图片文件名
# 获取图片要像picked_img = "images/Q_C_pick/" + img_name,前面的"images/Q_C_pick/"是必须的,不然会找不到图片
# 然后这里定义了一个变量用于临时储存获取的图片
# 在使用前,你需要使用request_permission获取权限,不然会失败
# 这里给出一个使用并且用show语句展示
label album_test:
    "准备调用相册..."
    python:
        # 授权权限,你可以把这一部分移动到别的地方,但是需要在调用相册前申请权限
        for pess in build.android_permissions:
            renpy.request_permission(pess)
        #
        album = AlbumManager()
        album.open_gallery()
    "选照片"
    python:
        img_name = album.fetch_and_copy_image()
    if img_name:
        $ picked_img = "images/Q_C_pick/" + img_name
        "成功:[img_name]"
        show expression picked_img at truecenter
        "[img_name]"
    jump album_test

至此功能实现完毕,本身这个方案的原理是复制多张玩家的照片到游戏的应用私有目录,然后读取私有目录下的图片来显示,理论可以实现更多效果,代价就是选择的图片多了会导致占用空间变大,这个调用可以结合画廊或者其他储存功能,例如保存玩家选择的图片名字,这样后续可以再次调用玩家选择过的图片而不只是当前选择的图片,这一部分我不展开
闲言:
其实这个功能前前后后做了许久,一开始是想着直接访问外部文件,结果总是失败,后面转化思路改为复制文件就成功了,目前这个方案还有许多欠缺
比如单次只能选择一张图片,选择图片多了会导致游戏占用空间很大,只能说我太菜不会解决了(
视频版本教程:



评分

参与人数 1活力 +300 干货 +3 收起 理由
烈林凤 + 300 + 3 感谢分享!

查看全部评分

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|RenPy中文空间 ( 苏ICP备17067825号|苏公网安备 32092302000068号 )

GMT+8, 2026-2-25 18:51 , Processed in 0.053211 second(s), 30 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表