I'm trying to upload an image from phone's gallery to a server. I tried Glide and Picasso library. But it is not showing the selected image in ImageView. So, I'm scaling down the image as mentioned here. This method works fine with most of the phones. But I found that it is not working in OnePlus 5 or 5T (Android - Oxygen: 8.0.0). It is showing following exception. The same code works fine if I take a picture using the camera and upload it.
04-14 16:13:43.207 26947-27295/me.example.app W/System.err: java.io.IOException: Resetting to invalid mark
at java.io.BufferedInputStream.reset(BufferedInputStream.java:450)
at me.example.app .Fragment.ImageUploadFragment.decodeSampledBitmapFromUri(ImageUploadFragment.java:172)
at me.example.app .Fragment.ImageUploadFragment$GetResizedBitmapTask.doInBackground(ImageUploadFragment.java:124)
at me.example.app .Fragment.ImageUploadFragment$GetResizedBitmapTask.doInBackground(ImageUploadFragment.java:105)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
Code for reference:
MainActivity:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_FROM_GALLARY && resultCode == Activity.RESULT_OK){
imageUploadFragment.resizeImage(data.getData(), IMAGE_MAX_DIMENSION);
}
}
....
#Override
public void onBitmapResized(Bitmap resizedBitmap, int maxDimension) {
if (resizedBitmap != null){
Log.i("image", "resized");
imgSelected.setImageBitmap(resizedBitmap);
} else {
Toast.makeText(this, "Something went wrong! Please try again.", Toast.LENGTH_SHORT).show();
}
}
ImageUploadFragment:
public void resizeImage(Uri uri, int maxDimension){
GetResizedBitmapTask getResizedBitmapTask = new GetResizedBitmapTask(maxDimension);
getResizedBitmapTask.execute(uri);
}
#SuppressLint("StaticFieldLeak")
private class GetResizedBitmapTask extends AsyncTask<Uri, Void, Bitmap> {
private int mMaxDimension;
GetResizedBitmapTask(int maxDimension) {
this.mMaxDimension = maxDimension;
}
#Override
protected Bitmap doInBackground(Uri... uris) {
Uri uri = uris[0];
if (uri != null){
Bitmap bitmap;
try {
bitmap = decodeSampledBitmapFromUri(uri, mMaxDimension, mMaxDimension);
return bitmap;
} catch (IOException e) {
e.printStackTrace();
//****this exception I'm getting****
return null;
}
}
return null;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (mImageTaskCallbacks != null)
mImageTaskCallbacks.onBitmapResized(bitmap, mMaxDimension);
}
}
public Bitmap decodeSampledBitmapFromUri(Uri imageUri, int reqWidth, int reqHeight) throws IOException{
//gets bytes of data from image file
InputStream inputStream = mContext.getContentResolver().openInputStream(imageUri);
if (inputStream == null){
return null;
}
InputStream bufferedInputStream = new BufferedInputStream(inputStream);
//marks the current position in the InputStream based on the number of bytes remaining to read in the buffer
bufferedInputStream.mark(bufferedInputStream.available());
BitmapFactory.Options options = new BitmapFactory.Options();
//decoder will return null (no bitmap)
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(bufferedInputStream, null, options);
bufferedInputStream.reset();
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
BitmapFactory.decodeStream(bufferedInputStream, null, options);
//****this line of code causing the exception****
bufferedInputStream.reset();
return BitmapFactory.decodeStream(bufferedInputStream, null, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){
int inSampleSize = 1;
final int width = options.outWidth;
final int height = options.outHeight;
if (width > reqWidth || height > reqHeight){
final int halfWidth = width/2;
final int halfHeight = height/2;
while ((halfWidth/inSampleSize) > reqWidth && (halfHeight/inSampleSize) > reqHeight){
inSampleSize *= 2;
}
}
return inSampleSize;
}
Thank you in advance.
Related
I'm trying to capture an image and set in imageView, like in this code that someone in stackoverflow offered, but i get blank white screen.
what could be the problem?
thanks.
private String pictureImagePath = "";
private void openBackCamera() {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = timeStamp + ".jpg";
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
pictureImagePath = storageDir.getAbsolutePath() + "/" + imageFileName;
File file = new File(pictureImagePath);
Uri outputFileUri = Uri.fromFile(file);
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
startActivityForResult(cameraIntent, 1);
}
Handle Image
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1) {
File imgFile = new File(pictureImagePath);
if(imgFile.exists()){
Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
ImageView myImage = (ImageView) findViewById(R.id.imageviewTest);
myImage.setImageBitmap(myBitmap);
}
}
}
Here's the code I used to get bitmap's to display within an ImageView (sorry it's in MonoDroid/C#, it will require light modification to work in Android/Java):
using System.IO;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Widget;
namespace MyApp.Util
{
public static class BitmapHelpers
{
/// <summary>
/// This method will recyle the memory help by a bitmap in an ImageView
/// </summary>
/// <param name="imageView">Image view.</param>
public static void RecycleBitmap(this ImageView imageView)
{
if (imageView == null) {
return;
}
Drawable toRecycle = imageView.Drawable;
if (toRecycle != null) {
((BitmapDrawable)toRecycle).Bitmap.Recycle ();
}
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
{
// Raw height and width of image
int height = options.OutHeight;
int width = options.OutWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth)
{
int halfHeight = height / 2;
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;
}
/// <summary>
/// Load the image from the device, and resize it to the specified dimensions.
/// </summary>
/// <returns>The and resize bitmap.</returns>
/// <param name="fileName">File name.</param>
/// <param name="width">Width.</param>
/// <param name="height">Height.</param>
public static Bitmap LoadAndResizeBitmap(this string fileName, int width, int height)
{
// First we get the the dimensions of the file on disk
BitmapFactory.Options options = new BitmapFactory.Options
{
InPurgeable = true,
InJustDecodeBounds = true
};
BitmapFactory.DecodeFile(fileName, options);
options.InSampleSize = calculateInSampleSize(options, width, height);
options.InJustDecodeBounds = false;
Bitmap resizedBitmap = BitmapFactory.DecodeFile(fileName, options);
return resizedBitmap;
}
}
}
Example Usage
public static Bitmap LoadBitmap( File imageFile, int w, int h )
{
Bitmap bitmap = null;
if (imageFile != null)
{
if( (new File(imageFile.Path)).Exists() )
{
bitmap = imageFile.Path.LoadAndResizeBitmap(w, h);
}
}
return bitmap;
}
OnActivityResult Snippet (Makes picture available in Gallery)
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
switch ((ActivityRequestCode)requestCode)
{
case ActivityRequestCode.Camera:
// make it available in the gallery
if (_imageFile != null)
{
Intent mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
Uri contentUri = Uri.FromFile(_imageFile);
mediaScanIntent.SetData(contentUri);
SendBroadcast(mediaScanIntent);
_pictureRequestingFragment.PictureReady(_imageFile);
}
break;
case ActivityRequestCode.Map:
break;
}
}
I am allowing user to choose image and using it to set as imageview in another activity. But the imageview remains blank. Following is my code.
private static final int REQUEST_CODE_GALLERY = 1;
private static final int REQUEST_IMAGE_CAPTURE = 2;
public ImageView CameraButton;
public ImageView GalleryButton;
public ImageView example;
public Bitmap imageBitmap;
public Bitmap bmp;
ongallery object = new ongallery();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GalleryButton = (ImageView) findViewById(R.id.GalleryButton);
CameraButton = (ImageView) findViewById(R.id.CameraButton);
example = (ImageView) findViewById(R.id.example);
GalleryButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent gallery = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(gallery, REQUEST_CODE_GALLERY);
}
});
CameraButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (camera.resolveActivity(getPackageManager()) != null) {
startActivityForResult(camera, REQUEST_IMAGE_CAPTURE);
}
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
try {
if (requestCode == REQUEST_CODE_GALLERY && resultCode == RESULT_OK && null != data) {
Uri chosen = data.getData();
String[] filepath = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(chosen, filepath, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filepath[0]);
String photoadd = cursor.getString(columnIndex);
cursor.close();
bmp = BitmapFactory.decodeFile(photoadd);
try {
//Write file
String filename = "bitmap.png";
FileOutputStream stream = this.openFileOutput(filename, Context.MODE_PRIVATE);
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
//Cleanup
stream.close();
bmp.recycle();
//Pop intent
Intent in1 = new Intent(this, ongallery.class);
in1.putExtra("picture", filename);
startActivity(in1);
} catch (Exception e) {
e.printStackTrace();
}
}
else if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK && null!=data) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
try {
//Write file
String filename = "bitmap.png";
FileOutputStream stream = this.openFileOutput(filename, Context.MODE_PRIVATE);
photo.compress(Bitmap.CompressFormat.PNG, 100, stream);
//Cleanup
stream.close();
photo.recycle();
//Pop intent
Intent in1 = new Intent(this, ongallery.class);
in1.putExtra("picture", filename);
startActivity(in1);
} catch (Exception e) {
e.printStackTrace();
}
}
}catch(Exception e){
Toast.makeText(this,"Something went wrong",Toast.LENGTH_SHORT).show();
}
}
}
//ongallery.java
public class ongallery extends Activity {
public ImageView imgView;
int xDim;
int yDim;
String filename;
public Bitmap finale = null ;
public Bitmap bmp = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ongallery);
imgView = (ImageView) findViewById(R.id.imgView);
filename = getIntent().getStringExtra("picture");
try {
FileInputStream is = this.openFileInput(filename);
bmp = BitmapFactory.decodeStream(is);
is.close();
} catch (Exception e) {
e.printStackTrace();
}
imgView.setImageBitmap(decoder(filename,400,400));
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
xDim=imgView.getWidth();
yDim=imgView.getHeight();
}
public Bitmap decoder(String filename, int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filename, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
finale = BitmapFactory.decodeFile(filename, options);
return finale;
}
int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) {
int inSampleSize = 1;
if (options.outHeight > reqHeight || options.outWidth > reqWidth) {
final int halfHeight = options.outHeight / 2;
final int halfWidth = options.outWidth / 2;
while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) >reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
}
BitmapFactory.decodeFile() needs the complete path name as the first argument.
public Bitmap decoder(String filename, int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
String filepath = getFileStreamPath(filename).getPath();
BitmapFactory.decodeFile(filepath, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
finale = BitmapFactory.decodeFile(filepath, options);
return finale;
}
Currently looking for help on saving images from a camera app to internal storage do to Nexus not having an SD card, our current code saves the photos taken to an SD card folder and the quality is not good.
public class MainActivity extends ActionBarActivity {
private ImageView imageHolder;
private final int requestCode = 20;
public final static String EXTRA_MESSAGE = "com.test1.cam.camapp.MESSAGE";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageHolder = (ImageView)findViewById(R.id.captured_photo);
Button capturedImageButton = (Button)findViewById(R.id.photo_button);
capturedImageButton.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent photoCaptureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(photoCaptureIntent, requestCode);
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(this.requestCode == requestCode && resultCode == RESULT_OK){
Bitmap bitmap = (Bitmap)data.getExtras().get("data");
String partFilename = currentDateFormat();
storeCameraPhotoInSDCard(bitmap, partFilename);
// display the image from SD Card to ImageView Control
String storeFilename = "photo_" + partFilename + ".jpg";
Bitmap mBitmap = getImageFileFromSDCard(storeFilename);
imageHolder.setImageBitmap(mBitmap);
}
}
public void showGreetings(View view)
{
String button_text;
button_text = ((Button) view) .getText().toString();
if(button_text.equals("Info"))
{
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
else if (button_text.equals("Info"))
{
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
}
public void saveImage(Context context, Bitmap b,String name,String extension){
name=name+"."+extension;
FileOutputStream out;
try {
out = context.openFileOutput(name, Context.MODE_PRIVATE);
b.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private String currentDateFormat(){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HH_mm_ss");
String currentTimeStamp = dateFormat.format(new Date());
return currentTimeStamp;
}
private void storeCameraPhotoInSDCard(Bitmap bitmap, String currentDate){
File outputFile = new File(Environment.getExternalStorageDirectory(), "photo_" + currentDate + ".jpg");
try {
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private Bitmap getImageFileFromSDCard(String filename){
Bitmap bitmap = null;
File imageFile = new File(Environment.getExternalStorageDirectory() + filename);
try {
FileInputStream fis = new FileInputStream(imageFile);
bitmap = BitmapFactory.decodeStream(fis);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return bitmap;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
To improve the image quality you should change compression to PNG, or change the second parameters to 100 (PNG is lossless and will ignore second params).
b.compress(Bitmap.CompressFormat.PNG, 100, out);
To change external into internal just change
File outputFile = new File(Environment.getExternalStorageDirectory(), "photo_" + currentDate + ".jpg");
into
File outputFile = new File(context.getFilesDir(), "photo_" + currentDate + ".jpg");
The image you are trying to save returns you jest the thumbnail of the actual image that is why you are getting low quality image. You should pass the image name to the intent to save the high quality image when it is captured
Following Helper class that I use for image capture may be of some help to you
public class CaptureImageHelper {
private static final int DEFAULT_WIDTH = 1024; // min pixels
private static final int DEFAULT_HEIGHT = 768; // min pixels
private static final String TEMP_IMAGE_NAME = "tempImage";
public static Intent getImageCaptureIntent(Context context, String title) {
Intent chooserIntent = null;
List<Intent> intentList = new ArrayList<>();
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(getTempFile(context)));
intentList = addIntentsToList(context, intentList, takePhotoIntent);
if (intentList.size() > 0) {
chooserIntent = Intent.createChooser(intentList.remove(intentList.size() - 1), title);
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentList.toArray(new Parcelable[]{}));
}
return chooserIntent;
}
private static File getTempFile(Context context) {
//Note you can change the path here according to your need
File imageFile = new File(Environment.getExternalStorageDirectory(), TEMP_IMAGE_NAME);
imageFile.getParentFile().mkdirs();
return imageFile;
}
private static List<Intent> addIntentsToList(Context context, List<Intent> list, Intent intent) {
List<ResolveInfo> resInfo = context.getPackageManager().queryIntentActivities(intent, 0);
for (ResolveInfo resolveInfo : resInfo) {
String packageName = resolveInfo.activityInfo.packageName;
Intent targetedIntent = new Intent(intent);
targetedIntent.setPackage(packageName);
list.add(targetedIntent);
}
return list;
}
public static Bitmap getImageFromResult(Context context, int resultCode, Intent imageReturnedIntent) {
return getImageFromResult(context, DEFAULT_WIDTH, DEFAULT_HEIGHT, resultCode, imageReturnedIntent);
}
public static Bitmap getImageFromResult(Context context, int width, int height, int resultCode, Intent imageReturnedIntent) {
Bitmap bm = null;
if (resultCode == Activity.RESULT_OK) {
Uri selectedImage;
File imageFile = getTempFile(context);
selectedImage = Uri.fromFile(imageFile);
bm = getImageResized(context, selectedImage, width, height);
int rotation = getRotation(context, selectedImage, true);
bm = rotate(bm, rotation);
}
return bm;
}
private static Bitmap getImageResized(Context context, Uri selectedImage, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
System.gc();
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap actuallyUsableBitmap = null;
AssetFileDescriptor fileDescriptor = null;
try {
fileDescriptor = context.getContentResolver().openAssetFileDescriptor(selectedImage, "r");
} catch (FileNotFoundException e) {
}
if (null != fileDescriptor) {
BitmapFactory.decodeFileDescriptor(fileDescriptor.getFileDescriptor(), null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
actuallyUsableBitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor.getFileDescriptor(), null, options);
}
return actuallyUsableBitmap;
}
private static Bitmap getImageResized(Context context, Uri selectedImage) {
return getImageResized(context, selectedImage, DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
private 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;
}
private static int getRotation(Context context, Uri imageUri, boolean isCamera) {
int rotation;
if (isCamera) {
rotation = getRotationFromCamera(context, imageUri);
} else {
rotation = getRotationFromGallery(context, imageUri);
}
return rotation;
}
private static int getRotationFromCamera(Context context, Uri imageFile) {
int rotate = 0;
try {
context.getContentResolver().notifyChange(imageFile, null);
ExifInterface exif = new ExifInterface(imageFile.getPath());
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
}
} catch (Exception e) {
e.printStackTrace();
}
return rotate;
}
private static int getRotationFromGallery(Context context, Uri imageUri) {
int orientation = 0;
String[] columns = {MediaStore.Images.Media.ORIENTATION};
Cursor cursor = context.getContentResolver().query(imageUri, columns, null, null, null);
if (null != cursor && cursor.moveToFirst()) {
int orientationColumnIndex = cursor.getColumnIndex(columns[0]);
orientation = cursor.getInt(orientationColumnIndex);
cursor.close();
}
return orientation;
}
private static Bitmap rotate(Bitmap bm, int rotation) {
if (rotation != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
return Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
}
return bm;
}
}
I want to load the Album-Artworks into my music app and preview them in a recyclerview. I've tried Googles Developer Guide, but I'm actually loading about 200 Bitmaps, so this doesn't work!
I don't have an idea about how to do that!
Here's my current code:
if (cSong != null && cSong.moveToFirst()) {
do {
int iIDCol = cSong.getColumnIndex(MediaStore.Audio.Media._ID);
int iTitleCol = cSong.getColumnIndex(MediaStore.Audio.Media.TITLE);
int iArtistCol = cSong.getColumnIndex(MediaStore.Audio.Media.ARTIST);
int iAlbumCol = cSong.getColumnIndex(MediaStore.Audio.Media.ALBUM);
int iDurationCol = cSong.getColumnIndex(MediaStore.Audio.Media.DURATION);
int iAlbumIDCol = cSong.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID);
Bitmap bCover = null;
BitmapFactory.Options bOptions = new BitmapFactory.Options();
bOptions.inJustDecodeBounds = true;
//Throws OutOfMemoryError
/*try {
Uri ArtworkUri = Uri.parse("content://media/external/audio/albumart");
Uri uri = ContentUris.withAppendedId(ArtworkUri, iAlbumIDCol);
ParcelFileDescriptor pfd = mContext.getContentResolver().openFileDescriptor(uri, "r");
if (pfd != null) {
FileDescriptor fd = pfd.getFileDescriptor();
BitmapFactory.decodeFileDescriptor(fd, null, bOptions);
bOptions.inSampleSize = calculateInSampleSize(bOptions, 100, 100);
bOptions.inJustDecodeBounds = false;
bCover = BitmapFactory.decodeFileDescriptor(fd, null, bOptions);
pfd = null;
fd = null;
}
}
catch (IOException ioe) {
BitmapFactory.decodeResource(mContext.getResources(), R.drawable.standardartwork, bOptions);
bOptions.inSampleSize = calculateInSampleSize(bOptions, 100, 100);
bOptions.inJustDecodeBounds = false;
bCover = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.standardartwork, bOptions);
}*/
SongList.add(new Song(cSong.getLong(iIDCol), cSong.getString(iTitleCol), cSong.getString(iArtistCol), cSong.getString(iAlbumCol), cSong.getInt(iDurationCol), bCover));
}
while (cSong.moveToNext());
Any my InSampleSize Method:
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int size = 1;
if (height > reqHeight && width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / size) > reqHeight && (halfWidth / size) > reqWidth) {
size *= 2;
}
}
return size;
}
Thanks!
Holding 200 bitmaps in memory is not a good idea even though they are downscaled. The easiest solution would be saving the Uri to the cover instead of Bitmap and decode a Bitmap only when you need it.
So the parsing code would look like this:
Uri artworkUri = Uri.parse("content://media/external/audio/albumart");
if (cSong != null && cSong.moveToFirst()) {
do {
int iIDCol = cSong.getColumnIndex(MediaStore.Audio.Media._ID);
int iTitleCol = cSong.getColumnIndex(MediaStore.Audio.Media.TITLE);
int iArtistCol = cSong.getColumnIndex(MediaStore.Audio.Media.ARTIST);
int iAlbumCol = cSong.getColumnIndex(MediaStore.Audio.Media.ALBUM);
int iDurationCol = cSong.getColumnIndex(MediaStore.Audio.Media.DURATION);
int iAlbumIDCol = cSong.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID);
Uri coverUri = ContentUris.withAppendedId(artworkUri, iAlbumIDCol);
SongList.add(new Song(cSong.getLong(iIDCol), cSong.getString(iTitleCol), cSong.getString(iArtistCol), cSong.getString(iAlbumCol), cSong.getInt(iDurationCol), coverUri));
}
while (cSong.moveToNext());
And in your RecyclerView.Adapter onBindViewHolder(RecyclerView.ViewHolder holder, int position) method do the decoding:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Bitmap image = decodeBitmap(songsList.get(position).getBitmapUrl());
if (image != null) {
holder.imageView.setImageBitmap(image);
} else {
holder.imageView.setImageResource(R.drawable.standardartwork);
}
}
private Bitmap decodeBitmap(Uri uri) {
ParcelFileDescriptor pfd = mContext.getContentResolver().openFileDescriptor(uri, "r");
try {
if (pfd != null) {
FileDescriptor fd = pfd.getFileDescriptor();
BitmapFactory.decodeFileDescriptor(fd, null, bOptions);
bOptions.inSampleSize = calculateInSampleSize(bOptions, 100, 100);
bOptions.inJustDecodeBounds = false;
return BitmapFactory.decodeFileDescriptor(fd, null, bOptions);
} else {
return null;
}
} catch (IOException ioe) {
return null;
}
}
This will allow you to keep in memory only the bitmaps that you actually have to show on the screen, others will be recycled.
NOTE: The snippets are here just for an illustration how your problem could be solved, but this can reduce performance of your RecyclerView, because decoding is done on the main thread. You won't notice it if your images are rather small, but you'll have to do decoding asynchronously if they are large. In case of large images, consider using libraries for image processing like Picasso or Glide.
I solved it by using the Library Picasso, which handles the Loading-Process of the Image.
Picasso.with(mContext).load(ImageUri).resize(Width, Height).into(ImageView);
I was having a problem where my bitmaps were causing my application to skip frames at a rapid rate. I have looked into android's "Displaying Bitmap's Efficiently" documentation, and have implemented some into my code.
I am pretty sure the image is loading correctly, but its not showing in my application. The height and width of the image view is 0 after I do this decoding, so this might be one of the reasons, but I can't change the size of the ImageView for some reason.
BitmapWorkerTask.java:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
private Context context;
public BitmapWorkerTask(ImageView imageView, Context context) {
this.context = context;
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
System.out.println("T" + buildBitmap(data, 100, 100));
return buildBitmap(data, 100, 100);
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask =
AsyncDrawable.getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
public Bitmap buildBitmap(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(context.getResources(), resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(context.getResources(), resId, options);
}
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int width = options.outWidth;
final int height = options.outHeight;
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;
}
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
public static boolean cancelPotentialWork(int data, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
final int bitmapData = bitmapWorkerTask.data;
// If bitmapData is not yet set or it differs from the new data
if (bitmapData == 0 || bitmapData != data) {
// Cancel previous task
bitmapWorkerTask.cancel(true);
} else {
// The same work is already in progress
return false;
}
}
// No task associated with the ImageView, or an existing task was cancelled
return true;
}
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
}
}
I am creating the image in the constructor of my View class, and then drawing the TextView in the onDraw method.
public Game(Context context) {
loadBitmap(R.drawable.logo, scoreHolderView);
}
#Override
protected void onDraw(Canvas canvas) {
if(STATE == State.Playing) {
synchronized(spawner.activeTiles) {
for(Iterator<Tile> iter = spawner.activeTiles.iterator(); iter.hasNext();) {
Tile tile = iter.next();
canvas.drawRect(tile, tile.getColor());
}
}
//canvas.drawBitmap(scoreholder, null, scoreIcon, null);
canvas.drawText(String.valueOf(SCORE), 50, 50, blackText);
scoreHolderView.draw(canvas);
}
}
Is the position and size of the ImageView just not being set for some reason? Or are there other problems with my code?
EDIT:
public void loadBitmap(int resId, ImageView imageView) {
if (AsyncDrawable.cancelPotentialWork(resId, imageView)) {
final BitmapWorkerTask task = new BitmapWorkerTask(imageView, getContext());
final AsyncDrawable asyncDrawable =
new AsyncDrawable(getResources(), logo, task);
imageView.setImageDrawable(asyncDrawable);
task.execute(resId);
}
}