Ist es möglich, Inline-Bilder von HTML in einer Android TextView anzuzeigen?

86

Angesichts des folgenden HTML:

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

Ist es möglich, das Bild zu rendern? Wenn mContentText.setText(Html.fromHtml(text));ich dieses Snippet: verwende , erhalte ich eine Cyan-Box mit schwarzen Rändern, was mich glauben lässt, dass eine TextView eine Vorstellung davon hat, was ein IMG-Tag ist.

Gunnar Lium
quelle
1
Kasse mein Beitrag @ javatechig.com javatechig.com/2013/04/07/how-to-display-html-in-android-view
Nilanchal

Antworten:

125

Wenn Sie sich die DokumentationHtml.fromHtml(text) ansehen, sehen Sie, dass dort steht:

Alle <img>Tags im HTML-Code werden als generisches Ersatzbild angezeigt, das Ihr Programm dann durchlaufen und durch echte Bilder ersetzen kann.

Wenn Sie diese Ersetzung nicht selbst durchführen möchten, können Sie die andere Html.fromHtml()Methode verwenden, die ein Html.TagHandlerund ein Html.ImageGetterals Argumente sowie den zu analysierenden Text verwendet.

In Ihrem Fall könnten Sie analysieren , nullwie für die Html.TagHandleraber müssen Sie Ihre eigenen implementieren , Html.ImageGetterda es keine Standardimplementierung ist.

Das Problem, das Sie haben werden, ist jedoch, dass die Html.ImageGetterSynchronisierung ausgeführt werden muss. Wenn Sie Bilder aus dem Internet herunterladen, möchten Sie dies wahrscheinlich asynchron tun. Wenn Sie Bilder hinzufügen können, die Sie als Ressourcen in Ihrer Anwendung anzeigen möchten, ImageGetterwird Ihre Implementierung viel einfacher. Sie könnten mit so etwas davonkommen wie:

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

Sie möchten wahrscheinlich etwas Klügeres herausfinden, um Quellzeichenfolgen Ressourcen-IDs zuzuordnen.

Dave Webb
quelle
4
OK. Ich fand es einfacher, stattdessen nur ein WebView zu verwenden. Ich kann jedoch sehen, dass Ihre Technik für andere ähnliche Szenarien nützlich ist. Vielen Dank!
Gunnar Lium
1
Die intelligentere Methode zum Abrufen der Ressourcen-ID aus dem Namen ist die Verwendung von Resources.getIdentifier (String-Name, String defType, String defPackage).
Timuçin
@ Gunnar Lium ... aber i8mage wird nicht in der Webansicht angezeigt .. !! Irgendeine Hilfe ??
kgandroid
Wenn sich das Bild auf einem Server befindet, wie können wir das Bild abrufen? In meinem Fall ist das Bild dynamisch. Ich kann keine anderen Bildansichten verwenden, da nicht sicher ist, ob ein Bild vorhanden sein muss.
Sourav Roy
19

Ich habe in meiner App implementiert, Referenz aus dem pskink .thanx viel genommen

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

Wie unten @rpgmaker Kommentar angegeben, habe ich diese Antwort hinzugefügt

Ja, Sie können die ResolveInfo- Klasse verwenden

Überprüfen Sie, ob Ihre Datei mit bereits installierten Apps unterstützt wird oder nicht

mit dem folgenden 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;
    }
}
madhu527
quelle
1
Hey, was genau ist der Zweck von "addlevel" und "setLevel"?
Aeefire
Gibt es eine Möglichkeit, die Bilder zu zentralisieren? Es wäre auch gut, wenn wir sie antippen und in der von uns installierten Bildbetrachter-App anzeigen lassen könnten.
Aspiring Dev
Ich denke, Sie haben den Kontext für meine Frage vergessen. Ich weiß, wie man eine Bilddatei mit einer Bildbetrachter-App öffnet, aber Ihre Antwort fügt Bitmaps in eine Textansicht ein, und soweit ich das beurteilen kann, gibt es keine Möglichkeit zu erkennen, wann ein Benutzer auf ein bestimmtes Bild darin tippt. Dies ist eher ein Problem, wenn Sie viele Bilder in der Textansicht haben. Gibt es eine Möglichkeit, dies zu tun?
Aspiring Dev
Aber ein Problem ist, dass es sehr langsam rollt. Können wir etwas dagegen tun?
Pratik Jamariya
versuchen Sie, glatte Schriftrollen Listener hinzuzufügen
madhu527
16

Dies ist, was ich verwende, was nicht erforderlich ist, dass Sie Ihre Ressourcennamen festschreiben und nach den zeichnbaren Ressourcen zuerst in Ihren Apps-Ressourcen und dann in den Standard-Android-Ressourcen suchen, wenn nichts gefunden wurde. So können Sie Standardsymbole und dergleichen verwenden.

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

 }

Welches kann als solches verwendet werden (Beispiel):

String myHtml = "This will display an image to the right <img src='ic_menu_more' />";
myTextview.setText(Html.fromHtml(myHtml, new ImageGetter(), null);
drawk
quelle
Diese Kombination eignet sich perfekt für das Abrufen des Internets durch AsyncTask.
Francis Rodrigues
1
Vielen Dank! Es hat mein Problem gelöst. Ich benötige nur lokale Bilder. Legen Sie sie einfach in einem zeichnbaren Ordner ab und entfernen Sie die Bilderweiterung, wenn Sie sie aus HTML aufrufen.
Dody Rachmat Wicaksono
Vielen Dank! Aber Vorsicht sourcekann null sein und getIdentifier()stürzt in diesem Fall ab. Fügen Sie besser eine explizite Prüfung hinzu.
gmk57
5

Ich hatte das gleiche Problem und eine ziemlich saubere Lösung gefunden: Nach Html.fromHtml () können Sie eine AsyncTask ausführen, die alle Tags durchläuft, die Bilder abruft und sie dann anzeigt.

Hier finden Sie Code, den Sie verwenden können (der jedoch angepasst werden muss): https://gist.github.com/1190397

julianisch
quelle
3

Ich habe Dave Webbs Antwort verwendet, sie aber etwas vereinfacht. Solange die Ressourcen-IDs in Ihrem Anwendungsfall zur Laufzeit gleich bleiben, müssen Sie nicht unbedingt Ihre eigene Klasse schreiben, um sie zu implementieren Html.ImageGetterund mit Quellzeichenfolgen herumzuspielen.

Ich habe die Ressourcen-ID als Quellzeichenfolge verwendet:

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

und benutze es direkt:

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);
Schwarzlicht
quelle
1

Sie können auch einen eigenen Parser schreiben, um die URL aller Bilder abzurufen, dann dynamisch neue Bildansichten zu erstellen und die URLs zu übergeben.

Falmarri
quelle
1

Wenn Sie den Ersatz selbst vornehmen möchten, müssen Sie nach [] suchen.

Wenn Sie jedoch Eclipse verwenden, wird es ausflippen, wenn Sie diesen Buchstaben in eine [Ersetzen] -Anweisung eingeben, die Ihnen mitteilt, dass er mit Cp1252 in Konflikt steht - dies ist ein Eclipse-Fehler. Um das Problem zu beheben, gehen Sie zu

Fenster -> Einstellungen -> Allgemein -> Arbeitsbereich -> Textdatei-Codierung,

und auswählen [UTF-8]

Spritzen
quelle
0

Für den Fall, dass jemand der Meinung ist, dass Ressourcen deklarativ sein müssen und die Verwendung von Spannable für mehrere Sprachen ein Chaos ist, habe ich eine benutzerdefinierte Ansicht erstellt

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

Verfolgen Sie ggf. Aktualisierungen unter https://gist.github.com/logcat/64234419a935f1effc67

logcat
quelle
0

KOTLIN

Es besteht auch die Möglichkeit zur Nutzung sufficientlysecure.htmltextview.HtmlTextView

Verwenden Sie wie unten in Gradle-Dateien:

Projekt Gradle-Datei:

repositories {
    jcenter()
}

App Gradle-Datei:

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

In der XML-Datei ersetzen Sie Ihre Textansicht durch:

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

Die letzte Zeile oben ist, wenn Sie Bindungsadapter verwenden, deren Code wie folgt lautet:

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

Weitere Infos von der Github- Seite und ein großes Dankeschön an die Autoren !!!!!

Landwirt
quelle