Apakah mungkin untuk menampilkan gambar sebaris dari html di Android TextView?

87

Diberikan HTML berikut:

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

Apakah mungkin membuat gambar dirender? Saat menggunakan cuplikan ini:, mContentText.setText(Html.fromHtml(text));saya mendapatkan kotak cyan dengan batas hitam, membuat saya percaya bahwa TextView memiliki beberapa gagasan tentang apa itu tag img.

Gunnar Lium
sumber
1
Lihat posting saya @ javatechig.com javatechig.com/2013/04/07/how-to-display-html-in-android-view
Nilanchal

Jawaban:

126

Jika Anda melihat dokumentasi untukHtml.fromHtml(text) Anda akan melihatnya mengatakan:

Setiap <img>tag dalam HTML akan ditampilkan sebagai gambar pengganti umum yang kemudian dapat dibuka oleh program Anda dan diganti dengan gambar asli.

Jika Anda tidak ingin melakukan penggantian ini sendiri, Anda dapat menggunakan metode lainHtml.fromHtml() yang menggunakan argumen an Html.TagHandlerdan an Html.ImageGettersebagai serta teks yang akan diurai.

Dalam kasus Anda, Anda dapat mengurai nullseperti untuk itu Html.TagHandlertetapi Anda harus menerapkannya sendiri Html.ImageGetterkarena tidak ada implementasi default.

Namun, masalah yang akan Anda hadapi adalah Html.ImageGetterkebutuhan untuk berjalan secara sinkron dan jika Anda mengunduh gambar dari web, Anda mungkin ingin melakukannya secara asinkron. Jika Anda dapat menambahkan gambar apa pun yang ingin ditampilkan sebagai sumber daya dalam aplikasi Anda, ImageGetterimplementasi Anda menjadi jauh lebih sederhana. Anda bisa lolos dengan sesuatu seperti:

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

Anda mungkin ingin mencari tahu sesuatu yang lebih cerdas untuk memetakan string sumber ke ID sumber daya.

Dave Webb
sumber
4
BAIK. Saya merasa lebih mudah menggunakan WebView saja. Saya dapat melihat teknik Anda berguna untuk skenario serupa lainnya. Terima kasih!
Gunnar Lium
1
cara cerdas untuk mendapatkan id sumber daya dari nama adalah dengan menggunakan Resources.getIdentifier (Nama string, String defType, String defPackage).
Timuçin
@ Gunnar Lium ... tapi i8mage tidak muncul di webview .. !! Ada bantuan ??
kgandroid
Jika gambar ada di server lalu bagaimana kita bisa mendapatkan gambar ... dalam kasus saya gambarnya dinamis ... Saya tidak bisa menggunakan tampilan gambar lain karena tidak pasti harus ada gambar ...
Sourav Roy
19

Saya telah menerapkan di aplikasi saya, banyak mengambil referensi dari 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);
        }
    }
}
}

Sesuai di bawah komentar @rpgmaker saya menambahkan jawaban ini

ya, Anda dapat melakukannya dengan menggunakan kelas ResolveInfo

periksa file Anda didukung dengan aplikasi yang sudah diinstal atau tidak

menggunakan kode di bawah ini:

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
sumber
1
Hei, sebenarnya apa sih maksud dari "addlevel" dan "setLevel"?
Aeefire
Apakah ada cara untuk memusatkan gambar? Akan lebih baik jika kita dapat mengetuknya dan menampilkannya di aplikasi penampil gambar apa pun yang telah kita pasang.
Aspiring Dev
Saya pikir Anda lupa konteks pertanyaan saya. Saya tahu cara membuka file gambar dengan aplikasi penampil gambar, tetapi jawaban Anda menempatkan bitmap di dalam TextView dan sejauh yang saya tahu tidak ada cara untuk membedakan saat pengguna mengetuk gambar tertentu di dalamnya. Ini lebih menjadi masalah jika Anda memiliki banyak gambar di dalam textview. Apakah ada cara untuk melakukan ini?
Aspiring Dev
tetapi satu masalah adalah gulungannya sangat lambat, dapatkah kita melakukan sesuatu tentang itu?
Pratik Jamariya
mencoba untuk menambahkan pendengar gulungan halus
madhu527
16

Inilah yang saya gunakan, yang tidak mengharuskan Anda untuk mengubah nama sumber daya Anda dan akan mencari sumber daya yang dapat digambar terlebih dahulu di sumber daya aplikasi Anda dan kemudian di sumber daya android stok jika tidak ada yang ditemukan - memungkinkan Anda untuk menggunakan ikon default dan semacamnya.

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

 }

Yang bisa digunakan seperti itu (contoh):

String myHtml = "This will display an image to the right <img src='ic_menu_more' />";
myTextview.setText(Html.fromHtml(myHtml, new ImageGetter(), null);
drawk
sumber
Kombinasi ini akan sempurna dengan pengambilan AsyncTask dari internet.
Francis Rodrigues
1
Terima kasih! Itu memecahkan masalah saya. Saya hanya perlu gambar lokal, jadi letakkan saja ke folder drawable dan pastikan untuk menghapus ekstensi gambar saat memanggilnya dari html.
Dody Rachmat Wicaksono
Terima kasih! Tapi hati source- hati mungkin nol, dan getIdentifier()crash dalam kasus ini. Lebih baik tambahkan pemeriksaan eksplisit.
gmk57
5

Saya menghadapi masalah yang sama dan saya telah menemukan solusi yang cukup bersih: Setelah Html.fromHtml () Anda dapat menjalankan AsyncTask yang mengulangi semua tag, mengambil gambar dan kemudian menampilkannya.

Di sini Anda dapat menemukan beberapa kode yang dapat Anda gunakan (tetapi perlu beberapa penyesuaian): https://gist.github.com/1190397

Julian
sumber
3

Saya menggunakan jawaban Dave Webb tetapi sedikit menyederhanakannya. Selama ID sumber daya akan tetap sama selama waktu proses dalam kasus penggunaan Anda, sebenarnya tidak perlu menulis implementasi kelas Anda sendiri Html.ImageGetterdan mengotak-atik string sumber.

Apa yang saya lakukan adalah menggunakan ID sumber daya sebagai string sumber:

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

dan gunakan secara langsung:

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);
Cahaya hitam
sumber
1

Anda juga dapat menulis parser Anda sendiri untuk menarik URL dari semua gambar dan kemudian secara dinamis membuat tampilan gambar baru dan memasukkan url.

Falmarri
sumber
1

Selain itu, jika Anda ingin melakukan pergantian sendiri, karakter yang perlu Anda cari adalah [].

Tetapi jika Anda menggunakan Eclipse, itu akan menjadi aneh saat Anda mengetik huruf itu ke dalam pernyataan [replace] yang memberi tahu Anda bahwa itu bertentangan dengan Cp1252 - ini adalah bug Eclipse. Untuk memperbaikinya, buka

Jendela -> Preferensi -> Umum -> Ruang Kerja -> Pengkodean file teks,

dan pilih [UTF-8]

guyuran
sumber
1

KOTLIN

Ada juga kemungkinan untuk digunakan sufficientlysecure.htmltextview.HtmlTextView

Gunakan seperti di bawah ini dalam file gradle:

File gradle proyek:

repositories {
    jcenter()
}

File gradle aplikasi:

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

Di dalam file xml, ganti textView Anda dengan:

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

Baris terakhir di atas adalah jika Anda menggunakan adaptor Binding dimana kodenya akan seperti:

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

Info lebih lanjut dari halaman github dan terima kasih banyak untuk penulis !!!!!

Petani
sumber
0

Jika seseorang berpikir bahwa sumber daya harus deklaratif dan menggunakan Spannable untuk banyak bahasa berantakan, saya melakukan beberapa tampilan khusus

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

melacak pembaruan, jika ada, di https://gist.github.com/logcat/64234419a935f1effc67

logcat
sumber