【问题标题】:android - TextView.setText() with very long string with pagination calculationandroid - TextView.setText() 带有非常长的字符串,带有分页计算
【发布时间】:2014-12-19 09:31:44
【问题描述】:

我添加了一个滚动视图(里面有 textview)来填满整个屏幕。 我在文本视图中添加了很长的文本(书)。 Textview 很好,它知道如何将文本填充到整个屏幕,即处理换行符并知道每行放多少个单词,包括处理非英语单词。

基于总行、滚动视图高度和行高,我可以使用滚动视图的滚动到快速移动到页面。

但是,当文本很长时,它会很慢并且会阻塞 UI 线程。有人建议使用延迟加载方法,即 textview 的 appendto。但是,这样做,我不知道有多少页,也不能长时间使用滚动来“更改”页面。

我想了好几天,但找不到更好的解决方案。谁能给我一些想法和建议?谢谢

【问题讨论】:

  • 也许使用异步任务顺序加载数据? Google android A.sych 任务,它专门用于解锁 UI 线程并在后台抛出任务,因此用户不会注意到 UI 级别的挂断。

标签: android pagination textview


【解决方案1】:

基本上,一个巨大的 ScrollView 会在一些地方挂起,并且在它们之间切换会变得很麻烦。一个更好的想法是将巨大的文本/书籍分成页面,然后显示每一页。换句话说:

  1. 获取一页/TextView 中可以容纳多少文本
  2. 在正确的两点之间拆分文本 [即,第一点是页面的开头,第二点是根据一页中可以容纳多少文本来计算的]
  3. 显示你喜欢的文本,虽然我个人更喜欢动态 ViewPager,我会每次都计算上一页和下一页

这将防止挂起,只要您在正确的时间有策略地计算上一页和下一页 [即延迟加载或异步任务]。

这应该可以帮助您进行分页: Pagination in Android TextView

对于延迟加载的示例[会比这些更简单,因为您只需在另一个线程上工作,将文本拆分为页面,然后返回那些格式化的页面]:

【讨论】:

  • 到目前为止,我找不到一种有效且准确的方法来执行 pt 1 到 pt 3,甚至比将所有文本添加到 textview 还要慢..
【解决方案2】:

一年多以前,我一直在寻找同样该死的东西。并最终由我自己实施。

线的宽度是符号间空间(字距调整)、符号之间的空白宽度、视图边界宽度的总和。

符号间空间(紧缩)对于您要使用的每对符号都不同。如果你的字体不是 MONOSPACE,WA 和 DA 有不同的空间。

whitespaces 相同,每对符号不同。 左右边界相同,每个符号不同。

所以我做了什么 - 为您的应用程序准备一个包含每个可能符号的数组(不是全部,而是那些可能出现在您提供给视图的文本中的符号)然后计算每个符号的宽度。

HashMap<String, Integer[]> charMap = new HashMap<>();

        String defaultSet = "ҢңӨөөҮүёЁQWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm\"'[]{}\\./`~!@#$%^&**()_+`12\uFEFF3456«;789»0—-=:|<>?йцуке…нгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,,–";
        String[] charArray = defaultSet.split("");
        Rect bounds = new Rect();
        for (String mChar : charArray) {
            if (mChar.length() == 0) {
                continue;
            }
            //textView.setText(mChar);
            textPaint.getTextBounds(String.valueOf(mChar), 0, 1, bounds);
            int height = bounds.height();
            int width = bounds.width();
            Integer[] size = {width, height};
            charMap.put(mChar, size);
        }

接下来是获取每对符号的字距调整、空格和边界值。每个方法中的最后一个参数是您在第一个代码框中计算的 charMap

  /**
 * Returns Bounds map for each symbol
 * @param charArray
 * @param textPaint
 * @param mCharMap
 * @return
 */

private HashMap<String, Integer> generateBoundsLeftMap(String[] charArray, TextPaint textPaint, HashMap<String, Integer[]> mCharMap){
    //individual letterspacing, and whitespace width.
    HashMap<String, Integer>  boundsMap= new HashMap<>();
    Rect bounds = new Rect();

    for(int i=1;i<charArray.length;i++){
        String mKeyChar=charArray[i];

        String testLine="skajd fnфыв";
        textPaint.getTextBounds(testLine,0,11,bounds);
        int testwidth=bounds.width();
        String checkLine= mKeyChar.concat(testLine).concat(mKeyChar);
        textPaint.getTextBounds(checkLine, 0, 13, bounds);
        boundsMap.put(charArray[i], bounds.left);
        MLog.sout("boundsleft", "" + bounds.left, true);
        MLog.sout("measuretext",""+textPaint.measureText(checkLine),true);
        MLog.sout("boundsright",""+(textPaint.measureText(checkLine)-testwidth-(mCharMap.get(mKeyChar)[0]*2)-bounds.left),true);

    }
    return boundsMap;
}

/**
 * Returns Bounds map for each symbol
 * @param charArray
 * @param textPaint
 * @param mCharMap
 * @return
 */

private HashMap<String, Integer> generateBoundsRightMap(String[] charArray, TextPaint textPaint,HashMap<String, Integer[]> mCharMap){
    //individual letterspacing, and whitespace width.
    HashMap<String, Integer>  boundsMap= new HashMap<>();
    Rect bounds=new Rect();

    for(int i=1;i<charArray.length;i++){
        String mKeyChar=charArray[i];

        String testLine="aba";
        String checkLine= mKeyChar.concat(testLine).concat(mKeyChar);
        textPaint.getTextBounds(checkLine, 0, 5, bounds);
        int testwidth=bounds.width();
        int boundsright =(int) textPaint.measureText(checkLine)-testwidth-bounds.left;
        boundsMap.put(charArray[i], boundsright);
    }
    return boundsMap;
}

/**
 * returns Pairs map. HashMap<String, HashMap<String,   Integer > >
 *                            char1           char2   letterspacing
 * @param charArray
 * @param textPaint
 * @return
 */
private HashMap<String, HashMap<String, Integer>> generateKerningPairs(String[] charArray, TextPaint textPaint, HashMap<String,Integer[]> mCharMap){
    //individual letterspacing, and whitespace width.
    HashMap<String, HashMap<String, Integer> > pairMap= new HashMap<>();
    Rect bounds=new Rect();
    int lineWidth;
    int interspaceWidth;
    for(int i=1;i<charArray.length;i++){
        String mKeyChar=charArray[i];
        HashMap<String , Integer> spaceMap=new HashMap<>();
        for(int z=1; z<charArray.length;z++){
            //getletterspacing
            String checkLine=mKeyChar.concat(charArray[z]);
            textPaint.getTextBounds(checkLine,0,2,bounds);
            lineWidth = bounds.width();
            interspaceWidth=lineWidth-(mCharMap.get(mKeyChar))[0] - (mCharMap.get(charArray[z]))[0];
            spaceMap.put(charArray[z], interspaceWidth);
        }
        pairMap.put(mKeyChar,spaceMap);
    }
    return pairMap;
}

/**
 * returns Pairs map. HashMap<String, HashMap<String,   Integer > >
 *                            char1           char2   letterspacing
 * @param charArray
 * @param textPaint
 * @return
 */
private HashMap<String, HashMap<String, Integer>> generateSpacePairs(String[] charArray, TextPaint textPaint, HashMap<String,Integer[]> mCharMap){
    //individual letterspacing, and whitespace width.
    HashMap<String, HashMap<String, Integer> > pairMap= new HashMap<>();
    Rect bounds=new Rect();
    int lineWidth;
    int spaceWidth;
    for(int i=1;i<charArray.length;i++){
        String mKeyChar=charArray[i];
        HashMap<String , Integer> spaceMap=new HashMap<>();
        for(int z=1; z<charArray.length;z++){
            //getletterspacing
            String checkLine=mKeyChar.concat(" ").concat(charArray[z]);
            textPaint.getTextBounds(checkLine,0,3,bounds);
            lineWidth = bounds.width();
            spaceWidth=lineWidth-(mCharMap.get(mKeyChar))[0] - (mCharMap.get(charArray[z]))[0];
            spaceMap.put(charArray[z], spaceWidth);
        }
        pairMap.put(mKeyChar,spaceMap);
    }
    return pairMap;
}

计算这些值需要很长时间,所以我为每个 DPI 做了一次(使用 genymotion 和更改屏幕密度)并保存到 txt 文件中的 JSON 中。 内核映射.txt 空间地图.txt ... 等等。

你把这些文件放到你的assets文件夹里,结构是这样的

assets/dpi/160/kernmap.txt ... 资产/dpi/240/kernmap.txt ... 等等。

从这一点开始 - 你不能使用你在开始时没有放在默认设置字符串中的符号

String defaultSet = "ҢңӨөөҮүёЁQWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm\"'[]{}\\./`~!@#$%^&**()_+`12\uFEFF3456«;789»0—-=:|<>?йцуке…нгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,,–";

即使准备好所有地图,您也会遇到性能问题。我使用 jni 来计算页面长度。

接下来呢?

你得到你的字符串,让我们说它

"This is a test line provided by blabla"

取“T”和“h”(前 2 个字符)

linewidth = boundsLeftmap.get("T") (你有它在地图中)+ widthofchars.get("T") + kerningbetween.get("T").get("h") + 等等.

使用 JNI 需要 3-4 秒来处理一个 80000 个字符的字符串并将其分解为完全适合您视图的页面。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多