如何在TextView中围绕部分文本设置矩形虚线/虚线轮廓? [英] How to set a rectangular-dashed/dotted-line-outline around partial text in TextView?

查看:13
本文介绍了如何在TextView中围绕部分文本设置矩形虚线/虚线轮廓?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

有很多方法可以设置 TextView 中显示的部分文本的样式,例如设置其前景色 (

我尝试过的

我试图寻找这样的解决方案,也试图阅读

这是当前代码:

要使用的字符串

<string name="text_to_format">test &#160;&#160;%1$s test</string>

使用代码

 final String textToDash="DASHED";String formattedStr = getString(R.string.text_to_format, textToDash+ "<bc/>");跨越 textToShow = Html.fromHtml(formattedStr, null, new TagHandler() {诠释开始;@覆盖public void handleTag(final boolean opening, final String tag, Editable output, final XMLReader xmlReader) {开关(标签){案例bc":如果(!打开)开始 = output.length() - textToDash.length();休息;案例html":如果(!打开)输出.setSpan(新的 DrawableSpan(ResourcesCompat.getDrawable(getResources(), R.drawable.dashed_border_shape, null)),开始,开始 + textToDash.length(), 0);}}});textView.setText(textToShow);

DrawableSpan

公共类 DrawableSpan 扩展 ReplacementSpan {私有可绘制 mDrawable;私有最终矩形 mPadding;公共DrawableSpan(可绘制可绘制){极好的();mDrawable = 可绘制;mPadding = 新矩形();mDrawable.getPadding(mPadding);}@覆盖public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {RectF rect = new RectF(x, top, x + measureText(paint, text, start, end), bottom);mDrawable.setBounds((int) rect.left - mPadding.left, (int) rect.top - mPadding.top, (int) rect.right + mPadding.right, (int) rect.bottom + mPadding.bottom);canvas.drawText(文本,开始,结束,x,y,绘画);mDrawable.draw(画布);}@覆盖public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {返回 Math.round(paint.measureText(text, start, end));}私人浮动 measureText(油漆油漆,CharSequence 文本,int start,int end){返回paint.measureText(文本,开始,结束);}}

res/drawable/dashed_border_shape.xml

textView 没有什么特别的:

 <TextViewandroid:id="@+id/..."android:layout_width="match_parent"android:layout_height="wrap_content"安卓:layout_gravity="center"android:layout_marginBottom="20dp"android:layout_marginLeft="8dp"android:layout_marginRight="8dp"android:layout_marginTop="30dp"android:gravity="center_horizo​​ntal"android:textSize="20sp"/>

我什至为这个视图的多个父级设置了 android:clipChildren="false", android:clipToPadding="false"(认为它不会像它尝试那样绘制).没有任何帮助.

这是怎么回事,我应该怎么做才能解决它?

解决方案

解决方案一

1 - 为破折号创建一个可绘制对象.像这样:

2 - 将其设置为文本视图的背景,可以只是一个单词.

结果:

重要提示:此解决方案仅适用于小文本,例如在游戏中显示分数或小消息.它不会适应大文本.

解决方案 2

如果您需要适用于大文本的更复杂的解决方案,您可以使用 Spannable.

1 -> 创建自定义 ReplacementSpan

 公共类 DashedBorderSpan 扩展 ReplacementSpan {私有可绘制 mDrawable;私人int mPadding;public DashedBorderSpan(Drawable drawable,int padding){极好的();mDrawable = 可绘制;mPadding = 填充;}@覆盖public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {RectF rect = new RectF(x - mPadding, top - mPadding, x + measureText(paint, text, start, end) + mPadding, bottom + mPadding);mDrawable.setBounds((int) rect.left, (int)rect.top, (int)rect.right, (int)rect.bottom);canvas.drawText(文本,开始,结束,x,y,绘画);mDrawable.draw(画布);}@覆盖public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {返回 Math.round(paint.measureText(text, start, end));}私人浮动 measureText(油漆油漆,CharSequence 文本,int start,int end){返回paint.measureText(文本,开始,结束);}}

2 -> 应用 Spannable

TextView textView = (TextView) findViewById(R.id.textasd);字符串 hello = "虚线!";SpannableStringBuilder stringBuilder = new SpannableStringBuilder();stringBuilder.append(你好);stringBuilder.setSpan(new DrawableSpan(getDrawable(R.drawable.dashed_border_shape)),0,stringBuilder.length(),跨度.SPAN_EXCLUSIVE_EXCLUSIVE);stringBuilder.append("不是虚线...无聊");textView.setText(stringBuilder);

此解决方案适用于所有情况.这是一个更好的解决方案,虽然它更复杂.

示例如果您想将它与占位符一起使用,请像这样使用它:

 String someText = "一些文本!";//R.string.placeholder = 你好:%sString formatedText = String.format(getString(R.string.placeholder), someText);SpannableStringBuilder stringBuilderPlaceHolder = new SpannableStringBuilder();stringBuilderPlaceHolder.append(formatedText);stringBuilderPlaceHolder.setSpan(new DashedBorderSpan(getDrawable(R.drawable.dashed_border_shape), 10),formatedText.length() - someText.length(),格式化文本长度(),跨度.SPAN_EXCLUSIVE_EXCLUSIVE);textViewPlaceHolder.setText(stringBuilderPlaceHolder);

结果:

这样,跨度将仅在您的占位符上设置.如果您有更复杂的持有人,请使用相同的逻辑来实现您的需求.

编辑

解决方案2有一个小问题,但有解决方案.

您必须注意可绘制虚线边框的填充.如果在虚线边框中使用填充,则需要在使用 Span 的 TextView 中设置填充.在问题的作者提供的图像中,您可以看到上面和底线被剪掉了(如果您增加填充,线条将完全消失),以避免在您的文本视图中使用这种填充.像这样:

这将解决问题 =]编码愉快!

Background

There are plenty of ways to style a part of the text that's shown in a TextView, such as setting its foreground color (here), and others (here).

The problem

I can't find out if there is a way to set a rectangular-dashed/dotted-line-outline on a partial text of a TextView. Something like this:

What I've tried

I tried to look for such a solution, and also I tried to read the documentation of CharacterStyle . Still, I don't see any of the available spans as good candidates for this style.

The question

Is there a built in solution for this, or do I need to use a customized implementation?


I've used a modified version of what was suggested below , and it worked fine on POC, but for some reason, on the real project, the vertical dashed lines on the sides of the text are bold:

Here's the current code:

string to use

<string name="text_to_format">test &#160;&#160;%1$s test</string>

usage code

        final String textToDash="DASHED";
        String formattedStr = getString(R.string.text_to_format, textToDash+ "<bc/>");
        Spanned textToShow = Html.fromHtml(formattedStr, null, new TagHandler() {
            int start;

            @Override
            public void handleTag(final boolean opening, final String tag, Editable output, final XMLReader xmlReader) {
                switch (tag) {
                    case "bc":
                        if (!opening)
                            start = output.length() - textToDash.length();
                        break;
                    case "html":
                        if (!opening)
                            output.setSpan(
                                    new DrawableSpan(ResourcesCompat.getDrawable(getResources(), R.drawable.dashed_border_shape, null)),
                                    start, start + textToDash.length(), 0);
                }
            }
        });
        textView.setText(textToShow);

DrawableSpan

public class DrawableSpan extends ReplacementSpan {
    private Drawable mDrawable;
    private final Rect mPadding;

    public DrawableSpan(Drawable drawable) {
        super();
        mDrawable = drawable;
        mPadding = new Rect();
        mDrawable.getPadding(mPadding);
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        RectF rect = new RectF(x, top, x + measureText(paint, text, start, end), bottom);
        mDrawable.setBounds((int) rect.left - mPadding.left, (int) rect.top - mPadding.top, (int) rect.right + mPadding.right, (int) rect.bottom + mPadding.bottom);
        canvas.drawText(text, start, end, x, y, paint);
        mDrawable.draw(canvas);
    }

    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
        return Math.round(paint.measureText(text, start, end));
    }

    private float measureText(Paint paint, CharSequence text, int start, int end) {
        return paint.measureText(text, start, end);
    }
}

res/drawable/dashed_border_shape.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
    <padding
        android:bottom="1dp"
        android:left="4dp"
        android:right="4dp"
        android:top="1dp"/>
    <solid android:color="@android:color/transparent"/>
    <stroke
        android:width="2dp"
        android:color="#ff474747"
        android:dashGap="10px"
        android:dashWidth="10px"/>
</shape>

The textView doesn't have anything special:

            <TextView
                android:id="@+id/..."
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginBottom="20dp"
                android:layout_marginLeft="8dp"
                android:layout_marginRight="8dp"
                android:layout_marginTop="30dp"
                android:gravity="center_horizontal"
                android:textSize="20sp"/>

I've even set android:clipChildren="false", android:clipToPadding="false" for multiple parents of this view (thinking that it doesn't draw as it tries to). Nothing helped.

How come, and what should I do to fix it?

解决方案

Solution 1

1 - Create a drawable for the dashes. Like this:

<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="5dp" />
    <solid android:color="@android:color/transparent" />
    <stroke
        android:color="@android:color/black"
        android:dashWidth="20px"
        android:dashGap="10px"
        android:width="3dp"/>
</shape>

2 - Set it as the background of your text view, it can be just a word.

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="8dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello!"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/button_shape"
        android:padding="4dp"
        android:text="world!"/>

</LinearLayout>

The result:

Important note: This solution is going to work only for small text, like show a score in a game or small messagens. It won't adapt to big texts.

Solution 2

If you need a more complex solution that works for big texts, you can use a Spannable.

1 -> Create a custom ReplacementSpan

  public class DashedBorderSpan extends ReplacementSpan {

    private Drawable mDrawable;
    private int mPadding;


    public DashedBorderSpan(Drawable drawable, int padding) {
        super();

        mDrawable = drawable;
        mPadding = padding;
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        RectF rect = new RectF(x - mPadding, top - mPadding, x + measureText(paint, text, start, end) + mPadding, bottom + mPadding);

        mDrawable.setBounds((int) rect.left, (int)rect.top, (int)rect.right, (int)rect.bottom);

        canvas.drawText(text, start, end, x, y, paint);
        mDrawable.draw(canvas);
    }

    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
        return Math.round(paint.measureText(text, start, end));
    }

    private float measureText(Paint paint, CharSequence text, int start, int end) {
        return paint.measureText(text, start, end);
    }
}

2 -> Apply the Spannable

TextView textView = (TextView) findViewById(R.id.textasd);

        String hello = "Dashed!";

        SpannableStringBuilder stringBuilder = new SpannableStringBuilder();

        stringBuilder.append(hello);
        stringBuilder.setSpan(new DrawableSpan(getDrawable(R.drawable.dashed_border_shape)),
                0,
                stringBuilder.length(),
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        stringBuilder.append("not dashed... boring");

        textView.setText(stringBuilder);

This solution will work for all the cases. It's a better solution, although it is more complicated.

Example If you would like to use it with a place holder use it like this:

    String someText = "Some Text!";

//R.string.placeholder = Hello: %s
            String formatedText = String.format(getString(R.string.placeholder), someText);

            SpannableStringBuilder stringBuilderPlaceHolder = new SpannableStringBuilder();
            stringBuilderPlaceHolder.append(formatedText);

            stringBuilderPlaceHolder.setSpan(new DashedBorderSpan(getDrawable(R.drawable.dashed_border_shape), 10),
                    formatedText.length() - someText.length(),
                    formatedText.length(),
                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

            textViewPlaceHolder.setText(stringBuilderPlaceHolder);

Result:

This way the span will be set only on your place holder. If you have a more complex holder use the same logic to achieve what you need.

Edit

There's a small problem with the solution 2, but there is a solution.

You must take care with padding of the dashed border drawable. If you use padding in the dashed border, you will need to set padding in the TextView that uses the Span. In the image that the author of the question provided, you can see that the upper and bottom lines got cut (if you increase the padding, the lines will be completly gone), in order to avoid this use padding in your textview. Like this:

<TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginBottom="20dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="30dp"
        android:paddingTop="3dp" <!-- This will fix the problem!  -->
        android:paddingBottom="3dp" <!-- This will fix the problem!  -->
        android:gravity="center_horizontal"
        android:text="blabla"
        android:textSize="20sp"/>

This will fix the problem =] Happy coding!

这篇关于如何在TextView中围绕部分文本设置矩形虚线/虚线轮廓?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆