From c7f180f67bfc636a52b7d39442451de0475d0c1c Mon Sep 17 00:00:00 2001 From: Jakob Felix Julius Sudau Date: Thu, 23 Jul 2026 21:01:04 +0200 Subject: [PATCH] fix(android): restore zoom and scroll offset when the view is re-attached to the window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The base PDFView calls recycle() in onDetachedFromWindow, resetting zoom, currentXOffset and currentYOffset. PdfView.onAttachedToWindow then reloads via drawPdf(), landing at the top of defaultPage — so any detach without unmount (a react-navigation screen stacked on top, a tab switch, a device rotation) loses the user's reading position and zoom. Snapshot the viewport before the base class recycles and restore it in loadComplete after the re-attach reload. The restore is post()-ed because the internal loadComplete fires the onLoad callback before jumpTo(defaultPage); a synchronous restore would be overwritten. The saved path guards against the source changing between detach and re-attach. Co-Authored-By: Claude Fable 5 --- .../src/main/java/org/wonday/pdf/PdfView.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/android/src/main/java/org/wonday/pdf/PdfView.java b/android/src/main/java/org/wonday/pdf/PdfView.java index e9a1d16c..c3d32451 100644 --- a/android/src/main/java/org/wonday/pdf/PdfView.java +++ b/android/src/main/java/org/wonday/pdf/PdfView.java @@ -90,6 +90,15 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl private float lastPageWidth = 0; private float lastPageHeight = 0; + // Viewport snapshot taken in onDetachedFromWindow so the reload triggered by + // onAttachedToWindow can restore zoom and scroll offset (restorePath doubles + // as the "snapshot valid" flag and guards against the source changing between + // detach and re-attach). + private String restorePath = null; + private float restoreZoom = 1; + private float restoreXOffset = 0; + private float restoreYOffset = 0; + // used to store the parameters for `super.onSizeChanged` private int oldW = 0; private int oldH = 0; @@ -155,6 +164,22 @@ public void loadComplete(int numberOfPages) { float height = pageSize.getHeight(); this.zoomTo(this.scale); + + // Restore the viewport saved in onDetachedFromWindow. The base class jumps + // to defaultPage right after this callback returns, so the restore must be + // posted to run after that jump or it would be overwritten. + if (this.restorePath != null && this.restorePath.equals(this.path)) { + final float zoom = this.restoreZoom; + final float xOffset = this.restoreXOffset; + final float yOffset = this.restoreYOffset; + this.restorePath = null; + this.post(() -> { + this.zoomTo(zoom); + this.moveTo(xOffset, yOffset); + this.loadPages(); + }); + } + WritableMap event = Arguments.createMap(); //create a new json Object for the TableOfContents @@ -289,6 +314,21 @@ protected void onAttachedToWindow() { this.drawPdf(); } + // The base PDFView recycles the document (zoom, offset, render state) whenever + // the view leaves the window — e.g. while another screen is stacked over this + // one on Android. Snapshot the viewport before that happens; loadComplete + // restores it after the re-attach reload. + @Override + protected void onDetachedFromWindow() { + if (!this.isRecycled() && this.path != null) { + this.restorePath = this.path; + this.restoreZoom = this.getZoom(); + this.restoreXOffset = this.getCurrentXOffset(); + this.restoreYOffset = this.getCurrentYOffset(); + } + super.onDetachedFromWindow(); + } + private int getPdfPageCount(File pdfFile) throws IOException { ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY);