I am writing a simple text/eBook viewer for Android, so I have used a TextView
to show the HTML formatted text to the users, so they can browse the text in pages by going back and forth. But my problem is that I can not paginate the text in Android.
I can not (or I don't know how to) get appropriate feedback from the line-breaking and page-breaking algorithms in which TextView
uses to break text into lines and pages. Thus, I can not understand where the content ends in the actual display, so that I continue from the remaining in the next page. I want to find way to overcome this problem.
If I know what is the last character painted on the screen, I can easily put enough characters to fill a screen, and knowing where tha actual painting was finished, I can continue at the next page. Is this possible? How?
Similar questions have been asked several times on StackOverflow, but no satisfactory answer was provided. These are just a few of them:
- How to paginate long text into pages in Android?
- Ebook reader pagination issue in android
- Paginate text based on rendered text size
There was a single answer, which seems to work, but it is slow. It adds characters and lines until the page is filled. I don't think this is a good way to do page breaking:
Rather than this question, it happens that PageTurner eBook reader does it mostly right, although it is somehow slow.
PS: I am not confined to TextView, and I know line breaking and page breaking algorithms can be quite complex (as in TeX), so I am not looking for an optimal answer, but rather a reasonably fast solution that can be usable by the users.
Update: This seems to be a good start for getting the right answer:
Is there a way of retrieving a TextView's visible line count or range?
Answer: After completing text layout, it is possible to find out the visible text:
ViewTreeObserver vto = txtViewEx.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ViewTreeObserver obs = txtViewEx.getViewTreeObserver();
obs.removeOnGlobalLayoutListener(this);
height = txtViewEx.getHeight();
scrollY = txtViewEx.getScrollY();
Layout layout = txtViewEx.getLayout();
firstVisibleLineNumber = layout.getLineForVertical(scrollY);
lastVisibleLineNumber = layout.getLineForVertical(height+scrollY);
}
});
Surprisingly finding libraries for Pagination is difficult. I think it's better to use another Android UI element besides TextView. How about WebView? An example @ android-webview-example. Code snippet:
Note: This simply loads data onto a WebView, similar to a web browser. But let's not stop with just this idea. Add this UI to using pagination by WebViewClient onPageFinished . Please read on SO link @ html-book-like-pagination. Code snippet from one of the best answer by Dan:
Notes:
I have not implemented pagination but I think this is a good start and shows promise, should be fast. As you can see, there are developers that have implemented this feature.
Take a look at my demo project.
The "magic" is in this code:
Background
What we know about text processing within
TextView
is that it properly breaks a text by lines according to the width of a view. Looking at theTextView's
sources we can see that the text processing is done by theLayout
class. So we can make use of the work theLayout
class does for us and utilizing its methods do pagination.Problem
The problem with
TextView
is that the visible part of text might be cut vertically somewhere at the middle of the last visible line. Regarding said, we should break a new page when the last line that fully fits into a view's height is met.Algorithm
bottom
exceeds the view's height;bottom
with (see the implementation). The new value is defined astop
value (red line in the picture below) of the line that hasn't fit into the previous page +TextView's
height.Implementation
Note 1
The algorithm works not just for
TextView
(Pagination
class usesTextView's
parameters in the implementation above). You may pass any set of parametersStaticLayout
accepts and later use the paginated layouts to draw text onCanvas
/Bitmap
/PdfDocument
.You can also use
Spannable
asyourText
parameter for different fonts as well asHtml
-formatted strings (like in the sample below).Note 2
When all text has the same font size, all lines have equal height. In that case you might want to consider further optimization of the algorithm by calculating an amount of lines that fits into a single page and jumping to the proper line at each loop iteration.
Sample
The sample below paginates a string containing both
html
andSpanned
text.Activity
's layout:Screenshot: