【问题标题】:How to break styled text into pages in Android?如何在Android中将样式文本分成页面?
【发布时间】:2013-12-10 20:16:23
【问题描述】:

我有一个很长的文本,我想分成多页。我还需要一种方法来设置此文本的样式。

【问题讨论】:

    标签: android text page-break spannablestring styledtext


    【解决方案1】:

    更新:我创建了sample application,它展示了如何使用 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;
        }
    }
    

    首先,您必须创建带有pageWidthpageHeight(以像素为单位)的PageSplitter 对象,您可以从View.getWidth()View.getHeight() 获得这些对象:

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

    lineSpacingMultiplierlineSpacingExtra 必须与 TextViews 的 lineSpacingMultiplierlineSpacingExtra 属性具有相同的值,这将保留页面文本。

    使用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();
        }
    }
    

    页面片段:

    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() 方法根据textPaint 设置将text 包装成SpannableString。在当前的方法实现中,我只考虑TextPaint.isFakeBoldText() 属性,但您也可以应用其他属性。例如,您可以将TextPaint.getTextSize() 属性与AbsoluteSizeSpan 一起应用。

    【讨论】:

    • 这会确保页面完全适合任何显示屏吗?我正在尝试使用 textpaint 进行类似的操作,但我的问题在于将文本准确地放入屏幕中,这样就不需要垂直滚动,而且还会发生自动换行,因为页面之间没有单词被分割。您可以在线程stackoverflow.com/questions/20830160/… 中查看我的解决方案
    • 我修复了一些错误并将源代码更新到最新版本。此代码在应用程序play.google.com/store/apps/… 中完美运行
    • 你尝试过大的html文件(1.4mb),需要多长时间??
    • @dreamfighter 不,我认为这需要很长时间。您可以做的是使用 PageSplitter 将大文件拆分为页面,并在您第一次打开应用程序时将它们保存在应用程序文件夹中。下次打开应用程序时,请使用这些页面。
    • @hugocarlmartin 您可以使用 Html.fromHtml() 方法从 html 标记中获取跨区字符串。然后,您可以编写自己的 PageSplitter,它具有 append(Spanned) 方法而不是 append(String, TextPaint)。但这不是一件小事。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-26
    • 1970-01-01
    • 2010-11-22
    • 1970-01-01
    • 2012-11-08
    • 2013-07-15
    • 1970-01-01
    相关资源
    最近更新 更多