Search This Blog

Monday, August 23, 2010

Experience - Configuring Android ListView

I have configured my ListView differently than the standard Android example. This article is what I did and how it was accomplished. The biggest difference between the android example and mine is I didn't want a full screen ListView. This impacted both the code and the visuals of the ListView. In the standard example it is suggested that a ListActivity is created. I on the other hand already have an Activity that defines a screen and is bound to a XML layout file. In the layout file I added a ListView element with the id MessageList. I then reference this ListView in my Activity and add the needed components to it. Below are excerpts from the Activity.

public class MessageActivity extends Activity {

private ListView mMessageList;
//Additional properties ...

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.messagepage);

//Bind XML objects
mMessageList = (ListView)findViewById(R.id.MessageList);
//...

//Additional setup and configuation
//...

//MessageList is a dummy content string array
//located in res/values/strings.xml
String[] mMessages =  

getResources().getStringArray(R.array.MessageList);

//Simple way for example
mMessageList.setAdapter(new ArrayAdapter(this,
android.R.layout.simple_list_item_1, mMessages));

// mMessageList.setAdapter(
// new EfficientAdapter(this, android.R.layout.simple_list_item_1,
// android.R.id.text1, mMessages));
}
}

 

At this point I have a ListView defined on a page with other content. The ListView is connected to an ArrayAdapter which supplies it with dummy content through a string array in res/values/strings.xml. Next I wanted to add a border to the ListView to help define its size and make it clear where the edges of the ListView were. I was surprised that it was so difficult to figure out. There is no border property in ListView which was disappointing. I tried various options and found the solution I like the best is putting the ListView in a FrameLayout. I set the Background color of the FrameLayout to the color I want for my border and set the padding to the desired border width. The results look something like this.

Thursday, August 19, 2010

Experience - Android Orientation Change

I must admit I fell into a bit of a trap on this experience. As a result I will present failed attempts I had at detecting an orientation change and then give the solution I ended up with at the end of the article. My goal was to change the layout of some widgets when in portrait mode. I had designed the screen for landscape mode. Then when I rotated it on the emulator I was unsatisfied with the results. I had set up my layout so that only two LinearLayouts needed to have their orientation set from horizontal to verticle. I then set out to discover the best way to detect a change in orientation.

The first approach I tried was to use an android.view.OrientationEventListener. I used some slightly modified code from this article to test it out. I think that article is great, but I was less than satisfied with the results from OrientationEventListener.  First the emulator appeared to take longer when switching orientation. But the biggest problem was the onOrientationChanged() method was never called! Very disappointing! I assume I forgot to set something up properly, but it is hard without finding some proper documentation. I have also seen various blog articles stating problems with it since 2007 and it still doesn't seem to function as one would think here in 2010. :(

My second approach was to follow this form post. I thought this was a cleaver little trick that just might do the job for me. I got it to work by adding android:configChanges="orientation" to my AndroidManifest.xml file and

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
}

to my Activity. I had to add this method without the @Override compile and run and then add back the @Override. Eclipse was throwing an error at me. Within the newConfig parameter was an orientation property that I used for changing the layout logic. I really liked this option but the downside was onConfigurationChanged() was only being called when changing from landscape to portrait which is all I really needed but I didn't like that it only worked half the time it was suppose to.

The third approach I looked at was from this article. I read the article which I liked and saw the comments about SensorManager's API has been changed three times, and could use at least one more update in my opinion. I thought that I would be able to make it work and was able to until I tried to register the SensorEventListener with the SensorManager. At this point I considered it another failure and maybe a lost cause.

After these three failed attempts I changed my thinking. I had been trying to find a clean and efficient way of detecting an orientation change and changing a couple properties on my layouts. A new thought that I had was what if I just create an entirely new layout to be shown in portrait mode.

These are the steps I used.
I opened up my xml layout that I wanted to change. At the top were some options that look like this.




I changed the config drop down to portrait, and then hit the create button on the right hand side.
The only option I selected was portrait.



and with that I had a new xml that I was editing and setting up for portrait mode. When I run the app in the emulator it chooses the right layout for portrait and the other layout for landscape.
Done deal.
P.S.
    Which turns out to be a good thing because the portrait layout is going to be arranged completely different now. :)

Monday, August 2, 2010

Experience - Android UI Tab View

I have started an Android app at work. I was diving in head first and started with hello views tutorial. The tab view was interesting, but after copy-n-pasting some code and running the app I was less than satisfied with the results. First I was unable to view the XML layout of the Tabs and second there was a null pointer exception. I have seen a couple other notes and questions about this type of problem.

TabWidget causing null pointer
Why do I get a null pointer exception from TabWidget?

This article explains some of the things I did to improve on this issue.

Using XML to layout and define the TabHost and TabWidget I found no way around the null pointer exception. I was able to instead define these in code. The activity that runs on startup is called SampleTab. Below is its source.

package app.tabsample;

import android.app.TabActivity;
import android.os.Bundle;
import android.widget.TabHost;
import android.content.Intent;

public class TabSample extends TabActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

TabHost tabHost = getTabHost();

tabHost.addTab(tabHost.newTabSpec("tab1")
.setIndicator("MSG")
.setContent(new Intent(this, SelectActivity.class)));

tabHost.addTab(tabHost.newTabSpec("tab2")
.setIndicator("ARROW")
.setContent(new Intent(this, ArrowsActivity.class)));

tabHost.addTab(tabHost.newTabSpec("tab3")
.setIndicator("OPT")
.setContent(new Intent(this, OptionsActivity.class)));

tabHost.addTab(tabHost.newTabSpec("tab4")
.setIndicator("EDIT")
.setContent(new Intent(this, EditActivity.class)));

tabHost.setCurrentTab(1);
}
}


For the tab indicator, your choices are: 1) set a label 2) set a label and an icon. Note: api level 4 also adds 3) set a view. For the tab content, your choices are: 1) the id of a View 2) a TabContentFactory that creates the View content. 3) an Intent that launches an android.app.Activity. For simplicity I chose to set a label for the tab indicator and an Intent that launches an activity for the tab content. Note that the Activities need to be listed in the AndroidManifest.xml. Now looking at the first tab which calls the SelectActivity.

package app.tabsample;

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

/**
* @author Administrator
*
*/
public class SelectActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tabstest1);
}
}


This activity simply sets its content to the XML file tabstest1.xml. There we have it a simple and easy to use way to create a TabHost and TabWidget without having a null pointer exception and still being able to see the XML tab content graphically in Eclipse. Just for reference my tabstest1.xml contains the following.

<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="@string/hello" />
<spinner android:id="@+id/Spinner01" android:layout_width="wrap_content">
android:layout_height="wrap_content" android:entries="@array/Spinner_Content">
</spinner>
</textview>
</linearlayout>


Below is a screen shot of my tabstest1 with the layout tab.