Skip to content
This repository was archived by the owner on Nov 10, 2025. It is now read-only.

Commit 389c1e4

Browse files
Flamedekbarteksc
authored andcommitted
Add page snapping and single page fling (DImuthuUpe#557)
* Add auto spacing feature * Add page snapping * Add single page flinging * Disable snap and page fling when zoomed * Update README.md
1 parent af4d4b2 commit 389c1e4

File tree

8 files changed

+329
-36
lines changed

8 files changed

+329
-36
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,11 @@ pdfView.fromAsset(String)
100100
.enableAntialiasing(true) // improve rendering a little bit on low-res screens
101101
// spacing between pages in dp. To define spacing color, set view background
102102
.spacing(0)
103+
.autoSpacing(false) // add dynamic spacing to fit each page on its own on the screen
103104
.linkHandler(DefaultLinkHandler)
104105
.pageFitPolicy(FitPolicy.WIDTH)
106+
.pageSnap(true) // snap pages to screen boundaries
107+
.pageFling(false) // make a fling change only a single page like ViewPager
105108
.load();
106109
```
107110

@@ -206,6 +209,15 @@ Configurator.onRender(new OnRenderListener() {
206209
});
207210
```
208211

212+
### How can I scroll through single pages like a ViewPager?
213+
You can use a combination of the following settings to get scroll and fling behaviour similar to a ViewPager:
214+
``` java
215+
.swipeHorizontal(true)
216+
.pageSnap(true)
217+
.autoSpacing(true)
218+
.pageFling(true)
219+
```
220+
209221
## One more thing
210222
If you have any suggestions on making this lib better, write me, create issue or write some code and send pull request.
211223

android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/AnimationManager.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class AnimationManager {
4141

4242
private boolean flinging = false;
4343

44+
private boolean pageFlinging = false;
45+
4446
public AnimationManager(PDFView pdfView) {
4547
this.pdfView = pdfView;
4648
scroller = new OverScroller(pdfView.getContext());
@@ -85,6 +87,15 @@ public void startFlingAnimation(int startX, int startY, int velocityX, int veloc
8587
scroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
8688
}
8789

90+
public void startPageFlingAnimation(float targetOffset) {
91+
if (pdfView.isSwipeVertical()) {
92+
startYAnimation(pdfView.getCurrentYOffset(), targetOffset);
93+
} else {
94+
startXAnimation(pdfView.getCurrentXOffset(), targetOffset);
95+
}
96+
pageFlinging = true;
97+
}
98+
8899
void computeFling() {
89100
if (scroller.computeScrollOffset()) {
90101
pdfView.moveTo(scroller.getCurrX(), scroller.getCurrY());
@@ -93,6 +104,7 @@ void computeFling() {
93104
flinging = false;
94105
pdfView.loadPages();
95106
hideHandle();
107+
pdfView.performPageSnap();
96108
}
97109
}
98110

@@ -109,6 +121,10 @@ public void stopFling() {
109121
scroller.forceFinished(true);
110122
}
111123

124+
public boolean isFlinging() {
125+
return flinging || pageFlinging;
126+
}
127+
112128
class XAnimation extends AnimatorListenerAdapter implements AnimatorUpdateListener {
113129

114130
@Override
@@ -121,11 +137,13 @@ public void onAnimationUpdate(ValueAnimator animation) {
121137
@Override
122138
public void onAnimationCancel(Animator animation) {
123139
pdfView.loadPages();
140+
pageFlinging = false;
124141
}
125142

126143
@Override
127144
public void onAnimationEnd(Animator animation) {
128145
pdfView.loadPages();
146+
pageFlinging = false;
129147
}
130148
}
131149

@@ -141,11 +159,13 @@ public void onAnimationUpdate(ValueAnimator animation) {
141159
@Override
142160
public void onAnimationCancel(Animator animation) {
143161
pdfView.loadPages();
162+
pageFlinging = false;
144163
}
145164

146165
@Override
147166
public void onAnimationEnd(Animator animation) {
148167
pdfView.loadPages();
168+
pageFlinging = false;
149169
}
150170
}
151171

@@ -173,6 +193,7 @@ public void onAnimationCancel(Animator animation) {
173193
public void onAnimationEnd(Animator animation) {
174194
pdfView.loadPages();
175195
hideHandle();
196+
pdfView.performPageSnap();
176197
}
177198

178199
@Override

android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/DecodingAsyncTask.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ protected Throwable doInBackground(Void... params) {
4848
try {
4949
PdfDocument pdfDocument = docSource.createDocument(pdfView.getContext(), pdfiumCore, password);
5050
pdfFile = new PdfFile(pdfiumCore, pdfDocument, pdfView.getPageFitPolicy(), getViewSize(),
51-
userPages, pdfView.isSwipeVertical(), pdfView.getSpacingPx());
51+
userPages, pdfView.isSwipeVertical(), pdfView.getSpacingPx(), pdfView.doAutoSpacing());
5252
return null;
5353
} catch (Throwable t) {
5454
return t;

android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/DragPinchManager.java

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import com.github.barteksc.pdfviewer.model.LinkTapEvent;
2626
import com.github.barteksc.pdfviewer.scroll.ScrollHandle;
27+
import com.github.barteksc.pdfviewer.util.SnapEdge;
2728
import com.shockwave.pdfium.PdfDocument;
2829
import com.shockwave.pdfium.util.SizeF;
2930

@@ -97,7 +98,7 @@ private boolean checkLinkTapped(float x, float y) {
9798
for (PdfDocument.Link link : pdfFile.getPageLinks(page)) {
9899
RectF mapped = pdfFile.mapRectToDevice(page, pageX, pageY, (int) pageSize.getWidth(),
99100
(int) pageSize.getHeight(), link.getBounds());
100-
fixCoords(mapped);
101+
mapped.sort();
101102
if (mapped.contains(mappedX, mappedY)) {
102103
pdfView.callbacks.callLinkHandler(new LinkTapEvent(x, y, mappedX, mappedY, mapped, link));
103104
return true;
@@ -106,26 +107,27 @@ private boolean checkLinkTapped(float x, float y) {
106107
return false;
107108
}
108109

109-
/** Fix different coordinate axis */
110-
private void fixCoords(RectF rect) {
111-
if (rect.top > rect.bottom) {
112-
swapTopBottom(rect);
110+
private void startPageFling(MotionEvent downEvent, MotionEvent ev, float velocityX, float velocityY) {
111+
if (!checkDoPageFling(velocityX, velocityY)) {
112+
return;
113113
}
114-
if (rect.left > rect.right) {
115-
swapLeftRight(rect);
116-
}
117-
}
118-
119-
private void swapTopBottom(RectF rect) {
120-
float tmp = rect.top;
121-
rect.top = rect.bottom;
122-
rect.bottom = tmp;
123-
}
124114

125-
private void swapLeftRight(RectF rect) {
126-
float tmp = rect.left;
127-
rect.left = rect.right;
128-
rect.right = tmp;
115+
int direction;
116+
if (pdfView.isSwipeVertical()) {
117+
direction = velocityY > 0 ? -1 : 1;
118+
} else {
119+
direction = velocityX > 0 ? -1 : 1;
120+
}
121+
// get the focused page during the down event to ensure only a single page is changed
122+
float delta = pdfView.isSwipeVertical() ? ev.getY() - downEvent.getY() : ev.getX() - downEvent.getX();
123+
float offsetX = pdfView.getCurrentXOffset() - delta * pdfView.getZoom();
124+
float offsetY = pdfView.getCurrentYOffset() - delta * pdfView.getZoom();
125+
int startingPage = pdfView.findFocusPage(offsetX, offsetY);
126+
int targetPage = Math.max(0, Math.min(pdfView.getPageCount() - 1, startingPage + direction));
127+
128+
SnapEdge edge = pdfView.findSnapEdge(targetPage);
129+
float offset = pdfView.snapOffsetForPage(targetPage, edge);
130+
animationManager.startPageFlingAnimation(-offset);
129131
}
130132

131133
@Override
@@ -180,6 +182,9 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d
180182
private void onScrollEnd(MotionEvent event) {
181183
pdfView.loadPages();
182184
hideHandle();
185+
if (!animationManager.isFlinging()) {
186+
pdfView.performPageSnap();
187+
}
183188
}
184189

185190
@Override
@@ -192,6 +197,15 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve
192197
if (!pdfView.isSwipeEnabled()) {
193198
return false;
194199
}
200+
if (pdfView.doPageFling()) {
201+
if (pdfView.pageFillsScreen()) {
202+
onBoundedFling(velocityX, velocityY);
203+
} else {
204+
startPageFling(e1, e2, velocityX, velocityY);
205+
}
206+
return true;
207+
}
208+
195209
int xOffset = (int) pdfView.getCurrentXOffset();
196210
int yOffset = (int) pdfView.getCurrentYOffset();
197211

@@ -207,10 +221,34 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve
207221

208222
animationManager.startFlingAnimation(xOffset, yOffset, (int) (velocityX), (int) (velocityY),
209223
(int) minX, 0, (int) minY, 0);
210-
211224
return true;
212225
}
213226

227+
private void onBoundedFling(float velocityX, float velocityY) {
228+
int xOffset = (int) pdfView.getCurrentXOffset();
229+
int yOffset = (int) pdfView.getCurrentYOffset();
230+
231+
PdfFile pdfFile = pdfView.pdfFile;
232+
233+
float pageStart = -pdfFile.getPageOffset(pdfView.getCurrentPage(), pdfView.getZoom());
234+
float pageEnd = pageStart - pdfFile.getPageLength(pdfView.getCurrentPage(), pdfView.getZoom());
235+
float minX, minY, maxX, maxY;
236+
if (pdfView.isSwipeVertical()) {
237+
minX = -(pdfView.toCurrentScale(pdfFile.getMaxPageWidth()) - pdfView.getWidth());
238+
minY = pageEnd + pdfView.getHeight();
239+
maxX = 0;
240+
maxY = pageStart;
241+
} else {
242+
minX = pageEnd + pdfView.getWidth();
243+
minY = -(pdfView.toCurrentScale(pdfFile.getMaxPageHeight()) - pdfView.getHeight());
244+
maxX = pageStart;
245+
maxY = 0;
246+
}
247+
248+
animationManager.startFlingAnimation(xOffset, yOffset, (int) (velocityX), (int) (velocityY),
249+
(int) minX, (int) maxX, (int) minY, (int) maxY);
250+
}
251+
214252
@Override
215253
public boolean onScale(ScaleGestureDetector detector) {
216254
float dr = detector.getScaleFactor();
@@ -261,4 +299,10 @@ private void hideHandle() {
261299
scrollHandle.hideDelayed();
262300
}
263301
}
302+
303+
private boolean checkDoPageFling(float velocityX, float velocityY) {
304+
float absX = Math.abs(velocityX);
305+
float absY = Math.abs(velocityY);
306+
return pdfView.isSwipeVertical() ? absY > absX : absX > absY;
307+
}
264308
}

0 commit comments

Comments
 (0)