Sauron
Sauron

Reputation: 6647

Using Picasso with Image Getter

I ma building a chat application and I am attempting to append an image to an EditText, through use of Picasso to get the image from a URL and the append and ImageGetter to attach the image to the EditText. However, what I have implemented below does not work, as appending messages when using the app displays nothing (but the message does show up in the database).

I have tested without using Picasso, as simply just using the ImageGetter with an image resource within the app works just fine, only it's not from a URL as is required.

What is the proper way to configure the ImageGetter and/or the append method so that this functionality will work with Picasso? Or is there a simpler way?

Append method:

public void appendToMessageHistory(final String username,
            final String message) {
        if (username != null && message != null) {

            Picasso.with(getBaseContext())
                    .load("http://localhost:3000/uploads/campaign/image/2/2.jpg")
                    .into(new Target() {

                        @Override
                        public void onPrepareLoad(Drawable arg0) {

                        }

                        @Override
                        public void onBitmapLoaded(Bitmap bitmap,
                                LoadedFrom arg1) {
                            Drawable drawImage = new BitmapDrawable(
                                    getBaseContext().getResources(), bitmap);

                            drawImage.setBounds(0, 0,
                                    drawImage.getIntrinsicHeight(),
                                    drawImage.getIntrinsicWidth());
                            messageHistoryText.append(Html.fromHtml("<b>"
                                    + username + ":" + "</b>" + "<br>"));
                            messageHistoryText.append(Html.fromHtml(message
                                    + "<hr>" + "<br>")
                                    + System.getProperty("line.separator") + "");

                            messageHistoryText.append(Html
                                    .fromHtml("<img src = '" + drawImage
                                            + "'/>",
                            imageGetter,
                            null));
                        }

                        @Override
                        public void onBitmapFailed(Drawable arg0) {

                        }
                    });

        }
    }

ImageGetter:

ImageGetter imageGetter = new ImageGetter() {
        Drawable imageUsed=null;

        @Override
        public Drawable getDrawable(String source) {

            Picasso.with(getBaseContext())
                    .load("http://localhost:3000/uploads/campaign/image/2/2.jpg")
                    .into(new Target() {

                        @Override
                        public void onPrepareLoad(Drawable arg0) {

                        }

                        @Override
                        public void onBitmapLoaded(Bitmap bitmap,
                                LoadedFrom arg1) {
                            Drawable drawImage = new BitmapDrawable(
                                    getBaseContext().getResources(), bitmap);

                            drawImage.setBounds(0, 0,
                                    drawImage.getIntrinsicHeight(),
                                    drawImage.getIntrinsicWidth());

                            imageUsed=drawImage;

                        }

                        @Override
                        public void onBitmapFailed(Drawable arg0) {

                        }
                    });

            return imageUsed;
        }

    };

Upvotes: 8

Views: 6664

Answers (4)

Hitesh Bisht
Hitesh Bisht

Reputation: 536

Another simple way to use Picasso is to create a custom TextView and make it implement Target interface of Picasso. Then you can do something as simple as this.

        Picasso.get().load("Some_img_url").placeholder(R.drawable.ic_launcher_foreground).into(tv)

Below is the class

class TextViewWithImageLoader @JvmOverloads constructor(

context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr), Target {
enum class Direction { START, END, TOP, BOTTOM }

private var mWidthInPixels: Int = 0
private var mHeightInPixels: Int = 0
private var mDirection: Direction = Direction.START //default value

// This method initialize the required parameters for the TextView to load the image
fun setupImageLoader(widthInPixels: Int, heightInPixels: Int, direction: Direction) {
    mWidthInPixels = widthInPixels
    mHeightInPixels = heightInPixels
    mDirection = direction
}

// sets the size of the drawable
private fun setDrawableBounds(drawable: Drawable) {
    drawable.setBounds(0, 0, mWidthInPixels, mHeightInPixels)
}

// Sets the initial placeholder drawable
override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
    //checking if height and width are valid
    if (placeHolderDrawable != null && mWidthInPixels > 0 && mHeightInPixels > 0) {
        setDrawableBounds(placeHolderDrawable)
        setDrawable(placeHolderDrawable)
    }
}

// set the drawable based on the Direction enum
private fun setDrawable(placeHolderDrawable: Drawable?) {
    when (mDirection) {
        Direction.START -> setCompoundDrawables(placeHolderDrawable, null, null, null);
        Direction.END -> setCompoundDrawables(null, null, placeHolderDrawable, null);
        Direction.TOP -> setCompoundDrawables(null, placeHolderDrawable, null, null);
        Direction.BOTTOM -> setCompoundDrawables(null, null, null, placeHolderDrawable);
    }
}

//In this method we receive the image from the url
override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
    //checking if height and width are valid
    if (mWidthInPixels > 0 && mHeightInPixels > 0) {
        val drawable = BitmapDrawable(resources, bitmap)
        setDrawableBounds(drawable)
        setDrawable(drawable)
    }
}

override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
    //Do nothing as we are already setting a default value in onPrepareLoad method
    // you can add your logic here if required
 }
}

You can use this class directly or if you currently have your own custom view then just extend this class.

You can either go through this medium article for more details

OR

You can see the sample project on github

Upvotes: 1

Droid Teahouse
Droid Teahouse

Reputation: 1013

I found a way to use ImageGetter with Picasso Target:

    public Drawable getDrawable(String source) {

            final BitmapDrawablePlaceHolder result = new BitmapDrawablePlaceHolder();

            Picasso.with(getContext()).load(source).into(new Target() {
                @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                    final BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(), bitmap);

                    drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());


                    result.setDrawable(drawable);
                    result.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());

                    content.setText(content.getText());




                    // cache is now warmed up
                }
                @Override public void onBitmapFailed(Drawable errorDrawable) { }
                @Override public void onPrepareLoad(Drawable placeHolderDrawable) { }
            });
            return result;

}

This uses the cache and is no longer a synchronous call requiring the AsyncTask.

Credit to: Fetch images with Callback in Picasso?

Upvotes: 1

Ufkoku
Ufkoku

Reputation: 2638

I built upon Thomas' answer. I used the existing Picasso methods to download any images on a different thread and accounted for the placeholder Drawables as well.

public class PicassoImageGetter implements Html.ImageGetter {
    private TextView textView = null;

    public PicassoImageGetter() {}

    public PicassoImageGetter(TextView target) {
        textView = target;
    }

    @Override
    public Drawable getDrawable(String source) {
        BitmapDrawablePlaceHolder drawable = new BitmapDrawablePlaceHolder();

        Context context = FeedSurferApp.getContext();
        FeedSurferApp
                .getPicasso()
                .load(source)
                .error(ResourcesCompat.getDrawable(context.getResources(), R.drawable.connection_error, context.getTheme()))
                .into(drawable);

        return drawable;
    }

    private class BitmapDrawablePlaceHolder extends BitmapDrawable implements Target {
        protected Drawable drawable;

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

        public void setDrawable(Drawable drawable) {
            this.drawable = drawable;
            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
            setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
            if (textView != null) {
                textView.setText(textView.getText());
            }
        }

        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
            setDrawable(new BitmapDrawable(FeedSurferApp.getContext().getResources(), bitmap));
        }

        @Override
        public void onBitmapFailed(Drawable errorDrawable) {
            setDrawable(errorDrawable);
        }

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {}
    }
}

Upvotes: 6

Thomas Moerman
Thomas Moerman

Reputation: 902

I couldn't get it to work with using Picasso's Target...

My workaround is:

  • use an AsyncTask for concurrency
  • use Picasso's get() method to load the image synchronously in the AsyncTask's background method

Like this:

public class PicassoImageGetter implements Html.ImageGetter {

final Resources resources;

final Picasso pablo;

final TextView textView;

public PicassoImageGetter(final TextView textView, final Resources resources, final Picasso pablo) {
    this.textView  = textView;
    this.resources = resources;
    this.pablo     = pablo;
}

@Override public Drawable getDrawable(final String source) {
    final BitmapDrawablePlaceHolder result = new BitmapDrawablePlaceHolder();

    new AsyncTask<Void, Void, Bitmap>() {

        @Override
        protected Bitmap doInBackground(final Void... meh) {
            try {
                return pablo.load(source).get();
            } catch (Exception e) {
                return null;
            }
        }

        @Override
        protected void onPostExecute(final Bitmap bitmap) {
            try {
                final BitmapDrawable drawable = new BitmapDrawable(resources, bitmap);

                drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());

                result.setDrawable(drawable);
                result.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());

                textView.setText(textView.getText()); // invalidate() doesn't work correctly...
            } catch (Exception e) {
                /* nom nom nom*/
            }
        }

    }.execute((Void) null);

    return result;
}

static class BitmapDrawablePlaceHolder extends BitmapDrawable {

    protected Drawable drawable;

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

    public void setDrawable(Drawable drawable) {
        this.drawable = drawable;
    }

}

Hope this is useful.

Upvotes: 24

Related Questions