Showing posts with label widgets. Show all posts
Showing posts with label widgets. Show all posts

Saturday, December 10, 2011

OutOfMemory errors when populating a Gallery


I've just spent the last couple of hours wrestling with OutOfMemory errors when populating a Gallery widget with photos, and as fun as it's been, I think I've sorted it out and thought I'd share my solution with you.

For starters, the reason we're getting OutOfMemory errors in the first place is that the heap size on Android devices is limited to something like 16 MB on a G1 and 24 MB on a Nexus one.

As soon as you start to work with media files you quickly start to learn that you need to work to manage your memory usage with Android as a result of this limitation.

I would also like to thank direct to device debugging, I couldn't have done this without utilizing it, I would go as far as saying that it is essential in situations like this.

Here is the full code of one of my imageAdapters,  I'm using this to provide the images to my Gallery widget like this:

galleryTop.setAdapter(new ImageAdapterTop(this));


The Adapter class :

public class ImageAdapterTop extends BaseAdapter {
   int mGalleryItemBackground;
   private Context mContext;


    File[] allTopSlices = Utils.getAllTopSlices();  
    String[] fullPathAllTopSlices = new String[allTopSlices.length];
    Uri[] uriAllTopSlices = new Uri[fullPathAllTopSlices.length];


   public ImageAdapterTop(Context c) {
       mContext = c;
       TypedArray a = obtainStyledAttributes(R.styleable.HelloGallery);
       mGalleryItemBackground = a.getResourceId(
               R.styleable.HelloGallery_android_galleryItemBackground, 0);
       a.recycle();
   }


   public int getCount() {
       return uriAllTopSlices.length;
   }


   public Object getItem(int position) {
       return position;
   }


   public long getItemId(int position) {
       return position;
   }


   public View getView(int position, View convertView, ViewGroup parent) {
    int screenHeightPx = Utils.GetScreenHeight(getWindowManager().getDefaultDisplay());
   
    for(int i = 0; i < allTopSlices.length; i++){
    fullPathAllTopSlices[i] = allTopSlices[i].getAbsolutePath();
    }
   
    for(int j=0; j < fullPathAllTopSlices.length; j++){
    uriAllTopSlices[j] = Uri.parse(fullPathAllTopSlices[j]);
    }
   
       ImageView i = new ImageView(mContext);
     
       recycleDrawable(i);


       i.setImageBitmap(Utils.readBitmap(uriAllTopSlices[position].toString()));
     
       i.setLayoutParams(new Gallery.LayoutParams(400, (screenHeightPx-50) /3));
       i.setScaleType(ImageView.ScaleType.FIT_XY);      
       i.setBackgroundResource(mGalleryItemBackground);


       System.gc();
       return i;
     
   }
}



and the referenced recycleDrawable :

private void recycleDrawable(ImageView i) {
BitmapDrawable currentBitmapDrawable = (BitmapDrawable)i.getDrawable();

if(currentBitmapDrawable != null){
currentBitmapDrawable.getBitmap().recycle();        
}
        System.gc();
}



and readBitmap methods:

public static Bitmap readBitmap(String selectedImage) {
Bitmap bm = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;


try {
bm = BitmapFactory.decodeFile(selectedImage, options);
}
catch (OutOfMemoryError e) {
e.printStackTrace();
}

return bm;
}

Probably worth mentioning that the 'options.inSampleSize' reference, which is downsizing the image is pretty much essential when working with large files with the memory constraints associated with current Android devices.

Hope that helps someone out there.


Sunday, January 31, 2010

How to populate a Spinner widget from an Array

Lets say we have an Array of colours, defined in a xml file in our res folder in our Eclipse project, like below.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, droidTest1!</string>
<string name="app_name">droidTest1</string>

<string-array name="colours">
<item>Red</item>
<item>Blue</item>
<item>White</item>
<item>Yellow</item>
<item>Black</item>
<item>Green</item>
<item>Purple</item>
<item>Orange</item>
<item>Grey</item>
</string-array>
</resources>

Spinners are Android's implementation of a dropdown menu, of sorts.
How do we insert these values into a spinner widget?

First, we need to create a layout for our Spinner widget, so it has a user interface.
We could do this is code, but the more accepted method is to define our layouts for Android applications in XML files, located in our res/layout folder in our project.

<Spinner

android:id="@+id/my_colour_spinner"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:drawSelectorOnTop="true”

/>




Then we create an instance of the Spinner class,

Spinner colourSpinner = (Spinner) findViewById(R.id.my_colour_spinner);



Next we create an ArrayAdapter to wrap our xml array (Array of colours) and we put some pre-defined styles.



ArrayAdapter adapter = ArrayAdapter.createFromResource( this, R.array.colors, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);


And we set the ArrayAdapter to our instance of the Spinner class:
colourSpinner.setAdapter(adapter);


And when we run our application:

The closed Spinner Widget



.. When selected shows us out colour options.





Aaah.. Sweet Androidy Success!


Next..

Thursday, January 14, 2010

Including layouts: a working example

Here's a working example of including one layout inside another.

Let me know if you have any issues or questions.
This works with, and probably requires, a AVD version of 2.1 or thereabouts.



contents of droidTest1.java:

package androidforbeginners.droidTest1;

import android.app.Activity;
import android.os.Bundle;

public class droidTest1 extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}


contents of main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="combining layouts"
/>

<include android:id="@+id/cell1" layout="@layout/layout2" />
<include android:id="@+id/cell2" layout="@layout/layout3" />


</LinearLayout>



Contents of layout2.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="100px"
android:background="#0033cc"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="40px"
android:text="layout2"
/>
<CheckBox
android:layout_width="fill_parent"
android:layout_height="40px"
/>
</LinearLayout>


Contents of layout3.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="100px"
android:background="#0066cc"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="layout3"
/>
<CheckBox
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>


Output:



You could also include multiple occurrences of the one layout in your main.xml like this if you wanted:

contents of main.xml (revised):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="combining layouts"
/>

<include android:id="@+id/cell1" layout="@layout/layout2" />
<include android:id="@+id/cell2" layout="@layout/layout2" />
<include android:id="@+id/cell3" layout="@layout/layout2" />
<include android:id="@+id/cell4" layout="@layout/layout2" />


</LinearLayout>


Although if you do this, I can't see a way to reference individual repeating items.
I think include is more including a single layout across multiple Activities.

Let me know in the comments if you know a way.

Till next time: Enjoy!