在設計Android App時,常常需要讓使用者選擇自己裝置內的圖片、音樂、影片或是文件,來進行一些處理,例如上傳檔案或是編輯檔案等等的功能。但是要如何讓使用者選擇檔案呢?Java程式語言的Swing函式庫中,提供了JFileChooser類別,可以直接產生出一個檔案選取的視窗,能讓使用者選取電腦內的檔案,還可以使用FileFilter過濾檔案選取視窗顯示出來的檔案類型,十分方便。雖然Android的程式是使用Java程式語言進行開發,但Android上的Java只能算是半殘的Java,有許多類別不能使用,其中當然包括JFileChooser,不然就不用著寫這篇文章了。



要讓Android程式擁有檔案選取的功能,最保險、可靠而且一定能Work的方式,那就是自己使用File類別或是ContentProvider實作出一個能選取檔案的Activity或是Dialog。如果不知道要怎麼自己做出檔案選取器的話,也可以使用別人寫好的,在網路上都可以找得到程式碼,只是品質無法保證。

這篇文章將不探討如何直接在自己的Android App內實作出檔案選取器,而是要應用Android裝置已經安裝的檔案瀏覽器之類的App來選取檔案。舉例來說,假設要選取一張圖片,而且裝置上已經有安裝「圖片庫(Google Gallery)」或是「快圖瀏覽(QuickPic)」之類的App,那麼將可以直接使用它們來選取圖片,就不需要在程式專案內實作出複雜的檔案選取和圖片瀏覽的功能了!

Android上的檔案類型

在選取檔案之前,必須要清楚知道要選取什麼樣類型的檔案。在Android系統中,跟內容(Content)相關的資訊都可由ContentProvider來取得,當使用者儲存了一個檔案,檔案的實際路徑與類型就會被儲存在ContentProvider中,因此只要查找ContentProvider中的檔案類型,就可以很容易地取得裝置中所有屬於這種類型的檔案。

Android的檔案類型採用MIME(Multipurpose Internet Mail Extensions)標準,MIME使用特定的字串格式來表示內容類型(當然也包括檔案),MIME的字串格式如下:

[主類型]/[子類型]

以檔案來說,可能會有以下幾種類型:

  • text
    • text/plain:純文字文件。
    • text/html:HTML文件。
  • application
    • application/xhtml+xml:XHTML文件。
    • application/pdf:PDF文件。
  • image
    • image/gif:GIF圖片。
    • image/jpeg:JPEG圖片。
    • image/png:PNG圖片。
  • audio
    • audio/midi:MIDI音樂。
    • audio/x-mpeg:MP2或是MP3音樂。
    • audio/x-wav:WAV音樂。
  • video
    • video/mp4:MP4影片。
    • video/mpeg:MPEG影片。
    • video/3gpp:3GP影片。

如果要表示所有類型的檔案,可以使用「*」字元。舉例來說,MIME類型若為「image/*」,表示所有的圖片類型;MIME類型若為「*/*」表示所有類型。

透過MIME選取檔案

有了其他Android App的幫助,設計師就不需要自己使用ContentProvider來取的指定MIME類型下的檔案清單,只需要讓這些擁有檔案選取功能的App知道應該要選擇什麼樣MIME類型的檔案即可。

Android的Intent可以用在Application或是Activity之間的溝通,只要使用Intent的ACTION_GET_CONTENT,系統就會幫使用者找到裝置內合適的App來取得指定MIME類型的內容,程式可以這樣寫:

...
Intent picker = new Intent(Intent.ACTION_GET_CONTENT);
picker.setType("*/*");
startActivity(picker);

建議用法

如果程式只有寫成這樣的話,將會遇到兩個恰恰相反的問題。第一個是Android系統找不到符合指定MIME類型的內容選取器,程式將會因為找不到可執行的Activity而直接閃退,這個問題甚至可能會沒辦法直接用try-catch來解決。第二個可能會遇到的問題是,當Android系統找到兩種以上可用的App或是Activity支援指定的MIME類型時,可能會自動使用其中一種,此時也許就會選到無法正常使用的App或是Activity,連帶使我們的App永遠無法正常使用。

要解決第一個找不到Activity的問題,可以事先使用PackageManager查詢可以使用該MIME類型的Activity列表來解決。而要解決第二個可用App或是Activity有兩個以上的問題的話,可以使用系統內建的Intent Chooser,跳出選單讓使用者選擇要使用哪個。因此選取檔案的程式可以寫成如以下的樣子,大概就沒有什麼問題了。

final String mimeType = "image/png";
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType(mimeType);
List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (list.size() > 0) {
    // 如果有可用的Activity
    Intent picker = new Intent(Intent.ACTION_GET_CONTENT);
    picker.setType(mimeType);
    // 使用Intent Chooser
    Intent destIntent = Intent.createChooser(picker, "選取PNG圖片");
    startActivity(destIntent);
} else {
    // 沒有可用的Activity
    ...
}

複選檔案

如果要複選檔案,可以在picker的Intent中,加入EXTRA_ALLOW_MULTIPLE欄位,並將其值設為true,如下:

picker.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);

EXTRA_ALLOW_MULTIPLE不一定能在所有的Activity上成功使用,要看該Activity有沒有用到EXTRA_ALLOW_MULTIPLE,並且實作出檔案複選的功能。

只能選擇本地端的檔案

如果只要選擇裝置內本地端的檔案,也可以在picker的Intent中,加入EXTRA_LOCAL_ONLY欄位,並將其值設為true。

picker.putExtra(Intent.EXTRA_LOCAL_ONLY, true);

至於什麼時候有能選擇裝置外的檔案的情況,當Activity有瀏覽雲端檔案或是同步之類的功能時,所選的檔案就可能不在本地端,如果此時要抓取檔案的實際路徑來做些應用,就可能會因實際路徑不在本地端上,而出現問題。EXTRA_LOCAL_ONLY也是一樣不一定能在所有的Activity上成功使用,要看該Activity的實作方式。

常見的錯誤用法

網路上常常可以看到有別人是使用Intent的ACTION_PICK來選取檔案,寫法其實和ACTION_GET_CONTENT類似,如下:

...
Intent picker = new Intent(Intent.ACTION_PICK);
picker.setType("*/*");
startActivity(picker);

雖然執行結果好像跟ACTION_GET_CONTENT沒有兩樣,但是其實這樣的用法是錯誤的。原因在於ACTION_PICK實際上是用來選擇一個URI目錄下的資料,比較正確的用法如下:

final Uri uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(Intent.ACTION_PICK, uri);
List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (list.size() > 0) {
    // 如果有可用的Activity
    Intent picker = new Intent(Intent.ACTION_PICK, uri);
    // 使用Intent Chooser
    Intent destIntent = Intent.createChooser(picker, "選取圖片");
    startActivity(destIntent);
} else {
    // 沒有可用的Activity
    ...
}

取得檔案位於裝置上的實際路徑

使用Intent的ACTION_GET_CONTENT和MIME類型來選擇檔案十分容易,但要如何取得使用者選定的檔案呢?那就得看使用者所用來選取檔案的Activity,是如何傳回檔案資料的,這部份並沒有特定的格式,而且也可能會有不符合指定MIME類型的檔案。

但是,大部份可選擇檔案的Activity都是在onActivityResult時,將選擇的結果包在傳回的Intent內。如果要使用onActivityResult來取得Activity的回傳結果,那麼當初在開啟Activity時,就要使用startActivityForResult方法,而不是startActivity方法。選取的結果,單選和複選的狀況應該要分開探討。

單選檔案

使用getData方法取得傳回的Intent所包含的URI,這個URI通常可以直接被用來反查ContentProvider內的檔案實際路徑。

Uri uri = intent.getData();

複選檔案

Intent的EXTRA_ALLOW_MULTIPLE欄位定義是在Android API 18之後才出現的,但是API 18之前的Activity究竟有沒有支援檔案複選也很難說。可以確定的是,複選檔案的結果大多是包在Intent的ClipData中,因此可以使用getClipData方法取得傳回的Intent所包含的ClipData,再用迴圈抓出ClipData中所有項目的URI,這個URI通常可以直接被用來反查ContentProvider內的檔案實際路徑。建議從Android API 16開始支援檔案複選的功能。

if(Build.VERSION.SDK_INT >= 16){
    ClipData clipData = intent.getClipData();
    int count = clipData.getItemCount();
    if (count > 0) {
        Uri[] uris = new Uri[count];
        for (int i = 0; i < count; i++) {
            uris[i] = clipData.getItemAt(i).getUri();
        }
    }
}

實際路徑

有了檔案的URI後,接著就要找出URI所對應的檔案,在Android裝置上實際的路徑。在Android 4.4(API 19)之前的版本,URI都可以直接透過ContentProvider來查詢其對應的檔案實際路徑,但在Android 4.4以及之後的版本,系統似乎內建了內容瀏覽器,並且使用了Android 4.4之後才新增的文件URI格式,回傳的URI Authority將會使用「com.android.externalstorage.documents」、「com.android.providers.downloads.documents」或是「com.android.providers.media.documents」。如果直接用這樣的URI查詢ContentProvider,將會得到空的路徑,因此這樣的URI需要進行一些轉換,才能被用來查詢ContentProvider。

實作檔案選取的程式

由於檔案選取算是一個常用的功能,所以可以將它寫成一個獨立的類別來重複使用。程式碼如下:

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ClipData;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

/**
 * 檔案選取器,支援檔案MIME類別篩選、強制可讀取以及複選功能。
 *
 * @author Magic Len
 */
@SuppressLint("NewApi")
public class MagicFileChooser {

    // TODO ------類別常數------
    /**
     * 檔案選取器的Activity Request Code。
     */
    public static final int ACTIVITY_FILE_CHOOSER = 9973;

    // TODO -----物件變數-----
    /**
     * 儲存使用這個檔案選取器的Activity。
     */
    private final Activity activity;

    // TODO -----物件變數-----
    /**
     * 儲存是否正在選取檔案。
     */
    private boolean choosing = false;
    /**
     * 儲存被選到的檔案是否一定要可以讀取。
     */
    private boolean mustCanRead;
    /**
     * 儲存被選到的檔案。
     */
    private File[] chosenFiles;

    // TODO -----建構子-----
    /**
     * 建構子,在Activity內使用檔案選取器。
     *
     * @param activity 傳入使用這個檔案選取器的Activity。
     */
    public MagicFileChooser(final Activity activity) {
        this.activity = activity;
    }

    // TODO -----類別方法-----
    /**
     * 從Uri取得絕對路徑。
     *
     * @param context 傳入Context
     * @param uris 傳入Uri陣列
     * @return 傳回絕對路徑字串陣列,若絕對路徑無法取得,則對應的陣列索引位置為null
     */
    public static String[] getAbsolutePathsFromUris(final Context context, final Uri[] uris) {
        return getAbsolutePathsFromUris(context, uris, false);
    }

    /**
     * 從多個Uri取得絕對路徑。
     *
     * @param context 傳入Context
     * @param uris 傳入Uri陣列
     * @param mustCanRead 傳入Uri所指的路徑是否一定要可以讀取
     * @return 傳回絕對路徑字串陣列,若絕對路徑無法取得或是無法讀取,則對應的陣列索引位置為null
     */
    public static String[] getAbsolutePathsFromUris(final Context context, final Uri[] uris, final boolean mustCanRead) {
        if (uris == null) {
            return null;
        }
        final int urisLength = uris.length;
        final String[] paths = new String[urisLength];
        for (int i = 0; i < urisLength; ++i) {
            final Uri uri = uris[i];
            paths[i] = getAbsolutePathFromUri(context, uri, mustCanRead);
        }
        return paths;
    }

    /**
     * 從多個Uri取得File物件。
     *
     * @param context 傳入Context
     * @param uris 傳入Uri陣列
     * @return 傳回File物件陣列,若File物件無法建立,則對應的陣列索引位置為null
     */
    public static File[] getFilesFromUris(final Context context, final Uri[] uris) {
        return getFilesFromUris(context, uris, false);
    }

    /**
     * 從多個Uri取得File物件。
     *
     * @param context 傳入Context
     * @param uris 傳入Uri陣列
     * @param mustCanRead 傳入Uri所指的路徑是否一定要可以讀取
     * @return 傳回File物件陣列,若File物件無法建立或是檔案路徑無法讀取,則對應的陣列索引位置為null
     */
    public static File[] getFilesFromUris(final Context context, final Uri[] uris, final boolean mustCanRead) {
        if (uris == null) {
            return null;
        }
        final int urisLength = uris.length;
        final File[] files = new File[urisLength];
        for (int i = 0; i < urisLength; ++i) {
            final Uri uri = uris[i];
            files[i] = getFileFromUri(context, uri, mustCanRead);
        }
        return files;
    }

    /**
     * 從Uri取得絕對路徑。
     *
     * @param context 傳入Context
     * @param uri 傳入Uri物件
     * @return 傳回絕對路徑,若絕對路徑無法取得,傳回null
     */
    public static String getAbsolutePathFromUri(final Context context, final Uri uri) {
        return getAbsolutePathFromUri(context, uri, false);
    }

    /**
     * 從Uri取得絕對路徑。
     *
     * @param context 傳入Context
     * @param uri 傳入Uri物件
     * @param mustCanRead 傳入Uri所指的路徑是否一定要可以讀取
     * @return 傳回絕對路徑,若絕對路徑無法取得或是無法讀取,傳回null
     */
    public static String getAbsolutePathFromUri(final Context context, final Uri uri, final boolean mustCanRead) {
        final File file = getFileFromUri(context, uri, mustCanRead);
        if (file != null) {
            return file.getAbsolutePath();
        } else {
            return null;
        }
    }

    /**
     * 從Uri取得File物件。
     *
     * @param context 傳入Context
     * @param uri 傳入Uri物件
     * @return 傳回File物件,若File物件無法建立,傳回null
     */
    public static File getFileFromUri(final Context context, final Uri uri) {
        return getFileFromUri(context, uri, false);
    }

    /**
     * 從Uri取得File物件。
     *
     * @param context 傳入Context
     * @param uri 傳入Uri物件
     * @param mustCanRead 傳入Uri所指的路徑是否一定要可以讀取
     * @return 傳回File物件,若File物件無法建立或是檔案路徑無法讀取,傳回null
     */
    @SuppressLint("NewApi")
    public static File getFileFromUri(final Context context, final Uri uri, final boolean mustCanRead) {
        if (uri == null) {
            return null;
        }

        // 判斷是否為Android 4.4之後的版本
        final boolean after44 = Build.VERSION.SDK_INT >= 19;
        if (after44 && DocumentsContract.isDocumentUri(context, uri)) {
            // 如果是Android 4.4之後的版本,而且屬於文件URI
            final String authority = uri.getAuthority();
            // 判斷Authority是否為本地端檔案所使用的
            if ("com.android.externalstorage.documents".equals(authority)) {
                // 外部儲存空間
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] divide = docId.split(":");
                final String type = divide[0];
                if ("primary".equals(type)) {
                    String path = Environment.getExternalStorageDirectory().getAbsolutePath().concat("/").concat(divide[1]);
                    return createFileObjFromPath(path, mustCanRead);
                } else {
                    String path = "/storage/".concat(type).concat("/").concat(divide[1]);
                    return createFileObjFromPath(path, mustCanRead);
                }
            } else if ("com.android.providers.downloads.documents".equals(authority)) {
                // 下載目錄
                final String docId = DocumentsContract.getDocumentId(uri);
                if (docId.startsWith("raw:")) {
                    final String path = docId.replaceFirst("raw:", "");
                    return createFileObjFromPath(path, mustCanRead);
                }
                final Uri downloadUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(docId));
                String path = queryAbsolutePath(context, downloadUri);
                return createFileObjFromPath(path, mustCanRead);
            } else if ("com.android.providers.media.documents".equals(authority)) {
                // 圖片、影音檔案
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] divide = docId.split(":");
                final String type = divide[0];
                Uri mediaUri = null;
                if ("image".equals(type)) {
                    mediaUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    mediaUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    mediaUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                } else {
                    return null;
                }
                mediaUri = ContentUris.withAppendedId(mediaUri, Long.parseLong(divide[1]));
                String path = queryAbsolutePath(context, mediaUri);
                return createFileObjFromPath(path, mustCanRead);
            }
        } else {
            // 如果是一般的URI
            final String scheme = uri.getScheme();
            String path = null;
            if ("content".equals(scheme)) {
                // 內容URI
                path = queryAbsolutePath(context, uri);
            } else if ("file".equals(scheme)) {
                // 檔案URI
                path = uri.getPath();
            }
            return createFileObjFromPath(path, mustCanRead);
        }
        return null;
    }

    /**
     * 將路徑轉成File物件。
     *
     * @param path 傳入檔案路徑
     * @return 傳回File物件,若File物件無法建立,傳回null。
     */
    public static File createFileObjFromPath(final String path) {
        return createFileObjFromPath(path, false);
    }

    /**
     * 將路徑轉成File物件。
     *
     * @param path 傳入檔案路徑
     * @param mustCanRead 傳入檔案路徑是否一定要可以讀取
     * @return 傳回File物件,若File物件無法建立或是檔案路徑無法讀取,傳回null
     */
    public static File createFileObjFromPath(final String path, final boolean mustCanRead) {
        if (path != null) {
            try {
                File file = new File(path);
                if (mustCanRead) {
                    file.setReadable(true);
                    if (!file.canRead()) {
                        return null;
                    }
                }
                return file.getAbsoluteFile();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 查詢MediaStroe Uri對應的絕對路徑。
     *
     * @param context 傳入Context
     * @param uri 傳入MediaStore Uri
     * @return 傳回絕對路徑
     */
    public static String queryAbsolutePath(final Context context, final Uri uri) {
        final String[] projection = {MediaStore.MediaColumns.DATA};
        Cursor cursor = null;
        try {
            cursor = context.getContentResolver().query(uri, projection, null, null, null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
                return cursor.getString(index);
            }
        } catch (final Exception ex) {
            ex.printStackTrace();
            if (cursor != null) {
                cursor.close();
            }
        }
        return null;
    }

    // TODO -----物件方法-----
    /**
     * 顯示檔案選取器,選取所有檔案,不設定檔案選取器的標題,僅進行單獨選取,被選到的檔案不一定要可以讀取。
     *
     * @return 傳回檔案選取器是否開啟成功
     */
    public boolean showFileChooser() {
        return showFileChooser("*/*");
    }

    /**
     * 顯示檔案選取器,不設定檔案選取器的標題,僅進行單獨選取,被選到的檔案不一定要可以讀取。
     *
     * @param mimeType 傳入篩選的MIME類型
     * @return 傳回檔案選取器是否開啟成功
     */
    public boolean showFileChooser(final String mimeType) {
        return showFileChooser(mimeType, null);
    }

    /**
     * 顯示檔案選取器,僅進行單獨選取,被選到的檔案不一定要可以讀取。
     *
     * @param mimeType 傳入篩選的MIME類型
     * @param chooserTitle 傳入檔案選取器的標題,若為null則用預設值
     * @return 傳回檔案選取器是否開啟成功
     */
    public boolean showFileChooser(final String mimeType, final String chooserTitle) {
        return showFileChooser(mimeType, chooserTitle, false);
    }

    /**
     * 顯示檔案選取器,被選到的檔案不一定要可以讀取。
     *
     * @param mimeType 傳入篩選的MIME類型
     * @param chooserTitle 傳入檔案選取器的標題,若為null則用預設值
     * @param allowMultiple 傳入檔案選取器是否使用複選
     * @return 傳回檔案選取器是否開啟成功
     */
    public boolean showFileChooser(final String mimeType, final String chooserTitle, final boolean allowMultiple) {
        return showFileChooser(mimeType, chooserTitle, allowMultiple, false);
    }

    /**
     * 顯示檔案選取器。
     *
     * @param mimeType 傳入篩選的MIME類型
     * @param chooserTitle 傳入檔案選取器的標題,若為null則用預設值
     * @param allowMultiple 傳入檔案選取器是否使用複選
     * @param mustCanRead 傳入被選到的檔案是否一定要可以讀取
     * @return 傳回檔案選取器是否開啟成功
     */
    public boolean showFileChooser(final String mimeType, final String chooserTitle, final boolean allowMultiple, final boolean mustCanRead) {
        if (mimeType == null || choosing) {
            return false;
        }
        choosing = true;
        // 檢查是否有可用的Activity
        final PackageManager packageManager = activity.getPackageManager();
        final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType(mimeType);
        final List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        if (list.size() > 0) {
            this.mustCanRead = mustCanRead;
            // 如果有可用的Activity
            final Intent picker = new Intent(Intent.ACTION_GET_CONTENT);
            picker.setType(mimeType);
            picker.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple);
            picker.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
            // 使用Intent Chooser
            final Intent destIntent = Intent.createChooser(picker, chooserTitle);
            activity.startActivityForResult(destIntent, ACTIVITY_FILE_CHOOSER);
            return true;
        } else {
            return false;
        }
    }

    /**
     * 當檔案選取器被關閉後,應該要呼叫這個方法,判斷檔案選取器是否有選取到檔案,接著再用getChosenFiles方法來取得選取結果。
     *
     * @param requestCode 傳入Activity的Request Code
     * @param resultCode 傳入Activity的Request Code
     * @param data 傳入Activity的data
     * @return 傳回檔案選取器是否有選取結果。
     */
    public boolean onActivityResult(final int requestCode, final int resultCode, final Intent data) {
        if (requestCode == ACTIVITY_FILE_CHOOSER) {
            choosing = false;
            if (resultCode == Activity.RESULT_OK) {
                final Uri uri = data.getData();
                if (uri != null) {
                    // 單選
                    chosenFiles = getFilesFromUris(activity, new Uri[]{uri}, mustCanRead);
                    return true;
                } else if (Build.VERSION.SDK_INT >= 16) {
                    // 複選
                    final ClipData clipData = data.getClipData();
                    if (clipData != null) {
                        int count = clipData.getItemCount();
                        if (count > 0) {
                            final Uri[] uris = new Uri[count];
                            for (int i = 0; i < count; ++i) {
                                uris[i] = clipData.getItemAt(i).getUri();
                            }
                            chosenFiles = getFilesFromUris(activity, uris, mustCanRead);
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    /**
     * 取得被選取到的檔案。
     *
     * @return 傳回被選取到的檔案,過濾掉不成功的部份
     */
    public File[] getChosenFiles(final boolean filter) {
        if (chosenFiles == null) {
            return new File[0];
        } else {
            final ArrayList<File> alFileList = new ArrayList<>();
            for (final File chosenFile : chosenFiles) {
                if (filter && chosenFile == null) {
                    continue;
                }
                alFileList.add(chosenFile);
            }
            final File[] files = new File[alFileList.size()];
            alFileList.toArray(files);
            return files;
        }
    }

    /**
     * 取得被選取到的檔案。
     *
     * @return 傳回被選取到的檔案,過濾掉不成功的部份
     */
    public File[] getChosenFiles() {
        return getChosenFiles(true);
    }
}

MagicFileChooser的用法十分容易,如果只是要用來取得URI的實體路徑,甚至不用使用new運算子來實體化出MagicFileChooser物件。

如果要使用MagicFileChooser來選取檔案的話,一開始必須先將MagicFileChooser實體化,並且要在建構子的參數傳入當前的Activity。接著使用showFileChooser方法來顯示出Android裝置內可用的檔案選取App,或是檔案選取App的列表。如果showFileChooser方法回傳false,表示裝置內並沒有適合的Activity能作為檔案選取器。為了取得MagicFileChooser的檔案選取結果,必須要覆寫Activity的onActivityResult方法,要在Activity的onActivityResult方法中呼叫MagicFileChooser的onActivityResult方法。如果選取結果有回傳成功,MagicFileChooser的onActivityResult方法會傳回true,此時可以使用MagicFileChooser的getChosenFiles方法,取得使用者所選的檔案,型態為File陣列。如此一來變能輕鬆無負擔地選取檔案了!