Custom ImageView performance - android

I created a custom ImageView - it's purpose is to fetch image from internet. It's declaration looks as below:
public class WebImageView extends ImageView {
private String mUrl;
private Bitmap mCachedBitmap;
public String getUrl() { return mUrl; }
public void setUrl(String url) {
mUrl = url;
if (mCachedImage == null) {
new ImageDownloader(this).execute(mUrl);
} else {
setImageBitmap(mCachedImage);
}
}
public WebImageView(Context context) {
super(context);
}
public WebImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WebImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public WebImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
private class ImageDownloader extends AsyncTask<String, Void, Bitmap> {
private final ImageView mView;
public ImageDownloader(ImageView view) {
mView = view;
}
#Override
protected Bitmap doInBackground(String... params) {
String url = params[0];
Bitmap image = null;
try {
InputStream in = new java.net.URL(url).openStream();
image = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error Message", e.getMessage());
e.printStackTrace();
}
return image;
}
protected void onPostExecute(Bitmap result) {
mView.setImageBitmap(result);
}
}
}
And it's usage is pretty straightforward:
<com.myapp.views.controls.WebImageView
android:layout_width="#dimen/restaurantLogoWidth"
android:layout_height="#dimen/restaurantLogoHeight"
url="#{restaurant.model.logoUrl}"
style="#style/ImageView" />
The above xml is placed inside a android.support.v7.widget.RecyclerView. The problem is that when I scroll (or perform some animation) on my items list it performs horribly bad, meaning that scrolling (or animating) is not smooth. Any advice what can I change here to make it perform better?

Don't build a custom view to do this. Just use Glide image loading library.
https://github.com/bumptech/glide
ImageView targetImageView = (ImageView) findViewById(R.id.imageView);
String internetUrl = "http://i.imgur.com/DvpvklR.png";
Glide
.with(context)
.load(internetUrl)
.into(targetImageView);
https://futurestud.io/tutorials/glide-getting-started
Recyclerview Adapter and Glide - same image every 4-5 rows

Related

using glide to show animated gif file with ImageView (android)

I make gif Files animated then, I put this one in the Application's imageview.
GlideDrawableImageViewTarget imageViewTarget = new GlideDrawableImageViewTarget(character6);
Glide.with(this).load(R.raw.company_normal8).into(imageViewTarget);
here's code and I have no problem with apply this one.
But I have serious issue that The image have afterimage.
I made this gif file with photoshop and It have 2 frame
There's A frame and B frame.
But at android app,
A -> B -> A -> B (this is the normal one)
But My app
A -> A+B -> A -> A+b (like this afterimage)
You can use asGif
Glide.with(context)
.load(imgUrl)
.asGif()
.placeholder(R.drawable.img)
.crossFade()
.into(imageView);
OR
Try this if Glide is not working for you,Its working for me
public class GIFView extends View{
Movie movie;
long moviestart;
public GIFView(Context context) throws IOException {
super(context);
}
public GIFView(Context context, AttributeSet attrs) throws IOException{
super(context, attrs);
}
public GIFView(Context context, AttributeSet attrs, int defStyle) throws IOException {
super(context, attrs, defStyle);
}
public void loadGIFResource(Context context, int id)
{
//turn off hardware acceleration
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
InputStream is=context.getResources().openRawResource(id);
movie = Movie.decodeStream(is);
}
public void loadGIFAsset(Context context, String filename)
{
InputStream is;
try {
is = context.getResources().getAssets().open(filename);
movie = Movie.decodeStream(is);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (movie == null) {
return;
}
long now=android.os.SystemClock.uptimeMillis();
if (moviestart == 0) moviestart = now;
int relTime;
relTime = (int)((now - moviestart) % movie.duration());
movie.setTime(relTime);
movie.draw(canvas,10,10);
this.invalidate();
}
}
XML
<yourpackagename.GIFView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginBottom="50dp"
android:layout_marginTop="110dp"
android:src="#drawable/yourgifimage"/>
To set the image
imagevw.loadGIFResource(this, R.drawable.yourgifimage);

How to create custom imageview with image load listener in android?

I want to load images into gridView and want to show progress bar during loading and dismiss it when the loading finished.
How to create image load listener for the images without using third party libraries?
Suggest me any sample code for reference?
Create a common class CustomNetworkImageView
public class CustomNetworkImageView extends NetworkImageView {
private Bitmap mLocalBitmap;
private boolean mShowLocal;
public void setLocalImageBitmap(Bitmap bitmap) {
if (bitmap != null) {
mShowLocal = true;
}
this.mLocalBitmap = bitmap;
requestLayout();
}
#Override
public void setImageUrl(String url, ImageLoader imageLoader) {
mShowLocal = false;
super.setImageUrl(url, imageLoader);
}
public CustomNetworkImageView(Context context) {
this(context, null);
}
public CustomNetworkImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomNetworkImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mShowLocal) {
setImageBitmap(mLocalBitmap);
}
}
}
In your xml use like this
<urpackagename.CustomNetworkImageView
android:id="#+id/uploadImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:scaleType="fitCenter"
android:src="#drawable/urDrawable" />
In your Activity or fragment initialize customImage View
public CustomNetworkImageView userImage;
userImage = (CustomNetworkImageView)getActivity().findViewById(R.id.uploadImage);

Facebook Fresco using wrap_content

I got a bunch of drawables that I want to load using fresco, I want to use wrap_content size for those images, how can I do it in xml with fresco? Or if xml is not possible how do you do it in code?
<com.facebook.drawee.view.SimpleDraweeView
android:id="#+id/myImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fresco:placeholderImage="#mipmap/myImage"/>
The above code is not working unless I set a fixed size.
I am part of the Fresco team and I was the one who made the design decision to not support wrap-content. The rationale is explained in the documentation. But in short, the problem is that you can't guarantee that the image will be available immediately (you may need to fetch it first) and that means that the view size would have to change once the image arrives. This is in most cases not desirable and you should probably rethink your UI.
Anyways, if you really really need/want to do that, you can do it like this:
void updateViewSize(#Nullable ImageInfo imageInfo) {
if (imageInfo != null) {
draweeView.getLayoutParams().width = imageInfo.getWidth();
draweeView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight());
}
}
ControllerListener listener = new BaseControllerListener {
#Override
public void onIntermediateImageSet(String id, #Nullable ImageInfo imageInfo) {
updateViewSize(imageInfo);
}
#Override
public void onFinalImageSet(String id, #Nullable ImageInfo imageInfo, #Nullable Animatable animatable) {
updateViewSize(imageInfo);
}
};
DraweeController controller = draweeControllerBuilder
.setUri(uri)
.setControllerListener(listener)
.build();
draweeView.setController(controller);
I wrote this code from the top of my head, I haven't actually tested it. But the idea should be clear, and it should work with minor adjustments.
Based on #plamenko's answer, I made a custom view as follows:
/**
* Works when either height or width is set to wrap_content
* The view is resized based on the image fetched
*/
public class WrapContentDraweeView extends SimpleDraweeView {
// we set a listener and update the view's aspect ratio depending on the loaded image
private final ControllerListener listener = new BaseControllerListener<ImageInfo>() {
#Override
public void onIntermediateImageSet(String id, #Nullable ImageInfo imageInfo) {
updateViewSize(imageInfo);
}
#Override
public void onFinalImageSet(String id, #Nullable ImageInfo imageInfo, #Nullable Animatable animatable) {
updateViewSize(imageInfo);
}
};
public WrapContentDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
super(context, hierarchy);
}
public WrapContentDraweeView(Context context) {
super(context);
}
public WrapContentDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public void setImageURI(Uri uri, Object callerContext) {
DraweeController controller = ((PipelineDraweeControllerBuilder)getControllerBuilder())
.setControllerListener(listener)
.setCallerContext(callerContext)
.setUri(uri)
.setOldController(getController())
.build();
setController(controller);
}
void updateViewSize(#Nullable ImageInfo imageInfo) {
if (imageInfo != null) {
setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight());
}
}
}
You can include this class in the XML, an example usage:
<com.example.ui.views.WrapContentDraweeView
android:id="#+id/simple_drawee_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
In Kotlin you can try something like this :
val listener = object : BaseControllerListener<ImageInfo>() {
override fun onFinalImageSet(id: String?, imageInfo: ImageInfo?, animatable: Animatable?) {
super.onFinalImageSet(id, imageInfo, animatable)
itemView.draweeGif.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
itemView.draweeGif.aspectRatio = (imageInfo?.width?.toFloat() ?: 0.toFloat()) / (imageInfo?.height?.toFloat() ?: 0.toFloat())
}
}
val controller = Fresco.newDraweeControllerBuilder()
.setUri(uriGif)
.setControllerListener(listener)
.setAutoPlayAnimations(true)
.build()
itemView.draweeGif.controller = controller
For me it was a solution in my RecyclerView because I was looking to set the layoutParams directly in my ViewHolder.
Found a solution by extending SimpleDraweeView, it allows me to use wrap_content and it works just fine! How ever I prevent you from setting the size in setContentView and the preview is not working, I be glad if could edit this answer to fix those.
Usage
<com.gazman.WrapContentDraweeView
android:id="#+id/myImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fresco:placeholderImage="#mipmap/myImage"/>
Source code
public class WrapContentDraweeView extends SimpleDraweeView {
private int outWidth;
private int outHeight;
public WrapContentDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
super(context, hierarchy);
}
public WrapContentDraweeView(Context context) {
super(context);
}
public WrapContentDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
TypedArray gdhAttrs = context.obtainStyledAttributes(
attrs,
R.styleable.GenericDraweeView);
try {
int placeholderId = gdhAttrs.getResourceId(
R.styleable.GenericDraweeView_placeholderImage,
0);
if(placeholderId != 0){
if(isInEditMode()){
setImageResource(placeholderId);
}
else {
loadSize(placeholderId, context.getResources());
}
}
} finally {
gdhAttrs.recycle();
}
}
private void loadSize(int placeholderId, Resources resources) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources, placeholderId, options);
outWidth = options.outWidth;
outHeight = options.outHeight;
}
#Override
public void setLayoutParams(ViewGroup.LayoutParams params) {
params.width = outWidth;
params.height = outHeight;
super.setLayoutParams(params);
}
}
invoke updateWrapSize in onFinalImageSet
void updateWrapSize(#Nullable ImageInfo imageInfo) {
if (imageInfo != null) {
boolean wrapH = getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT;
boolean wrapW = getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT;
if (wrapH || wrapW) {
if (wrapW && !wrapH) {
getLayoutParams().width = (int) (imageInfo.getWidth() * (float) getLayoutParams().height / imageInfo.getHeight());
} else if (wrapH && !wrapW) {
getLayoutParams().height = (int) (imageInfo.getHeight() * (float) getLayoutParams().width / imageInfo.getWidth());
} else {
getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
}
setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight());
}
}
}

Volley's NetworkImageView - setImageBitmap method doesn't work

Im using volley library in my project.
I usually let the NetworkImageView download images using setImageUrl method:
networkImageView.setImageUrl(imageUrl, mImageLoader)
This works fine, But.. When I try to download the bitmap "manually" using ImageLoader's get method, and then set the bitmap by myself, it doesn't work:
mImageLoader.get(imageUrl,new ImageLoader.ImageListener()
{
#Override
public void onResponse(ImageLoader.ImageContainer imageContainer, boolean b)
{
if (imageContainer.getBitmap() != null)
{
networkImageView.setImageBitmap(imageContainer.getBitmap());
}
}
#Override
public void onErrorResponse(VolleyError volleyError)
{
}
});
networkImageView.setImageBitmap(imageContainer.getBitmap()) line does nothing.
How could it be?
Thanks in advance!
This version of NetworkImageView fixes this issue.
public class CustomNetworkImageView extends NetworkImageView {
private Bitmap mLocalBitmap;
private boolean mShowLocal;
public void setLocalImageBitmap(Bitmap bitmap) {
if (bitmap != null) {
mShowLocal = true;
}
this.mLocalBitmap = bitmap;
requestLayout();
}
#Override
public void setImageUrl(String url, ImageLoader imageLoader) {
mShowLocal = false;
super.setImageUrl(url, imageLoader);
}
public CustomNetworkImageView(Context context) {
this(context, null);
}
public CustomNetworkImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomNetworkImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mShowLocal) {
setImageBitmap(mLocalBitmap);
}
}
}
You can achieve that by simply adding several lines of code in the source code of NetWorkImageView(I suppose you have the right to edit the source code, if you can't, you can just extends NetWorkImageView, it is pretty easy).
public class NetworkImageView extends ImageView {
private Bitmap bitmap;
public void setLocalImageBitmap(Bitmap bitmap){
this.bitmap=bitmap;
}
/**The volley verison of NetworkImageView has This method, you just need to add
a new condition, which is else if(bitmap!=null).
**/
private void setDefaultImageOrNull() {
if(mDefaultImageId != 0) {
setImageResource(mDefaultImageId);
}
else if(bitmap!=null){
setImageBitmap(bitmap);
}
else {
setImageBitmap(null);
}
}
}
The accepted answer did not work for me... The following code works:
public class CustomNetworkImageView extends NetworkImageView {
Context mContext;
public CustomNetworkImageView(Context context) {
super(context);
mContext = context;
}
public CustomNetworkImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
mContext = context;
}
public CustomNetworkImageView(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
mContext = context;
}
#Override
public void setImageBitmap(Bitmap bm) {
if (bm == null) return;
setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));
}
}
As Lefteris mentioned, just use a normal ImageView if you are loading with the listener paradigm. This worked for me.
You can also set your local bitmap through the image loader and not touch NetworkImageView
imageSaver = new ImageLoader(VolleyWebServiceManager.getInstance().getRequestQueue(), new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(50);
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
FileManager.getInstance().saveImage(bitmap, FileManager.getInstance().getLastPathComponent(url), true, false);
}
public Bitmap getBitmap(String url) {
// Puts the bitmap from the file system into the cache
if (mCache.get(url) == null && FileManager.getInstance().getLocalImage(FileManager.getInstance().getLastPathComponent(url)) != null) {
putBitmap(url, FileManager.getInstance().getLocalImage(FileManager.getInstance().getLastPathComponent(url)));
}
return mCache.get(url);
}
});
Get NetworkImageView url it Will Use full.
private static CustomVolleyRequestQueue mInstance;
private static Context mCtx;
private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;
private CustomVolleyRequestQueue(Context context) {
mCtx = context;
mRequestQueue = getRequestQueue();
mImageLoader = new ImageLoader(mRequestQueue,
new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap>
cache = new LruCache<String, Bitmap>(20);
#Override
public Bitmap getBitmap(String url) {
return cache.get(url);
}
#Override
public void putBitmap(String url, Bitmap bitmap) {
cache.put(url, bitmap);
}
});
}
public static synchronized CustomVolleyRequestQueue getInstance(Context context) {
if (mInstance == null) {
mInstance = new CustomVolleyRequestQueue(context);
}
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
Cache cache = new DiskBasedCache(mCtx.getCacheDir(), 10 * 1024 * 1024);
Network network = new BasicNetwork(new HurlStack());
mRequestQueue = new RequestQueue(cache, network);
// Don't forget to start the volley request queue
mRequestQueue.start();
}
return mRequestQueue;
}
public ImageLoader getImageLoader() {
return mImageLoader;
}

Getting resource ID or InputStream from ImageView Drawable

I am developing a GifView class, extending android.view.ImageView to show an animation gif in Android.
The problem is getting android:src="#drawable/myGif" from XML layout to load it in Movie automatically. When I override setImageDrawable(Drawable drawable) to intercept default android behaviour, I have two differents ways to do it: getting the id resource from #drawable/myGif and save it for later use, or load it on demand in Movie. But with the latter option I need to convert Drawable in InputStream unused compress method from Bitmap class to can safe GIF layers.
How can I do it?
public class GifView extends ImageView {
...
private Movie movie;
private int resId;
public GifView(Context context) {
super(context);
this.context = context;
}
public GifView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
public GifView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
}
#Override
public void setImageDrawable(Drawable drawable) {
// ONE OF THOSE WAYS TO DO
// this.redId = ...
// this.movie = Movie.decodeStream(...);
}
...
}
ok, you can do it automatically, but does it really pay of?
public class GifImageView extends ImageView {
private final static String TAG = "GifImageView";
public GifImageView(Context context, AttributeSet attrs) {
super(context, attrs);
int[] attrArray = {
android.R.attr.src
};
TypedArray a = context.obtainStyledAttributes(attrs, attrArray);
int id = a.getResourceId(0, 0);
a.recycle();
if (id != 0) {
try {
Drawable d = new GifDrawable(getResources(), id);
setImageDrawable(d);
} catch (Exception e) {
Log.d(TAG, "GifImageView " + e.getMessage());
}
}
}
class GifDrawable extends Drawable implements Runnable {
...
}
}

Categories

Resources