Glide: Add Glide Html.ImageGetter to the core library

Created on 24 Sep 2018  路  6Comments  路  Source: bumptech/glide

Using TextViews as simple HTML containers that display decorated text together with images is quite popular these days as an alternative to heavy-weight WebView. To properly display images in a TextView, one needs an implementation of Html.ImageGetter. While there is an example Glide app with such implementation, it's aimed at Glide 3, and I think such GlideImageGetter deserves being included in the lib (it's small and useful).

Here's my implementation for Glide 4.8 loosely based on others that work with other image libraries. Could be buggy, but may serve as a good starting point:

```package com.example.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Html;
import android.widget.TextView;

import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.target.SizeReadyCallback;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition;

public class GlideImageGetter implements Html.ImageGetter {

private Context context;
private TextView textView;

GlideImageGetter(Context context, TextView target) {
    this.context = context;
    textView = target;
}

@Override
public Drawable getDrawable(String url) {
    BitmapDrawablePlaceholder drawable = new BitmapDrawablePlaceholder();

    GlideApp.with(context)
            .asBitmap()
            .load(url)
            .into(drawable);

    return drawable;
}

private class BitmapDrawablePlaceholder extends BitmapDrawable implements Target<Bitmap> {

    protected Drawable drawable;

    BitmapDrawablePlaceholder() {
        super(context.getResources(), Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888));
    }

    @Override
    public void draw(final Canvas canvas) {
        if (drawable != null) {
            drawable.draw(canvas);
        }
    }

    private void setDrawable(Drawable drawable) {
        this.drawable = drawable;
        int drawableWidth = drawable.getIntrinsicWidth();
        int drawableHeight = drawable.getIntrinsicHeight();
        int maxWidth = textView.getMeasuredWidth();
        if (drawableWidth > maxWidth) {
            int calculatedHeight = maxWidth * drawableHeight / drawableWidth;
            drawable.setBounds(0, 0, maxWidth, calculatedHeight);
            setBounds(0, 0, maxWidth, calculatedHeight);
        } else {
            drawable.setBounds(0, 0, drawableWidth, drawableHeight);
            setBounds(0, 0, drawableWidth, drawableHeight);
        }

        textView.setText(textView.getText());
    }

    @Override
    public void onLoadStarted(@Nullable Drawable placeholderDrawable) {
        if(placeholderDrawable != null) {
            setDrawable(placeholderDrawable);
        }
    }

    @Override
    public void onLoadFailed(@Nullable Drawable errorDrawable) {
        if (errorDrawable != null) {
            setDrawable(errorDrawable);
        }
    }

    @Override
    public void onResourceReady(@NonNull Bitmap bitmap, @Nullable Transition<? super Bitmap> transition) {
        setDrawable(new BitmapDrawable(context.getResources(), bitmap));
    }

    @Override
    public void onLoadCleared(@Nullable Drawable placeholderDrawable) {
        if(placeholderDrawable != null) {
            setDrawable(placeholderDrawable);
        }
    }

    @Override
    public void getSize(@NonNull SizeReadyCallback cb) {
        textView.post(() -> cb.onSizeReady(textView.getWidth(), textView.getHeight()));
    }

    @Override
    public void removeCallback(@NonNull SizeReadyCallback cb) {}

    @Override
    public void setRequest(@Nullable Request request) {}

    @Nullable
    @Override
    public Request getRequest() {
        return null;
    }

    @Override
    public void onStart() {}

    @Override
    public void onStop() {}

    @Override
    public void onDestroy() {}

}

}

stale

Most helpful comment

First Thanks to @yrajabi
I have modified his code in Kotlin, here is my version: https://gist.github.com/iamriajul/e0026c1c8e1783e6ffa2da511d1d4f37

This might help others.
Thanks...

All 6 comments

This issue has been automatically marked as stale because it has not had activity in the last seven days. It will be closed if no further activity occurs within the next seven days. Thank you for your contributions.

Using TextViews as simple HTML containers that display decorated text together with images is quite popular these days as an alternative to heavy-weight WebView. To properly display images in a TextView, one needs an implementation of Html.ImageGetter. While there is an example Glide app with such implementation, it's aimed at Glide 3, and I think such GlideImageGetter deserves being included in the lib (it's small and useful).

Here's my implementation for Glide 4.8 loosely based on others that work with other image libraries. Could be buggy, but may serve as a good starting point:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Html;
import android.widget.TextView;

import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.target.SizeReadyCallback;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition;

public class GlideImageGetter implements Html.ImageGetter {

    private Context context;
    private TextView textView;

    GlideImageGetter(Context context, TextView target) {
        this.context = context;
        textView = target;
    }

    @Override
    public Drawable getDrawable(String url) {
        BitmapDrawablePlaceholder drawable = new BitmapDrawablePlaceholder();

        GlideApp.with(context)
                .asBitmap()
                .load(url)
                .into(drawable);

        return drawable;
    }

    private class BitmapDrawablePlaceholder extends BitmapDrawable implements Target<Bitmap> {

        protected Drawable drawable;

        BitmapDrawablePlaceholder() {
            super(context.getResources(), Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888));
        }

        @Override
        public void draw(final Canvas canvas) {
            if (drawable != null) {
                drawable.draw(canvas);
            }
        }

        private void setDrawable(Drawable drawable) {
            this.drawable = drawable;
            int drawableWidth = drawable.getIntrinsicWidth();
            int drawableHeight = drawable.getIntrinsicHeight();
            int maxWidth = textView.getMeasuredWidth();
            if (drawableWidth > maxWidth) {
                int calculatedHeight = maxWidth * drawableHeight / drawableWidth;
                drawable.setBounds(0, 0, maxWidth, calculatedHeight);
                setBounds(0, 0, maxWidth, calculatedHeight);
            } else {
                drawable.setBounds(0, 0, drawableWidth, drawableHeight);
                setBounds(0, 0, drawableWidth, drawableHeight);
            }

            textView.setText(textView.getText());
        }

        @Override
        public void onLoadStarted(@Nullable Drawable placeholderDrawable) {
            if(placeholderDrawable != null) {
                setDrawable(placeholderDrawable);
            }
        }

        @Override
        public void onLoadFailed(@Nullable Drawable errorDrawable) {
            if (errorDrawable != null) {
                setDrawable(errorDrawable);
            }
        }

        @Override
        public void onResourceReady(@NonNull Bitmap bitmap, @Nullable Transition<? super Bitmap> transition) {
            setDrawable(new BitmapDrawable(context.getResources(), bitmap));
        }

        @Override
        public void onLoadCleared(@Nullable Drawable placeholderDrawable) {
            if(placeholderDrawable != null) {
                setDrawable(placeholderDrawable);
            }
        }

        @Override
        public void getSize(@NonNull SizeReadyCallback cb) {
            textView.post(() -> cb.onSizeReady(textView.getWidth(), textView.getHeight()));
        }

        @Override
        public void removeCallback(@NonNull SizeReadyCallback cb) {}

        @Override
        public void setRequest(@Nullable Request request) {}

        @Nullable
        @Override
        public Request getRequest() {
            return null;
        }

        @Override
        public void onStart() {}

        @Override
        public void onStop() {}

        @Override
        public void onDestroy() {}

    }
}

There will be a memory issue if you do it this way tho :/

Any updates on this? @AzimLord Can you elaborate on why this would be a memory issue? There does not appear to be a solid implementation I've found for Html.ImageGetter.

I have created a gist based on @ddekanski code with some changes and improvements:

  • Added weak reference to avoid context memory leak
  • Added parameter matchParentWidth to determine whether image should fit its parent or not
  • Added an inner class interface to optionally populate list of all image URIs loaded within this getter
  • Added density awareness to drawables (useful when they are loaded from assets or network)

https://gist.github.com/yrajabi/5776f4ade5695009f87ce7fcbc08078f
The above gist is tested with Glide 4.9 and 4.11 successfully.

First Thanks to @yrajabi
I have modified his code in Kotlin, here is my version: https://gist.github.com/iamriajul/e0026c1c8e1783e6ffa2da511d1d4f37

This might help others.
Thanks...

Was this page helpful?
0 / 5 - 0 ratings