Có thể hiển thị hình ảnh nội tuyến từ html trong TextView Android không?


87

Cho HTML sau:

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

Có thể làm cho hình ảnh hiển thị? Khi sử dụng đoạn mã này:, mContentText.setText(Html.fromHtml(text));tôi nhận được một hộp màu lục lam có viền đen, khiến tôi tin rằng TextView có một số ý tưởng về thẻ img là gì.


1
Kiểm tra bài đăng của tôi @ javatechig.com javatechig.com/2013/04/07/how-to-display-html-in-android-view
Nilanchal

Câu trả lời:


125

Nếu bạn xem tài liệu choHtml.fromHtml(text) bạn, bạn sẽ thấy nó nói:

Bất kỳ <img>thẻ nào trong HTML sẽ hiển thị dưới dạng hình ảnh thay thế chung mà chương trình của bạn có thể đi qua và thay thế bằng hình ảnh thực.

Nếu bạn không muốn tự mình thực hiện việc thay thế này, bạn có thể sử dụng phương thức khácHtml.fromHtml() lấy tham số an Html.TagHandlervà an Html.ImageGettercũng như văn bản để phân tích cú pháp.

Trong trường hợp của bạn, bạn có thể phân tích cú pháp nullnhư đối với Html.TagHandlernhưng bạn cần triển khai của riêng bạn Html.ImageGettervì không có triển khai mặc định.

Tuy nhiên, vấn đề bạn sẽ gặp phải là Html.ImageGettercần phải chạy đồng bộ và nếu bạn đang tải hình ảnh từ web xuống, bạn có thể muốn thực hiện điều đó không đồng bộ. Nếu bạn có thể thêm bất kỳ hình ảnh nào bạn muốn hiển thị dưới dạng tài nguyên trong ứng dụng của mình, ImageGetterviệc triển khai của bạn trở nên đơn giản hơn rất nhiều. Bạn có thể thoát khỏi những thứ như:

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

Bạn có thể muốn tìm ra thứ gì đó thông minh hơn để ánh xạ các chuỗi nguồn với ID tài nguyên.


4
ĐỒNG Ý. Tôi thấy việc sử dụng WebView sẽ dễ dàng hơn. Tuy nhiên, tôi có thể thấy kỹ thuật của bạn hữu ích cho các trường hợp tương tự khác. Cảm ơn!
Gunnar Lium

1
cách thông minh hơn để lấy id tài nguyên từ tên là sử dụng Resources.getIdentifier (String name, String defType, String defPackage).
Timuçin

@Gunnar Lium ... nhưng i8mage không hiển thị trong webview .. !! Bất kỳ giúp đỡ ??
kgandroid

Nếu hình ảnh nằm trong máy chủ thì làm cách nào chúng ta có thể lấy hình ảnh… trong trường hợp của tôi, hình ảnh là động… Tôi không thể sử dụng các chế độ xem hình ảnh khác vì không chắc chắn rằng phải có hình ảnh…
Sourav Roy

19

Tôi đã triển khai trong ứng dụng của mình, lấy tham khảo từ pskink .thanx rất nhiều

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

Theo nhận xét dưới đây @rpgmaker, tôi đã thêm câu trả lời này

vâng bạn có thể làm bằng cách sử dụng lớp ResolveInfo

kiểm tra tệp của bạn có được hỗ trợ với các ứng dụng đã được cài đặt hay không

sử dụng mã dưới đây:

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
Này, mục đích chính xác của "addlevel" và "setLevel" là gì?
Aeefire

Có cách nào để tập trung các hình ảnh không? Sẽ rất tốt nếu chúng ta có thể chạm vào chúng và hiển thị chúng trong bất kỳ ứng dụng xem hình ảnh nào mà chúng tôi đã cài đặt.
Aspiring Dev

Tôi nghĩ rằng bạn đã quên bối cảnh cho câu hỏi của tôi. Tôi biết cách mở tệp hình ảnh bằng ứng dụng xem hình ảnh nhưng câu trả lời của bạn đặt ảnh bitmap bên trong TextView và theo như tôi có thể nói thì không có cách nào nhận biết được khi nào người dùng chạm vào một hình ảnh cụ thể bên trong nó. Đây là một vấn đề nhiều hơn nếu bạn có nhiều hình ảnh bên trong textview. Có cách nào để làm việc này không?
Aspiring

nhưng một vấn đề là nó cuộn rất chậm, chúng ta có thể làm gì đó với điều đó không?
Pratik Jamariya

cố gắng thêm người nghe cuộn mượt mà
madhu527

16

Đây là những gì tôi sử dụng, không cần bạn đặt tên tài nguyên của mình và sẽ tìm kiếm các tài nguyên có thể rút ra trước trong tài nguyên ứng dụng của bạn và sau đó trong tài nguyên android kho nếu không tìm thấy gì - cho phép bạn sử dụng các biểu tượng mặc định và tương tự.

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

 }

Có thể được sử dụng như vậy (ví dụ):

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

Sự kết hợp này sẽ hoàn hảo với việc truy xuất Internet của AsyncTask.
Francis Rodrigues

1
Cảm ơn! Nó đã giải quyết được vấn đề của tôi. Tôi chỉ cần hình ảnh cục bộ, vì vậy chỉ cần thả chúng vào thư mục có thể vẽ và đảm bảo loại bỏ phần mở rộng hình ảnh khi gọi nó từ html.
Dody Rachmat Wicaksono

Cảm ơn! Nhưng hãy cẩn thận sourcecó thể vô hiệu và getIdentifier()bị treo trong trường hợp này. Tốt hơn hãy thêm một kiểm tra rõ ràng.
gmk57

5

Tôi gặp phải vấn đề tương tự và tôi đã tìm thấy một giải pháp khá rõ ràng: Sau Html.fromHtml (), bạn có thể chạy AsyncTask lặp lại trên tất cả các thẻ, tìm nạp hình ảnh và sau đó hiển thị chúng.

Ở đây bạn có thể tìm thấy một số mã mà bạn có thể sử dụng (nhưng nó cần một số tùy chỉnh): https://gist.github.com/1190397


3

Tôi đã sử dụng câu trả lời của Dave Webb nhưng đơn giản hóa nó một chút. Miễn là các ID tài nguyên sẽ giữ nguyên trong thời gian chạy trong trường hợp sử dụng của bạn, không thực sự cần thiết phải viết triển khai lớp của riêng bạn Html.ImageGettervà lộn xộn với chuỗi nguồn.

Những gì tôi đã làm là sử dụng ID tài nguyên dưới dạng chuỗi nguồn:

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

và sử dụng trực tiếp:

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

Bạn cũng có thể viết trình phân tích cú pháp của riêng mình để kéo URL của tất cả các hình ảnh và sau đó tạo động các lần xem hình ảnh mới và chuyển vào các url.


1

Ngoài ra, nếu bạn muốn tự mình thay thế, ký tự bạn cần tìm là [].

Nhưng nếu bạn đang sử dụng Eclipse, bạn sẽ thấy khó chịu khi bạn nhập ký tự đó vào câu lệnh [Replace] cho bạn biết rằng nó xung đột với Cp1252 - đây là một lỗi Eclipse. Để khắc phục, hãy truy cập

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

và chọn [UTF-8]


0

Trong trường hợp ai đó nghĩ rằng tài nguyên phải được khai báo và sử dụng Spannable cho nhiều ngôn ngữ là một mớ hỗn độn, tôi đã thực hiện một số chế độ xem tùy chỉnh

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

theo dõi cập nhật, nếu có, tại https://gist.github.com/logcat/64234419a935f1effc67


0

KOTLIN

Ngoài ra còn có khả năng sử dụng sufficientlysecure.htmltextview.HtmlTextView

Sử dụng như dưới đây trong các tệp gradle:

Tệp gradle dự án:

repositories {
    jcenter()
}

Tệp phân loại ứng dụng:

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

Tệp bên trong xml thay thế textView của bạn bằng:

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

Dòng cuối cùng ở trên là nếu bạn sử dụng bộ điều hợp Binding, nơi mã sẽ như sau:

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

Thêm thông tin từ trang github và xin gửi lời cảm ơn sâu sắc đến các tác giả !!!!!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.