【问题标题】:Justifying text in Android在 Android 中对齐文本
【发布时间】:2013-11-09 07:56:14
【问题描述】:

我需要证明一些文本 (RTL),它是来自服务器的字符串 (S1)。但是TextView 不能证明文本的合理性,所以我必须使用WebView,现在我必须创建一个 HTML 文件,其中 将显示S1。然后我将该 html 文件的地址存储在数据库中,然后显示该 html 文件。我之前在 SO 上看到过这个问题,许多人建议使用 3rd 方库,我尝试了所有这些方法都无济于事(它们在 90% 的情况下都有效,但并不完全可靠)。

我觉得这个方法看起来很复杂,不知道有没有更好的方法?

【问题讨论】:

  • 你能提供一些样品吗?你想如何证明?...检查这个answer
  • 我想证明波斯文本,如果我不能用某些类或库证明我应该获取字符串并创建 HTML 文件并加载到 WebView。
  • 这正是您要找的。 stackoverflow.com/a/4314724/2219600
  • @amalBit 您的第一个链接所做的是将文本左对齐或右对齐。这不能证明文本和第二个链接是我必须做的,但这是静态的,我必须在数据库中创建许多 html 文件
  • 好吧,这就是android可以做的最好的证明。

标签: java android textview text-justify


【解决方案1】:

我使用下面的代码来回答非常需要这个主题的人,并且我创建了在每个显示中都支持的公式。

    public class TextJustify {

final static String SYSTEM_NEWLINE = "\n";
final static float COMPLEXITY = 5.12f; // Reducing this will increase
                                        // efficiency but will decrease
                                        // effectiveness
final static Paint p = new Paint();

/* @author Mathew Kurian */

public static void run(final TextView tv, float origWidth, int paddingLeft, int paddingRight, int marginLeft, int marginRight) {


    origWidth-= paddingRight+marginRight+paddingLeft+marginLeft;
    String s = tv.getText().toString();
    p.setTypeface(tv.getTypeface());
    String[] splits = s.split(SYSTEM_NEWLINE);
    float width = origWidth - 5;
    for (int x = 0; x < splits.length; x++)
        if (p.measureText(splits[x]) > width) {
            splits[x] = wrap(splits[x], width, p);
            String[] microSplits = splits[x].split(SYSTEM_NEWLINE);
            for (int y = 0; y < microSplits.length - 1; y++)
                microSplits[y] = justify(removeLast(microSplits[y], " "),
                        width, p);
            StringBuilder smb_internal = new StringBuilder();
            for (int z = 0; z < microSplits.length; z++)
                smb_internal.append(microSplits[z]
                        + ((z + 1 < microSplits.length) ? SYSTEM_NEWLINE
                                : ""));
            splits[x] = smb_internal.toString();
        }
    final StringBuilder smb = new StringBuilder();
    for (String cleaned : splits)
        smb.append(cleaned + SYSTEM_NEWLINE);
    tv.setGravity(Gravity.RIGHT);
    tv.setText(smb);
}

private static String wrap(String s, float width, Paint p) {
    String[] str = s.split("\\s"); // regex
    StringBuilder smb = new StringBuilder(); // save memory
    smb.append(SYSTEM_NEWLINE);
    for (int x = 0; x < str.length; x++) {
        float length = p.measureText(str[x]);
        String[] pieces = smb.toString().split(SYSTEM_NEWLINE);
        try {
            if (p.measureText(pieces[pieces.length - 1]) + length > width)
                smb.append(SYSTEM_NEWLINE);
        } catch (Exception e) {
        }
        smb.append(str[x] + " ");
    }
    return smb.toString().replaceFirst(SYSTEM_NEWLINE, "");
}

private static String removeLast(String s, String g) {
    if (s.contains(g)) {
        int index = s.lastIndexOf(g);
        int indexEnd = index + g.length();
        if (index == 0)
            return s.substring(1);
        else if (index == s.length() - 1)
            return s.substring(0, index);
        else
            return s.substring(0, index) + s.substring(indexEnd);
    }
    return s;
}

private static String justifyOperation(String s, float width, Paint p) {
    float holder = (float) (COMPLEXITY * Math.random());
    while (s.contains(Float.toString(holder)))
        holder = (float) (COMPLEXITY * Math.random());
    String holder_string = Float.toString(holder);
    float lessThan = width;
    int timeOut = 100;
    int current = 0;
    while (p.measureText(s) < lessThan && current < timeOut) {
        s = s.replaceFirst(" ([^" + holder_string + "])", " "
                + holder_string + "$1");
        lessThan = p.measureText(holder_string) + lessThan
                - p.measureText(" ");
        current++;
    }
    String cleaned = s.replaceAll(holder_string, " ");
    return cleaned;
}

private static String justify(String s, float width, Paint p) {
    while (p.measureText(s) < width) {
        s = justifyOperation(s, width, p);
    }
    return s;
}
  }

要调用它,您必须使用以下代码,我测试了波斯语,并且在每个显示器和设备上都运行良好。

     public static final int FinallwidthDp  = 320 ;
     public static final int widthJustify  = 223 ;

     DisplayMetrics metrics = new DisplayMetrics();
     getWindowManager().getDefaultDisplay().getMetrics(metrics);
     int widthPixels = metrics.widthPixels;

     float scaleFactor = metrics.density;
     float widthDp = widthPixels / scaleFactor;

     TextView tv = (TextView) findViewById(R.id.textView1);
     ViewGroup.MarginLayoutParams lp1 = (ViewGroup.MarginLayoutParams) tv.getLayoutParams();

     tv.setText(text);
     TextJustify.run(tv,widthDp / FinallwidthDp * widthJustify , tv.getPaddingLeft(),tv.getPaddingRight() , lp1.leftMargin, lp1.rightMargin);

此算法在各种设备上进行了测试,在正常活动(不是对话框)和TextView 的宽度和 TextView 中运行良好,并且适用于每个填充和边距。如果对您不利,您可以更改 widthJustify 直到看起来不错,希望对你有用。 最新更新见This

【讨论】:

  • 您好,我是编写这个库的人。如果您可以为所有设备添加对所有计算 widthDp 的支持,我将不胜感激。我没有安卓设备,所以我很难编写代码和测试。要添加此支持,请分叉此github.com/bluejamesbond/TextJustify-Android,进行更改,然后执行拉取请求,我将合并代码。我也会将您添加为贡献者。非常感谢。
  • @mk1 我编辑了你的图书馆,但我不知道我是否确认编辑,因为我从不这样做,请告诉我我编辑的结果,我编辑了两个文件,读我和你的类,在读我时,我添加 10 - 15 行,在您的类中添加 2 行,
  • 我看到你的编辑我已经合并了。我在那里重新打开了一个问题,希望你能调查一下。
  • @mk1 在某些行中,单词之间的间距太大,看起来不太好。如何解决这个问题?
  • @Shayanpourvatan 如果你知道同样的问题,请帮助我。
【解决方案2】:

图书馆https://github.com/bluejamesbond/TextJustify-Android

支持:Android 2.0 到 5.X;字符串/可扩展; RTL 语言支持! 没有 WEBVIEW :)

屏幕截图

【讨论】:

    【解决方案3】:

    试试这个:

    在 src 文件夹中添加一个 TextViewJustify.java 文件。

    TextViewJustify.java会是这样的

    import android.graphics.Paint;
    import android.view.Gravity;
    import android.widget.TextView;
    
    public class TextViewJustify {
    
        /*
         * PLEASE DO NOT REMOVE Coded by Mathew Kurian I wrote this code for a
         * Google Interview for Internship. Unfortunately, I got too nervous during
         * the interview that I messed, but anyhow that doesn't matter. I have
         * resent my work in hopes that I might still get a position there. Thank
         * you :DD
         */
    
        final static String SYSTEM_NEWLINE = "\n";
        final static float COMPLEXITY = 5.12f; // Reducing this will increase
                                                // efficiency but will decrease
                                                // effectiveness
        final static Paint p = new Paint();
    
        public static void justifyText(final TextView tv, final float origWidth) {
            String s = tv.getText().toString();
            p.setTypeface(tv.getTypeface());
            String[] splits = s.split(SYSTEM_NEWLINE);
            float width = origWidth - 5;
            for (int x = 0; x < splits.length; x++)
                if (p.measureText(splits[x]) > width) {
                    splits[x] = wrap(splits[x], width, p);
                    String[] microSplits = splits[x].split(SYSTEM_NEWLINE);
                    for (int y = 0; y < microSplits.length - 1; y++)
                        microSplits[y] = justify(removeLast(microSplits[y], " "),
                                width, p);
                    StringBuilder smb_internal = new StringBuilder();
                    for (int z = 0; z < microSplits.length; z++)
                        smb_internal.append(microSplits[z]
                                + ((z + 1 < microSplits.length) ? SYSTEM_NEWLINE
                                        : ""));
                    splits[x] = smb_internal.toString();
                }
            final StringBuilder smb = new StringBuilder();
            for (String cleaned : splits)
                smb.append(cleaned + SYSTEM_NEWLINE);
            tv.setGravity(Gravity.LEFT);
            tv.setText(smb);
        }
    
        private static String wrap(String s, float width, Paint p) {
            String[] str = s.split("\\s"); // regex
            StringBuilder smb = new StringBuilder(); // save memory
            smb.append(SYSTEM_NEWLINE);
            for (int x = 0; x < str.length; x++) {
                float length = p.measureText(str[x]);
                String[] pieces = smb.toString().split(SYSTEM_NEWLINE);
                try {
                    if (p.measureText(pieces[pieces.length - 1]) + length > width)
                        smb.append(SYSTEM_NEWLINE);
                } catch (Exception e) {
                }
                smb.append(str[x] + " ");
            }
            return smb.toString().replaceFirst(SYSTEM_NEWLINE, "");
        }
    
        private static String removeLast(String s, String g) {
            if (s.contains(g)) {
                int index = s.lastIndexOf(g);
                int indexEnd = index + g.length();
                if (index == 0)
                    return s.substring(1);
                else if (index == s.length() - 1)
                    return s.substring(0, index);
                else
                    return s.substring(0, index) + s.substring(indexEnd);
            }
            return s;
        }
    
        private static String justifyOperation(String s, float width, Paint p) {
            float holder = (float) (COMPLEXITY * Math.random());
            while (s.contains(Float.toString(holder)))
                holder = (float) (COMPLEXITY * Math.random());
            String holder_string = Float.toString(holder);
            float lessThan = width;
            int timeOut = 100;
            int current = 0;
            while (p.measureText(s) < lessThan && current < timeOut) {
                s = s.replaceFirst(" ([^" + holder_string + "])", " "
                        + holder_string + "$1");
                lessThan = p.measureText(holder_string) + lessThan
                        - p.measureText(" ");
                current++;
            }
            String cleaned = s.replaceAll(holder_string, " ");
            return cleaned;
        }
    
        private static String justify(String s, float width, Paint p) {
            while (p.measureText(s) < width) {
                s = justifyOperation(s, width, p);
            }
            return s;
        }
    }
    

    并像这样使用这个类:

    TextViewJustify.justifyText(your_text_view, 225f);
    

    就我而言,它是 225f。根据你的需要改变它。

    【讨论】:

    • 我使用这个类,但它在 90% 的场景中都有效,但并不完全可靠
    • 这是因为您无法证明来自 XML 的文本。
    • 对于您的号码 (225f),您必须设置屏幕宽度,使其在每个屏幕上都可以正常工作,如果您在其他 dp 屏幕上设置最终数字效果不佳
    • 不,我没有设置任何宽度。我在对话屏幕中使用了它。它在每个设备中都像魅力一样工作。 @Shayan
    • @Andru 我不认为人们会真正使用它。所以我会优化它并增加它的可靠性。如果您是此版本的用户,请关注其在 github 上的存储库,以便您了解最新版本。
    【解决方案4】:

    您可以简单地使用 WebView 来证明文本的合理性

    LinearLayout lv=(LinearLayout)dialog.findViewById(R.id.**yourId**);
    
    
                         String text1 = "<html><body>"
                                    + "<p align=\"justify\">"                
                                       +**your text**
                                    + "</p> "
                                    + "</body></html>";
    
    
    
                         WebView wv=new WebView(getApplicationContext());
                         wv.loadData(text1,"text/html","utf-8");
    
    
    
                        lv.removeAllViews();
    
                        lv.addView(wv);
    

    【讨论】:

    • 是的,你说得对,但在那个项目中我不想使用 html 文件,因为我在数据库中有很多文本,但感谢你的解决方案。
    • 如果你可以分割你的文本并在不同的文本视图中打印,你可以使用 html 标签来设置文本颜色、文本大小、文本样式等
    • 我需要 100 页多行的法律书籍,此解决方案不适用于这种情况。无论如何,我在这篇文章中的回答已经解决了问题
    【解决方案5】:

    我做了简单的课。
    这可以像 TextView 一样使用

    import android.content.Context;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.widget.TextView;
    
    /**
     * text justifying
     * you can just use like TextView
     * @author hyunsikkim
     *
     */
    public class JustifiedTextView extends TextView {
    
    public JustifiedTextView(Context context) {
        super(context);
    }
    
    public JustifiedTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    
    private void setBreakText(String text) {
        if(text == null) return;
    
        String breakText = breakText(getPaint(), text, 
                getWidth()-this.getPaddingLeft()-this.getPaddingRight());
    
        if(breakText.equals(getText()) == false) {
            setText(breakText);
        }
    }
    
    public String breakText(Paint textPaint, String strText, int breakWidth) { 
        StringBuilder sb = new StringBuilder(); 
        int endValue = 0; 
        final String NEW_LINE = "\n";
        do{ 
            endValue = textPaint.breakText(strText, true, breakWidth, null); 
            if(endValue > 0) {
                /**
                 * handle if text contains NEW_LINE
                 */
                final int index = strText.indexOf(NEW_LINE);
                if(0<=index && index <= endValue) {
                    endValue = index + NEW_LINE.length();
                }
                final String sub = strText.substring(0, endValue);
                sb.append(sub);
                /**
                 * handle breaked text endWidth NEW_LINE
                 */
                if(sub.endsWith(NEW_LINE) == false) {
                    if(strText.length() != endValue) {
                        sb.append(NEW_LINE);
                    }
                }
    
                strText = strText.substring(endValue); 
            } 
        } while(endValue > 0);
        return sb.toString();
    } 
    
    public String breakText(Paint textPaint, int id, int breakWidth) {
        String strText = getResources().getString(id);
        return breakText(textPaint, strText, breakWidth);
    }
    
    @Override
    protected void onTextChanged(CharSequence text, int start,
            int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
        /**
         * this control changes from setText(Charsequence text)
         */
        if(getWidth() != 0) {
            setBreakText(text.toString());
        }
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        /**
         * this help to break initial text.
         */
        if(w != oldw) {
            setBreakText(getText().toString());
        }
    }
    
    
    }
    

    【讨论】:

      【解决方案6】:

      使用网页视图

        WebView tv = (WebView) findViewById(R.id.aboutme); 
        String youtContentStr = String.valueOf(Html
                              .fromHtml("<![CDATA[<body style=\"text-align:justify;background-color:#00222222;\">"
                                          + text
                                          + "</body>]]>"));
      
        tv.setBackgroundColor(Color.TRANSPARENT);
        tv.loadData(youtContentStr, "text/html", "utf-8");`
      

      【讨论】:

        猜你喜欢
        • 2011-12-26
        • 1970-01-01
        • 2015-08-23
        • 2017-06-02
        • 2017-11-06
        • 2012-02-16
        • 2014-12-12
        • 1970-01-01
        相关资源
        最近更新 更多