È possibile visualizzare immagini in linea da html in un TextView Android?


87

Dato il seguente HTML:

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

È possibile eseguire il rendering dell'immagine? Quando utilizzo questo frammento:, mContentText.setText(Html.fromHtml(text));ottengo una scatola ciano con bordi neri, che mi porta a credere che un TextView abbia un'idea di cosa sia un tag img.


Risposte:


126

Se dai un'occhiata alla documentazioneHtml.fromHtml(text) , vedrai che dice:

Qualsiasi <img>tag nell'HTML verrà visualizzato come un'immagine sostitutiva generica che il programma può quindi esaminare e sostituire con immagini reali.

Se non si desidera eseguire da soli questa sostituzione, è possibile utilizzare l'altro Html.fromHtml()metodo che accetta un Html.TagHandlere un Html.ImageGettercome argomenti oltre al testo da analizzare.

Nel tuo caso potresti analizzare nullcome per il Html.TagHandlerma dovresti implementare il tuo in Html.ImageGetterquanto non esiste un'implementazione predefinita.

Tuttavia, il problema che avrai è che è Html.ImageGetternecessario eseguire in modo sincrono e se stai scaricando immagini dal web probabilmente vorrai farlo in modo asincrono. Se puoi aggiungere qualsiasi immagine che desideri visualizzare come risorse nella tua applicazione, la tua ImageGetterimplementazione diventa molto più semplice. Potresti farla franca con qualcosa come:

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

Probabilmente vorresti trovare qualcosa di più intelligente per mappare le stringhe di origine agli ID risorsa.


4
OK. Ho scoperto che era più semplice usare semplicemente un WebView. Tuttavia, vedo che la tua tecnica è utile per altri scenari simili. Grazie!
Gunnar Lium

1
il modo più intelligente per ottenere l'ID della risorsa da nome è utilizzare Resources.getIdentifier (String name, String defType, String defPackage).
Timuçin

@Gunnar Lium ... ma i8mage non viene mostrato in webview .. !! Qualsiasi aiuto ??
kgandroid

Se l'immagine è in un server, allora come possiamo ottenere l'immagine ... nel mio caso l'immagine è dinamica ... Non posso usare altre visualizzazioni di immagini perché non è certo che debba esserci un'immagine ...
Sourav Roy

19

Ho implementato nella mia app, preso riferimento da pskink .thanx molto

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

Come sotto il commento di @rpgmaker ho aggiunto questa risposta

sì, puoi farlo usando la classe ResolveInfo

controlla che il tuo file sia supportato o meno con app già installate

utilizzando il codice seguente:

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

1
Ehi, qual è esattamente lo scopo di "addlevel" e "setLevel"?
Aeefire

C'è un modo per centralizzare le immagini? Sarebbe anche bello se potessimo toccarli e mostrarli in qualsiasi app di visualizzazione di immagini che abbiamo installato.
Aspiring Dev

Penso che tu abbia dimenticato il contesto per la mia domanda. So come aprire un file di immagine con un'app visualizzatore di immagini, ma la tua risposta inserisce bitmap all'interno di un TextView e per quanto ne so non c'è modo di discernere quando un utente sta toccando un'immagine specifica al suo interno. Questo è più un problema se hai molte immagini all'interno della visualizzazione del testo. C'è un modo per fare questo?
Aspiring Dev

ma un problema è che scorre molto lentamente, possiamo fare qualcosa al riguardo?
Pratik Jamariya

prova ad aggiungere ascoltatori scorrevoli
madhu527

16

Questo è quello che uso, che non richiede che tu impari i nomi delle tue risorse e cercherà le risorse disegnabili prima nelle risorse delle tue app e poi nelle risorse Android di serie se non viene trovato nulla, permettendoti di utilizzare icone predefinite e simili.

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

 }

Che può essere utilizzato come tale (esempio):

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

Questa combinazione sarebbe perfetta con il recupero di AsyncTask da Internet.
Francis Rodrigues

1
Grazie! Ha risolto il mio problema. Ho solo bisogno di immagini locali, quindi trascinale nella cartella disegnabile e assicurati di rimuovere l'estensione dell'immagine quando la chiami da html.
Dody Rachmat Wicaksono

Grazie! Ma attenzione sourcepotrebbe essere nullo e getIdentifier()in questo caso si blocca. Meglio aggiungere un segno di spunta esplicito.
gmk57

5

Ho affrontato lo stesso problema e ho trovato una soluzione abbastanza pulita: dopo Html.fromHtml () puoi eseguire un AsyncTask che itera su tutti i tag, recupera le immagini e quindi le visualizza.

Qui puoi trovare del codice che puoi usare (ma necessita di alcune personalizzazioni): https://gist.github.com/1190397


3

Ho usato la risposta di Dave Webb ma l'ho semplificata un po '. Finché gli ID risorsa rimarranno gli stessi durante il runtime nel tuo caso d'uso, non è davvero necessario scrivere l'implementazione della tua classe Html.ImageGettere scherzare con le stringhe di origine.

Quello che ho fatto è stato utilizzare l'ID risorsa come stringa di origine:

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

e usalo direttamente:

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

1

Puoi anche scrivere il tuo parser per estrarre l'URL di tutte le immagini e quindi creare dinamicamente nuove visualizzazioni di immagini e passare gli URL.


1

Inoltre, se vuoi sostituire tu stesso, il carattere che devi cercare è [].

Ma se stai usando Eclipse, andrà fuori di testa quando digiti quella lettera in un'istruzione [sostituisci] che ti dice che è in conflitto con Cp1252 - questo è un bug di Eclipse. Per risolverlo, vai a

Finestra -> Preferenze -> Generale -> Area di lavoro -> Codifica file di testo,

e seleziona [UTF-8]


1

KOTLIN

C'è anche la possibilità di utilizzare sufficientlysecure.htmltextview.HtmlTextView

Usa come sotto nei file gradle:

File gradle del progetto:

repositories {
    jcenter()
}

File gradle app:

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

All'interno del file xml sostituisci il tuo textView con:

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

L'ultima riga sopra è se usi gli adattatori Binding in cui il codice sarà come:

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

Maggiori informazioni dalla pagina GitHub e un grande ringraziamento agli autori !!!!!


0

Nel caso in cui qualcuno pensi che le risorse debbano essere dichiarative e l'utilizzo di Spannable per più lingue è un pasticcio, ho fatto una visualizzazione personalizzata

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

tenere traccia degli eventuali aggiornamenti su https://gist.github.com/logcat/64234419a935f1effc67

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.