android - 无法在 Oreo 上选择文件

标签 android file-permissions android-8.0-oreo filepicker

在 Android Nougat 及更低版本上,我可以使用以下代码简单地在我的存储中获取一些文件:

Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*.jpg");
chooseFile = Intent.createChooser(chooseFile, "Choose a file");
startActivityForResult(chooseFile, 111);

并获取文件路径使用:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 111 && resultCode == RESULT_OK && data.getData() != null)
       String path = data.getData().getPath();

}

但是在 Android Oreo 上,这是行不通的。文件选择器出现了,但我什至无法使用默认文件选择器选择文件。起初,我认为这与permission有关.但是我在运行时添加了对外部存储的读写权限并授予后,这个问题仍然出现。

最佳答案

由于 OREO 上的默认文件选择器很麻烦,目前我使用自定义类来选择文件或目录。另一种解决方案是您可以使用 ES File Explorer 等,但并非所有用户都拥有它并且主要问题仍然存在。

public class FileChooser {

    private Activity activity;
    private Item[] fileList;
    private File path;

    private boolean rootDir  = true; //check if the current directory is rootDir
    private boolean pickFile = true; //flag to get directory or file

    private String title = "";
    private String upTitle = "Up";
    private String positiveTitle = "Choose Path";
    private String negativeTitle = "Cancel";

    private ListAdapter adapter;
    private ArrayList<String> str = new ArrayList<>(); //Stores names of traversed directories, to detect rootDir
    private Listener listener;

    /**
     * @param pickFile true for file picker and false for directory picker
     * */
    public FileChooser(Activity activity, boolean pickFile, Listener fileChooserListener) {
        this.activity = activity;
        this.pickFile = pickFile;
        this.listener = fileChooserListener;
        title = pickFile ? "Choose File" : "Choose Directory";
        path = new File(String.valueOf(Environment.getExternalStorageDirectory()));
    }


    /**
     * The view of your file picker
     * */
    public void openDirectory() {
        loadFileList();
        AlertDialog.Builder builder = new AlertDialog.Builder(activity, AlertDialog.THEME_DEVICE_DEFAULT_DARK);
        if (fileList == null)
            builder.create();

        builder.setTitle(title + "\n" + path.toString());
        builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int position) {
                String chosenFile = fileList[position].file;
                File selectedFile = new File(path + File.separator + chosenFile);

                if (selectedFile.isDirectory()) { // user click on folder
                    rootDir = false;
                    str.add(chosenFile); // Adds chosen directory to list
                    path = selectedFile;
                    openDirectory();
                }
                else if (chosenFile.equalsIgnoreCase(upTitle) && !selectedFile.exists()) { // 'up' was clicked
                    String s = str.remove(str.size() - 1); // present directory
                    path = new File(
                            path.toString().substring(0, path.toString().lastIndexOf(s))); // exclude present directory
                    if (str.isEmpty()) // no more directories in the list, rootDir
                        rootDir = true;
                    openDirectory();

                }
                else if (listener != null && pickFile)
                    listener.onSelectedPath(selectedFile.getAbsolutePath());

            }
        });

        if (!pickFile) {
            builder.setPositiveButton(positiveTitle, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    if (listener != null)
                        listener.onSelectedPath(path.getPath());
                }
            });
        }
        builder.setNegativeButton(negativeTitle, null);
        builder.show();
    }

    /**
     * Setup your file picker data
     * */
    private void loadFileList() {
        fileList = null;
        if (path.exists()) {
            FilenameFilter filter = new FilenameFilter() {
                @Override
                public boolean accept(File dir, String filename) {
                    File file = new File(dir, filename);
                    // Filters based on whether the file is hidden or not
                    return ((pickFile && file.isFile()) || file.isDirectory()) && !file.isHidden();
                }
            };

            String[] fList = path.list(filter); //set filter
            if (fList != null) {
                fileList = new Item[fList.length];
                for (int i = 0; i < fList.length; i++)
                    fileList[i] = new Item(fList[i], new File(path, fList[i]).isDirectory() ?
                            R.drawable.ic_folder : R.drawable.ic_file); //set icon, directory or file

                if (!rootDir) {
                    Item temp[] = new Item[fileList.length + 1];
                    System.arraycopy(fileList, 0, temp, 1, fileList.length);
                    temp[0] = new Item(upTitle, R.drawable.ic_undo);
                    fileList = temp;
                }
            }

        } else
            path = new File(String.valueOf(Environment.getExternalStorageDirectory()));

        try {
            adapter = new ArrayAdapter<Item>(activity,
                    android.R.layout.select_dialog_item, android.R.id.text1,
                    fileList) {
                @NonNull
                @Override
                public View getView(int position, View convertView, @NonNull ViewGroup parent) {
                    // creates view
                    View view = super.getView(position, convertView, parent);
                    TextView textView = view.findViewById(android.R.id.text1);
                    textView.setTextColor(Color.WHITE);

                    // put the image on the text view
                    textView.setCompoundDrawablesWithIntrinsicBounds(fileList[position].icon, 0, 0, 0);

                    // add margin between image and text (support various screen densities)
                    int dp5 = (int) (5 * activity.getResources().getDisplayMetrics().density + 0.5f);
                    textView.setCompoundDrawablePadding(dp5);

                    return view;
                }
            };
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private class Item {
        public String file;
        public int icon;

        private Item(String file, Integer icon) {
            this.file = file;
            this.icon = icon;
        }

        @Override
        public String toString() {
            return file;
        }
    }

    public interface Listener {
        void onSelectedPath(String path);
    }

}

关于android - 无法在 Oreo 上选择文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50961397/

相关文章:

android - 是否有适用于 Android 的精简版 SVG 查看器?

android - 将预构建的 java 类包含到 Android 项目中

php - PDO SQLite创建数据库默认权限

java - File.canWrite() 没有按预期工作

Android 8.0 Oreo 在聚焦 TextInputEditText 时崩溃

android - 如何在 Android Oreo 中管理来自未知来源的安装?

android - RESTful API 或 Socket.IO

android - Android 工具位置的最佳实践是什么?

Node.js:SCP 文件并保留权限

Android O Developer Preview 模拟器始终离线