You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter实现从根存储扫描并获取所有PDF文件列表的方法

实现扫描设备PDF文件并在ListView展示的方案

一、权限准备

首先要获取存储访问权限,不同Android版本权限要求不同:

  • Android 10及以下:需READ_EXTERNAL_STORAGE权限
  • Android 11及以上:推荐用MANAGE_EXTERNAL_STORAGE权限(需引导用户到系统设置开启),或通过分区存储的MediaStore接口访问文件

在AndroidManifest.xml中声明权限:

<!-- Android 10及以下 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Android 11+ -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

动态申请权限(以READ_EXTERNAL_STORAGE为例):

if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 100);
}

二、扫描PDF文件

推荐用MediaStore查询,比直接遍历文件系统更高效且符合系统规范:

private List<PDFItem> scanPDFs() {
    List<PDFItem> pdfList = new ArrayList<>();
    ContentResolver resolver = getContentResolver();
    Uri uri = MediaStore.Files.getContentUri("external");
    
    // 要查询的字段:文件名、大小、路径
    String[] projection = {
            MediaStore.Files.FileColumns.DISPLAY_NAME,
            MediaStore.Files.FileColumns.SIZE,
            MediaStore.Files.FileColumns.DATA
    };
    
    // 筛选PDF文件:匹配mime类型或后缀
    String selection = MediaStore.Files.FileColumns.MIME_TYPE + "=? OR " + MediaStore.Files.FileColumns.DATA + " LIKE ?";
    String[] selectionArgs = {"application/pdf", "%.pdf"};
    
    Cursor cursor = resolver.query(uri, projection, selection, selectionArgs, null);
    if (cursor != null) {
        while (cursor.moveToNext()) {
            String fileName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME));
            long fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.SIZE));
            String filePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA));
            
            // 二次过滤确保是PDF文件
            if (fileName.endsWith(".pdf")) {
                pdfList.add(new PDFItem(fileName, formatFileSize(fileSize), filePath));
            }
        }
        cursor.close();
    }
    return pdfList;
}

// 格式化文件大小为易读的KB/MB格式
private String formatFileSize(long size) {
    if (size < 1024) {
        return size + " B";
    } else if (size < 1024 * 1024) {
        return String.format("%.2f KB", (float) size / 1024);
    } else {
        return String.format("%.2f MB", (float) size / (1024 * 1024));
    }
}

三、创建PDF数据模型

public class PDFItem {
    private String fileName;
    private String fileSize;
    private String filePath;

    public PDFItem(String fileName, String fileSize, String filePath) {
        this.fileName = fileName;
        this.fileSize = fileSize;
        this.filePath = filePath;
    }

    // getter方法
    public String getFileName() { return fileName; }
    public String getFileSize() { return fileSize; }
    public String getFilePath() { return filePath; }
}

四、ListView自定义适配器

先创建列表项布局item_pdf.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_file_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/tv_file_size"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:textColor="#666666"
        android:layout_marginTop="4dp"/>
</LinearLayout>

编写适配器代码:

public class PDFAdapter extends BaseAdapter {
    private Context context;
    private List<PDFItem> pdfList;

    public PDFAdapter(Context context, List<PDFItem> pdfList) {
        this.context = context;
        this.pdfList = pdfList;
    }

    @Override
    public int getCount() {
        return pdfList.size();
    }

    @Override
    public Object getItem(int position) {
        return pdfList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.item_pdf, parent, false);
            holder = new ViewHolder();
            holder.tvFileName = convertView.findViewById(R.id.tv_file_name);
            holder.tvFileSize = convertView.findViewById(R.id.tv_file_size);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        PDFItem item = pdfList.get(position);
        holder.tvFileName.setText(item.getFileName());
        holder.tvFileSize.setText(item.getFileSize());
        return convertView;
    }

    static class ViewHolder {
        TextView tvFileName;
        TextView tvFileSize;
    }
}

五、绑定数据到ListView

扫描操作耗时,必须放在子线程执行,这里用AsyncTask示例:

private ListView listView;
private PDFAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    listView = findViewById(R.id.list_view);
    
    // 权限通过后启动扫描
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
        new ScanPDFTask().execute();
    }
}

private class ScanPDFTask extends AsyncTask<Void, Void, List<PDFItem>> {
    @Override
    protected List<PDFItem> doInBackground(Void... voids) {
        return scanPDFs();
    }

    @Override
    protected void onPostExecute(List<PDFItem> pdfItems) {
        adapter = new PDFAdapter(MainActivity.this, pdfItems);
        listView.setAdapter(adapter);
        
        // 添加列表项点击事件,可在此处实现打开PDF的逻辑
        listView.setOnItemClickListener((parent, view, position, id) -> {
            PDFItem item = (PDFItem) parent.getItemAtPosition(position);
            // 这里写打开PDF的代码,比如调用系统阅读器或自定义阅读组件
        });
    }
}

注意事项

  • Android 11+使用MANAGE_EXTERNAL_STORAGE时,需通过Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)引导用户开启权限
  • 也可以用Coroutine、RxJava替代AsyncTask实现异步扫描
  • 若需实时监听PDF文件变化,可注册ContentObserver监听MediaStore的更新

内容的提问来源于stack exchange,提问作者Jackson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 19:20:41