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

Android Studio开发PDF阅读器如何实现退出后记忆上次阅读页码,集成barteksc/AndroidPdfViewer

实现PDF阅读进度持久化方案

你当前使用的onSaveInstanceState仅能处理页面临时销毁(如屏幕旋转、系统后台回收)的场景,退出Activity后存储的页码会被清空。要实现退出阅读页后重新进入仍能定位到上次阅读位置,需要使用SharedPreferences做持久化存储,且以书籍ID为存储key区分不同书籍的阅读进度,具体修改如下:

修改步骤

  1. 加载PDF前读取对应书籍的已存储进度
    在loadBookFromUrl方法中,调用fromBytes加载PDF之前,先从SharedPreferences读取该bookId对应的阅读进度,默认值为0(即第一页)
  2. 页码变化时实时存储进度
    在OnPageChangeListener的onPageChanged回调中,每次页码切换后将最新页码写入SharedPreferences

完整修改后代码

public class PdfViewActivity extends AppCompatActivity {
    private ActivityPdfViewBinding binding;
    private String bookId;
    private static final String TAG = "PDF_VIEW_TAG";
    private int mCurrentPage = 0;
    // 定义SharedPreferences相关常量
    private static final String SP_NAME = "PdfReadProgress";
    private static final String SP_KEY_PREFIX = "progress_";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityPdfViewBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        Intent intent = getIntent();
        bookId = intent.getStringExtra("bookId");
        Log.d(TAG, "onCreate: BookId :"+bookId);

        loadBookDetails();

        binding.backBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                onBackPressed();
            }
        });
    }

    private void loadBookDetails() {
        Log.d(TAG, "loadBookDetails: Get PDF Url from database");
        //Step 1 : Get bookUrl using bookId
        DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Books");
        ref.child(bookId)
                .addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(@NonNull DataSnapshot snapshot) {
                        //get bookUrl
                        String pdfUrl = ""+snapshot.child("url").getValue();
                        Log.d(TAG, "onDataChange: PDF Url: "+pdfUrl);

                        //Step 2 : Load pdf using that url from Firebase storage
                        loadBookFromUrl(pdfUrl);
                    }

                    @Override
                    public void onCancelled(@NonNull DatabaseError error) {

                    }
                });
    }

    private void loadBookFromUrl(String pdfUrl) {
        Log.d(TAG, "loadBookFromUrl: Get PDF from storage");
        
        StorageReference reference = FirebaseStorage.getInstance().getReferenceFromUrl(pdfUrl);
        reference.getBytes(Constants.MAX_BYTES_PDF)
                .addOnSuccessListener(new OnSuccessListener<byte[]>() {
                    @Override
                    public void onSuccess(byte[] bytes) {
                        // 读取已保存的阅读进度
                        SharedPreferences sp = getSharedPreferences(SP_NAME, MODE_PRIVATE);
                        mCurrentPage = sp.getInt(SP_KEY_PREFIX + bookId, 0);
                        
                        //load pdf book using bytes
                        binding.pdfView.fromBytes(bytes)
                                .swipeHorizontal(false) // 设置false为竖直滚动
                                .defaultPage(mCurrentPage)
                                .onPageChange(new OnPageChangeListener() {
                                    @Override
                                    public void onPageChanged(int page, int pageCount) {
                                        // 工具栏显示当前页码和总页码
                                        int currentpage = page + 1;
                                        binding.toolbarSubtitleTv.setText(currentpage+"/"+pageCount);
                                        Log.d(TAG, "onPageChanged: "+currentpage+"/"+pageCount);
                                        // 保存最新阅读进度
                                        sp.edit().putInt(SP_KEY_PREFIX + bookId, page).apply();
                                    }
                                })
                                .onError(new OnErrorListener() {
                                    @Override
                                    public void onError(Throwable t) {
                                        Log.d(TAG, "onError: "+t.getMessage());
                                        Toast.makeText(PdfViewActivity.this, ""+t.getMessage(), Toast.LENGTH_SHORT).show();
                                    }
                                })
                                .onPageError(new OnPageErrorListener() {
                                    @Override
                                    public void onPageError(int page, Throwable t) {
                                        Log.d(TAG, "onPageError: "+t.getMessage());
                                        Toast.makeText(PdfViewActivity.this, ""+t.getMessage(), Toast.LENGTH_SHORT).show();
                                    }
                                })
                                .load();
                        binding.progressBar.setVisibility(View.GONE);

                    }
                })
                .addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        Log.d(TAG, "onFailure: "+e.getMessage());
                        binding.progressBar.setVisibility(View.GONE);
                    }
                });
    }
}

补充说明

如果你的应用支持多账号登录,可在存储进度的key中拼接用户ID,避免不同账号的同本书籍进度冲突。原有用于临时状态保存的onSaveInstanceState和onRestoreInstanceState方法可直接删除,也可保留用于处理屏幕旋转等临时销毁场景,逻辑不冲突。

内容的提问来源于stack exchange,提问作者Phạm Hải Đăng - 18A02

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 18:45:03