如何在文本中插入可绘制对象

20

我想在TextView的某些位置插入小图片,比如箭头图标。

下面的图片准确地反映了我的要求:

这就是我想要在文本中嵌入图标的样子

显然,最简单的解决方案是在小的ImageView对象两侧使用多个TextView。但这种方法不够可扩展。

我很想知道是否有人用一些简单而巧妙的技巧克服了这个问题。 (也许借助 HTML 或外部库)

任何高效的解决方案都将不胜感激。


请查看此项目:https://github.com/JoanZapata/android-iconify - Egor
@Egor:这很棒,但是只提供在图像之后放置文本的选项,因此在上面的图片情况下,我需要使用和对齐5个IconTextView对象以及1个TextView。 - Behnam
这个错误是在哪个阶段发生的?我无法相信它会立即出现,为了能够正确地帮助您,您应该同时添加您当前的布局。 - reVerse
我已经包含了布局,但只有当我为我的TextViewWithImage对象设置两行以上的文本时才会出现问题。 - Behnam
3个回答

30

您可以创建SpannableString并向其中添加任何对象到您的字符串中。

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

ImageSpan imageSpan = new ImageSpan(this, R.drawable.ic_launcher);
SpannableString spannableString = new SpannableString(textView.getText());

int start = 3;
int end = 4;
int flag = 0;
spannableString.setSpan(imageSpan, start, end, flag);

textView.setText(spannableString);

请您能否详细说明一下?我有5张图片需要转换成文本,而不仅仅是一张。您的解决方案是否可以扩展以满足我的需求? - Behnam
4
为每张想要插入的图片在 spannableString 中添加一个新的 ImageSpan。 - Kevin Coppock
如何从网络加载ImageSpan?谢谢。 - famfamfam

14

前不久有一个类似的问题,有人想出了一个很棒的解决方案。我只是稍微调整了一下,使图像大小始终与行高相同。因此,基本上你的图标将会随着文本大小进行缩放

步骤1 - 创建新视图

创建一个扩展TextView的新Java类。

public class TextViewWithImages extends TextView {

    public TextViewWithImages(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

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

    @Override
    public void setText(CharSequence text, BufferType type) {
        Spannable s = getTextWithImages(getContext(), text, this.getLineHeight());
        super.setText(s, BufferType.SPANNABLE);
    }

    private static final Spannable.Factory spannableFactory = Spannable.Factory.getInstance();

    private static boolean addImages(Context context, Spannable spannable, float height) {
        Pattern refImg = Pattern.compile("\\Q[img src=\\E([a-zA-Z0-9_]+?)\\Q/]\\E");
        boolean hasChanges = false;

        Matcher matcher = refImg.matcher(spannable);
        while (matcher.find()) {
            boolean set = true;
            for (ImageSpan span : spannable.getSpans(matcher.start(), matcher.end(), ImageSpan.class)) {
                if (spannable.getSpanStart(span) >= matcher.start()
                        && spannable.getSpanEnd(span) <= matcher.end()
                        ) {
                    spannable.removeSpan(span);
                } else {
                    set = false;
                    break;
                }
            }
            String resName = spannable.subSequence(matcher.start(1), matcher.end(1)).toString().trim();
            int id = context.getResources().getIdentifier(resName, "drawable", context.getPackageName());
            Drawable mDrawable = context.getResources().getDrawable(id);
            mDrawable.setBounds(0, 0, (int)height, (int)height);
            if (set) {
                hasChanges = true;
                spannable.setSpan(  new ImageSpan(mDrawable),
                        matcher.start(),
                        matcher.end(),
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                );
            }
        }

        return hasChanges;
    }
    private static Spannable getTextWithImages(Context context, CharSequence text, float height) {
        Spannable spannable = spannableFactory.newSpannable(text);
        addImages(context, spannable, height);
        return spannable;
    }
}

第二步 - 在布局中使用

现在在你的布局XML中,只需使用TextViewWithImages类即可。

<com.stacko.examples.TextViewWithImages
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="14sp"
    android:text="@string/my_string_with_icons" />

第三步 - 使用图标创建字符串

正如您可以在TextViewWithImages类的addImages(...)函数中看到的那样,为了添加图像,字符串中使用了一种特殊的模式([img src=my_icon/])。 这里是一个例子:

<string name="my_string_with_icons">The [img src=ic_action_trash/] is used to delete an item while the [img src=ic_action_edit/] is to edit one.</string>

输出:

enter image description here

正如之前所说,它将会根据您的textSize进行缩放:

enter image description here

作为最初提到的,这篇文章的大部分内容来自18446744073709551615here的回答。我认为这应该被发布为一个库,因为在文本中有图片是一个常见的用例。 :<

1
哇,这太美了,一定要试试!而且我完全理解你在图书馆项目上的想法。 - Behnam
很遗憾,我无法重现这个错误。我刚刚将20行设置到我的TextViewWithImages中,没有发生崩溃。您的设备运行哪个Android版本? - reVerse
4.2.2,我需要插入最多5个可绘制对象。 - Behnam
1
@j2emanue 嗯,drawable的大小设置为线本身的高度(在添加图像之前)。在Drawable#setBounds方法中减小大小可能是一个选项。 - reVerse
不错的解决方案 :) 如果像我一样遇到使用 proguardshrinkResources 时出现问题(其中资源将被丢弃,除非您有其他用途)-请查看 https://developer.android.com/studio/build/shrink-code.html -> 自定义保留哪些资源 - Florian Mötz
显示剩余5条评论

1

这是个好主意,不过我更倾向于使用drawable文件夹而不是assets,因为我需要高效地为不同的语言环境和屏幕使用不同的图片。 无论如何,我很感谢你的解决方案。 - Behnam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接