【问题标题】:Is it possible to display inline images from html in an Android TextView?是否可以在 Android TextView 中显示来自 html 的内联图像?
【发布时间】:2011-02-21 08:50:13
【问题描述】:

给定以下 HTML:

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

是否可以进行图像渲染?当使用这个 sn-p:mContentText.setText(Html.fromHtml(text)); 时,我得到一个带有黑色边框的青色框,这让我相信 TextView 对 img 标签是什么有所了解。

【问题讨论】:

标签: android image textview


【解决方案1】:

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

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

如果您不想自己进行此替换,您可以使用the other Html.fromHtml() method,它将Html.TagHandlerHtml.ImageGetter 作为参数以及要解析的文本。

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

但是,您将遇到的问题是Html.ImageGetter 需要同步运行,如果您从网络下载图像,您可能希望异步执行该操作。如果您可以在应用程序中添加任何想要显示为资源的图像,那么您的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;
    }
};

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

【讨论】:

  • 好的。我发现只使用 WebView 更容易。不过,我可以看到您的技术对其他类似情况很有用。谢谢!
  • 从 name 获取资源 id 的更聪明的方法是使用 Resources.getIdentifier(String name, String defType, String defPackage)。
  • @Gunnar Lium...但是 i8mage 没有显示在 webview 中..!!有什么帮助吗??
  • 如果图像在服务器中,那么我们如何获取图像……在我的情况下,图像是动态的……我不能使用其他图像视图,因为不确定是否必须有图片…
【解决方案2】:

我已经在我的应用程序中实现了,从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);
        }
    }
}
}

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

是的,您可以使用 ResolveInfo

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

使用以下代码:

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;
    }
}

【讨论】:

  • 你好,“addlevel”和“setLevel”的目的到底是什么?
  • 有没有办法集中图像?如果我们可以点击它们并让它们显示在我们安装的任何图像查看器应用程序中,那也很好。
  • 我想你忘记了我的问题的上下文。我知道如何使用图像查看器应用程序打开图像文件,但您的回答将位图放在 TextView 中,据我所知,无法辨别用户何时点击其中的特定图像。如果您在 textview 中有许多图像,这将是一个更大的问题。有没有办法做到这一点?
  • 但是一个问题是它滚动很慢,我们能做点什么吗?
  • 尝试添加平滑滚动监听器
【解决方案3】:

这就是我使用的,它不需要你硬核你的资源名称,并且会首先在你的应用程序资源中查找可绘制资源,然后在没有找到的情况下在股票 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;
        }
     }

 }

可以这样使用(示例):

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

【讨论】:

  • 这种组合将与互联网的 AsyncTask 检索完美结合。
  • 谢谢!它解决了我的问题。我只需要本地图片,所以只需将它们放到可绘制文件夹中,并确保在从 html 调用时删除图片扩展。
  • 谢谢!但要注意source 可能为空,并且getIdentifier() 在这种情况下会崩溃。最好添加明确的检查。
【解决方案4】:

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

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

【讨论】:

    【解决方案5】:

    我使用了 Dave Webb 的答案,但稍微简化了一点。只要资源 ID 在运行时在您的用例中保持不变,就不需要编写自己的类来实现 Html.ImageGetter 并搞乱源字符串。

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

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

    直接使用:

    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);
    

    【讨论】:

      【解决方案6】:

      科特林

      也可以使用sufficientlysecure.htmltextview.HtmlTextView

      在 gradle 文件中使用如下:

      项目 gradle 文件:

      repositories {
          jcenter()
      }
      

      应用 gradle 文件:

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

      在 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}" />
      

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

      @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)
              );
          }
      }
      

      更多信息来自 github page,非常感谢作者!!!!!!

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

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

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

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

          然后选择[UTF-8]

          【讨论】:

            【解决方案9】:

            如果有人认为资源必须是声明性的,并且将 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;
                    }
                }
            }
            

            跟踪更新,如果有的话,https://gist.github.com/logcat/64234419a935f1effc67

            【讨论】:

              【解决方案10】:

              我使用下面的代码并为我使用Html.ImageGetterAsyncTask在Android TextView中加载HTMl&lt;img&gt;标签:

              String text = your html code;
              
              Spanned spanned = Html.fromHtml(text,
                      new Html.ImageGetter() {
                          @Override
                          public Drawable getDrawable(String source) {
                              LevelListDrawable d = new LevelListDrawable();
                              Drawable empty = getResources().getDrawable(R.drawable.abc_btn_check_material);
                              ;
                              d.addLevel(0, 0, empty);
                              d.setBounds(0, 0, empty.getIntrinsicWidth(), empty.getIntrinsicHeight());
                              new ImageGetterAsyncTask(yourActivity.this, source, d).execute(binding.yourTextView);
              
                              return d;
                          }
                      }, new MyHtmlTagHandler());
                                      binding.yourTextView.setText(spanned);
              

              那么你应该将这个 AsyncTask 类添加到你的代码中以加载图像:

              class ImageGetterAsyncTask extends AsyncTask<TextView, Void, Bitmap> {
              
                      private LevelListDrawable levelListDrawable;
                      private Context context;
                      private String source;
                      private TextView t;
              
                      public ImageGetterAsyncTask(Context context, String source, LevelListDrawable levelListDrawable) {
                          this.context = context;
                          this.source = source;
                          this.levelListDrawable = levelListDrawable;
                      }
              
                      @Override
                      protected Bitmap doInBackground(TextView... params) {
                          t = params[0];
                          try {
                              Log.d(TAG, "Downloading the image from: " + source);
              
                              return Picasso.with(context).load(source).get();
                          } catch (Exception e) {
                              e.printStackTrace();
                              return null;
                          }
                      }
              
                      @Override
                      protected void onPostExecute(final Bitmap bitmap) {
                          try {
                              Drawable d = new BitmapDrawable(context.getResources(), bitmap);
                              Point size = new Point();
                              ((Activity) context).getWindowManager().getDefaultDisplay().getSize(size);
                              // Lets calculate the ratio according to the screen width in px
                              int multiplier = size.x / bitmap.getWidth();
                              Log.d(TAG, "multiplier: " + multiplier);
                              levelListDrawable.addLevel(1, 1, d);
                              // Set bounds width  and height according to the bitmap resized size
                              levelListDrawable.setBounds(0, 0, bitmap.getWidth() * multiplier, bitmap.getHeight() * multiplier);
                              levelListDrawable.setLevel(1);
                              t.setText(t.getText()); // invalidate() doesn't work correctly...
                          } catch (Exception e) { /* Like a null bitmap, etc. */ }
                      }
                  }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-01-23
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-11-25
                • 1970-01-01
                • 2012-05-28
                相关资源
                最近更新 更多