본문 바로가기
안드로이드 자바

[JAVA][Android]Html.ImageGetter 을 사용하여 이미지 비동기로 보이게 하기

by teamnova 2024. 9. 29.
728x90

Html.ImageGetter는 Android에서 HTML 문자열을 처리할 때,

특히 HTML에 포함된 이미지 태그를 동적으로 처리하는 인터페이스입니다.

왜 사용하는가?

  • HTML 텍스트 렌더링: HTML 콘텐츠를 TextView와 같은 UI 요소에 표시할 때, 이미지가 포함된 HTML 문자열을 손쉽게 렌더링할 수 있습니다. 기본적으로 Android는 HTML 콘텐츠를 텍스트로 변환할 수 있지만, 이미지와 같은 비트맵 리소스는 별도로 처리해야 합니다.
  • 비동기 이미지 로딩: ImageGetter를 구현하면 비동기적으로 이미지를 로드하여 UI 스레드를 차단하지 않고도 사용자 경험을 향상시킬 수 있습니다. 이를 통해 큰 이미지나 네트워크에서 로드되는 이미지를 효율적으로 처리할 수 있습니다.
  • 유연한 이미지 처리: 다양한 원본(로컬 파일, URL 등)에서 이미지를 로드할 수 있으며, 필요한 경우 이미지 크기를 조정하거나 필터를 적용할 수 있습니다.

어디에 사용하면 좋은가?

  • 채팅 애플리케이션: 사용자 메시지에 HTML 형식의 텍스트와 이미지를 포함할 수 있어, 대화 내용을 더욱 풍부하게 표현할 수 있습니다.
  • 뉴스 앱: 기사 내용에 이미지를 포함하여 사용자가 더 많은 정보를 시각적으로 얻을 수 있도록 할 수 있습니다.
  • 블로그 또는 포럼 앱: 사용자가 작성한 콘텐츠에 HTML 형식이 포함된 경우, ImageGetter를 사용하여 이미지가 포함된 텍스트를 적절히 렌더링할 수 있습니다.
  • 소셜 미디어 앱: 사용자 포스트나 댓글에 HTML 형식으로 링크와 이미지를 포함할 수 있어, 더 매력적인 사용자 경험을 제공할 수 있습니다.
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.Html;
import android.text.Spanned;
import android.util.Log;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.io.InputStream;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MainActivity extends AppCompatActivity {

    private TextView textView;
    private ExecutorService executorService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.textView);

        // ExecutorService 초기화 (이미지를 비동기로 로드하기 위한 스레드 풀)
        executorService = Executors.newSingleThreadExecutor();

        // HTML 텍스트에 포함된 이미지 태그 예시
        String htmlText = "<h2>HTML Image Example</h2>"
                + "<p>This is an image: <img src=\"https://via.placeholder.com/300\"></p>";

        // Html.ImageGetter를 사용하여 이미지를 로드
        Spanned spannedText = Html.fromHtml(htmlText, new Html.ImageGetter() {
            @Override
            public Drawable getDrawable(String source) {
                UrlDrawable urlDrawable = new UrlDrawable();
                // 비동기로 이미지를 로드
                loadImageAsync(source, urlDrawable);
                return urlDrawable;
            }
        }, null);

        // TextView에 HTML 텍스트 설정
        textView.setText(spannedText);
    }

    // Drawable을 반환하기 위한 클래스
    private class UrlDrawable extends Drawable {
        private Drawable drawable;

        @Override
        public void draw(android.graphics.Canvas canvas) {
            if (drawable != null) {
                drawable.draw(canvas); // 이미지를 그리기
            }
        }

        @Override
        public void setAlpha(int alpha) {
            if (drawable != null) {
                drawable.setAlpha(alpha); // 알파 값 설정
            }
        }

        @Override
        public void setColorFilter(android.graphics.ColorFilter colorFilter) {
            if (drawable != null) {
                drawable.setColorFilter(colorFilter); // 색 필터 설정
            }
        }

        @Override
        public int getOpacity() {
            return android.graphics.PixelFormat.TRANSLUCENT;
        }

        // Drawable 설정 (크기 인자를 추가)
        public void setDrawable(Drawable drawable, int width, int height) {
            this.drawable = drawable;
            // 이미지의 크기를 설정 (사용자가 지정한 크기)
            drawable.setBounds(0, 0, width, height);
            invalidateSelf(); // Drawable이 갱신되었음을 알림
        }
    }

    // 비동기로 이미지를 로드하는 메서드
    private void loadImageAsync(final String source, final UrlDrawable urlDrawable) {
        executorService.submit(() -> {
            try {
                // URL로부터 이미지를 가져옴
                InputStream inputStream = (InputStream) new URL(source).getContent();
                Drawable drawable = Drawable.createFromStream(inputStream, "src");

                // drawable이 null인지 확인
                if (drawable != null) {
                    // 원하는 이미지 크기 설정 (예: 300x300)
                    int width = 300;  // 너비
                    int height = 300; // 높이

                    // 메인 스레드에서 UI 업데이트
                    runOnUiThread(() -> {
                        urlDrawable.setDrawable(drawable, width, height); // 크기 지정 후 설정
                        textView.invalidate();  // TextView 갱신
                    });
                } else {
                    // drawable이 null일 경우 로그 출력
                    Log.e("ImageLoad", "Failed to load image: drawable is null.");
                }

            } catch (Exception e) {
                e.printStackTrace();
                Log.e("ImageLoad", "Error loading image: " + e.getMessage());
            }
        });
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 앱 종료 시 ExecutorService를 종료
        if (executorService != null && !executorService.isShutdown()) {
            executorService.shutdown();
        }
    }
}

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:padding="16dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

시연화면