Showing posts with label 2D Graphics. Show all posts
Showing posts with label 2D Graphics. Show all posts

Monday, 24 October 2011

Android: Programmatically loading large image/bitmap files

Hello!

The use of imagery in our Apps is practically mandatory. After all, the Man said "make it look great".
Embedding icons and images as resources in an App is trivial and the Resources class makes loading them even easier.

But have you tried lately loading a large image file, let's say like an 8Mp picture? Well if you haven't, try it and you'll see that there's not enough memory for that. And don't ever embed a picture that large as a resource in your app.

So how can we load something that we can't load because there's not enough memory? Very carefully and in steps.

1) Loading bitmaps
The Android Bitmap class possesses several factory methods to create bitmaps but unfortunately it does not solve our problems - and I understand is also not recommended. What is left to us is the super awesome BitmapFactory class.
To create a bitmap object using BitmapFactory just call one of its several decode* factory methods, for instance:

...
Bitmap testBitmap=BitmapFactoryClass.decodeResource(resources, R.drawable.myimage);
// do stuff with testBitmap
// recycle testBitmap
...

this creates a Bitmap from the resource  myimage and stores in testBitmap. We can also load from a file on, let's say, the SD card

...
Bitmap testBitmap=BitmapFactoryClass.decodeFile("/mnt/sdcard/picture.jpg";
// do stuff with testBitmap
// recycle testBitmap
...


if we do that to load a 3000x4000 pixels large picture, it will blowup in our faces. It will probably run out of memory loading an image half this size.

To get around this problem, we need to sample the image during the decoding phase.

2) Sampling a large bitmap
the code snippet below does all the hard work of sampling an image and returning it as a Bitmap object.
A few notes before you use it:

BitmapFactoriy.Options is the secret weapon, more specifically the field inJustDecodeBounds. Setting it to true will tell the framework to not try loading the image, instead just return its size (returned in options.outHeight and options.outWidth). Once we have the dimensions of the image, we can read it scaled down by a factor defined in the field inSampleSize.

The SDK recomends using inSampleSize in powers of 2 so the line

int sampleSize=(int) Math.pow(2, Math.floor(Math.sqrt(factor)));
 
tries to find the best power of 2 close to the sample rate you requested. It's simplistic but works  fine for almost all cases. My suggestion is you find what's best for your app. I recommend sampling the image to a bigger size than what you really want and then use Bitmap.createScaledBitmap to set the image to the size you want.
  
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeFile(fileName, options);
     
long totalImagePixes=options.outHeight*options.outWidth;
totalScreenPixels=__some_maximum_number_of_pixels_supported;
   
if(totalImagePixes>totalScreenPixels){    
 double factor=(float)totalImagePixes/(float)(totalScreenPixels);
 int sampleSize=(int) Math.pow(2, Math.floor(Math.sqrt(factor)));
 options.inJustDecodeBounds=false;
 options.inSampleSize=sampleSize;
 return BitmapFactory.decodeFile(fileName, options);
}
      
return BitmapFactory.decodeFile(fileName); 
 
Also, not this the variable sets the maximum number of pixels (i.e, total bytes used) that we want the sampled image to have.
You can use an approach like that or just find a sample size based on your needs. Again, it's simplistic but works majority of cases.

To recap, all  you need is to set inJustDecodeBounds to true, call decodeFile, set inSampleSize to the desired value (power of 2 preferably) and call decodeFile again.

Have fun!

Tuesday, 27 September 2011

Android: Creating a transparent/translucent applications (activity)

Recent posts:
Android: Localizing your Android app - Part 1
Android: displaying widgets in a grid-like layout
Android: displaying widgets in a grid-like layout - Part 2, The TableLayout
Android: Getting all and maximum megapixels supported by the camera
Android: Taking pictures without a preview window
Android: Sending emails without user intervention

I get this question all the time and the solution is very simple.
Just set the theme of the app to one of the following:

  • Theme.Translucent
  • Theme.Translucent.NoTitleBar
  • Theme.Translucent.NoTitleBar.Fullscreen

To do that, just edit the manifest file and add, in the application section, android:theme="@android:style/Theme.Translucent" (or any of the above)

for instance
<application android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@android:style/Theme.Translucent">
...

This will make your app look like this


Theme.Translucent.NoTitleBar and Theme.Translucent.NoTitleBar.Fullscreen  produce


I think Theme.Translucent.NoTitleBar.Fullscreen was supposed to hide the system's status bar but what happens is that the status bar is never hidden. When using any of the *Fullscreen themes, the app window just extends on top of the status bar.

Have fun.


---
Programming tricks and tips for android developers

Friday, 23 September 2011

Android: displaying widgets in a grid-like layout - Part 2, The TableLayout

Continuing the layout series, I'll talk about how to display content in a grid style using TableLayout.

To make use of the TableLayout we need 2 elements: TableLayout and TableRow. One TableLayout element can have multiple TableRows. Each TableRow represents a row (I'm so obvious) and everything we put inside a TableRow creates 1 column.

For example

<TableLayout android:layout_width="match_parent" android:layout_height="wrap_content">
  <TableRow android:layout_width="wrap_content" android:layout_height="wrap_content" >
    <TextView android:background="#555555" android:text="Cras in eros nunc" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
    <TextView android:background="#999999" android:text="non viverra justo" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
  </TableRow>
</TableLayout>


this layout snipped will produce this

Each text view is one column. I again changed the background colour just to make it easier to differentiate each column.

and when I add 2 more TextViews like this


<TableLayout android:layout_width="match_parent" android:layout_height="wrap_content">
  <TableRow android:layout_width="wrap_content" android:layout_height="wrap_content" >
    <TextView android:background="#555555" android:text="Cras in eros nunc" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
    <TextView android:background="#999999" android:text="non viverra justo" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> 


    <TextView android:background="#555555" android:text="1" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
    <TextView android:background="#999999" android:text="01/01/2011" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
  </TableRow>
</TableLayout>



I get this

2 more columns, which is pretty cool, except for the fact they look like a single string of text.

To make the columns use all available width, we just need to the table which columns we want to "stretch" changing the attribute android:stretchColumns. This attribute takes a list of columns indices (zero-based), separated by commas if you want to stretch more than one.


For instance, I want to give more space for the last two columns (2 and 3)

<TableLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:stretchColumns="2,3">
  <TableRow android:layout_width="wrap_content" android:layout_height="wrap_content" >
    <TextView android:background="#555555" android:text="Cras in eros nunc" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
    <TextView android:background="#999999" android:text="non viverra justo" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> 


    <TextView android:background="#555555" android:text="1" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
    <TextView android:background="#999999" android:text="01/01/2011" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
  </TableRow>
</TableLayout>


This makes my table look like this



Much better but still not very good. Let's tell the table to stretch all columns. Just put a * as the value for stretchColumns

<TableLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:stretchColumns="*">

and bang, there you have it


We can also do the opposite, instead of giving more space for columns, we can shrink them using android:shrinkColumns. Let's say that 3rd column ( index 2), the one with the number "1" is using too much space. Changing the TableLayout definition to 


<TableLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:stretchColumns="0,1,3" android:shrinkColumns="2">


produces


To hide (collapse) a column, use android:collapseColumns again passing the indices of the columns you want to get rid of.

And if you want more lines in your table, just keep adding TableRows. You can put virtually anything inside a TableRow just remember that the column with the largest width wins!

Naturally you'll have to find what fits your app best. Remember to test with different screen densities and orientations. Be aware of TABS!!


---
Programming tricks and tips for android developers