Użyj Picassa, aby uzyskać oddzwonienie za pomocą mapy bitowej

83

Używam Picassa do pobierania obrazów do mojej aplikacji.

Jestem w sytuacji, w której muszę uzyskać dostęp do Bitmappierwszego, zanim zostanie załadowany do ImageView. Obecność Downloader.Responseklasy wydaje się sugerować, że jest to możliwe, ale nie mogę znaleźć żadnych przykładów użycia. Nie chcę pisać więcej kodu, aby asynchronicznie obsłużyć ten jeden konkretny przypadek, jeśli można to zrobić z Picassem.

Czy ktoś może mi pokazać, jak to zrobić?

Steve M.
źródło

Odpowiedzi:

173

Znalazłem odpowiedź na githubie na wypadek, gdyby ktoś się zastanawiał:

private Target target = new Target() {
      @Override
      public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
      }

      @Override
      public void onBitmapFailed(Drawable errorDrawable) {
      }

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

private void someMethod() {
   Picasso.with(this).load("url").into(target);
}

@Override 
public void onDestroy() {  // could be in onPause or onStop
   Picasso.with(this).cancelRequest(target);
   super.onDestroy();
}

Post zaleca, aby nie używać anonimowego wywołania zwrotnego, a zamiast tego używać zmiennej instancji dla celu.

Steve M.
źródło
30
Idealnie byłoby zaimplementować bezpośrednio Targetna widoku lub obiekcie posiadacza widoku. Jeśli tego nie robisz, musisz utrzymywać silne odniesienie do instancji w innym miejscu, w przeciwnym razie zostanie ona usunięta.
Jake Wharton
@JakeWharton: jeśli chcę zastosować animację niestandardową na każdej pozycji spośród A ListView, to proponuję zrobić coś takiego: private static ViewHolder { private ImageView imageView; private Target target = new Target() { public void onBitmapLoaded() { // do animation on imageView } } }?
mbmc
@JakeWharton wyjaśnia to tutaj github.com/square/picasso/issues/308 w ostatnim komentarzu.
toobsco42
8
onBitmapLoaded nie wywołuje po raz pierwszy po onPrepareLoad
Amit Thaper
Dzięki za przykład. Brakuje mi tylko super wezwania do onDestroy.
Ben Groot
71

wzięte stąd :

Picasso.with(this)
    .load(url)
    .into(new Target() {
        @Override
        public void onBitmapLoaded (final Bitmap bitmap, Picasso.LoadedFrom from){
            /* Save the bitmap or do something with it here */

            //Set it in the ImageView
            theView.setImageBitmap(bitmap); 
        }
});

Zaktualizowano (4 maja 2016 r.):

            Picasso.with(this)
                .load(youUrl)
                .into(new Target() {
                    @Override
                    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {

                    }

                    @Override
                    public void onBitmapFailed(Drawable errorDrawable) {

                    }

                    @Override
                    public void onPrepareLoad(Drawable placeHolderDrawable) {

                    }
                });

Zaktualizowano (22 listopada 2016 r.)

lub używając silnego odniesienia dla, Targetaby nie było zbierane jako śmieci

Target target = new Target() {
            @Override
            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {

            }

            @Override
            public void onBitmapFailed(Drawable errorDrawable) {

            }

            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {

            }
        };


void foo() {
        Picasso.with(getContext()).load(getUrl()).into(target);
}

Kotlin

object: com.squareup.picasso.Target {
                  override fun onBitmapFailed(e: java.lang.Exception?, errorDrawable: Drawable?) {
                    TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
                }

                  override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
                    TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
                  }

                  override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {

                  }
                }
AbdulMomen عبدالمؤمن
źródło
6
anonimowy cel może być zbierany jako śmieci
Steve M
@SteveM A co by to zrobiło? Anulować prośbę? Zapobiegać ładowaniu bitmapy?
nurettin
@nurettin Zakładam, że Picasso (lub Glide) ma jakieś słabe odniesienie do celu. Więc jeśli jest anonimowy, nie ma twardego odniesienia i jest podatny na GC. Kiedy Picasso sprawdza odniesienie, będzie ono zerowe, więc wywołanie zwrotne nie zostanie wywołane.
Steve M
Więc to rozwiązanie jest naprawdę złe, ponieważ GC może czasami działać między ładowaniem a wywołaniem wywołania zwrotnego i spowodować, że callback nie zostanie wywołany.
Steve M
@SteveM Hostuję aplikację w Google Play, która ładuje kilka ikon podczas przewijania swoich widoków list z co najmniej 2000 użytkownikami w każdym przypadku, skalując niektóre ikony za pomocą tej metody, ale nie widziałem żadnej skargi, komentarza ( Dostaję dużo) lub raport awarii o nie ładowaniu ikon. Więc przynajmniej anegdotycznie, z jakiegoś powodu nie zbiera się śmieci.
nurettin
7

Co może być łatwiejsze niż następne:

val url: String = "https://...."
val bitmap: Bitmap = Picasso.with(context).load(url).get()

Powinien być wywoływany nie z głównego wątku!

lub z RxJava 2:

fun getBitmapSingle(picasso: Picasso, url: String): Single<Bitmap> = Single.create {
    try {
        if (!it.isDisposed) {
            val bitmap: Bitmap = picasso.load(url).get()
            it.onSuccess(bitmap)
        }
    } catch (e: Throwable) {
        it.onError(e)
    }
}

Pobierz mapę bitową:

getBitmapSingle(Picasso.with(context), "https:/...")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ bitmap ->
                // val drawable = BitmapDrawable(context, bitmap)
                }, Throwable::printStackTrace)

Użyłem Picassa 2.5.2

Oleg Tarashkevich
źródło
2

Pomyślałem, że może niektórzy z was chcieliby mieć wersję RxJava powyższej odpowiedzi ... Oto ona:

    public static Observable<Bitmap> loadBitmap(Picasso picasso, String imageUrl) {
    return Observable.create(new Observable.OnSubscribe<Bitmap>() {
        @Override
        public void call(Subscriber<? super Bitmap> subscriber) {
            Target target = new Target() {
                @Override
                public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                    subscriber.onNext(bitmap);
                    subscriber.onCompleted();
                }

                @Override
                public void onBitmapFailed(Drawable errorDrawable) {
                    subscriber.onError(new Exception("failed to load " + imageUrl));
                }

                @Override
                public void onPrepareLoad(Drawable placeHolderDrawable) {
                }
            };
            subscriber.add(new Subscription() {
                private boolean unSubscribed;

                @Override
                public void unsubscribe() {
                    picasso.cancelRequest(target);
                    unSubscribed = true;
                }

                @Override
                public boolean isUnsubscribed() {
                    return unSubscribed;
                }
            });
            picasso.load(imageUrl).into(target);
        }
    });
}

PS Podczas subskrybowania przechowuj odniesienie do subskrypcji na temat swojej aktywności, w przeciwnym razie cel zostanie GC przed otrzymaniem odpowiedzi ...

Sergey Aldoukhov
źródło
Wygląda na to, że ten kod nie działa teraz z najnowszym RxAndroid
Pavandroid