Android TextView에서 HTML의 인라인 이미지를 표시 할 수 있습니까?


87

다음 HTML이 주어지면 :

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

이미지를 렌더링 할 수 있습니까? 이 스 니펫을 사용하면 mContentText.setText(Html.fromHtml(text));검은 색 테두리가있는 청록색 상자가 표시되어 TextView에 img 태그가 무엇인지 알 수 있습니다.


1
내 게시물을 체크 아웃 @ javatechig.com javatechig.com/2013/04/07/how-to-display-html-in-android-view
Nilanchal

답변:


125

문서를Html.fromHtml(text) 살펴보면 다음과 같이 표시됩니다.

<img>HTML의 모든 태그는 프로그램이 통과하여 실제 이미지로 대체 할 수있는 일반 대체 이미지로 표시됩니다.

이 대체를 직접 수행하지 않으려면 구문 분석 할 텍스트뿐만 아니라 및 as 인수를 사용 하는 다른 Html.fromHtml()메서드 를 사용할 수 있습니다 .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에 매핑하는 데 더 똑똑한 방법을 찾고 싶을 것입니다.


4
확인. 대신 WebView를 사용하는 것이 더 쉽다는 것을 알았습니다. 당신의 기술이 다른 유사한 시나리오에 유용하다는 것을 알 수 있습니다. 감사!
Gunnar Lium

1
이름에서 리소스 ID를 얻는 더 현명한 방법은 Resources.getIdentifier (String name, String defType, String defPackage)를 사용하는 것입니다.
Timuçin

@Gunnar Lium ...하지만 i8mage가 webview에 표시되지 않습니다 .. !! 도움이 필요하십니까 ??
kgandroid

이미지가 서버에 있으면 이미지를 얻는 방법… 제 경우에는 이미지가 동적입니다… 이미지가 있어야한다는 것이 확실하지 않기 때문에 다른 이미지보기를 사용할 수 없습니다…
Sourav Roy

19

나는 내 앱에서 구현했으며 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;
    }
}

1
"addlevel"과 "setLevel"의 목적은 정확히 무엇입니까?
Aeefire

이미지를 중앙 집중화하는 방법이 있습니까? 또한 탭하여 설치 한 이미지 뷰어 앱에 표시 할 수 있다면 좋을 것입니다.
Aspiring Dev

내 질문에 대한 컨텍스트를 잊은 것 같습니다. 이미지 뷰어 앱으로 이미지 파일을 여는 방법을 알고 있지만 귀하의 대답은 TextView 내부에 비트 맵을 넣고 사용자가 특정 이미지를 탭할 때 식별 할 방법이 없다고 말할 수 있습니다. 이것은 textview 안에 많은 이미지가있는 경우 더 문제가됩니다. 이를 수행하는 방법이 있습니까?
Aspiring Dev

하지만 한 가지 문제는 스크롤이 매우 느리다는 것입니다. 이에 대해 뭔가를 할 수 있습니까?
Pratik Jamariya

부드러운 스크롤 리스너를 추가하려고
madhu527

16

이것은 내가 사용하는 것입니다. 리소스 이름을 하드 코어 할 필요가 없으며 먼저 앱 리소스에서 드로어 블 리소스를 찾은 다음 아무것도 발견되지 않으면 재고 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 검색에 완벽합니다.
Francis Rodrigues

1
감사! 그것은 내 문제를 해결했습니다. 로컬 이미지 만 필요하므로 드로어 블 폴더에 드롭하고 html에서 호출 할 때 이미지 확장자를 제거해야합니다.
Dody Rachmat Wicaksono

감사! 그러나 sourcenull 일 수 있으며이 getIdentifier()경우 충돌합니다. 명시 적 검사를 추가하는 것이 좋습니다.
gmk57

5

나는 똑같은 문제에 직면했고 꽤 깨끗한 해결책을 찾았습니다. Html.fromHtml () 후에 모든 태그를 반복하고 이미지를 가져 와서 표시하는 AsyncTask를 실행할 수 있습니다.

여기에서 사용할 수있는 코드를 찾을 수 있습니다 (하지만 사용자 지정이 필요함) : https://gist.github.com/1190397


3

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

1

또한 모든 이미지의 URL을 가져 오는 자체 파서를 작성하고 동적으로 새 이미지보기를 만들고 URL을 전달할 수 있습니다.


1

또한 직접 교체하고 싶다면 찾아야 할 문자는 []입니다.

하지만 Eclipse를 사용하는 경우 [replace] 문에 해당 문자를 입력하면 Cp1252와 충돌한다는 메시지가 표시됩니다. 이것은 Eclipse 버그입니다. 문제를 해결하려면 다음으로 이동하십시오.

창-> 환경 설정-> 일반-> 작업 공간-> 텍스트 파일 인코딩,

그리고 선택 [UTF-8]


0

누군가 리소스가 선언적이어야하고 여러 언어에 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 에서 추적 하십시오.


0

코 틀린

사용 가능성도 있습니다 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 페이지 에서 더 많은 정보 와 저자들에게 큰 감사 !!!!!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.