Droidin' out tips and tricks from an android developer

27Apr/120

How to properly use the viewHolder pattern on a listView adapter with multiple viewTypes.

Posted by noslen

The viewHolder pattern is great for making your listViews feel more responsive. Examples abound.
It seems to work great up until you start doing more complex things with it. Things can get a bit tricky once you have more than viewType that your adapter needs to provide.
Consider this code:


package com.oner.test;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class Adapter_testActivity extends Activity {
	private ListView mListView;

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

		mListView = (ListView) findViewById(android.R.id.list);
		mListView.setAdapter(new AdapterTest((Context) this));
	}

	private class AdapterTest extends BaseAdapter {
		String[] items = { "a", "b", "c", "d", "e", "a", "b", "c", "d", "e",
				"a", "b", "c", "d", "e", "a", "b", "c", "d", "e", "a", "b",
				"c", "d", "e", "a", "b", "c", "d", "e", "a", "b", "c", "d", "e" };
		private Context mContext;
		private LayoutInflater inflater;

		public AdapterTest(final Context context) {
			mContext = context;
			inflater = LayoutInflater.from(mContext);
		}

		@Override
		public int getCount() {
			return items.length;
		}

		@Override
		public Object getItem(int position) {
			return items[position];
		}

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

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View v = convertView;
			ViewHolder holder;

			int type = getItemViewType(position);
			Log.d("Adapter test","setting type:"+type);
			if (v == null) {

				holder = new ViewHolder();
				if (type == 0) {
					v = inflater.inflate(R.layout.item_type_one, null);
					holder.txtOne = (TextView) v.findViewById(R.id.txt_one);
				}else if(type ==1){
					v = inflater.inflate(R.layout.item_type_two, null);
					holder.txtTwo = (TextView) v.findViewById(R.id.txt_two);
				}
				else {
					v = inflater.inflate(R.layout.item_type_three, null);
					holder.txtThree = (TextView) v.findViewById(R.id.txt_three);
				}
				holder.type = type;
				v.setTag(holder);
			} else {
				holder = (ViewHolder) v.getTag();
				Log.d("Adapter test", " holder ::" + holder);
			}
                        String item = (String) getItem(position);
			if (item != null) {
				if (type == 0) {
					holder.txtOne.setText(item);

				} else if(type ==1 ) {
					holder.txtTwo.setText(item);
				}else{
					holder.txtThree.setText(item);
				}

			}
			return v;

		}

		@Override
		public int getItemViewType(int position) {
                        String item = (String) getItem(position);
                        if(item.equals("a"))return =0;
			else if(item.equals("b"))return=1;
			else   (item.equals("c"))return=2;
		}

		@Override
		public int getViewTypeCount() {
			return 3;
		}

	}

	static class ViewHolder {
		public int type;
		TextView txtOne;
		TextView txtTwo;
		TextView txtThree;
		@Override
		public String toString() {
			return "ViewHolder [type=" + type + ", txtOne=" + txtOne
					+ ", txtTwo=" + txtTwo + ", txtThree=" + txtThree + "]";
		}

	}
}

For the purposes of this example the list items being inflated all contain the same TextView with just a different id. I'm sure you get the idea.
Here's what the layout for the items looks like in case you're feeling lazy.




The listView will keep a cache of every view type you give it. The trick to avoid problems is to make sure that you properly implement getViewItemType().

                @Override
		public int getItemViewType(int position) {
                        String item = (String) getItem(position);
                        if(item.equals("a"))return =0;
			else if(item.equals("b"))return=1;
			else   (item.equals("c"))return=2;
		}

A common mistake (my first mistake) was to implement that logic inside of getView instead of getItemViewType. Prior to getting one of the view types out of it's cache the listView will call getItemViewType for its cache lookup, so you need to make sure it knows how to do so. Also make sure that your getItemId return something other than just 0. This will also make sure things work smoothly.

In my example I determine the item type by looking at the data.
After that it's a simply matter of looking at the type to re-populate the viewHolder.

I hope this helps. Thanks to Simon and Noah for helping me work through the problems.

Filed under: android, general No Comments
10Apr/120

eclipse – autocomplete has stopped working with android sdk

Posted by noslen

After my recent update to the latest ADT the autocomplete features of eclipse suddenly stopped working.
Luckily a quick search lead to the indispensable stackoverflow.

The way to fix it is to go to:
Window -> Preferences -> Java -> Editor -> Content Assist -> Advanced

And check the boxes labeled "Java Proposals" and hit Apply.

Thanks to dchimento for the answer.

Link to the question / answer here:

eclipse - autocomplete has stopped working with android sdk - Stack Overflow.

Filed under: general No Comments
4Apr/120

The Gmail Public Labels API | Android Developers Blog

Posted by noslen

This is pretty cool. I'm gonna have to play with it.

The Gmail Public Labels API | Android Developers Blog.

Filed under: general No Comments
3Jan/120

++dave;: Cucumber for Android!

Posted by noslen

Very useful post that's helping us probe the mysteries of android + cucumber.

++dave;: Cucumber for Android!.

Filed under: general No Comments
13Dec/110

How to control orientation changes for fragments in a FragmentActivity with multiple fragments

Posted by noslen

I wasted several hours trying to fix this problem so once again i'm paying it forward in the hope it's helpful to someone else.

The problem is that it's difficult to control configuration changes in a fragment activity with multiple fragments when you need fragments to behave differently.

Here is my scenario:
I have a fragment activity that displays different fragments one at a time. The first fragment displayed is not allowed to change orientation, it must be locked in portrait.
However the next fragment I show in the same activity needs to be able to change on both portrait and landscape orientations.

Sure I could have just rewritten my code to have the other fragment start in a different activity but that's besides the point.

Anyway, what you have to do is make sure you can get configuration changes

Manifest code:



Activity code:

//
//
	@Override
	public void onConfigurationChanged(Configuration newConfig) {

		if(fragment!=null && fragment.isResumed()){
			//do nothing here if we're showing the fragment
		}else{
			setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT); // otherwise lock in portrait
		}
		super.onConfigurationChanged(newConfig);
	}

The code above doesn't work by itself. You would think so but there's a problem where you never receive more onConfigurationChanged callbacks after you call setRequestedOrientation.

Luckily we can take advantage of the lifecycle of our fragment to help our activity get out of it's funk.
When our fragment resumes we can do the following:

Fragment code:

//
//
@Override
	public void onResume() {
		getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
..}

I also make sure that my activity goes back to portrait mode if I exit this fragment.

//
//
	@Override
	public void onPause() {
		getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); // set the activity back to //whatever it needs to be when going back.
		super.onPause();
	}

This ensures that the activity can go back to being stubborn about orientation changes while letting an individual fragment take over configuration changes while it's active.
I hope this is helpful! cheers!

Filed under: general No Comments
11Sep/110

More on testing automation in Android

Posted by noslen

Test automation has been a particularly interesting topic for me in the past few weeks. I wanted to share some of the info I've gathered hoping that it will be useful to someone else. In my previous post I talked about Testdroid which makes use of robotium and also can make MonkeyRunner tests The plugin provided by Testdroid is essentially a way to record and script UI events.
It will generate test cases for you where you can then go in and add assertions. It's pretty neat.

Now i'd like to talk a little bit about Robolectric. Robolectric is created and maintained by folks at Pivotal labs. Robolectric runs within your VM which makes it very cool.
This means you don't need the emulator to run tests. That's pretty cool! I definitely recommend giving this a try. I'm going to deep dive into Robolectric to use on my current personal project.
I'll post some of those results later.

Filed under: general No Comments
5Sep/110

Testdroid – a great test solution for Testing Automation in Android

Posted by noslen

Because of fragmentation in Android it is very important to test you apps in as many devices as you can get your hands on. You've heard it all before right?

So have I. Testing mobile apps still remains quite a daunting task. For larger projects such as the one I'm currently working on Test Automation becomes a necessity very quickly. It is simply too time consuming, error prone and difficult to test manually. So what can you do? What tools are available to help you automate UI testing?

Luckily, Android comes to the rescue. Android's testing framework is top notch and easy to work with. They make life worth living.

Android's testing toolset is based on JUnit and is very well integrated for android projects into Eclipse. Android instrumentation provides a way to hook into android components and load applications.

The testing framework also provides a way to create Mock objects to help you isolate parts of your application for specific tests.

You also get the monkeyrunner tool, a python framework for executing android apps.

All in all you get a very powerful set of tools that can enable you to produce detailed and efficient testing solutions.

But wait! there's more! Since of course this framework is so great, there are developers building great testing solutions based on these tools. One that i'm starting to dig into is robotium. Robotium is a test framework  aimed at test case developers to write function, system and acceptance tests scenarios for apps.

Adding yet more value to this are companies like bitbar who provide a plugin for Eclipse called testdroid which makes recording a UI test of your app, child's play. Very cool and worth checking out!

Filed under: general No Comments
7Jul/110

Lawrys Digital Dinner Bell – Android Market

Posted by noslen

My first major android app!

Over 10k downloads already!

Lawrys Digital Dinner Bell - Android Market.

Filed under: android, general No Comments
25May/110

And AA steps it up a notch

Posted by noslen

image

Filed under: general No Comments
15May/110

adb command line: How to launch an app from adb

Posted by noslen

If you ever need to launch an app from the adb command line here's how.

First, if you haven't added the platform tools to you %PATH% environment you should. It's a great time saving shortcut.

Ok now that you're cool like us on to the adb command:

You need to use the am command which is an adb shell command. adb has its own set of commands, adb shell has its own other set.

So what does the syntax for the am command look like? First i'll show you the command and then i'll explain it.

Ok so there it is. adb shell am start -a android.intent.action.MAIN -n com.rga.sony/.ZeusMain

Here is also a pretty great explanation:

http://www.anddev.org/using_the_am-tool_start_activities-intens_from_a_shell-t368.html

 

Filed under: android, general No Comments