【问题标题】:Justifying text inside a TextView in android在 android 的 TextView 中对齐文本
【发布时间】:2017-11-06 04:35:37
【问题描述】:

所以,正如你们大多数人所知,Android 中的 TextView 中没有文本对齐。因此,我构建了一个自定义 TextView 来解决这个问题。但是,由于某种原因,有时标点符号会在某些设备中因某种原因而中断。我在 LG G3 和模拟器(运行最新版本的 Nexus 4)和逗号“,”上进行了测试,例如在 LG G3 上打破了理由,但在模拟器上没有。

如果我添加至少为 2 的 Padding start 和 end(或 left 和 right),问题就解决了。这在我看来很随意。

基本上,我的逻辑是,为了证明文本的合理性,我需要知道 TextView 本身的宽度,将文本构造成最大长度的行。然后,通过查找行中的空格数和剩余的空白空间,根据剩余像素(或视图中的空间)拉伸要缩放的“”(空格)字符。

它工作得几乎完美,而且大多数时候它也支持 RTL 文本。

这里有一些带有和不带有违规标记的文本图片(一个简单的 lorem impsum)(第一个是在运行 7.1.1 的模拟器 nexus 4 上,第二个是在运行 v5.0 的 LG G3 上)

代码如下:

public class DTextView extends AppCompatTextView {

    private boolean justify;

    public DTextView(Context context) {
        super(context);
    }

    public DTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public DTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void setJustify(boolean justify) {
        this.justify = justify;
        if (justify) {
            justify();
        }
    }

    private void init(@Nullable AttributeSet attrs) {
        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.DTextView, 0, 0);
        justify = ta.getBoolean(R.styleable.DTextView_justify, false);

        ta.recycle();
    }

    private SpannableStringBuilder justifyText() {

        String[] words = getText().toString().split(" ");
        setText("");

        int maxLineWidth = getWidth() - getPaddingLeft() - getPaddingRight();

        SpannableStringBuilder justifiedTextSpannable = new SpannableStringBuilder();

        //This will build the new text with the lines rearranged so that they will have a width
        //bigger than the View's own width
        ArrayList<String> lines = new ArrayList<>(0);
        String line = "";
        for (String word : words) {
            if (getWordWidth(line + word) < maxLineWidth) {
                line += word + " ";
            } else {
                line = line.substring(0, line.length() - 1);
                lines.add(line);
                line = word + " ";
            }
        }
        //Add the last line
        lines.add(line);

        for (int i = 0; i < lines.size() - 1; i++) {
            justifiedTextSpannable.append(justifyLine(lines.get(i), maxLineWidth));
            justifiedTextSpannable.append("\n");
        }

        justifiedTextSpannable.append(lines.get(lines.size() - 1));


        return justifiedTextSpannable;
    }

    private SpannableString justifyLine(String line, float maxWidth) {

        SpannableString sLine = new SpannableString(line);
        float spaces = line.split(" ").length - 1;

        float spaceCharSize = getWordWidth(" ");
        float emptySpace = maxWidth - getWordWidth(line);
        float newSpaceSize = (emptySpace / spaces) + spaceCharSize;
        float scaleX = newSpaceSize / spaceCharSize;

        for (int i = 0; i < line.length(); i++) {
            if (line.charAt(i) == ' ') {
                sLine.setSpan(new ScaleXSpan(scaleX), i, i + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }

        return sLine;
    }

    private void justify() {
        justify = false;
        setText(justifyText());
        invalidate();
    }

    private float getWordWidth(String word) {
        return getPaint().measureText(word);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (!justify)
            super.onDraw(canvas);
        else
            justify();
    }
}

我非常感谢任何可以对此有所了解的人。

【问题讨论】:

  • 解决了吗
  • 是的,我自己解决了,并根据您提供的链接之一将其标记为已解决。当然是写在答案里了。

标签: android textview android-custom-view spannablestring


【解决方案1】:

因此,在查看了更多信息后:https://github.com/ufo22940268/android-justifiedtextview 和 TextView 总体而言,我发现我的主要问题是我的方法。

使用缩放“”字符宽度的方法在理论上是合理的,但是这样做之后,线的宽度再次改变,因为线的宽度似乎不是其部分的总和。

我改变了我的方法并从上面的链接中获得灵感,所以在我的新方法中,我单独绘制每个角色,而不是绘制整条线。如果文本需要对齐(基于自定义的“justify”布尔属性),那么它将绘制线条并对齐它,否则它只会绘制线条。

编辑:我现在更改了代码,使其也支持 RTL 文本。我将在接下来的几天内将代码上传到某个地方。

结果如下:

代码如下:

public class DTextView extends AppCompatTextView {


    private boolean justify;
    private float textAreaWidth;
    private float spaceCharSize;
    private float lineY;

    public DTextView(Context context) {
        super(context);
    }

    public DTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public DTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    /**
     * @param attrs the attributes from the xml
     *              This function loads all the parameters from the xml
     */
    private void init(AttributeSet attrs) {

        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.DTextView, 0, 0);

        justify = ta.getBoolean(R.styleable.DTextView_justify, false);

        ta.recycle();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        drawText(canvas);
    }

    private void drawText(Canvas canvas) {
        TextPaint paint = getPaint();
        paint.setColor(getCurrentTextColor());
        paint.drawableState = getDrawableState();
        textAreaWidth = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight());

        spaceCharSize = paint.measureText(" ");

        String text = getText().toString();
        lineY = getTextSize();

        Layout textLayout = getLayout();

        if (textLayout == null)
            return;

        Paint.FontMetrics fm = paint.getFontMetrics();
        int textHeight = (int) Math.ceil(fm.descent - fm.ascent);
        textHeight = (int) (textHeight * getLineSpacingMultiplier() + textLayout.getSpacingAdd());

        for (int i = 0; i < textLayout.getLineCount(); i++) {

            int lineStart = textLayout.getLineStart(i);
            int lineEnd = textLayout.getLineEnd(i);

            float lineWidth = StaticLayout.getDesiredWidth(text, lineStart, lineEnd, paint);
            String line = text.substring(lineStart, lineEnd);

            if (line.charAt(line.length() - 1) == ' ') {
                line = line.substring(0, line.length() - 1);
            }

            if (justify && i < textLayout.getLineCount() - 1) {
                drawLineJustified(canvas, line, lineWidth);
            } else {
                canvas.drawText(line, 0, lineY, paint);
            }

            lineY += textHeight;
        }

    }

    private void drawLineJustified(Canvas canvas, String line, float lineWidth) {
        TextPaint paint = getPaint();

        float emptySpace = textAreaWidth - lineWidth;
        int spaces = line.split(" ").length - 1;
        float newSpaceSize = (emptySpace / spaces) + spaceCharSize;

        float charX = 0;

        for (int i = 0; i < line.length(); i++) {
            String character = String.valueOf(line.charAt(i));
            float charWidth = StaticLayout.getDesiredWidth(character, paint);
            if (!character.equals(" ")) {
                canvas.drawText(character, charX, lineY, paint);
            }

            if (character.equals(" ") && i != line.length() - 1)
                charX += newSpaceSize;
            else
                charX += charWidth;
        }

    }
}

和 XML:

<il.co.drapp.views.text.DTextView
                android:layout_width="match_parent"
                android:inputType="textMultiLine|textNoSuggestions"
                app:justify="true"
                android:id="@+id/justifyText"
                android:text="@string/article_dummy_text"
                android:layout_height="wrap_content" />

感谢 Aditya Vyas-Lakhan 提供链接

【讨论】:

  • 这是你的全部代码吗?您的解决方案是否适用于所有 Android 版本?
  • 这是旧代码。新代码应该支持 RTL(包括括号等)。好久没用的老边项目了,这周的某个时间我会上传到Github。
  • 谢谢。我使用了你的代码,它正确地证明了文本。请分享您的新代码以支持 RTL 上下文。
  • 好的,今天有时间就到这里,你可以在这个 git repo 中找到它:github.com/shayr1/android
【解决方案2】:

像下面这样的用户

<TextView
    android:id="@+id/tvMessageDetails"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:justificationMode="inter_word"/>
                        

【讨论】:

    【解决方案3】:

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

    支持:Android 2.0 到 5.X

    屏幕截图

    【讨论】:

    • 我在网上搜索的时候已经看到了这个。对我不好的两个原因:1.它不支持Android>5和2.我想自己做一个练习
    • 感谢您的热情。每个人来这里只是为了学习新事物。但您也可以将其作为参考来创建自己的自定义视图。
    • 我正在深入研究这个库,虽然它看起来很酷,但它使用 ScrollView 作为其基础。我使用 TextView 作为基础,所以我仍然可以享受 TextView 的所有其他方面。此外,由于 TextView 内置了对滚动的支持,因此不需要 ScrollView
    【解决方案4】:

    尝试这种方式来证明文本的合理性,它对我有用

    public class MainActivity extends Activity {
    
        private JustifiedTextView mJTv;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mJTv=(JustifiedTextView) findViewById(R.id.activity_main_jtv_text);
            mJTv.setText(getResources().getString(R.string.test));
            mJTv.setTextSize(TypedValue.COMPLEX_UNIT_SP,20);
            mJTv.setLineSpacing(15);
            mJTv.setBackgroundColor(Color.RED);
            mJTv.setAlignment(Align.LEFT);
            mJTv.setTypeFace(Typeface.createFromAsset(getAssets(), "fonts/naskh_bold.ttf"));
    
        }
    }
    

    XML

    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main_jsv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <ir.noghteh.JustifiedTextView
            android:id="@+id/activity_main_jtv_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:padding="25dp"
            xmlns:noghteh="http://noghteh.ir"
            noghteh:text="@string/hello_world"
            noghteh:textColor="@color/text"
            noghteh:textSize="18sp"
           >
        </ir.noghteh.JustifiedTextView>
    
    </ScrollView>
    

    https://github.com/navabi/JustifiedTextView

    https://github.com/ufo22940268/android-justifiedtextview

    https://github.com/PareshMayani/Android-JustifyText

    【讨论】:

    • 我看到我的实现有点类似于github.com/navabi/JustifiedTextView 的实现。我实际上以相同的方式获得了 textview 的宽度。我仍然不知道为什么它可以在某些设备上运行而有些则不行。我尝试过使用 6.x、5.x 和 7.x,它似乎可以在需要时使用
    猜你喜欢
    • 1970-01-01
    • 2021-05-01
    • 2012-02-16
    • 2020-09-06
    • 1970-01-01
    • 1970-01-01
    • 2013-08-25
    • 1970-01-01
    相关资源
    最近更新 更多