This question already has answers here:
High resolution Image - OutOfMemoryError
(2 answers)
Closed 8 years ago.
I want to show my images (stored in drawable folder) in gridview. There are 12 images. For that i made a ImageAdapter class. When i open my GridViewActivity my app crashes by saying "Out of Memory: Bitmap size exceeds VM Budget". Here is the code.
ImageAdapter.java
public class ImageAdapter extends BaseAdapter {
Context context;
Integer[] imageIDs = { R.drawable.lw01_04_001, R.drawable.lw01_04_002,
R.drawable.lw01_04_003, R.drawable.lw01_04_004,
R.drawable.lw01_04_005, R.drawable.lw01_04_006,
R.drawable.lw01_04_007, R.drawable.lw01_04_008,
R.drawable.lw01_04_009, R.drawable.lw01_04_010,
R.drawable.lw01_04_011, R.drawable.lw01_04_012, };
public ImageAdapter(Context ctx) {
context = ctx;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return imageIDs.length;
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(context);
imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(5, 5, 5, 5);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(imageIDs[position]);
return imageView;
}
}
GridViewActivity.java
public class GridViewActivity extends ActionBarActivity {
private Utils utils;
private ImageAdapter imageAdapter;
private GridView gridView;
private int columnWidth;
Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grid_view);
context = this;
gridView = (GridView) findViewById(R.id.grid_view);
utils = new Utils(this);
// Initilizing Grid View
InitilizeGridLayout();
imageAdapter = new ImageAdapter(context);
// setting grid view adapter
gridView.setAdapter(imageAdapter);
}
Your images are probably too large. When you give an ImageView an image resource ID, the ImageView generates a bitmap of that resource, then holds it in memory. While most modern devices are built with enough memory to easily handle multiple large images, Android allocates a very small amount of that memory to each app. This means that you need to be clever when you work with images, especially when you need to display many of them.
Here are some options:
Make sure the images in your drawable folder aren't bigger than they need to be (i.e., they should be no larger than the dimensions of the view that's displaying them). This is probably the best option, since in addition to decreasing your memory requirements, you'll also be reducing the size of your resulting APK file.
If you don't want to scale down your images in your drawable folder (for example, you might need to show them in full size if the user clicks them), scale down the images during runtime. This option will require a bit more code, but gives you the flexibility of displaying images exactly how you need to, without running into out of memory issues.
Here's a code example for option #2:
Options opts = new Options();
opts.outWidth = 85; //Based on your size requirement.
opts.outHeight = 85;
Bitmap b = BitmapFactory.decodeResource(getResources(), imageIDs[position], opts);
imageView.setImageBitmap(b);
This should get you started.
Your Bitmaps are probably too big.
I suggest you check this question: Out of Memory Error ImageView issue
this tutorial https://developer.android.com/training/displaying-bitmaps/index.html
Related
This question already has answers here:
Strange OutOfMemory issue while loading an image to a Bitmap object
(44 answers)
Closed 8 years ago.
I have this code of a custom grid view and each time I try to run it, it crashes caused by an OutOfMemoryException. I guess the solution is to resize the images in the array ...
main activity code :
public class AndroidGridLayoutActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.grid_layout);
GridView gridView = (GridView) findViewById(R.id.grid_view);
// Instance of ImageAdapter Class
gridView.setAdapter(new ImageAdapter(this));
/**
* On Click event for Single Gridview Item
* */
gridView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
// Sending image id to FullScreenActivity
Intent i = new Intent(getApplicationContext(), FullImageActivity.class);
// passing array index
i.putExtra("id", position);
startActivity(i);
}
});
}
}
image adapter code :
public class ImageAdapter extends BaseAdapter {
private Context mContext;
// Keep all Images in array
public Integer[] mThumbIds = {
R.drawable.pic_1, R.drawable.pic_2,
R.drawable.pic_3, R.drawable.pic_4,
R.drawable.pic_5, R.drawable.pic_6,
R.drawable.pic_7, R.drawable.pic_8,
R.drawable.pic_9, R.drawable.pic_10,
R.drawable.pic_11, R.drawable.pic_12,
R.drawable.pic_13, R.drawable.pic_14,
R.drawable.pic_15
};
// Constructor
public ImageAdapter(Context c){
mContext = c;
}
#Override
public int getCount() {
return mThumbIds.length;
}
#Override
public Object getItem(int position) {
return mThumbIds[position];
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(mContext);
imageView.setImageResource(mThumbIds[position]);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setLayoutParams(new GridView.LayoutParams(70, 70));
return imageView;
}
}
full screen code :
public class FullImageActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.full_image);
// get intent data
Intent i = getIntent();
// Selected image id
int position = i.getExtras().getInt("id");
ImageAdapter imageAdapter = new ImageAdapter(this);
ImageView imageView = (ImageView) findViewById(R.id.full_image_view);
imageView.setImageResource(imageAdapter.mThumbIds[position]);
}
}
any advice please??
You are attempting to allocate too much memory for image data. As you explained in the comments, your images are fairly large and they are being read as soon as the adapter is created.
Reading this into memory will consume 4 bytes per pixel, so if your images are 1024 pixels square, the math is:
1024 × 1024 × 4 = 4MB
15 images × 4 MB/image = 60MB
This likely exceeds the heap budget of 48MB.
You will likely want to improve the efficiency through which you display these images. Consider:
Only loading the image when it is shown, using GridView's mechanisms for getView();
Loading bitmaps with a sample size using BitmapFactory.
Esp for "..loading the images in a GridView:
you can use inSampleSize to load all images and replace the visible images by the original ones- meaning for the view part that are visible, dynamically load images without the inSampleSize for the BitmapFactory and its Options.
You could scale them down depending on different devices to load the images without facing a memory problem.
In your case, some phones may not exhibit the same behavior on the first run, but eventually, without handling an optimized image loading solution, app will crash.
Topic under Load a Scaled Down Version into Memory.
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Out of memory error on Android
On how to avoid them:
How to avoid an out of memory error while using bitmaps in Android
For an overview:
http://blogs.innovationm.com/android-out-of-memory-error-causes-solution-and-best-practices/
http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html
Before setting a drawable as the background for your imageview, i.e.:
iv.setBackgroundResource(R.drawable.image);
Get a scaled bitmap or try other options like inSampleSize, do check how your solution affects the quality of your image too.
For the comment:
Bitmap drawableImage=BitmapFactory.decodeResource(context.getResources(),R.drawable.image);
Bitmap bitmap = Bitmap.createScaledBitmap(drawableImage, width, height, false);
Drawable drawableScaled = new BitmapDrawable(context.getResources(), bitmap);
I'm trying to make an app that takes videos and then displays the videos in a gridview. I have gotten to the point where all the video thumbnails are showing up in the gridview like they should. However, my problem is that the method I am using has a lot of lag time. It takes some time to get the gridview loaded and when I try to scroll there is always some sort of lag. This is most likely due to the fact that I'm using bit maps for every video thumbnail.
I was wondering what I could do to make this a lot quicker and smoother. My code is posted below.
class VideoAdapter extends BaseAdapter {
Context context;
ArrayList<String> videopathlist;
Bitmap bmthumb;
VideoAdapter(Context c, ArrayList<String> filepathlist){
context = c;
videopathlist = new ArrayList<String>();
this.videopathlist.addAll(filepathlist);
Log.i(TAG, "" + videopathlist);
}
#Override
public int getCount() {
return videopathlist.size();
}
#Override
public Object getItem(int position) {
return videopathlist.get(position);
}
#Override
public long getItemId(int arg0) {
return 0;
}
#Override
public View getView(int posiiton, View arg1, ViewGroup arg2) {
ImageView imageview = new ImageView(context);
bmthumb = ThumbnailUtils.createVideoThumbnail(videopathlist.get(posiiton), MediaStore.Video.Thumbnails.MINI_KIND);
if (bmthumb != null) {
Log.i(TAG1, "There is a thumbnail");
imageview.setImageBitmap(bmthumb);
} else {
Log.i(TAG1, "There is NOT a thumbnail");
}
imageview.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageview.setLayoutParams(new GridView.LayoutParams(160, 160));
return imageview;
}
Thanks for your help
2 things:
Use a ViewHolder to cache everything, as described here: Making ListView Scrolling Smooth (applies to GridViews too)
Also, use an ImageLoader to load images dynamically. There are many available or you can build your own. Here are some:
Volley
Android-Universal-Image-Loader
ImageLoader
All these loaders make use of Bitmap caches as described here: Caching Bitmaps
Also, you could potentially be actually creating the thumbnail bitmaps when returning the view. This strikes me as very expensive. I would do this as a seperate step, unrelated to displaying them. For example, kick off an AsyncTask when your activity is first created to generate any new thumbnails. Then in your adapter just query the MediaStore to get the generated thumbnails.
I programmatically created a GridView of images to be used inside a Dialog. If I set the columns to autofit, I always get exactly two columns, whatever the size of the images is and whatever the screen size is.
If I force the columns to a fixed number, then it works, but I cannot avoid the images to overlap on certain screen sizes, so an automatic management of the number of columns would be much better.
Even more, when I try to set streching, it simply does not show anything!
My ImageAdapter:
public class ImageAdapter extends BaseAdapter {
private Context mContext;
// Constructor
public ImageAdapter(Context c){
mContext = c;
}
public int getCount() {
return (IconC.Res.length);
}
public Object getItem(int position) {
return (IconC.Res[position]);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(mContext);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setLayoutParams(new GridView.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(IconC.Res[position]);
return (imageView);
}
}
And the function that creates the Dialog (that is called elsewhere with a .show() )
private void createPopUp() {
d_icons = new Dialog(this);
GridView gv_icons = new GridView(this);
// gv_icons.setNumColumns(GridView.AUTO_FIT); // autofit disabled, shows ONLY TWO COLUMNS
gv_icons.setNumColumns(4); // instead, autofit forced, now it works, but I don't want it fixed!
gv_icons.setGravity(Gravity.CENTER);
gv_icons.setHorizontalSpacing(3);
gv_icons.setVerticalSpacing(1);
// gv_icons.setStretchMode(GridView.STRETCH_SPACING); // stretching disabled, shows ZERO COLUMNS
gv_icons.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
gv_icons.setAdapter(new ImageAdapter(this));
gv_icons.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick (AdapterView<?> parent, View view, int position, long id) {
eventIcons.set(selectedItem, position);
updateEventView();
d_icons.dismiss();
}
});
d_icons.setTitle(R.string.text_chooseicon);
d_icons.addContentView(gv_icons, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
d_icons.setCancelable(true);
}
Can anyone spot why this happens? Thank you!!
Here's how it appears with the 4 fixed columns: i50.tinypic.com/2ebdfec.png
Here's how it is with the autofit: i50.tinypic.com/25jxo9s.png
Here is the layout with autofit on a horizontal tab: i49.tinypic.com/23r7qj5.png
This post answers your question:
Android: How does GridView auto_fit find the number of columns?
In order for the auto_fit to work you need to specify the column width:
gridView.setColumnWidth( width );
Actually it depends on device size. As you first tested your application with making the no of columns fixed, grid displays only 2 columns because the space for the 3 image is not enough large so that it can fit in.
I have infinite gallery based on this example :
http://blog.blundell-apps.com/infinite-scrolling-gallery/ ,
It runs nicely, now I want to have the Gallery still scroll through the images and under each image there should be a text caption.
I searched net with no result, would you please help me in coding that, just beginner in development.
==================================================================================
NEW Update :
upload photo explane what i need exactly which i get it with normal gallery (applied text to each image and able to customize text too as shown down image ,and each image has diffrenet text than others , but still not succeed to do it with infinite gallery :
PLEASE ANY HELP AND ADVICE .
THANKS ALOT.
I went through Blundell's tutorial and thanks to him I know how to make an Infinitelyscrolling gallery :)
To answer the question, about how to add a text caption below each of the images , I made same small changes to Blundell's nice tut and used some of his suggestions in the above answer and I think I got a nice way of doing the task.
The code below doesnt inflate or use gallery_item.xml at all, so it will increase the performance significantly compared to the way when you are inflating it every time.
Trimmed down code of classes from Blundell's tutorial ( because in the question, you are using only resources and not sdcard).
public class InfiniteScrollingGalleryActivity extends Activity {
public class GalleryItem{
int imageId;
String caption;
public int getImageId() {
return imageId; }
public String getCaption() {
return caption;
}
public GalleryItem(int i,String s) {
imageId=i;
caption=s; }
}
int[] resourceImages = {R.drawable.ic_launcher,R.drawable.ic_launcher,R.drawable.ic_launcher,
R.drawable.ic_launcher,R.drawable.ic_launcher,R.drawable.ic_launcher};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GalleryItem[] item = new GalleryItem[6];
//initialising all items, change member variables according to needs
for(int i=0;i<6;i++){
item[i] = new GalleryItem(resourceImages[i], "pic no" +(i+1)); }
setContentView(R.layout.activity_main);
InfiniteGallery galleryOne = (InfiniteGallery) findViewById(R.id.galleryOne);
galleryOne.setResourceGallery(item);
} }
Here I have added the GalleryItem class array and passed it.
Also added the below code in InfiniteGalley class.
public void setResourceGallery(GalleryItem[] item) {
setAdapter(new InfiniteGalleryResourceAdapter(getContext(), item));
setSelection((getCount() / 2));
}
below code's getView() is where the good things happen :
public class InfiniteGalleryResourceAdapter extends BaseAdapter {
/** The context your gallery is running in (usually the activity) */
private Context mContext;
GalleryItem[] myItems;
public InfiniteGalleryResourceAdapter(Context context, GalleryItem[] item) {
this.mContext = context;
myItems=item;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// convertView is always null in android.widget.Gallery
TextView t = new TextView(mContext);
try {
int itemPos = (position % myItems.length);
t.setText(myItems[itemPos].getCaption());
Drawable d = mContext.getResources().getDrawable(myItems[itemPos].getImageId());
((BitmapDrawable) d).setAntiAlias(true); // Make sure we set anti-aliasing otherwise we get jaggies (non-smooth lines)
//d.setBounds(0,0,60,60); //use this to change dimens of drawable,if needed
t.setCompoundDrawablesWithIntrinsicBounds(null, d, null, null);
} catch (OutOfMemoryError e) {
// a 'just in case' scenario
Log.e("InfiniteGalleryResourceAdapter", "Out of memory creating imageview. Using empty view.", e);
}
return t;
}
#Override
public int getCount() {
return Integer.MAX_VALUE;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
/** The width of each child image */
private static final int G_ITEM_WIDTH = 120;
/** The height of each child image */
private static final int G_ITEM_HEIGHT = 80;
private int imageWidth;
private int imageHeight;
}
In getView(), I am just creating a textView and assigning the drawable to it using the handy t.setCompoundDrawablesWithIntrinsicBounds(null, d, null, null); . So it excludes the need of inflating layouts which is a heavy operation.
Below is the output image:
In the adapter you can see the method: getView, you can see this method returns an ImageView, so now you want the getView method to return an imageview and textview...
U can do this in a few ways, here how you can do it with a LayoutInflater
View v = getLayoutInflater().inflate(R.layout.gallery_item, null);
((ImageView) v.findViewById(R.id.img)).setImageResource(imageIds[position]);
((TextView) v.findViewById(R.id.caption)).setText(captions[position]);
So in your res/layout folder you should have an 'gallery_item' layout that contains an ImageView (img) and a TextView (caption)
i.e.
gallery_item.xml
<LinearLayout>
<ImageView ... />
<TextView ... />
</LinearLayout>
Hope this was helpfull!
EDIT
so as the above example shows you would need two arrays, one of imageIds and one of textCaptions. To keep your Adapter nice and clean it's screaming for you to make an object.
public class GalleryItem {
int imageId;
String caption
// Constructor
// getters and setters
}
You could then pass an Array or List of your GalleryItems to the Adapter (replacing the setAdapter method). i.e:
GalleryItem[] items = new GalleryItem[];
Then in your getView method as outlined above you would extract each object:
GalleryItem item = items[position];
View v = getLayoutInflater().inflate(R.layout.gallery_item, null);
((ImageView) v.findViewById(R.id.img)).setImageResource(item.getImageId());
((TextView) v.findViewById(R.id.caption)).setText(item.getCaption());
Hope thats clear
I'm implementing a application that allows user to drag pages though a Gallery to read a newspaper.
I wrote a Adapter that returns ImageViews with some Bitmaps loaded on application.
My problem is, the first image shows perfectly, the next ones has a gray foreground that make the image looks darker. After dragging somewhere the first one also get this foreground. How can I avoid my views getting this foreground?
Now I realized that the ImageViews are getting some value on alpha channel, this is why together with my black background the images look darker. How can I avoid it?
As solicited, here it's my code:
Gallery view = new Gallery(context);
view.setSpacing(10);
view.setAdapter(new PageAdapter(context, pages));
LayoutParams params = new LayoutParams(600, 300);
layout.addView(view, params);
And the Page Adapter
private class PageAdapter extends ArrayAdapter {
private List pages;
public PageAdapter(Context c, List<Page> pages) {
super(c, 0);
this.pages = pages;
}
public View getView(int position, View convertView, ViewGroup parent) {
Page page = getItem(position);
ImageView image;
if (convertView != null)
image = (ImageView) convertView;
else
image = new ImageView(context);
image.setImageBitmap(ImageUtils.rgb(page.loadNormal()));
return image;
}
public int getCount() {
return pages.size();
}
public Page getItem(int position) {
return pages.get(position);
}
}
This ImageUtils.rgb was created to create a copy of any Bitmap into a Bitmap.RGB_586, but the same behaviour occurs.
You need to specify the unselectedAlpha property on the Gallery.
Either via your java (as per your example)
view.setUnselectedAlpha(1.0);
Of if you are using xml to declare the Gallery, add
android:unselectedAlpha="1.0"