是否可以在 Android TextView 中显示来自 html 的内嵌图像?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2865452/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 07:38:59  来源:igfitidea点击:

Is it possible to display inline images from html in an Android TextView?

androidimagetextview

提问by Gunnar Lium

Given the following HTML:

鉴于以下 HTML:

<p>This is text and this is an image <img src="http://www.example.com/image.jpg" />.</p>

<p>This is text and this is an image <img src="http://www.example.com/image.jpg" />.</p>

Is it possible to make the image render? When using this snippet: mContentText.setText(Html.fromHtml(text));, I get a cyan box with black borders, leading me to believe that a TextView has some idea of what an img tag is.

是否可以使图像渲染?使用此代码段时:mContentText.setText(Html.fromHtml(text));,我得到一个带有黑色边框的青色框,这让我相信 TextView 对 img 标签有一定的了解。

回答by Dave Webb

If you have a look at the documentation for Html.fromHtml(text)you'll see it says:

如果您查看文档,Html.fromHtml(text)您会看到它说:

Any <img>tags in the HTML will display as a generic replacement image which your program can then go through and replace with real images.

<img>HTML 中的任何标签都将显示为通用替换图像,然后您的程序可以通过并替换为真实图像。

If you don't want to do this replacement yourself you can use the other Html.fromHtml()methodwhich takes an Html.TagHandlerand an Html.ImageGetteras arguments as well as the text to parse.

如果您不想自己进行此替换,则可以使用其他Html.fromHtml()方法方法将 anHtml.TagHandler和 anHtml.ImageGetter作为参数以及要解析的文本。

In your case you could parse nullas for the Html.TagHandlerbut you'd need to implement your own Html.ImageGetteras there isn't a default implementation.

在您的情况下,您可以解析null为,Html.TagHandler但您需要实现自己的,Html.ImageGetter因为没有默认实现。

However, the problem you're going to have is that the Html.ImageGetterneeds to run synchronously and if you're downloading images from the web you'll probably want to do that asynchronously. If you can add any images you want to display as resources in your application the your ImageGetterimplementation becomes a lot simpler. You could get away with something like:

但是,您将遇到的问题是Html.ImageGetter需要同步运行,如果您从 Web 下载图像,您可能希望异步执行该操作。如果您可以在应用程序中添加要显示为资源的任何图像,那么您的ImageGetter实现就会变得简单得多。你可以逃脱类似的事情:

private class ImageGetter implements Html.ImageGetter {

    public Drawable getDrawable(String source) {
        int id;

        if (source.equals("stack.jpg")) {
            id = R.drawable.stack;
        }
        else if (source.equals("overflow.jpg")) {
            id = R.drawable.overflow;
        }
        else {
            return null;
        }

        Drawable d = getResources().getDrawable(id);
        d.setBounds(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight());
        return d;
    }
};

You'd probably want to figure out something smarter for mapping source strings to resource IDs though.

不过,您可能想找出更智能的方法将源字符串映射到资源 ID。

回答by madhu527

I have implemented in my app ,taken referece from the pskink.thanx a lot

我已经在我的应用程序中实现了,从pskink.thanx 中获取了很多参考

package com.example.htmltagimg;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LevelListDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Html;
import android.text.Html.ImageGetter;
import android.text.Spanned;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends Activity implements ImageGetter {
private final static String TAG = "TestImageGetter";
private TextView mTv;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    String source = "this is a test of <b>ImageGetter</b> it contains " +
            "two images: <br/>" +
            "<img src=\"http://developer.android.com/assets/images/dac_logo.png\"><br/>and<br/>" +
            "<img src=\"http://www.hdwallpapersimages.com/wp-content/uploads/2014/01/Winter-Tiger-Wild-Cat-Images.jpg\">";
    String imgs="<p><img alt=\"\" src=\"http://images.visitcanberra.com.au/images/canberra_hero_image.jpg\" style=\"height:50px; width:100px\" />Test Article, Test Article, Test Article, Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,v</p>";
    String src="<p><img alt=\"\" src=\"http://stylonica.com/wp-content/uploads/2014/02/Beauty-of-nature-random-4884759-1280-800.jpg\" />Test Attractions Test Attractions Test Attractions Test Attractions</p>";
    String img="<p><img alt=\"\" src=\"/site_media/photos/gallery/75b3fb14-3be6-4d14-88fd-1b9d979e716f.jpg\" style=\"height:508px; width:640px\" />Test Article, Test Article, Test Article, Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,Test Article,v</p>";
    Spanned spanned = Html.fromHtml(imgs, this, null);
    mTv = (TextView) findViewById(R.id.text);
    mTv.setText(spanned);
}

@Override
public Drawable getDrawable(String source) {
    LevelListDrawable d = new LevelListDrawable();
    Drawable empty = getResources().getDrawable(R.drawable.ic_launcher);
    d.addLevel(0, 0, empty);
    d.setBounds(0, 0, empty.getIntrinsicWidth(), empty.getIntrinsicHeight());

    new LoadImage().execute(source, d);

    return d;
}

class LoadImage extends AsyncTask<Object, Void, Bitmap> {

    private LevelListDrawable mDrawable;

    @Override
    protected Bitmap doInBackground(Object... params) {
        String source = (String) params[0];
        mDrawable = (LevelListDrawable) params[1];
        Log.d(TAG, "doInBackground " + source);
        try {
            InputStream is = new URL(source).openStream();
            return BitmapFactory.decodeStream(is);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        Log.d(TAG, "onPostExecute drawable " + mDrawable);
        Log.d(TAG, "onPostExecute bitmap " + bitmap);
        if (bitmap != null) {
            BitmapDrawable d = new BitmapDrawable(bitmap);
            mDrawable.addLevel(1, 1, d);
            mDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
            mDrawable.setLevel(1);
            // i don't know yet a better way to refresh TextView
            // mTv.invalidate() doesn't work as expected
            CharSequence t = mTv.getText();
            mTv.setText(t);
        }
    }
}
}

As per below @rpgmaker comment i added this answer

根据下面@rpgmaker评论,我添加了这个答案

yes you can do using ResolveInfoclass

是的,您可以使用ResolveInfo

check your file is supported with already installed apps or not

检查已安装的应用程序是否支持您的文件

using below code:

使用以下代码:

private boolean isSupportedFile(File file) throws PackageManager.NameNotFoundException {
    PackageManager pm = mContext.getPackageManager();
    java.io.File mFile = new java.io.File(file.getFileName());
    Uri data = Uri.fromFile(mFile);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(data, file.getMimeType());
    List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);

    if (resolveInfos != null && resolveInfos.size() > 0) {
        Drawable icon = mContext.getPackageManager().getApplicationIcon(resolveInfos.get(0).activityInfo.packageName);
        Glide.with(mContext).load("").placeholder(icon).into(binding.fileAvatar);
        return true;
    } else {
        Glide.with(mContext).load("").placeholder(R.drawable.avatar_defaultworkspace).into(binding.fileAvatar);
        return false;
    }
}

回答by drawk

This is what I use, which does not need you to hardcore your resource names and will look for the drawable resources first in your apps resources and then in the stock android resources if nothing was found - allowing you to use default icons and such.

这就是我使用的,它不需要您对资源名称进行硬核,并且会首先在您的应用程序资源中查找可绘制资源,然后在未找到任何内容的情况下在库存 android 资源中查找 - 允许您使用默认图标等。

private class ImageGetter implements Html.ImageGetter {

     public Drawable getDrawable(String source) {
        int id;

        id = getResources().getIdentifier(source, "drawable", getPackageName());

        if (id == 0) {
            // the drawable resource wasn't found in our package, maybe it is a stock android drawable?
            id = getResources().getIdentifier(source, "drawable", "android");
        }

        if (id == 0) {
            // prevent a crash if the resource still can't be found
            return null;    
        }
        else {
            Drawable d = getResources().getDrawable(id);
            d.setBounds(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight());
            return d;
        }
     }

 }

Which can be used as such (example):

可以这样使用(示例):

String myHtml = "This will display an image to the right <img src='ic_menu_more' />";
myTextview.setText(Html.fromHtml(myHtml, new ImageGetter(), null);

回答by Julian

I faced the same problem and I've found a pretty clean solution: After Html.fromHtml() you can run an AsyncTask that iterates over all the tags, fetches the images and then displays them.

我遇到了同样的问题,我找到了一个非常干净的解决方案:在 Html.fromHtml() 之后,您可以运行一个 AsyncTask 迭代所有标签,获取图像然后显示它们。

Here you can find some code that you can use (but it needs some customization): https://gist.github.com/1190397

在这里你可以找到一些你可以使用的代码(但它需要一些自定义):https: //gist.github.com/1190397

回答by Blacklight

I used Dave Webb's answer but simplified it a bit. As long as the resource IDs will stay the same during runtime in your use-case, there's not really a need to write your own class implementing Html.ImageGetterand mess around with source-strings.

我使用了 Dave Webb 的答案,但对其进行了一些简化。只要资源 ID 在您的用例中在运行时保持不变,就没有必要编写自己的类来实现Html.ImageGetter并处理源字符串。

What I did was using the resource ID as a source-string:

我所做的是使用资源 ID 作为源字符串:

final String img = String.format("<img src=\"%s\"/>", R.drawable.your_image);
final String html = String.format("Image: %s", img);

and use it directly:

并直接使用它:

Html.fromHtml(html, new Html.ImageGetter() {
  @Override
  public Drawable getDrawable(final String source) {
    Drawable d = null;
    try {
      d = getResources().getDrawable(Integer.parseInt(source));
      d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
    } catch (Resources.NotFoundException e) {
      Log.e("log_tag", "Image not found. Check the ID.", e);
    } catch (NumberFormatException e) {
      Log.e("log_tag", "Source string not a valid resource ID.", e);
    }

    return d;
  }
}, null);

回答by splash

Also, if you do want to do the replacement yourself, the character you need to look for is [ ? ].

另外,如果您确实想自己进行替换,则需要查找的字符是 [ ? ]。

But if you're using Eclipse, it will freak out when you type that letter into a [replace] statement telling you it conflicts with Cp1252 - this is an Eclipse bug. To fix it, go to

但是,如果您使用的是 Eclipse,当您将该字母输入到 [replace] 语句中时,它会吓坏您,告诉您它与 Cp1252 冲突——这是一个 Eclipse 错误。要修复它,请转到

Window -> Preferences -> General -> Workspace -> Text file encoding,

窗口 -> 首选项 -> 常规 -> 工作区 -> 文本文件编码,

and select [UTF-8]

并选择 [UTF-8]

回答by Falmarri

You could also write your own parser to pull the URL of all the images and then dynamically create new imageviews and pass in the urls.

您还可以编写自己的解析器来提取所有图像的 URL,然后动态创建新的图像视图并传入 url。

回答by logcat

In case somebody think that resources must be declarative and using Spannable for multiple languages is a mess, I did some custom view

如果有人认为资源必须是声明性的并且将 Spannable 用于多种语言是一团糟,我做了一些自定义视图

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.text.Html;
import android.text.Html.ImageGetter;
import android.text.Spanned;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * XXX does not support android:drawable, only current app packaged icons
 *
 * Use it with strings like <string name="text"><![CDATA[Some text <img src="some_image"></img> with image in between]]></string>
 * assuming there is @drawable/some_image in project files
 *
 * Must be accompanied by styleable
 * <declare-styleable name="HtmlTextView">
 *    <attr name="android:text" />
 * </declare-styleable>
 */

public class HtmlTextView extends TextView {

    public HtmlTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HtmlTextView);
        String html = context.getResources().getString(typedArray.getResourceId(R.styleable.HtmlTextView_android_text, 0));
        typedArray.recycle();

        Spanned spannedFromHtml = Html.fromHtml(html, new DrawableImageGetter(), null);
        setText(spannedFromHtml);
    }

    private class DrawableImageGetter implements ImageGetter {
        @Override
        public Drawable getDrawable(String source) {
            Resources res = getResources();
            int drawableId = res.getIdentifier(source, "drawable", getContext().getPackageName());
            Drawable drawable = res.getDrawable(drawableId, getContext().getTheme());

            int size = (int) getTextSize();
            int width = size;
            int height = size;

//            int width = drawable.getIntrinsicWidth();
//            int height = drawable.getIntrinsicHeight();

            drawable.setBounds(0, 0, width, height);
            return drawable;
        }
    }
}

track updates, if any, at https://gist.github.com/logcat/64234419a935f1effc67

跟踪更新(如果有),请访问https://gist.github.com/logcat/64234419a935f1effc67

回答by Farmaker

KOTLIN

科特林

There is also the possibility to use sufficientlysecure.htmltextview.HtmlTextView

也有可能使用 sufficientlysecure.htmltextview.HtmlTextView

Use like below in gradle files:

在 gradle 文件中使用如下:

Project gradle file:

项目gradle文件:

repositories {
    jcenter()
}

App gradle file:

应用程序gradle文件:

dependencies {
implementation 'org.sufficientlysecure:html-textview:3.9'
}

Inside xml file replace your textView with:

在 xml 文件中,将您的 textView 替换为:

<org.sufficientlysecure.htmltextview.HtmlTextView
      android:id="@+id/allNewsBlockTextView"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_margin="2dp"
      android:textColor="#000"
      android:textSize="18sp"
      app:htmlToString="@{detailsViewModel.selectedText}" />

Last line above is if you use Binding adapters where the code will be like:

上面的最后一行是如果您使用绑定适配器,代码将如下所示:

@BindingAdapter("htmlToString")
fun bindTextViewHtml(textView: HtmlTextView, htmlValue: String) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    textView.setHtml(
        htmlValue,
        HtmlHttpImageGetter(textView, "n", true)
    );
    } else {
        textView.setHtml(
        htmlValue,
        HtmlHttpImageGetter(textView, "n", true)
        );
    }
}

More info from github pageand a big thank you to the authors!!!!!

来自 github页面的更多信息,非常感谢作者!!!!!!