Qt for Android (三) 開啟Android相簿並選一個圖片進行顯示

鬼谷子com發表於2020-05-08

這兩天弄了一下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

相關文章