I have an Android app that allows the user to upload profile pics. They are stored 300px X 300px on the server.
In the app, I use them in either 40dp X 40dp or sometimes 100dp X 100dp. The 40dp's images are in ListView
's. I actually use a very popular third party LazyList
to store the images (using an ImageLoader
class) in a cache. Here is the exact library.
I simply show image in my adapter
like this:
imageLoader.DisplayImage(profileUrl, holder.iv1);
I take the web URL and display it directly into the ImageView
40dp x 40dp.
Sometimes I notice image degradation. Where the image is partially loaded, then is corrupted.
One theory is that going from a large image on the server and trying to store it in a small area in the actual app, then doing this many times in a ListView
, is causing some loading issues.
The question: What is the best way to handle this situation when you have a much larger image in the server than you need to display? I am assuming I may need to use BitMapFactory
or a similar class to build a new image. But I am not sure. I feel like I am missing a step.
Here is an example of an image in a ListView
that has become corrupted, notice this does not happen to all. (And yes that is Tobias from Arrested Development.)
Follow-up: And how do we take in consideration file size for xxhdpi
, xhdpi
, etc when the image is coming from server and not the resources folder?
If you want the In-App to appear like a modal, you need to upload a square image, with a width of 1200px. If you want the In-App to appear like a fullscreen, you need to upload an image with a 2:1 ratio (for example 1200x600 pixels).
Android 12 (API level 31) and higher support images that use the AV1 Image File Format (AVIF). AVIF is a container format for images and sequences of images encoded using AV1. AVIF takes advantage of the intra-frame encoded content from video compression.
Compressed JPEG format. YCbCr format, used for video. YCrCb format used for images, which uses the NV21 encoding format.
The library you are using looks like it is barely maintained anymore (last update is at least a year old). There are several newer and likely better libraries that solve the same problem. My favorite is Square's Picasso. It is very simple to use and solves the image loading inside a ListView problem very well.
What is the best way to handle this situation when you have a much larger image in the server than you need to display?
You need to subsample the Bitmap down to the target size. Google has a pretty good write up on all of this here.
First you have to decode the bitmap setting the inJustDecodeBounds
boolean to get the bitmap size.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
Then you can compute your sample size based on your target height and width.
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Then you can use the computed sample size to load the down sampled Bitmap. The final code would look something like
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
And how do we take in consideration file size for xxhdpi, xhdpi, etc when the image is coming from server and not the resources folder?
You can compute the target pixel size you want to downsize your bitmap to using the following snippet:
float pixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, "10", context.getResources().getDisplayMetrics());
Use Square's Picasso and it will solve all of your problems.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With