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权限**/
...
}
在回调函数中添加上面所示代码块。用来动态添加”存储权限“