¿Cómo dividir el texto con estilo en páginas de Android?

Tengo un texto largo que quiero dividir en varias páginas. También necesito una forma de estilo este texto.

ACTUALIZACIÓN: Creé la aplicación de muestra , que muestra cómo usar PageSplitter.

¿Cómo funciona? Aplicación de ejemplo (ruso) – Cleverum . Solo necesitas la clase PageSplitter . Otro código muestra cómo usar esta clase.

 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 pages = new ArrayList(); 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 getPages() { List copyPages = new ArrayList(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; } } 

En primer lugar, debe crear el objeto PageSplitter con pageWidth y pageHeight (en píxeles), que puede obtener de View.getWidth() y View.getHeight() :

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

lineSpacingMultiplier y lineSpacingExtra deben tener los mismos valores que los lineSpacingMultiplier y lineSpacingExtra de TextView s, que mantendrán los textos de las páginas.

Usando el método PageSplitter.append() puede agregar text que se medirá con 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); } } 

Luego, al usar el método PageSplitter.getPages() , puede obtener el texto original dividido en páginas y poner cada una de ellas en TextView :

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

TextPagerAdapter:

 public class TextPagerAdapter extends FragmentPagerAdapter { private final List pageTexts; public TextPagerAdapter(FragmentManager fm, List 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; } } 

donde R.layout.page es

   

PageSplitter.renderToSpannable() método PageSplitter.renderToSpannable() ajusta el text a SpannableString según la configuración de textPaint . En la implementación del método actual, considero solo la propiedad TextPaint.isFakeBoldText() , pero también puede aplicar otras propiedades. Por ejemplo, puede aplicar la propiedad TextPaint.getTextSize() con AbsoluteSizeSpan .