Monday, March 12, 2012

Tables and cell selections: iOS UITableView vs Android ListView



One of the first things every dev learns when he/she starts developing on a mobile platform is how to present sets of data. They could be a list of shops, an array of products and so on.
Both Android and iOS have a component to manage this kind of data: ListView for the former, and UITableView for the latter.

These classes surprisingly offer a similar interface, and have similar ways to provide the datasource to it.

Both platforms have a component to show the elements on screen and a datasource (or adapter) that manages the presentation of the tableview cells, the cells reuse and how data are presented (in which order).

iOS has a protocol, UITableViewDataSource, that every class that wants to provide some data to a UITableView must implement. The key method used to provide the tableView cells is

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

On the other side, Android datatsource is a class that extends ListAdapter.
Also this class has a method that must be always implemented:

public View getView (int position, View convertView, ViewGroup parent) 


In both platforms it is very important to reuse the cells: with iOS 5 the cell recycle is managed by the system, in case the cell is declared in the .xib file as the default subview of the UITableView. It is very important though to specify a cell-id in code that matches the one declared in the .xib file.


Android use a system quite similar to that used by iOS 4.x and lower. You fetch a view inflating it from an xml file only if the view provided by the framework is null (i.e. there isn't currently a reusable cell).


This is a typical iOS 5.0 implementation:



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellId"];
    cell.textLabel.text = [_list objectAtIndex:indexPath.row];
    cell.selectionStyle = UITableViewCellSelectionStyleGray;
   
    return cell;
}


While this one is an Android implementation of getView():



public View getView (int position, View convertView, ViewGroup parent){
    LinearLayout layout;
    if(convertView == null){
    LayoutInflater inflater = (LayoutInflater)context.getSystemService(LAYOUT_INFLATER_SERVICE);
    layout = (LinearLayout)inflater.inflate(R.layout.cellnull);
    }
    else{
    layout = (LinearLayout)convertView;
    }
    
   ((TextView)layout.findViewById(R.id.cell_title)).setText(dataSource.get(position));
    
    return layout;
    }

The highlithed row shows the comparision made to establish if a convertView is available. If not, it is inflated from the XML layout file.

Another important issue to face is how to customize the list selector.
In iOS it is a very straightforward procedure: just specify the selection style, or if you want a different color, create a background view with the color you want as its background and assign it to the selectedBackgroundView property (see: http://stackoverflow.com/questions/1998775/uitableview-cell-selected-color).

On Android, a premise is necessary. Every component (button, tetview, and also cell views) has different states that define how the rendering engine should show them on screen. The states are: pressed, focused, selected, enabled.
Concerning the ListView component, the system provides a way to select cells that is specified by a property called listSelector. The listSelector is an xml file with a set of rules, similar to the rules you specify on a css web page, that define how an item is shown in a set of possible states.
I've found very difficult to customize this behavior. Every time I tried to customize the cell selection appeareance, I came across many different issues: some times the selection changed the appeareance of all the tableview items, some other times the cell divider disappeared from screen.

In the official samples app there isn't a single example on how to customize such property, and also watching the World of ListView Google IO Session didn't helped me a lot (see link here : http://www.youtube.com/watch?v=wDBM6wVEO70 )

So I came up with a simple yet effective solution: get rid of the listSelector property by imposing a transparent view for every possible state, and by modifying the background of the cell items instead.

This is the selector I've used:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<!--    <item android:drawable="@color/test_color" android:state_pressed="true" android:state_selected="true"></item> -->
<!--     <item android:drawable="@color/test_color" android:state_pressed="false" android:state_selected="true"></item> -->
    <item android:drawable="@color/test_color" android:state_pressed="true" android:state_selected="false"></item>
    <item android:drawable="@color/black" android:state_pressed="false" android:state_selected="false"></item>
</selector> 

You can also uncomment the first two rows if you want to manage the selected state (it is useful if your device has a D-Pad).









Sunday, March 4, 2012

Eclipse: Java compiler compliance

If you want to avoid build errors on the @Override annotation, you must change the Java compiler compliance.

The steps to do so:

Preferences -> Java -> Compiler -> JDK Compliance

and set it to 1.6

Eclipse and Android: projects build order

When you work on a project it may happen that at a certain point you include other projects in your workspace and you want to include them as dependencies.

The build order is very important and you have to specify it correctly otherwise the Eclipse IDE will generate compile errors almost every time the project is re-built (i.e every time you open Eclipse or you get the latest version from a repo).

To customize the build order in Eclipse:

General -> Workspace -> Build Order