Qt for Android (二) Qt打开android的相册

以下有一个可以用的Demo

https://files.cnblogs.com/files/wzxNote/Qt-Android-Gallery-master.zip

网盘地址:

https://pan.baidu.com/s/1DY7N50NcVSLanBeFw0WhvQ&shfl=sharepset

提取码:

ccy5

例子中的widgets程序可以直接跑起来,但是移植到自己的工程之后总是出现问题,提示找不到调用的class。

总的来说,Qt调用Android的原生相册需要三个步骤:

  • 编写操作相册的类,这里使用到QAndroidJniObject类的静态方法
#include <QFile>
#include <QDebug>
#include <QtAndroidExtras>
#include "androidgallery.h"

#ifdef __cplusplus
extern "C" {
#endif

QString selectedFileName;

JNIEXPORT void JNICALL
Java_com_amin_QtAndroidGallery_QtAndroidGallery_fileSelected(JNIEnv */*env*/,
                                                             jobject /*obj*/,
                                                             jstring results)
{
    selectedFileName = QAndroidJniObject(results).toString();
     qDebug() << "============5============";
}

#ifdef __cplusplus
}
#endif

AndroidGallery::AndroidGallery(QObject *parent) : QObject(parent)
{

}

QString AndroidGallery::openGalley()
{
    selectedFileName = "#";
    QAndroidJniObject::callStaticMethod<void>("com/amin/QtAndroidGallery/QtAndroidGallery",
                                              "openAnImage",
                                              "()V");

    while(selectedFileName == "#")
          qApp->processEvents();

    qDebug()<<"file name:"<<selectedFileName;
    if(QFile(selectedFileName).exists())
    {
        return  selectedFileName;
    }
}
com/amin/QtAndroidGallery/QtAndroidGallery

这个是“openAnImage"方法所在的java包。运行的时候我就总是碰到说找不到这个包的错误提示,搞了一两天还没弄明白;

 原因:QAndroidJniObject::callStaticMethod<void>("org/qtproject/example/OpenAndroidAlbum/OpenAndroidAlbum",
                                              "openAnImage",
"()V");
这个地方的路径没有写完整,前面的OpenAndroidAlbum表示文件夹,后面的OpenAndroidAlbum表示java文件,只不过省略的.java后缀。我开始只写了一个,粗心大意
  • 编写java文件
// http://www.amin-ahmadi.com

package com.amin.QtAndroidGallery;

import org.qtproject.qt5.android.bindings.QtApplication;
import org.qtproject.qt5.android.bindings.QtActivity;

import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.os.Build;
import android.os.Build.VERSION;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import android.provider.DocumentsContract;
import     android.content.ContentUris;

public class QtAndroidGallery extends QtActivity
{

    public static native void fileSelected(String fileName);

    static final int REQUEST_OPEN_IMAGE = 1;

    private static QtAndroidGallery m_instance;

    public QtAndroidGallery()
    {
        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)
    {
       if (resultCode == RESULT_OK)
        {
            if(requestCode == REQUEST_OPEN_IMAGE)
            {
            //    String filePath = getRealPathFromURI(getApplicationContext(), data.getData());
                 String filePath = getPath(getApplicationContext(), data.getData());
//                System.out.println("debug 123456789");
//                System.out.println(filePath);
                fileSelected(filePath);
            }
        }
        else
        {
            fileSelected(":(");
        }

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

    private void dispatchOpenGallery()
    {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        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();
            return cursor.getString(column_index);
        }
        finally
        {
            if (cursor != null)
            {
                cursor.close();
            }
        }
    }



/**
     * Get a file path from a Uri. This will get the the path for Storage Access
     * Framework Documents, as well as the _data field for the MediaStore and
     * other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @author paulburke
     */
    public static String getPath(final Context context, final Uri uri) {

        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }

                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {

                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };

                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
            String[] selectionArgs) {

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }


    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

}

点击open按钮调用的就是

static void openAnImage()
{
    m_instance.dispatchOpenGallery();
}

这个方法

  • AndroidManifest.xml的编写

关于AndroidManifest.xml文件的作用介绍在

https://www.jianshu.com/p/3b5b89d4e154 这篇博客里面有详细的介绍。简单来讲就是这个文件里面配置了一些jar包路径,资源路径,packageName,屏幕方向,是否全屏等Android程序加载需要的各种配置属性。现在怀疑问题就出在这个文件里。

项目的目录结构如下:

终于找到解决办法,崩溃的原因是android 6.0之后

    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {

     /**用于打开READ_EXTERNAL_STORAGE权限**/
        if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {

            // Should we show an explanation?
            if (shouldShowRequestPermissionRationale(
                    Manifest.permission.READ_EXTERNAL_STORAGE)) {
                // Explain to the user why we need to read the contacts
            }

            requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                9);

            // MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE is an
            // app-defined int constant that should be quite unique

            return;
        }
     /**用于打开READ_EXTERNAL_STORAGE权限**/

...
}

在回调函数中添加上面所示代码块。用来动态添加”存储权限“

原文地址:https://www.cnblogs.com/wzxNote/p/11693822.html