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

Android全版本自定义路径PDF获取及RecyclerView展示问题求助

解决Android自定义路径PDF读取与RecyclerView展示问题

原代码核心问题分析

  1. 目录路径构造错误:Environment.DIRECTORY_DOCUMENTS是公共目录的相对标识,直接拼接File对象在Android Q+会导致路径无效,无法正确访问目标目录。
  2. 作用域存储限制未适配:Android 10(API 29)及以上禁用了直接通过File API访问外部公共目录的自定义子目录,必须使用MediaStore或存储访问框架(SAF)。
  3. MediaStore查询逻辑缺陷:getStatusScope方法未过滤指定的AppName目录,且仅获取了文件ID而非可访问的路径/Uri,导致无法正确加载目标PDF。

解决方案与示例代码

一、权限配置

根据目标Android版本添加对应权限到AndroidManifest.xml:

<!-- Android 10及以下需要 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Android 11及以上访问公共目录需要(可选,若用SAF则无需) -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
    android:minSdkVersion="30" />

注意:MANAGE_EXTERNAL_STORAGE权限需要引导用户手动开启,建议优先使用应用私有目录。

二、推荐方案:使用应用私有目录(无需额外权限)

将PDF存储在应用私有外部目录,所有Android版本都可直接用File API访问:

1. 创建私有目录

// 获取应用私有Documents子目录
File privatePdfDir = new File(getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "AppName");
if (!privatePdfDir.exists()) {
    privatePdfDir.mkdirs();
}

2. 读取私有目录下的PDF并更新RecyclerView

private void loadPrivatePdfs() {
    new Thread(() -> {
        List<PDFModel> pdfList = new ArrayList<>();
        File privatePdfDir = new File(getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "AppName");
        
        File[] files = privatePdfDir.listFiles((dir, name) -> name.endsWith(".pdf"));
        if (files != null) {
            // 按修改时间倒序排序
            Arrays.sort(files, (o1, o2) -> Long.compare(o2.lastModified(), o1.lastModified()));
            for (File file : files) {
                pdfList.add(new PDFModel(file.getAbsolutePath(), file.getName()));
            }
        }

        // 主线程更新UI
        runOnUiThread(() -> {
            PdfAdapter adapter = new PdfAdapter(pdfList, this);
            recyclerView.setAdapter(adapter);
            recyclerView.setHasFixedSize(true);
            // 无数据时的处理
            if (pdfList.isEmpty()) {
                // noResultSwipeImage.setVisibility(View.VISIBLE);
            } else {
                // noResultSwipeImage.setVisibility(View.GONE);
            }
        });
    }).start();
}

三、适配公共Documents/AppName目录(Android Q+)

若必须访问公共目录下的自定义子目录,需使用MediaStore查询指定路径的PDF:

1. 查询指定目录的PDF

private void loadPublicPdfs() {
    new Thread(() -> {
        List<PDFModel> pdfList = new ArrayList<>();
        ContentResolver resolver = getContentResolver();
        Uri uri = MediaStore.Files.getContentUri("external");

        // 构造查询条件:筛选PDF且路径包含Documents/AppName
        String[] projection = {
                MediaStore.Files.FileColumns._ID,
                MediaStore.Files.FileColumns.DISPLAY_NAME,
                MediaStore.Files.FileColumns.DATA // Android Q以下可用,Q+需用Uri
        };
        String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf");
        String selection = MediaStore.Files.FileColumns.MIME_TYPE + "=? AND "
                + MediaStore.Files.FileColumns.RELATIVE_PATH + " LIKE ?";
        String[] selectionArgs = {
                mimeType,
                "%Documents/AppName/%" // 匹配相对路径
        };

        Cursor cursor = resolver.query(uri, projection, selection, selectionArgs, null);
        if (cursor != null) {
            int nameIndex = cursor.getColumnIndex(MediaStore.Files.FileColumns.DISPLAY_NAME);
            int idIndex = cursor.getColumnIndex(MediaStore.Files.FileColumns._ID);
            
            while (cursor.moveToNext()) {
                String fileName = cursor.getString(nameIndex);
                // Android Q+通过ID构造Uri访问文件
                Uri fileUri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"),
                        cursor.getLong(idIndex));
                pdfList.add(new PDFModel(fileUri.toString(), fileName));
            }
            cursor.close();
        }

        runOnUiThread(() -> {
            PdfAdapter adapter = new PdfAdapter(pdfList, this);
            recyclerView.setAdapter(adapter);
            recyclerView.setHasFixedSize(true);
            if (pdfList.isEmpty()) {
                // 处理无数据情况
            }
        });
    }).start();
}

2. PDFModel适配Uri或路径

确保PDFModel能存储Uri或路径,方便Adapter加载:

public class PDFModel {
    private String fileUri; // 或private String filePath;
    private String fileName;

    public PDFModel(String fileUri, String fileName) {
        this.fileUri = fileUri;
        this.fileName = fileName;
    }

    // getter方法
    public String getFileUri() { return fileUri; }
    public String getFileName() { return fileName; }
}

3. Adapter中加载PDF(示例)

若需预览PDF,可使用第三方库(如AndroidPdfViewer),通过Uri加载:

public class PdfAdapter extends RecyclerView.Adapter<PdfAdapter.PdfViewHolder> {
    private List<PDFModel> pdfList;
    private Context context;

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

    @NonNull
    @Override
    public PdfViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.item_pdf, parent, false);
        return new PdfViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull PdfViewHolder holder, int position) {
        PDFModel model = pdfList.get(position);
        holder.tvFileName.setText(model.getFileName());
        // 点击跳转PDF预览页面
        holder.itemView.setOnClickListener(v -> {
            Intent intent = new Intent(context, PdfPreviewActivity.class);
            intent.putExtra("PDF_URI", model.getFileUri());
            context.startActivity(intent);
        });
    }

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

    static class PdfViewHolder extends RecyclerView.ViewHolder {
        TextView tvFileName;

        public PdfViewHolder(@NonNull View itemView) {
            super(itemView);
            tvFileName = itemView.findViewById(R.id.tv_file_name);
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 04:35:19