Correctly download and display image from server - android

Now, in my app, each placeholder has size, that was set in xml and after image was downloaded it resizes ImageView height with horrible visual effect.
How can I get image size, that is downloading and should be displayed in
ImageView, to make the same placeholder size like in Facebook application live feed? Maybe I should include image ratio for each link in json file that is downloading from server, or Fresco, Glide or Picasso has some tricks to acheive this?

If you want to resize the placeholder to the actual image dimensions before downloading the image, you have to somehow get this information from your server. You could, like you've already suggested, include the image dimensions / aspect ratio to the request and then use that information to properly size your views upfront.
Because of this horrible visual effect you've mentioned, Fresco doesn't support wrap_content but it supports setting an aspect ratio for the DraweeView that you can set using your server data.
See also http://frescolib.org/docs/wrap-content.html

Try using ResourceTranscoder with glide request as mentioned below:
Glide
.with(this)
.load(imageUrl)
.asBitmap()
.transcode(new BitmapSizeTranscoder(), Size.class)
.into(new SimpleTarget<Size>() {
#Override public void onResourceReady(Size size, GlideAnimation glideAnimation) {
Log.w("SIZE", String.format(Locale.ENGLISH, "%dx%d", size.width, size.height));
}
});
class Size {
int width;
int height;
}
class BitmapSizeTranscoder implements ResourceTranscoder<Bitmap, Size> {
#Override public Resource<Size> transcode(Resource<Bitmap> toTranscode) {
Bitmap bitmap = toTranscode.get();
Size size = new Size();
size.width = bitmap.getWidth();
size.height = bitmap.getHeight();
return new SimpleResource<>(size);
}
#Override public String getId() {
return getClass().getName();
}
}

Related

How to get resulting ImageView size in Glide after photo has been loaded

I need to retrieve the resulting ImageView size after loading an image with Glide. This is what I have tried so far:
Glide.with(context).load(ULTRAWIDE_PHOTO_ASSET).transition(DrawableTransitionOptions.withCrossFade()).into(imageview)
.getSize(new SizeReadyCallback() {
#Override
public void onSizeReady(int width, int height) {
Log.d("DIMENSIONS X", "anchor " + width);
Log.d("DIMENSIONS Y", "anchor " + height);
}
});
These are the dimensions I'm getting 2094x2094, but the photo and the resulting ImageView are definitely not square (it has more of a 2:1 aspect). Additionally, calling imageView.getWidth() , imageView.getHeight() inside the callback returns 0.0.
How can I get the resulting dimensions. I need to do some postprocessing with them.

Glide, possible to get image file size (not dimensions)?

I am using ACTION_GET_CONTENT and load selected images using Glide. I would like to display the image file size in bytes, but does Glide support a way to get this data?
I could find ways to get file size when using ACTION_GET_CONTENT, but I am afraid that if I use those methods, it may unnecessarily read the same file twice. (I read it once for the size, and Glide reads it once more to get the image). This would be particularly bad if the image is a remote image (users could select images from Google Drive).
You can get the bitmap using SimpleTarget and then you can calculate the size in kb,mb by fetching bytes using getByteCount or getAllocationByteCount
Glide
.with(context)
.load("https://www.somelink.com/image.png")
.asBitmap()
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
int byteCount = Integer.MIN_VALUE;
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT){
byteCount = resource.getByteCount();
}else{
byteCount = resource.getAllocationByteCount();
}
int sizeInKB = byteCount / 1024;
int sizeInMB = sizeInKB / 1024;
image.setImageBitmap(resource);
}
});

AndroidSVG fuzzy edges on image

I am want to display Barcode on android. As input I get SVG string. As a SVG library I use AndroidSVG. I used sample code from library website and everything seem to be fine. But when I zoom on image, I get distorted edges (Anti-alias?). I tried to disable all the flags. But the image still has fuzzy edges. What can be wrong with my code?
Picture:
Try to zoom to max, you will see the fuzzy edges.
Code:
private void loadQRCode(String svgString) {
SVG svg = null;
try {
svg = SVG.getFromString(svgString);
} catch (SVGParseException e) {
e.printStackTrace();
}
if (svg.getDocumentWidth() != -1) {
int widthPx = Utils.pxFromDp(400);
int heightDp = Utils.pxFromDp(300);
svg.setDocumentWidth(widthPx);
svg.setDocumentHeight(heightDp);
int width = (int) Math.ceil(svg.getDocumentWidth());
int height = (int) Math.ceil(svg.getDocumentHeight());
Bitmap newBM = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas bmcanvas = new Canvas(newBM);
final DrawFilter filter = new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG| Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG, 0);
bmcanvas.setDrawFilter(filter);
barcode.setLayerType(View.LAYER_TYPE_SOFTWARE,null);
bmcanvas.drawRGB(255, 255, 255);
svg.renderToCanvas(bmcanvas);
barcode.setImageBitmap(newBM);
}
}
If the edges of the bars do not lie exactly on pixel boundaries, you will get anti-aliasing. On a high resolution screen, this should not normally be visible.
However, in your code, you are rendering the SVG to a bitmap and setting the bitmap to an ImageView. If that ImageView has a size larger than the bitmap - ie. greater than 400 x 300, then the anti-aliased pixels in that bitmap will likely be rendered larger and thus more visible.
One solution is to avoid using a bitmap. Use a Picture/PictureDrawable instead. That way the barcode will be rendered at highest quality no matter what size it is. As vector graphics are supposed to be.
Follow the example on this page:
http://bigbadaboom.github.io/androidsvg/use_with_ImageView.html
So your code should probably look something like the following:
private void loadQRCode(String svgString) {
try {
SVG svg = SVG.getFromString(svgString);
barcode.setLayerType(View.LAYER_TYPE_SOFTWARE,null);
Drawable drawable = new PictureDrawable(svg.renderToPicture());
barcode.setImageDrawable(drawable);
} catch (SVGParseException e) {
e.printStackTrace();
}
}
If for some reason you need to use bitmaps - maybe you are caching them or something - then you should watch for changes in the size of the ImageView and then recreate the bitmap at the new size. So the bitmap is always the same size as the ImageView to which it is assigned.

how can I resize gif with custom height and width in android?

I am using Koush/Ion library to load gif from internet into imageview but I cannot resize the gifs aspectly, upon calling resize function, gif becomes still
so my question is, is there any way I can resize gif aspectly in android ?
here's my calculation to get aspect height and width of image if that helps
final int newWidth = deviceWidth;
final int newHeight = (int) ((int) newWidth * (imageHeight / imageWidth));
I think I finally found the solution,
First scale image with FIT_XY and then set 'newHeight' as the height of image view
imageView.setScaleType(ScaleType.FIT_XY);
imageView.getLayoutParams().height = newHeight;
Code Snippet
Ion.with(imageView)
.animateGif(AnimateGifMode.ANIMATE)
.load(imgUrl)
.setCallback(new FutureCallback < ImageView > () {
#SuppressLint("NewApi")
#Override
public void onCompleted(Exception arg0,
ImageView arg1) {
imageView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
imageView.getViewTreeObserver()
.removeOnPreDrawListener(this);
imageView.setScaleType(ScaleType.FIT_XY);
imageView.getLayoutParams().height = newHeight;
return true;
}
});
}
});
You can resize a gif in android programatically. When you do so the gif does not animate as it becomes a normal Image. In order to resize the gif and animate it, the only suggestion that I can give you is to use online resizing tools like
EzGif
GifMaker
Picason
Later save the resized gif in your assets folder and then call that using the Ion library which you use like this
Ion.with(yourImageView).load("yourAssetFolder/yourGifImage.gif")

"Bitmap too large to be uploaded into a texture"

I'm loading a bitmap into an ImageView, and seeing this error. I gather this limit relates to a size limit for OpenGL hardware textures (2048x2048). The image I need to load is a pinch-zoom image of about 4,000 pixels high.
I've tried turning off hardware acceleration in the manifest, but no joy.
<application
android:hardwareAccelerated="false"
....
>
Is it possible to load an image larger than 2048 pixels into an ImageView?
This isn't a direct answer to the question (loading images >2048), but a possible solution for anyone experiencing the error.
In my case, the image was smaller than 2048 in both dimensions (1280x727 to be exact) and the issue was specifically experienced on a Galaxy Nexus. The image was in the drawable folder and none of the qualified folders. Android assumes drawables without a density qualifier are mdpi and scales them up or down for other densities, in this case scaled up 2x for xhdpi. Moving the culprit image to drawable-nodpi to prevent scaling solved the problem.
I have scaled down the image in this way:
ImageView iv = (ImageView)waypointListView.findViewById(R.id.waypoint_picker_photo);
Bitmap d = new BitmapDrawable(ctx.getResources() , w.photo.getAbsolutePath()).getBitmap();
int nh = (int) ( d.getHeight() * (512.0 / d.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(d, 512, nh, true);
iv.setImageBitmap(scaled);
All rendering is based on OpenGL, so no you can't go over this limit (GL_MAX_TEXTURE_SIZE depends on the device, but the minimum is 2048x2048, so any image lower than 2048x2048 will fit).
With such big images, if you want to zoom in out, and in a mobile, you should setup a system similar to what you see in google maps for example. With the image split in several pieces, and several definitions.
Or you could scale down the image before displaying it (see user1352407's answer on this question).
And also, be careful to which folder you put the image into, Android can automatically scale up images. Have a look at Pilot_51's answer below on this question.
Instead of spending hours upon hours trying to write/debug all this downsampling code manually, why not use Picasso? It was made for dealing with bitmaps of all types and/or sizes.
I have used this single line of code to remove my "bitmap too large...." problem:
Picasso.load(resourceId).fit().centerCrop().into(imageView);
Addition of the following 2 attributes in (AndroidManifest.xml) worked for me:
android:largeHeap="true"
android:hardwareAccelerated="false"
Changing the image file to drawable-nodpi folder from drawable folder worked for me.
I used Picasso and had the same problem. image was too large at least in on size, width or height. finally I found the solution here. you can scale the large image down according to display size and also keep the aspect ratio:
public Point getDisplaySize(Display display) {
Point size = new Point();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
display.getSize(size);
} else {
int width = display.getWidth();
int height = display.getHeight();
size = new Point(width, height);
}
return size;
}
and use this method for loading image by Picasso:
final Point displySize = getDisplaySize(getWindowManager().getDefaultDisplay());
final int size = (int) Math.ceil(Math.sqrt(displySize.x * displySize.y));
Picasso.with(this)
.load(urlSource)
.resize(size, size)
.centerInside()
.into(imageViewd);
also for better performance you can download the image according to width and height of the display screen, not whole the image:
public String reviseImageUrl(final Integer displayWidth, final Integer displayHeight,
final String originalImageUrl) {
final String revisedImageUrl;
if (displayWidth == null && displayHeight == null) {
revisedImageUrl = originalImageUrl;
} else {
final Uri.Builder uriBuilder = Uri.parse(originalImageUrl).buildUpon();
if (displayWidth != null && displayWidth > 0) {
uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_WIDTH, String.valueOf(displayWidth));
}
if (displayHeight != null && displayHeight > 0) {
uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_HEIGHT, String.valueOf(displayHeight));
}
revisedImageUrl = uriBuilder.toString();
}
return revisedImageUrl;
}
final String newImageUlr = reviseImageUrl(displySize.x, displySize.y, urlSource);
and then:
Picasso.with(this)
.load(newImageUlr)
.resize(size, size)
.centerInside()
.into(imageViewd);
EDIT: getDisplaySize()
display.getWidth()/getHeight() is deprecated. Instead of Display use DisplayMetrics.
public Point getDisplaySize(DisplayMetrics displayMetrics) {
int width = displayMetrics.widthPixels;
int height = displayMetrics.heightPixels;
return new Point(width, height);
}
BitmapRegionDecoder does the trick.
You can override onDraw(Canvas canvas), start a new Thread and decode the area visible to the user.
As pointed by Larcho, starting from API level 10, you can use BitmapRegionDecoder to load specific regions from an image and with that, you can accomplish to show a large image in high resolution by allocating in memory just the needed regions. I've recently developed a lib that provides the visualisation of large images with touch gesture handling. The source code and samples are available here.
View level
You can disable hardware acceleration for an individual view at runtime with the following code:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
I ran through same problem, here is my solution. set the width of image same as android screen width and then scales the height
Bitmap myBitmap = BitmapFactory.decodeFile(image.getAbsolutePath());
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Log.e("Screen width ", " "+width);
Log.e("Screen height ", " "+height);
Log.e("img width ", " "+myBitmap.getWidth());
Log.e("img height ", " "+myBitmap.getHeight());
float scaleHt =(float) width/myBitmap.getWidth();
Log.e("Scaled percent ", " "+scaleHt);
Bitmap scaled = Bitmap.createScaledBitmap(myBitmap, width, (int)(myBitmap.getWidth()*scaleHt), true);
myImage.setImageBitmap(scaled);
This is better for any size android screen. let me know if it works for you.
Scale down image:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// Set height and width in options, does not return an image and no resource taken
BitmapFactory.decodeStream(imagefile, null, options);
int pow = 0;
while (options.outHeight >> pow > reqHeight || options.outWidth >> pow > reqWidth)
pow += 1;
options.inSampleSize = 1 << pow;
options.inJustDecodeBounds = false;
image = BitmapFactory.decodeStream(imagefile, null, options);
The image will be scaled down at the size of reqHeight and reqWidth. As I understand inSampleSize only take in a power of 2 values.
Use Glide library instead of directly loading into imageview
Glide : https://github.com/bumptech/glide
Glide.with(this).load(Uri.parse(filelocation))).into(img_selectPassportPic);
I tried all the solutions above, one-after-the-other, for quite many hours, and none seemed to work! Finally, I decided to look around for an official example concerning capturing images with Android's camera, and displaying them. The official example (here), finally gave me the only method that worked. Below I present the solution I found in that example app:
public void setThumbnailImageAndSave(final ImageView imgView, File imgFile) {
/* There isn't enough memory to open up more than a couple camera photos */
/* So pre-scale the target bitmap into which the file is decoded */
/* Get the size of the ImageView */
int targetW = imgView.getWidth();
int targetH = imgView.getHeight();
/* Get the size of the image */
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
/* Figure out which way needs to be reduced less */
int scaleFactor = 1;
if ((targetW > 0) || (targetH > 0)) {
scaleFactor = Math.min(photoW/targetW, photoH/targetH);
}
/* Set bitmap options to scale the image decode target */
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
/* Decode the JPEG file into a Bitmap */
Bitmap bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);
/* Associate the Bitmap to the ImageView */
imgView.setImageBitmap(bitmap);
imgView.setVisibility(View.VISIBLE);
}
NOTE FOR THOSE WHO WANT TO PUT IMAGES OF SMALL SIZE:
Pilot_51's solution (moving your images to drawable-nodpi folder) works, but has another problem:
It makes images TOO SMALL on screen unless the images are resized to a very large (like 2000 x 3800) resolution to fit screen -- then it makes your app heavier.
SOLUTION: put your image files in drawable-hdpi -- It worked like a charm for me.
Using the correct drawable subfolder solved it for me. My solution was to put my full resolution image (1920x1200) into the drawable-xhdpi folder, instead of the drawable folder.
I also put a scaled down image (1280x800) into the drawable-hdpi folder.
These two resolutions match the 2013 and 2012 Nexus 7 tablets I'm programming. I also tested the solution on some other tablets.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
///*
if (requestCode == PICK_FROM_FILE && resultCode == RESULT_OK && null != data){
uri = data.getData();
String[] prjection ={MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(uri,prjection,null,null,null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(prjection[0]);
ImagePath = cursor.getString(columnIndex);
cursor.close();
FixBitmap = BitmapFactory.decodeFile(ImagePath);
ShowSelectedImage = (ImageView)findViewById(R.id.imageView);
// FixBitmap = new BitmapDrawable(ImagePath);
int nh = (int) ( FixBitmap.getHeight() * (512.0 / FixBitmap.getWidth()) );
FixBitmap = Bitmap.createScaledBitmap(FixBitmap, 512, nh, true);
// ShowSelectedImage.setImageBitmap(BitmapFactory.decodeFile(ImagePath));
ShowSelectedImage.setImageBitmap(FixBitmap);
}
}
This code is work

Categories

Resources