Как разбить стилизованный текст на страницы в Android?

У меня длинный текст, который я хочу разбить на несколько страниц. Также мне нужен способ стилизации этого текста.

UPDATE: я создал пример приложения , в котором показано, как использовать PageSplitter.

Как это работает? Пример приложения (русский) – Cleverum . Вам нужен только класс PageSplitter . В другом коде показано, как использовать этот класс.

 import android.graphics.Typeface; import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.TextPaint; import android.text.style.StyleSpan; import java.util.ArrayList; import java.util.List; public class PageSplitter { private final int pageWidth; private final int pageHeight; private final float lineSpacingMultiplier; private final int lineSpacingExtra; private final List<CharSequence> pages = new ArrayList<CharSequence>(); private SpannableStringBuilder currentLine = new SpannableStringBuilder(); private SpannableStringBuilder currentPage = new SpannableStringBuilder(); private int currentLineHeight; private int pageContentHeight; private int currentLineWidth; private int textLineHeight; public PageSplitter(int pageWidth, int pageHeight, float lineSpacingMultiplier, int lineSpacingExtra) { this.pageWidth = pageWidth; this.pageHeight = pageHeight; this.lineSpacingMultiplier = lineSpacingMultiplier; this.lineSpacingExtra = lineSpacingExtra; } public void append(String text, TextPaint textPaint) { textLineHeight = (int) Math.ceil(textPaint.getFontMetrics(null) * lineSpacingMultiplier + lineSpacingExtra); String[] paragraphs = text.split("\n", -1); int i; for (i = 0; i < paragraphs.length - 1; i++) { appendText(paragraphs[i], textPaint); appendNewLine(); } appendText(paragraphs[i], textPaint); } private void appendText(String text, TextPaint textPaint) { String[] words = text.split(" ", -1); int i; for (i = 0; i < words.length - 1; i++) { appendWord(words[i] + " ", textPaint); } appendWord(words[i], textPaint); } private void appendNewLine() { currentLine.append("\n"); checkForPageEnd(); appendLineToPage(textLineHeight); } private void checkForPageEnd() { if (pageContentHeight + currentLineHeight > pageHeight) { pages.add(currentPage); currentPage = new SpannableStringBuilder(); pageContentHeight = 0; } } private void appendWord(String appendedText, TextPaint textPaint) { int textWidth = (int) Math.ceil(textPaint.measureText(appendedText)); if (currentLineWidth + textWidth >= pageWidth) { checkForPageEnd(); appendLineToPage(textLineHeight); } appendTextToLine(appendedText, textPaint, textWidth); } private void appendLineToPage(int textLineHeight) { currentPage.append(currentLine); pageContentHeight += currentLineHeight; currentLine = new SpannableStringBuilder(); currentLineHeight = textLineHeight; currentLineWidth = 0; } private void appendTextToLine(String appendedText, TextPaint textPaint, int textWidth) { currentLineHeight = Math.max(currentLineHeight, textLineHeight); currentLine.append(renderToSpannable(appendedText, textPaint)); currentLineWidth += textWidth; } public List<CharSequence> getPages() { List<CharSequence> copyPages = new ArrayList<CharSequence>(pages); SpannableStringBuilder lastPage = new SpannableStringBuilder(currentPage); if (pageContentHeight + currentLineHeight > pageHeight) { copyPages.add(lastPage); lastPage = new SpannableStringBuilder(); } lastPage.append(currentLine); copyPages.add(lastPage); return copyPages; } private SpannableString renderToSpannable(String text, TextPaint textPaint) { SpannableString spannable = new SpannableString(text); if (textPaint.isFakeBoldText()) { spannable.setSpan(new StyleSpan(Typeface.BOLD), 0, spannable.length(), 0); } return spannable; } } 

Во-первых, вам нужно создать объект PageSplitter с pageWidth и pageHeight (в пикселях), который вы можете получить из View.getWidth() и View.getHeight() :

 ViewPager pagesView = (ViewPager) findViewById(R.id.pages); PageSplitter pageSplitter = new PageSplitter(pagesView.getWidth(), pagesView.getHeight(), 1, 0); 

lineSpacingMultiplier и lineSpacingExtra должны иметь те же значения, что и атрибуты lineSpacingMultiplier и lineSpacingExtra TextView которые будут содержать тексты страниц.

Используя PageSplitter.append() вы можете добавить text который будет измеряться с помощью textPaint :

 TextPaint textPaint = new TextPaint(); textPaint.setTextSize(getResources().getDimension(R.dimen.text_size)); for (int i = 0; i < 1000; i++) { pageSplitter.append("Hello, ", textPaint); textPaint.setFakeBoldText(true); pageSplitter.append("world", textPaint); textPaint.setFakeBoldText(false); pageSplitter.append("! ", textPaint); if ((i + 1) % 200 == 0) { pageSplitter.append("\n", textPaint); } } 

Затем, используя PageSplitter.getPages() вы можете получить исходный текст, разделенный на страницы, и поместить каждый из них в TextView :

 pagesView.setAdapter(new TextPagerAdapter(getSupportFragmentManager(), pageSplitter.getPages())); 

TextPagerAdapter:

 public class TextPagerAdapter extends FragmentPagerAdapter { private final List<CharSequence> pageTexts; public TextPagerAdapter(FragmentManager fm, List<CharSequence> pageTexts) { super(fm); this.pageTexts = pageTexts; } @Override public Fragment getItem(int i) { return PageFragment.newInstance(pageTexts.get(i)); } @Override public int getCount() { return pageTexts.size(); } } 

PageFragment:

 public class PageFragment extends Fragment { private final static String PAGE_TEXT = "PAGE_TEXT"; public static PageFragment newInstance(CharSequence pageText) { PageFragment frag = new PageFragment(); Bundle args = new Bundle(); args.putCharSequence(PAGE_TEXT, pageText); frag.setArguments(args); return frag; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { CharSequence text = getArguments().getCharSequence(PAGE_TEXT); TextView pageView = (TextView) inflater.inflate(R.layout.page, container, false); pageView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen.text_size)); pageView.setText(text); return pageView; } } 

Где R.layout.page

 <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:textSize="@dimen/text_size" android:lineSpacingMultiplier="1" android:lineSpacingExtra="0sp"> </TextView> 

PageSplitter.renderToSpannable() переносит text в SpannableString соответствии с настройками textPaint . В реализации текущего метода я рассматриваю только TextPaint.isFakeBoldText() , но вы также можете применить другие свойства. Например, вы можете применить TextPaint.getTextSize() с AbsoluteSizeSpan .