Qt for Android (三) 打开Android相册并选一个图片进行显示

这两天弄了一下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文件。

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

    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要做相应的修改

    上面也提到了,这个文件是清单列表可以用来指定要调用j的ava包

    <manifest package="org.qtproject.example.OpenAndroidAlbum" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
        <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="28"/>
    

    package=这个包名的属性做对应的修改。

  3. 完成了上述5点之后还要修改软件的权限,获取允许打开相册的权限之后才能正常的打开相册图片

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

Qt for Android (三) 打开Android相册并选一个图片进行显示

上一篇:uniapp页面滚动到页面可指定位置


下一篇:调用微信退款接口或发红包接口时出现System.Security.Cryptography.CryptographicException: 出现了内部错误 解决办法