Pages

Showing posts with label example. Show all posts
Showing posts with label example. Show all posts

Sunday, April 6, 2014

SQLiteDatabase: Handling upgrades

When you're looking for tutorials online on how to create an SQLiteOpenHelper. You will probably find one where the onUpgrade() method drops your whole table and recreates it. In a lot of cases, you don't want the user to lose all their data whenever a database change has been made in a new version of the app.
It would be really bad practice when this would happen.

Now, how can we handle database upgrades then?
For this, let's have a look at the arguments that are passed in the onUpgrade() method. We have our SQLiteDatabase, the version number of the old database and the version number of the new database.

You could use a switch, checking the new version number and run some SQLite ALTER methods in the case block. That way, every time a new version is installed it will only run the ALTER methods of that new version. But when a user switches from version number 1 to version number 3, the changes for version number 2 will not have happened.

Next option is using a fall-through switch, where it jumps in on the right version number and runs all the changes that have happened since then. So suppose the version number is 3, then it will run all the changes for version 3 and version 2. The problem is, if the changes have already been done, it will try to run them again. This might cause a SQLiteDatabaseException for example when you're adding a new column, it might have a duplicate column name.

There's one way to solve this:
Use a fall-through switch and check if the old version number is the same as the case. When this happens, break out of the switch before running the changes.

Here's an example of this:

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  switch (newVersion) {
  case 3:
   if(oldVersion == 3) {
    break;
   }
   db.execSQL("ALTER TABLE " + CONTACTS_TABLE + " ADD COLUMN " + CONTACTS_EMAIL + " TEXT");
  case 2:
   if(oldVersion == 2) {
    break;
   }
   db.execSQL("ALTER TABLE " + CONTACTS_TABLE + " ADD COLUMN " + CONTACTS_AGE + " INTEGER");
   
  }
 }
This method might be helpful to you and your users.

At all costs, try to avoid this if it's possible:
@Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + CONTACTS_TABLE);
        onCreate(db);
    }

Wednesday, January 15, 2014

ImageView ScaleTypes

When there are images that need to be displayed in an Android app, we need to be careful with the way they are scaled. A first basic step is setting the right scaletype in your layout file.

The different scaletypes are:

  • center
  • centerInside
  • centerCrop
  • fitXY
  • fitStart
  • fitCenter (which is the default)
  • fitEnd
  • matrix (can be used for transformations but is not covered here)


This post will just simple show the different scaletypes with a screenshot of how it looks in the app.
Here's the code of my layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/logo"
        android:scaleType="centerCrop" />

</RelativeLayout>

Image larger than its given space


center
centerInside
centerCrop
fitXY
fitStart
fitCenter (default)

fitEnd

Image smaller than its given space



center
centerInside
centerCrop
fitXY
fitStart

fitCenter

fitEnd

Thursday, October 10, 2013

Create Code Template for Log.d in Eclipse and Android Studio

Hi,

I've just found a way to save a lot of time while developing Android apps using Eclipse or Android Studio, by creating a shortcut for the typical Log.d statement.

Eclipse (or ADT)

In Eclipse go to Preferences > Java > Editor > Templates and click on "New...".
This will let you create a new Code Template.

Fill in the form as followed:

  • Name = logd
  • Context = Java statements
  • Select 'Automatically insert'
  • Description = Log.d shortcut
  • Pattern = ${:import(android.util.Log)}Log.d("${enclosing_type}", "${enclosing_method}: ${cursor}");
Press OK to save the template and choose OK to close and apply the changes.

Now go inside your Java-file where you want to place a new Log.d, type "logd" and press CTRL + Space.

The code template will be printed in your editor and you can start writing what you want. For example it will print Log.d("MainActivity", "onCreate: "); and your cursor will be ready after "onCreate: "
It will also automatically import android.Util.Log.

Android Studio (or intelliJ)

In Android Studio it's pretty much the same. Go to Settings and select Live Templates in the list under IDE Settings. I've selected output in the list of Live Templates and pressed the plus sign on the right-hand side to add a new Live Template.

Fill in the form as followed:
  • Abbreviation = logd
  • Description = Prints a Log.d statement
  • Template text = android.util.Log.d("$CLASS_NAME$", "$METHOD_NAME$ (line $LINE$): $MESSAGE$");$END$
  • Shorten FQ names = checked
Next, go to Edit Variables and fill in the table as followed:
  • MESSAGE
    • Default value: "Write a log message"
  • LINE
    • Expression: lineNumber()
    • Skip if defined: checked
  • METHOD_NAME
    • Expression: methodName()
    • Skip if defined: checked
  • CLASS_NAME
    • Expression: groovyScript("if(_1.length() > 23) { return _1.substring(0, 23)} else { return _1}", className())
    • Skip if defined: checked
The reason why I've used a groovy script is because Android Studio will give an error if the tag name is longer than 23 characters.
Last but not least, select an applicable context. I've just selected the whole Java field.
Here's a screenshot of what it should look like


Apply changes and you're done.
Just type logd and hit tab (unless you've changed it to something else). Your Log.d message will be displayed, start typing the preferred message and hit Enter. Here's an example of output:
Log.d("MainActivity", "onCreateOptionsMenu (line 43): Write a log message");
Or
Log.d("MyWayTooLongClassNameAc", "onCreateOptionsMenu (line 43): Write a log message");

Once again, the Log class will automatically be added to the imports.

Good luck with this and don't forget to share ;-)

Wednesday, April 24, 2013

Parcelable versus Serializable

In this post I'm going to talk about the differences between Parcelables and Serializables.

Android Activities are great, aren't they? They allow you to structure your app in a nice and easy way. Switching between two Activities is also really easy. Just write this.startActvitiy(intent) somewhere in your first Activity and you're done.
There's only one thing that's not so easy to do, and that's passing data between these two Activities. If we want to pass data we have to put it in an Intent or a Bundle. This is quite easy for primitive classes such as String or int, but it's a bit harder for other (custom) objects.

If you have some experience in Java, you probably know something about 'serialization'. Since we're using Java in our Android app, we could let our object implement Serializable and we're done, right? Well, not quite. Android provides us with another interface called Parcelable to do this and there's a reason why.

Serializable

First of all let's have a look at our TestObject that implements Serializable.
public class TestObject implements Serializable{



 private static final long serialVersionUID = 643301981519798569L;

 private String mString;

 private int mInt;

 private List<String> mList;

 

 public TestObject() {

  this.mString = "This is a String";

  this.mInt = 777;

  mList = new ArrayList<String>();

  for(int i=0; i<100; i++){

   mList.add(String.valueOf(i));

  }

 }

    //Getters and Setters...

}

The only thing we had to do to make sure TestObject could be passed with an Intent or Bundle, was implementing it with java.io.Serializable and add a generated serialVersionUID. Now let's have a look at the same class but as a Parcelable.

Parcelable

This is our TestObject, implementing android.os.Parcelable.

public class TestObject implements Parcelable{



 private String mString;

 private int mInt;

 private List<String> mList;



 public TestObject() {

  this.mString = "This is a String";

  this.mInt = 777;

  mList = new ArrayList<String>();

  for(int i=0; i<100; i++){

   mList.add(String.valueOf(i));

  }

 }

 

 public TestObject(Parcel in) {

  mString = in.readString();

  mInt = in.readInt();

  mList = new ArrayList<String>();

  in.readStringList(mList);

 }



 @Override

 public int describeContents() {

  return 0;

 }



 @Override

 public void writeToParcel(Parcel dest, int flags) {

  dest.writeString(mString);

  dest.writeInt(mInt);

  dest.writeStringList(mList);

  

 }

 

 public static final Parcelable.Creator<TestObject> CREATOR = new Parcelable.Creator<TestObject>() {

        public TestObject createFromParcel(Parcel in) {

            return new TestObject(in); 

        }



        public TestObject[] newArray(int size) {

            return new TestObject[size];

        }

    };

    

    //Getters and Setters...

}
The first thing we notice here is that our class just became a lot bigger. As you can see we have to override two methods, write a Parcelable.Creator and write a new Constructor. Why should you go through all this trouble if you can just simple use Serializable instead? The answer is performance.

Parcelable splits your object up into primitives, sends those primitives and rebuilds the object afterwards. By doing this, it should run a lot faster. Serializable creates a lot of temporary objects and this causes quite some garbage collection, which should always be avoided, especially on mobile devices.

Putting it to the test

Of course, I ran a small test on this. I created an instance of TestObject, added it to an Intent, started another Activity and took our TestObject out of the Intent. I used TraceView for to check how long this process took for Parcelable and Serializable.
Here's our result for using the Serializable interface:


So we can see this whole process took 248ms. 
Now let's have a look at the results of using the Parcelable interface:

As you can see, for only this one object, the process was completed in less than half of the time for our first way. And let's not forget, this is the whole process of switching to another Activity, not just writing and reading the object!

Conclusion

Parcelable works a lot faster than Serializable and is the best way to go. But if we don't need to be faster, and it's possible to just use Serializable (e.g. android.graphics.Bitmap causes problems), just use Serializable and you don't have to bother about anything anymore.

Important notes:

  • Read from the Parcel in the same order that you wrote to it
  • Don't use raw Creators (specify by adding <TestObject> in this example)

Sunday, April 21, 2013

Image Caching with LruCache

Caching images

When we are building an application, whether it's a web app or a mobile app or whatsoever, we want to make sure it runs as fast as possible. One of the most important things we have to keep in mind is caching. If we are using certain resources in our application, we should know that it isn't always that easy to retrieve them. So we should always avoid to retrieve the same resources multiple times. This can be done by caching them. In this tutorial I'm going to give an example of how to cache images in your Android app.

Images can easily get quite large, so retrieving them can take up quite some time. Since mobile devices have limited resources (such as battery life, data transfers, etc), it's twice as important to make sure we cache images the right way.

SoftReferences?

Java already provides an easy way to do this. It's called SoftReferences. You can read all about it here. The only problem is that there is an issue in Android that causes the garbage collector not to respect these SoftReferences. You can read all about this issue here. The Android System provides another way of caching your objects, called LruCache.

LruCache

LRU stands for Least Recently Used. This already tells us a lot about how this cache would work. LruCaches have a fixed cache size and work like a queue, you might say.  If a new object is added to the cache, it's placed in the beginning of the queue. If the app asks to get an object from the cache, it's also placed in the beginning of this queue. This way the least recently used objects are left at the beginning of the queue.



If the cache doesn't have enough memory left, it deletes the least recently used objects until it has enough free space to store the new object(s).


API levels

LruCache was only added to API level 12 but don't worry, they have included this in the support library v4.

Code it!

Okay, it's nice to know how it works but now we still have to implement it in our code. For this tutorial I've made a small app that puts all the thumbnails of the images on the device in a large list. If the user scolls down, the images are loaded. This is actually really simple to implement. First let's have a look on how we could code the LruCache.


public class ThumbnailCache extends LruCache<Long, Bitmap>{



  public ThumbnailCache(int maxSizeInBytes) {

   super(maxSizeInBytes);

  }



  @Override

  protected int sizeOf(Long key, Bitmap value) {

   return value.getByteCount();

   

   //before API 11

   //return value.getRowBytes() * value.getHeight();

  }

  

  

 }

We have to add a value in the constructor of this LruCache. This is the maximum size of the cache in bytes. It can be hard to know how much this should be. You can get some help of the ActivityManager to do this.
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
int memoryClassBytes = am.getMemoryClass() * 1024 * 1024;
If we run this code, we'll get the memory limit of our application. This depends on what device you are using (on a Nexus S and Galaxy SII it's 48MB). Have a look here for more information.
It's obvious that we don't want our image cache to take up all of the memory of my application. So we only take a part of this. For this example let's say we use a sixth.
mCache = new ThumbnailCache(memoryClassBytes / 6);
Now place the image in our LruCache when it is retrieved using this line of code.
mCache.put(photoId, bitmap);

And check if it is in cache before retrieving it like this:
Bitmap cachedResult = mCache.get(photoId);

             if (cachedResult != null) {

                 imageView.setImageBitmap(cachedResult);

             }else{
              //retrieves the thumbnail and puts it in an imageview
              new ThumbnailTask(imageView).execute(photoId);

That's all you need to do. Give it a try!

Saturday, April 13, 2013

Supporting multiple languages

Hi there,

This post is kind of like a follow-up of my previous post on Supporting screen orientations. It's also an example of how to improve your app by using different resource files. If you create a new Android project with the Android SDK Tools, for example in Eclipse, there will be a res/ folder created in the top level of your project. You will also find res/values/strings.xml. This file contains UI strings of your project.

Values

The Android Developers team advises you to place all your UI strings in this file. One of the advantages of this is that you'll be able to add different languages to the project without having to make any changes in your code. The only thing you need to do is create a new values folder in the res/ directory. The name of this folder should be values followed by a hyphen and the two-lettered language code defined by ISO 639-1. For example: values-fr for French. Just copy the strings.xml file from the default res/values/ folder in there and translate the values.

Once this is done you can just get the right String by adding @string/some_example_string in the layout files or R.string.some_example_string in the Java files. The app will check what the device's language is and check for strings.xml file in the right values folder. If it can't find it, it will just use the strings.xml file in the default values folder.

Raw

This is not only applicable to the values folder. For example, I'm building an app for this thesis I'm writing. It will contain pretty large articles so all this text should be available in Dutch (my mother tongue) and English. I've decided to place these text files in the res/raw/ directory. Just like the values folders, I can create a different raw folder for each language. The files are then available by referring to R.raw.some_example_text_file. In the example below, I got my text by putting getResources().openRawResource(R.raw.introduction) in my Java code. This can also come in handy if you have images that should be different in according to the language of the device (example: a flag).

Here's an example of how these files and folders are structured.
Notice that there are other files (dimens.xml & styles.xml) in the default values folder but not in the other ones. This is because I don't need to have different dimensions and styles for using another language.

Hope you liked this post and feel free to comment below!

Tuesday, April 9, 2013

Optimizing your Java code in Android (part 2)

It took me a bit longer than I had hoped, but I was finally able to write this next post in my series on Android Optimization. Some of the things in this post may seem obvious for people that have some experience in Java, but I have noticed that there are a lot of people who haven't seen that much (or none at all) Java code that get started on Android right away. So I'll mention these things in here as well.

I/O streams

I/O stands for Input and Output streams. We use these streams to read from and write to files on the device or on other devices. The classes for this can be found in the java.io package.

Closing streams

The first thing I want to start is closing streams. I just spent quite some time today at work figuring out why some files could not get deleted. The person who wrote the code forgot to close the InputStream of a file :/

So, whenever you are using an Input or OutputStream, always close it afterwards. The best way to do this is in a finally block. This way it will always be closed when the job is done, even if something went wrong. Here's an example:

FileInputStream fileInputStream = null;
  try
  {
   fileInputStream = new java.io.FileInputStream(someFile);
   fileInputStream.read ();
  }
  catch (java.io.FileNotFoundException e1)
  {
   System.out.println("Exception : File not found");
  }
  catch (java.lang.Exception e)
  {
   e.printStackTrace();
  }
  finally
  {
   fileInputStream.close ();
  }

Remember this. This will save you (and others) a lot of trouble.



Buffering

Most of the streams read and write one byte at a time. Unfortunately, this causes bad performance because it takes a lot of time to read/write when you are dealing with large amounts of data. But have no fear, java.io provides Buffered streams that can override this byte by byte behavior.

These classes are BufferedInputStream and BufferedOutputStream. The BufferedInputStream stores all the bytes that the FileInputStream sends until it reaches a limit. This limit is by default 512 bytes, but you can change this. Once this limit is reached, the BufferedInputStream sends the data.

If you want, you can also create a custom buffer. This can be done really easy by adding a byte[] to the constructor. This is probably one of the fastest ways to send a file. It all depends on the size of your file. Since I haven't got a lot of time, I looked for an example of using the streams online instead of writing one myself. But nevertheless it should explain everything.

This first method uses no Buffered stream, so sends everything byte by byte.
public static void readWrite(String fileFrom, String fileTo) throws IOException{

             InputStream in = null;

             OutputStream out = null;

             try{

                          in = new FileInputStream(fileFrom);

                          out = new FileOutputStream(fileTo);

                          while(true){

                                          int bytedata = in.read();

                                          if(bytedata == -1)

                                          break;

                                          out.write(bytedata);

                          }

             }

             finally{

              if(in != null)

                          in.close();

              if(out !=null)

                          out.close();

             }

}

This second method uses a Buffered stream with the default buffer of 512 bytes
public static void readWriteBuffer(String fileFrom, String fileTo) throws IOException{

             InputStream inBuffer = null;

             OutputStream outBuffer = null;

             try{

                          InputStream in = new FileInputStream(fileFrom);

                          inBuffer = new BufferedInputStream(in);

                          OutputStream out = new FileOutputStream(fileTo);

                          outBuffer = new BufferedOutputStream(out);

                          while(true){

                                          int bytedata = inBuffer.read();

                                          if(bytedata == -1)

                                          break;

                                          out.write(bytedata);

                          }

             }

             finally{

              if(inBuffer != null)

                          inBuffer.close();

              if(outBuffer !=null)

                          outBuffer.close();

             }

}

This last methods uses the method available() to define the size of the byte array. This is not always the best way, but it will do for this example.
         

public static void readWriteArray(String fileFrom, String fileTo) throws IOException{

             InputStream in = null;

             OutputStream out = null;

             try{

                          in = new FileInputStream(fileFrom);

                          out = new FileOutputStream(fileTo);

                          int availableLength = in.available();

                          byte[] totalBytes = new byte[availableLength];

                          int bytedata = in.read(totalBytes);

                          out.write(totalBytes);

                            

             }

             finally{

              if(in != null)

                          in.close();

              if(out !=null)

                          out.close();

             }

}          

}

A possible outcome of these methods (depending on the file):

  • the first method took 660 ms
  • the second method took 270ms
  • the third method took only 1 ms
Very important note: Custom buffering takes lot of memory if your file size is large, you should be careful about the memory capability of your system. If custom buffering takes lot of memory, try to reduce the array size so that the memory usage will not be large.

Monday, March 18, 2013

Optimizing your Java code in Android (Part 1)

Hi guys,
This post is a follow-up from my last post on using Traceview for better performance. I explained how to use Traceview to find out which part of the code is slowing down your app. In the example I've written a small method in two ways, a bad way and a better way. Using Traceview we could monitor the usage of the CPU and compare the two parts of code.

I've explained some principles of optimizing your Java code in there. That is:

  • Don't initialize objects in a loop
  • Avoid String concatenation in a loop
  • Use StringBuilder to build a large String.
In this post I'll take some other of these principles for good Java-coding, put them in a small app and compare 'the good way' with 'the bad way' using Traceview.

Use System.arraycopy

In this first example, I've compared two ways of copying an array. First by using a loop, then by using System.arraycopy(). This is the code I've used in the first way:

int[] array = new int[100000];

int l = array.length;

for(int i=0;i<l;i++){

 array[i] = i;

}

  

Debug.startMethodTracing("bonappetit");

int length = array.length;

int[] copy = new int [length];

for(int i=0;i<length;i++)

{

 copy [i] = array[i];

}

Debug.stopMethodTracing();

And this is the code that does the same thing.

Debug.startMethodTracing("bonappetit");

int length = array.length;

int[] copy = new int [length];

System.arraycopy(array, 0, copy, 0, length);

Debug.stopMethodTracing();

I've monitored the both processes with Traceview and saw that the second method ran 3 times as fast.

(To be continued very soon)

Monday, March 11, 2013

Using Traceview for better performance

As some of you might already know, I'm starting a series of posts on optimizing Android apps for my thesis. In this post here, I'm going to give quick preview of how to use Traceview and why it's a great tool to use.

TraceWhat?

What is Traceview and why should I use it? These are the two questions we're going to start with. According to the Android Developers website, Traceview is a graphical viewer for execution logs that you create by using the Debug class to log tracing information in your code. Traceview can help you debug your application and profile its performance.
Basically what it does is it shows you what part of your code is using the CPU a lot. So that already covers the 'what'-part, now let's continue to the why part.

Why should we be interested in knowing how much CPU-time my awesome method needs to run? It's all about performance. Performance is one of the most important topics to focus on, especially in mobile development.We all want our app to be the fastest in the whole Play Store, don't we? By using Traceview properly, we can get a nice graph, showing us what part of our code is slowing down the app the most. Once we know this, we know where to start looking for better performance and get our app running twice as fast. I'll show you in an example below.

Example

For this small example, I'm going to write a simple app that does nothing. Well, nothing interesting that is. This is the code I'm going to run in the doInBackground() method of an AsyncTask.


String result = "";

for(int i=0; i<1000; i++){

 String number = String.valueOf(i);

 result += number;

}

Basically, it just makes on really long String (012345678910111213...).

Now how are we going to make sure that Traceview checks this part of the code? Really simple. All we have to do is add the next two lines somewhere in the beginning and at the end of our code:

Debug.startMethodTracing("bonappetit");

//...

//OUR CODE COMES HERE

//...

Debug.stopMethodTracing();

This will create a file called bonappetit.trace on the SD card of the device. Everything that happens between those two lines of code will be recorded in that file. I've started tracing ath the beginning and the end of the doInBackground() method.
IMPORTANT NOTE: Don't forget to add the permission in the manifest for writing to the SD card!
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

If we open the DDMS perspective in Eclipse, we can have a look at the files on our plugged-in device (or emulator). Select the device on the left side, then go to 'File Explorer' and look into the sdcard-folder for the file. (Mine was under /storage/sdcard0/). Once selected we can click 'Pull a file from the device'. Save the file somewhere on your computer.
Next use your command prompt and go to the folder of your android-sdks. Continue into the tools-folder and type the command 'traceview' followed by the file we just pulled from the device.



If we have a look at the results, we notice that almost 27% of the time was spend on StringBuilder <init>.
In the upper right corner we can see that the whole process took about 800ms, so that's more than 200ms.
If we have a look at our code we can see that we are actually creating a new String object every time in the for-loop. This is why the app spends so much time on StringBuilder <init>.

Now how can we make this better? As a good developer, you should know that using new String objects for creating one long string, isn't a good idea. Java has a class called StringBuilder. Let's try to change our code a bit like this and run the Traceview tool again:

StringBuilder sb = new StringBuilder(4000);

for(int i=0; i<1000; i++){

 sb.append(String.valueOf(i));

}

String result = sb.toString();


If we look in the upper right corner again this time, we can see that the whole process now only took 332ms.
This is more than twice as fast. It is possible to speed this process up even more, but always ask yourself "Does it have to be faster?". Don't waste time on optimizing things that don't need further optimization!

This was an example of Java-optimization, but because Android = Java, I'll make a post on this sort of code optimization as well. Feel free to leave comments below.

Try this out, it could be a great tool for you to use!



source: www.parleys.com

Thursday, December 27, 2012

Using InfoWindowAdapter Part 2: getInfoWindow

As promised, here's the second part on how to use the InfoWindowAdapter in Google Maps Android API v2. This is a sequel to my last blog post and also to the post before that. So be sure to check that out first.

In my last blog I told you how to change the contents of your InfoWindow without changing the default window layout. Now I'll show you how to change that window as well. First of all let me quote something that I read on the Android developers site:
The API will first call getInfoWindow(Marker) and if null is returned, it will then callgetInfoContents(Marker). If this also returns null, then the default info window will be used.
So this means you have to implement the contents of your InfoWindow in getInfoWindow(Marker) as well, if you want to change the window. The API will ignore what's in getInfoContents(Marker) if getInfoWindow(Marker) does not return null.

This is a screenshot of what our InfoWindow should look like:

You'll see I've copied the code of getInfoContents(Marker) of my last post and used this in getInfoWindow(Marker). I also did this with my xml layout file.

First of all, we're going to create this oval shape with the gradient blue background. We'll use this as background of our window. I've named it circle_window.xml and placed it in (one of) my drawable folder(s).


<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >

    <gradient android:startColor="#FFA1A4E3" android:endColor="#AFA1CDED"

            android:angle="270"/>

</shape>

Next I made a new layout file called custom_window.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  android:layout_width="wrap_content"

  android:layout_height="wrap_content"

  android:orientation="horizontal"

  android:background="@drawable/circle_window"

  android:padding="15dp">

  <ImageView

    android:id="@+id/ivInfoWindowMain"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_marginRight="5dp"

    android:adjustViewBounds="true"

    android:src="@drawable/ic_launcher">

  </ImageView>

  <LinearLayout

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:orientation="vertical">

    <TextView

      android:id="@+id/txtInfoWindowTitle"

      android:layout_width="wrap_content"

      android:layout_height="wrap_content"

      android:layout_gravity="center_horizontal"

      android:ellipsize="end"

      android:singleLine="true"

      android:textColor="#ff000000"

      android:textSize="14dp"

      android:textStyle="bold"/>

    <TextView

      android:id="@+id/txtInfoWindowEventType"

      android:layout_width="wrap_content"

      android:layout_height="wrap_content"

      android:ellipsize="end"

      android:singleLine="true"

      android:textColor="#ff7f7f7f"

      android:textSize="14dp"/>

  </LinearLayout>

</LinearLayout>

It's basically exactly the same as in my previous post, except for two things.

  • set the oval shape as background of my layout.
  • set a padding of about 15dp so that our contents is placed nicely within our oval shape
To finish of we need to implement our InfoWindowAdapter like this:

mapFragment.getMap().setInfoWindowAdapter(new InfoWindowAdapter() {

   

   private final View window = getLayoutInflater().inflate(R.layout.custom_window, null);

   

   @Override

   public View getInfoWindow(Marker marker) {

    EventInfo eventInfo = eventMarkerMap.get(marker);

    

    String title = marker.getTitle();

             TextView txtTitle = ((TextView) window.findViewById(R.id.txtInfoWindowTitle));

             if (title != null) {

                 // Spannable string allows us to edit the formatting of the text.

                 SpannableString titleText = new SpannableString(title);

                 titleText.setSpan(new ForegroundColorSpan(Color.RED), 0, titleText.length(), 0);

                 txtTitle.setText(titleText);

             } else {

                 txtTitle.setText("");

             }

             

             TextView txtType = ((TextView) window.findViewById(R.id.txtInfoWindowEventType));

             txtType.setText(eventInfo.getType());

             

    return window;

   }

   

   @Override

   public View getInfoContents(Marker marker) {

    //this method is not called if getInfoWindow(Marker) does not return null

    return null;

   }

  });

As always, if you have any questions, feel free to leave a comment below and I'll see what I can do.

Download source code here

Good luck!

Wednesday, December 19, 2012

Using AsyncTask

A very usefull class in Android is the AsyncTask.

It's an easy way to make your application do something in background and bringing the results of this action to the UI. In this post, I'm going to show you how you can use AsyncTask in your application.
I made a simple App that generates a random number between 0 and 10 every second. After 7 times, it returns the sum of these generated values. In the meanwhile, each time a number is generated, the user will be informed about it.

First of all

Before we start the AsyncTask to do its job, we have to give it some parameters to work with.
While working, we want to see the progress of the AsyncTask.
And of course, eventually, we want to have a result.

If we want to create an AsyncTask, we need to specify these 3 things. Our AsyncTask class will extend AsyncTask<Params, Progress, Result>

In this case, we will make a RandomCountingTask.
The parameters we want to give to the task are the number of times it should generate a number and how large this generated number may be. These are both Integers, in this case 10 the maximum value of a number and 7 for the amount of numbers we want.

The Progress in this case is the generated number, so this is also an Integer.

The Result will also be an Integer, the sum of all generated numbers.

So our task has to look like this:

class RandomCountingTask extends AsyncTask<Integer, Integer, Integer>{ }

If we would write this in Eclipse, we would have to add unimplemented methods. This is the method doInBackground(Integer... params){}


AsyncTask 'Lifecycle'

Since the AsyncTask is created to do stuff in background and bring results and progress to the UI, it has methods on the Main Thread (UI) and methods on a single Worker Thread (Background).
This is how it goes:



Code

Now without further ado, here's the code of this simple example:


package com.bonappetit.demos.asynctask;

import java.util.Random;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class AsyncTaskDemo extends Activity implements OnClickListener {

 private TextView txtMain;
 private Button btnStartStop;
 private AsyncTask<Integer, Integer, Integer> countingTask;
 private boolean isCounting;



 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_async_task_demo);
  setUpViews(); 

 }

 private void setUpViews() {
  txtMain = (TextView)findViewById(R.id.txtAsyncTaskDemoMainText);
  btnStartStop = (Button)findViewById(R.id.btnAsyncTaskDemoStartStop);
  btnStartStop.setOnClickListener(this);
  btnStartStop.setText("Start"); 

 }



 @Override
 public void onClick(View v) {
  if(v == btnStartStop){
   if(!isCounting){
    txtMain.setText("");//clear the text
    countingTask = new RandomCountingTask(); // create a new task
    //a task can be executed only once;
    Integer[] values = new Integer[2];
    values[0] = 10; //number between 0 and 10
    values[1] = 7; //generate 7 times
    countingTask.execute(values);
   }else{
    countingTask.cancel(true);
   }
  }  

 }

 public boolean isCounting() {
  return isCounting;
 }

 public void setCounting(boolean isCounting) {
  this.isCounting = isCounting;
 }


 class RandomCountingTask extends AsyncTask<Integer, Integer, Integer>{


  @Override
  protected void onPreExecute() {
   super.onPreExecute();
   Toast.makeText(AsyncTaskDemo.this, "Started counting", Toast.LENGTH_SHORT).show();
   setCounting(true);
   btnStartStop.setText("Stop");
  }

  @Override
  protected Integer doInBackground(Integer... params) {
   int maxValue = params[0];
   int maxTimes = params[1];
   int sum = 0;
   int randomNumber;
   Random r = new Random();
   for(int i = 0; i<maxTimes; i++){
    randomNumber = r.nextInt(maxValue);
    sum += randomNumber;
    publishProgress(randomNumber);   

    try {
     Thread.sleep(1000); //Sleep for 1 second
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
   return sum;
  }  

  @Override
  protected void onProgressUpdate(Integer... values) {
   super.onProgressUpdate(values);
   txtMain.append("Added " + values[0] + " to the sum\n");
  }



  @Override
  protected void onPostExecute(Integer result) {
   super.onPostExecute(result);
   setCounting(false);
   Toast.makeText(AsyncTaskDemo.this, "Stopped counting", Toast.LENGTH_SHORT).show();
   txtMain.append("Result is " + result);
  }  

  @Override
  protected void onCancelled() {
   super.onCancelled();
   setCounting(false);
   Toast.makeText(AsyncTaskDemo.this, "Counting was cancelled", Toast.LENGTH_SHORT).show();  
  }
 }

}


Important notes

  • There is a limit of AsyncTasks to use in your application (another post on this will follow)
  • An instance of an AsyncTask can only be executed once.
  • Something like Integer... can be one or more integers

Tuesday, December 18, 2012

Hi there!

Hi and welcome to Bon App-etit!

Let me introduce myself:
My name is Fré Dumazy and I'm a 22-year old computer science student from Belgium.
Lately I've been programming quite a lot in Android and that's why I wanted to start this blog.
Since Android names all its versions after snacks, I think "Bon App-etit" would be a good name for the blog.

I've been looking for a lot of good Android tutorials on the web, regarding specific subjects such as using the ActionBar, Google Maps integration, connecting to a remote database, etc.
I've found some good tutorials on these subjects but not on all of them.

I will try to write some good posts on specific subjects with example code, screenshots, etc. whenever I've got the time. So stay tuned!

PS: If there is a certain subject you would like to see in this blog, please place a comment and I'll see what I can do.