rxjava handle callback in observable - android

I have a simple recyclerview that contains a custom ImageGLSurfaceView in which it shows a bitmap. If i had a simple imageview i would have used (Picasso or glide etc) for this easily. But i have ImageGLSurfaceView so i am trying to do is to mimic ImageLoader lib class. So i have ImageLoader class that has loadImage method which i call from recycler view's adapter and i cache/store ImageGLSurfaceView object in hashmap and handle it like below:
public void loadImage(final FilteredImage filteredImage, final ImageGLSurfaceView imageView) {
if(images.containsKey(filteredImage)) {
images.get(filteredImage).setImageBitmap(filteredImage.getBitmap());
images.get(filteredImage).setFilterWithConfig(filteredImage.getFilter());
} else {
images.put(filteredImage, imageView);
imageView.setSurfaceCreatedCallback(new ImageGLSurfaceView.OnSurfaceCreatedCallback() {
#Override
public void surfaceCreated() {
imageView.setImageBitmap(filteredImage.getBitmap());
imageView.setFilterWithConfig(filteredImage.getFilter());
}
});
}
}
But list is slow or i think it is recreating ImageGLSurfaceView object again on list scroll. Or maybe i should put surfaceCreatedCallback on background thread. So i decided to use RxJava for this purpose. I want to subscribe for ImageGLSurfaceView to be created and once it is created then set Image bitmap and dont create ImageGLSurfaceView again. Please guide me if this is the right approach to achieve this? Or should i completely change the code?

Related

How to listen for Android View transformation change?

I am creating a custom View and I would like to listen for the transformation changes. For example, the ones triggered by View#setScaleX. One way to do it is overriding all the methods:
setTranslationX
setTranslationY
setTranslationZ
setElevation
setRotation
setRotationX
setRotationY
setScaleX
setScaleY
setPivotX
setPivotY
setCameraDistance
setAnimationMatrix
Am I missing anything? I don't care for the top/left/bottom/right properties so they are left out intentionally. However this is cumbersome. It would be better if I can just get a callback and listen for it. Is that possible?
//Make some kind of callback
public interface TransformationCallback{
//String whatWho is an example it can be anything.
void onTransform(String whatWho);
}
public class YourView extends View{
private TransformationCallback callback;
//Pass an interface into the View constructor
public YourView(Context context, TransformationCallback callback){
super(context);
this.callback = callback;
}
}
#Override
public void setTranslationX(float x){
//call onTransform from the callback
callback.onTransform("setTranslationX was called");
super.setTranslationX(x);
}
The only problem with this, is it will not detect internal changes to the underlining values that these functions "set".
For example there is a variable inside View called protected int mLeft; Which is modified multiple times, internally not using functions.
The variable is also protected meaning abstractions of View can also modify it without function calls.
For the most part only external classes that mess with Views will use those functions which may or may not effect you.

Fresco image loading callback

I have just migrated to the Fresco library for loading images in my app.
I need to listen to Image Loading Events, of course I read this article in documentation Listening to download events
This is exactly what I need, but....
There few things that I don't like.
My goal is to hide View if it fails to download it from the net.
I cannot reference SimpleDraweeView from controller, even on callback method. I need to hide View, but it seems that I cannot get reference to it.
Each time I need to load image, I need to create object of controller using Builder, and this can cause performance issues when using this approach with list of a lot of items with images.
holder.simpleDraweeViewImage.setController(Fresco.newDraweeControllerBuilder()
.setControllerListener(controllerListener)
.setUri(currentItem.getImage())
.build());
I need to able to have reference to the SimpleDraweeView from controller, and in MVC pattern approach it seems okay if controller is aware about view.
Please suggest the best way to rich my goal.
Thanks.
Can hide on onFailure method:
ControllerListener listener = new BaseControllerListener<ImageInfo>() {
#Override
public void onFinalImageSet(String id, #Nullable ImageInfo imageInfo, #Nullable Animatable animatable) {
//Action on final image load
}
#Override
public void onFailure(String id, Throwable throwable) {
//Action on failure
}
};
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(uri)
.setControllerListener(listener)
.build();
draweeView.setController(controller);
Regarding 1, perhaps you can do something like this:
class ControllerListenerWithView() extends BaseControllerListener {
private final WeakReference<View> mViewReference;
ControllerListenerWithView(View view) {
mViewReference = new WeakReference<>(view);
}
#Nullable
protected View getView() {
return mViewReference.get();
}
}
Then:
ControllerListener controllerListener = new ControllerListenerWithView(holder.simpleDraweeViewImage) {
#Override
public void onFailure(String id, Throwable throwable) {
View view = getView();
if (view != null) {
view.setVisibility(View.GONE);
}
}
};
If you don't have the view accessible at the listener creation time, instead of passing view via listener constructor, you can add a setter method and do:
controllerListener.setView(holder.simpleDraweeViewImage);
controller = ...
holder.simpleDraweeViewImage.setController(controller);
If this looks ugly to you, well, that's because it is ugly :) Design that involves circular references is just ugly. DraweeController doesn't have a reference to the view (not directly at least). DraweeController references a DraweeHierarchy which references Drawables and the top-level drawable has a WeakReference to the parent view in order to propagate Drawable.Callback events. But that's it. DraweeController doesn't need view and we can't/won't keep reference to the view in it. The reason for that is that DraweeControllers and DraweeHierarchies can be used in contexts other than View and it is unnecessary for controller to have a back reference to the view. DraweeController controls DraweeHierarchy, not the view.
Regarding 2, while building controller, you can specify setOldController(view.getController()). That way the old controller which you are replacing will be reused while building a new one. This saves allocations and helps scroll-perf.

Android + Volley: how to get image bitmap using ImageLoader?

I'm trying to get images for list view using Volley Library. I created simple HTTP helper with the following method.
/**
* Processing Image request and gets the image with given URL
*/
public Bitmap makeImageRequest(String url) {
ImageLoader il = new ImageLoader(queue, new BitmapLruCache());
il.get(url, new ImageLoader.ImageListener() {
#Override
public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
mBitmap = processImageResponse(response);
}
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(Constants.Global.ERROR, "Error: " + error.getMessage());
mBitmap = null;
}
});
return mBitmap;
}
But problem is that:
new BitmapLruCache()
Method is not recognized.
So i tried to create ImageLoader using the following code which i found on the URL:
http://www.androidhive.info/2014/05/android-working-with-volley-library-1/
ImageLoader imageLoader = AppController.getInstance().getImageLoader();
But in thos code i cannot find out where to get
AppController
Because the method code is triggered from the custom
public class HttpHelperClass
And called from the activity using the:
// Try to load remote image from URL
Bitmap bm = http.makeImageRequest("http://camranger.com/wp-content/uploads/2014/10/Android-Icon.png");
ImageView iv = (ImageView) findViewById(R.id.imageView);
iv.setImageBitmap(bm);
Is it the right approach how to load images and how can i repair my code to make succcessfull request?
Many thanks for any advice.
I think your makeImageRequest method will always return null because it takes some time for the onResponse or onErrorResponse listeners to get called but you return the mBitmap immidately!
If you want to use Volley's ImageLoader you'd better get image inside your activity or ... not from another class like your HttpHelperClass.
Also AppController is a class that extends Application and you should create it yourself. (It's in your AndroidHive link. Section 3. Creating Volley Singleton class)
And also for caching images you should not create a new ImageLoader everytime because in this way the caching gets meaningless. You should get that from your AppController class.
Furthermore I suggest you using Picasso because it's way better that Volley in image loading and a lot easier!
With Picasso you only need to call the following line to load an image from web into an ImageView:
Picasso.with(context).load(urlString).to(imageView);

Picasso requests with respect to success and error count does not match

Below is sample code. The number of times the Picasso gets called is more than the number of times it reaches Success or OnError(). How can I deal with the ProgressBar in this situation? This happens , since i am invoking this method from the ArrayAdapter.
How can I get and print the reason for OnError in picasso loading.
/**
* To get the Customized Picasso with preset placeholders, CallBack for progressBar, sqSize on req
*/
public static Picasso getAuthorizedPicasso(final ImageView imgView, final String url, final int sqSize) {
EventBus.post(ToggleProgressBar.requestStarted());
final Picasso picasso = CustomPicasso.getInstance();
picasso.load(url)
.resize(sqSize, sqSize)
.error(R.drawable.error)
.placeholder(R.drawable.placeholder).noFade().into(imgView, new Callback() {
#Override
public void onSuccess() {
EventBus.post(ToggleProgressBar.requestFinished());
}
#Override
public void onError() {
EventBus.post(ToggleProgressBar.requestFinished());
}
});
return picasso;
}
Found the issue. The ListView ArrayAdapter gets called often. Hence updated the ListView with match_parent or fill_parent avoids calling the Picasso multiple times. This solved the infinitely running ProgressBar with Picasso issue as well.
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
PS: Please voteup if you find this helpful.Thanks

Use of Target in Picasso on Adapter

Im having big troubles using a Target inside an adapter. Im confused about the documentation on the code
Objects implementing this class must have a working implementation of
{#link #equals(Object)} and {#link #hashCode()} for proper storage internally. Instances of this
interface will also be compared to determine if view recycling is occurring. It is recommended
that you add this interface directly on to a custom view type when using in an adapter to ensure
correct recycling behavior.
Im trying to use the Target in this way:
class CustomTarget implements Target {
private ImageView imageView;
public CustomTarget(ImageView imageView) {
this.imageView = imageView;
}
#Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
imageView.setImageDrawable(new RoundedAvatarDrawable(bitmap));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
imageView.setImageDrawable(errorDrawable);
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
imageView.setImageDrawable(placeHolderDrawable);
}
#Override
public boolean equals(Object o) {
return imageView.equals(o);
}
#Override
public int hashCode() {
return imageView.hashCode();
}
}
#Override
public View getView(int position, View v, ViewGroup parent) {
....
RoundedAvatarDrawable r = new RoundedAvatarDrawable(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_avatar_seahorse));
ImageCacheController.with(mContext).getPicasso().load(member.getPicture_url()).resize(100, 100).centerCrop().placeholder(r).error(r).into(new CustomTarget(viewHolder.ivAvatar));
....
}
It's doesn't work and the images change between each others randomly
You don't show your whole getView function, so without knowing how you use the viewHandler, here's my take on what's going on:
Your problem is that you're creating a new CustomTarget every time getView gets called. You are going against the point of having a Target object. Let me elaborate.
When a new download request is made, previous requests to the same target get stopped or don't result in a call to the Target's callbacks. (so if the Target gets reused for a different row in a list it doesn't get both rows' images).
You are using a new object for each request, effectively hinting Picasso that each request is for a different row so to speak. The doc says "Instances of this interface will also be compared to determine if view recycling is occurring", so since each request has a newly created CustomTarget object, no two requests will have the same object and a row recycle won't be detected.
You're also using viewHolder. In this case I think the viewHolder should be extending the Target interface (if you only have 1 image per row). This way everytime you request a download you can use the same object and not create a new one.
You're also delegating the implementation of your CustomTarget to the ImageView's implementation. Make sure that ImageView's equals and hashCode functions fullfill the requirements Picasso asks for.
Some info on how to implement equals and hashCode: What issues should be considered when overriding equals and hashCode in Java?
It seems your equals method is broken. You are comparing an imageview to a custom target. This might fix it:
public boolean equals(Object o) {
if(o instanceof CustomTarget) {
return ((CustomTarget) o).imageView.equals(this.imageView);
}
return super.equals(o);
}

Categories

Resources