更新:

  1. 修改了一些逻辑错误
  2. 添加了一些图示说明

这两天弄了一下android相册的相关功能。还是花了挺长时间的,这里总结一下,避免以后再踩坑。同时也在这篇文章里面补齐一些android开发的基础支持

打开Android相册并选一个图片进行显示

分为几个步骤:

  1. QtCreator新建Android工程

    本例使用的是arm64-v8 Android开发套件。

  2. 构建工程并在构建目录中找到AndroidManifest.xml

    创建的Android工程build之后都会在android-build根目录下生成一个AndroidManifest.xml文件。这个文件是android开发很重要个的一个文件,是应用清单。项目中引用的java包、app的横屏和竖屏、app的是否全屏等等很多功能都是在里面设置的。下面有一些详细的参考文章:

    AndroidManifest.xml详解

  3. 在工程中添加AndroidManifest.xml和java文件。

    这里需要详细说一下Qt创建android工程的特殊步骤,如下图所示:

    创建android工程我们需要点击上图的按钮来创建模板,创建好的工程目录如下图:

    QtCreator会自动为我们创建如下一系列文件其中我们暂时用到的是AndroidManifest.xml文件。之后我们要把步骤2中提到的相同文件拷贝到该目录下面做替换。同时我们生成的调用相册方法的java文件也需要拷贝到这个目录下面,存放的目录结构如下:

    那么这么一长串的目录的创建依据是什么呢,就是我们的java文件里面定义的包名

    package org.qtproject.example.OpenAndroidAlbum;程序运行起来之后会根据目录结构找到我们的java包。当然我们也可以自己在工程根目录下创建android目录,只放入java后缀的文件和AndroidManifest.xml文件。这个目录的最终作用还是在程序构建的时候会把目录下面的文件拷贝到我们的构建目录下面,

    这里红色框框里面的东西会被我们创建的工程目录下的东西给替换掉,里面的一些文件具体是做什么的感兴趣的小伙伴可以自己去研究一下,总之我们需要的主要就是两个文件。

    java文件是自己创建的用来写一些java代码调用android原生功能的相当于c++中的一个namespace的文件。

    java文件可以从网上找一个来参考着写。在文章的末尾我会附上gitee的地址供大家参考。

    放出java的代码简单看一下吧:

    public class OpenAndroidAlbum extends QtActivity
    { public static native void fileSelected(String fileName); static final int REQUEST_OPEN_IMAGE = 1;
    public String lastCameraFileUri;
    static final int REQUEST_CAPTURE_IMAGE = 2; private static OpenAndroidAlbum m_instance; public OpenAndroidAlbum()
    {
    m_instance = this;
    } @Override
    public void onCreate(Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    } @Override
    protected void onDestroy()
    {
    super.onDestroy();
    } static void openAnImage()
    {
    m_instance.dispatchOpenGallery();
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
    System.out.println("===dispatchOpenGallery1===");
    if (resultCode == RESULT_OK)
    {
    if(requestCode == REQUEST_OPEN_IMAGE)
    {
    String filePath = getRealPathFromURI(getApplicationContext(), data.getData());
    fileSelected(filePath);
    }
    }
    else
    {
    // fileSelected(":(");
    } super.onActivityResult(requestCode, resultCode, data);
    } private void dispatchOpenGallery()
    {
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType("image/*");
    startActivityForResult(intent, REQUEST_OPEN_IMAGE);
    } public String getRealPathFromURI(Context context, Uri contentUri)
    {
    Cursor cursor = null;
    try
    {
    String[] proj = { MediaStore.Images.Media.DATA };
    cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
    int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
    cursor.moveToFirst();
    System.out.println(column_index);
    return cursor.getString(column_index);
    }
    finally
    {
    if (cursor != null)
    {
    cursor.close();
    }
    }
    } }
  • fileSelected这个静态函数是我们在c++代码中定义的。java和c++的混合编程是通过JNI来实现的,

    #ifdef Q_OS_ANDROID
    #ifdef __cplusplus
    extern "C" {
    #endif JNIEXPORT void JNICALL
    Java_org_qtproject_example_OpenAndroidAlbum_OpenAndroidAlbum_fileSelected(JNIEnv */*env*/,
    jobject /*obj*/,
    jstring results)
    {
    selectedFileName = QAndroidJniObject(results).toString();
    qDebug() << "fileName = " << selectedFileName;
    }
    #ifdef __cplusplus
    }
    #endif
    #endif

    名字看起来很长Java是固定头部,org_qtproject_example_OpenAndroidAlbum这个是java包名,OpenAndroidAlbum是类名,最后fileSelected这个才是函数名。需要注意的是由于jni函数名映射成java函数名的时候是依靠“”来间隔包、类、方法的,如果你的函数中有“”字符的话,jni必须能够区分函数名中的“_”是字符还是分隔符,所以在函数名前面需要加1用于区分。

    由于JNI的函数是c函数,所以要加上extern "C"。这样定义好之后的函数就可以在java中直接调用了,还是很方便的。

  • openAnImage是我们定义打开图片按钮的响应函数

    java中定义的函数在c++中调用的方法是通过Qt的QAndroidJniObject类的一个静态方法实现的:

    QAndroidJniObject::callStaticMethod<void>("org/qtproject/example/OpenAndroidAlbum/OpenAndroidAlbum",
    "openAnImage",
    "()V");

    第一个参数是类名其实也是包名,是在java文件中通过

    package org.qtproject.example.OpenAndroidAlbum;

    定义的。

  • dispatchOpenGallery这个方法用来调用相册

    通过Intent对象和startActivityForResult实现调用。这里有一个坑,Intent intent = new Intent(Intent.ACTION_PICK);创建对象的时候ACTION_PICK这个枚举要用对,4.4以后的版本好像要用这个,我也是之前怎么都打不开相册,改了这个枚举之后就可以了。

  • onActivityResult在相册中选中一张图片之后会调用这个回调

    很自然就会想到我们在c++中定义的fileSelected函数要在这个地方调用了。把路径转换成java的String类型,调用fileSelected(filePath)就可以在Qt代码中处理图片路径了。

  1. 在Qt代码中调用java的打开相册的方法,同时利用JNI定义一个c++的处理方法

    这个我们在上一条中也提到了。这里提一下编码中容易出错的地方

        QAndroidJniObject::callStaticMethod<void>("org/qtproject/example/OpenAndroidAlbum/OpenAndroidAlbum",
    "openAnImage",
    "()V");

    第一个字符串代表java包,相当于一个c++的类。如果遇到编译过程中遇到“找不到类"的类似错误提示检查一下第一个字符串,最后是以OpenAndroidAlbum.java包的前缀结尾的,切记!!!

  2. AndroidManifest.xml要做相应的修改

    完成了以上步骤还不算完,如果这时候直接执行程序会报错:

    JNI DETECTED ERROR IN APPLICATION: JNI GetStaticMethodID called with pending exception java.lang.NullPointerException: Attempt to invoke direct method 'void org.qtproject.example.OpenAndroidAlbum.OpenAndroidAlbum.dispatchOpenGallery()' on a null object reference

    从错误提示上可以推断出是调用了空对象的方法导致的。分析java代码m_instance.dispatchOpenGallery();打开相册的时候我们调用了这句话,而m_instance我们是在

     public OpenAndroidAlbum()
    {
    m_instance = this;
    }

    构造函数进行初始化的,我们调用的是静态方法,确实没有进行实例化。这里怎么解决呢?这就要用到两个地方了,我们定义的java类继承于QtActivity,而清单文件AndroidManifest.xml里面有一个activity标签,这个标签就是用来指定activity对应的类的,清单文件此时发挥了它”清单“的作用。此标签介绍如下:

    该元素声明一个实现应用可视化界面的Activity(Activity类子类)。这是元素中必要的子元素。所有Activity都必须由清单文件中的元素表示。任何未在该处声明的Activity对系统都不可见,并且永远不会被执行

    作者:闪电的蓝熊猫

    链接:https://www.jianshu.com/p/3b5b89d4e154

    来源:简书

    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    我们把activity标签下的属性 修改成org.qtproject.example.OpenAndroidAlbum.OpenAndroidAlbum即可。这个是不是很眼熟呢,没错就是我们在cpp中写的

    QAndroidJniObject::callStaticMethod<void>("org/qtproject/example/OpenAndroidAlbum/OpenAndroidAlbum",
    "openAnImage",
    "()V");

    第一个参数。这样的对应关系保证没错的话就可以顺利打开相册了。

  3. 完成了上述5点之后只是能打开相册而已,如果要读取里面的图片还要修改软件的权限,获取允许打开相册的权限之后才能正常的打开相册图片

gitee地址:https://gitee.com/guiguzicom/Demo/tree/master/OpenAndroidAlbum

Qt for Android (三) 打开Android相册并选一个图片进行显示的更多相关文章

  1. Android 解决调用系统相册打不开图片 DecodeServices报解码错误

    这是由于系统相册不知道你图片目录是一个相册.打开前需要向系统相册“注册一下”,说白了就是让系统相册知道你这个图片所在的文件夹是个相册. private static void scanImageFil ...

  2. Android问题-打开DelphiXE8与DelphiXE10新建一个空工程提示"out of memory"

    错误信息: [DCC Error] E2597 d:\XE8\Embarcadero\Studio\16.0\PlatformSDKs\android-ndk-r9c\toolchains\arm-l ...

  3. 【Android】读取sdcard卡上的全部图片而且显示,读取的过程有进度条显示

    尽管以下的app还没有做到快图浏览.ES文件浏览器的水平,遇到大sdcard还是会存在读取过久.内存溢出等问题,可是基本思想是这种. 例如以下图.在sdcard卡上有4张图片, 打开app,则会吧sd ...

  4. [转]Android使用WebView从相册/拍照中添加图片

    原地址:http://blog.csdn.net/djcken/article/details/46379929 解决这个问题花了很长时间搜索了解,网上大部分使用openFileChooser但都没解 ...

  5. Android使用Glide加载https链接的图片不显示的原因

    平时我们使用Glide加载http网址的图片的时候,图片可以正常加载出来,但是如果服务器端加上了安全认证,当加载自签名的https图片的时候就会报如下错误(证书路径验证异常). 我们如果不修改Glid ...

  6. 在Android中如何获取视频的第一帧图片并显示在一个ImageView中

    String path  = Environment.getExternalStorageDirectory().getPath(); MediaMetadataRetriever media = n ...

  7. Android 获取imageview的图,在另一个imageview里显示。

    当我点击默认头像里的其中一个然后在点确定就在最上面的那个imageview里显示选择的头像.求大神. img1和img2都是ImageView,要把img1中的图片显示到img2中 前景(对应src属 ...

  8. Stage3D学习笔记(三):使用GPU绘制一个图片

    首先准备我们需要的图片,尺寸必须是2的幂数,我修改了一下Starling的图标拿来用: 还是先看看最终效果: 代码是居于上一节的代码进行修改的: package { import com.adobe. ...

  9. android studio配置android开发环境

    1.下载安装android-studio-bundle 地址:https://developer.android.com/sdk/index.html 注意:指定android sdk和android ...

随机推荐

  1. 通过bat文件 进行mysql 连接 或者 操作涉及 密码的,如果密码 中有 % 号的话要特殊处理

    比如我想在bat文件中进行一个数据库的连接 或者进行一个数据库中的 数据 导入或者导出(mysqldump) 这样子都会用到数据库密码, 假如这个数据库的密码 中又有 % 的话就要特殊转义一下才行执行 ...

  2. 返回指定字符串位置的函数FIELD(S,S1,S2,...) 与 FIND_IN_SET(S1,S2) 函数

    FIELD(S,S1,S2,...)  与 FIND_IN_SET(S,S1) 函数  ------> 这2个函数都是返回指定字符串在源串中的出现的位置(皆是第一次出现的位置),但2个函数的参数 ...

  3. 在IDEA中搭建Java源码学习环境并上传到GitHub上

    打开IDEA新建一个项目 创建一个最简单的Java项目即可 在项目命名填写该项目的名称,我这里写的项目名为Java_Source_Study 点击Finished,然后在项目的src目录下新建源码文件 ...

  4. Android Them+SharedPreferences 修改程序所有view字体颜色、大小和页面背景

    有这么一个需求,可以对页面的样式进行选择,然后根据选择改变程序所有字体颜色和页面背景.同时下一次启动程序,当前设置依然有效. 根据需求,我们需要一种快速,方便,有效的方式来实现需求,然后可以通过And ...

  5. nmon 的下一代工具 njmon

    njmon njmon = nmon + JSON format + real-time push to a stats database + instant graphing of "al ...

  6. pomelo环境配置(windows环境)

    目录 简介 准备 安装 工程的创建 简介 1.网易开源,免费,业(diao)界(si)良(fu)心(li)呀,^.^ 2.游戏服务器框架(当然也可以用于web服务器) 3.高性能.高可伸缩.分布式,多 ...

  7. HTML学习过程-(1)

    记录我HTML的学习 (1) 最开始学习html是在因为在听北京理工大学教授讲的网络公开课上.当时老师讲的是网络爬虫,因为要爬取特定网页的信息,需要借助[正则表达式](https://baike.ba ...

  8. C++写日志方法调试

    调试方法有很多 介绍一种奇怪的?调试方法哈哈 通过WriteLog记录返回值查看返回结果. string str_log;stringstream ssteam;ssteam << &qu ...

  9. 基于 HTML WebGL 的会展中心智能监控系统

    前言 随着近几年物联网.万物互联等诸多概念的大行其道,智慧城市的概念也早已经被人们耳熟能详,而作为城市的组成部分,智慧建筑也是重中之重,智慧园区,智慧小区等也如雨后春笋般的相继出现. 智慧建筑是指通过 ...

  10. 终止过久没有返回的 Windows API 函数 ---- “CancelSynchronousIo”

    Marks pending synchronous I/O operations that are issued by the specified thread as canceled. BOOL W ...