Showing posts with label XML. Show all posts
Showing posts with label XML. Show all posts

Friday, July 12, 2013

Dynamic resize of views and offscreen view size calculation in LinearLayout

The layout is usually handled by a set of xml files, where we specify how the components of our view group are displayed.
It should be the preferred way of generating and composing the views. It is possible to merge layouts, to inflate custom views etc.
There are some occasions, though, where the xml syntax and attributes are not powerful enough.
For example, we can have a vertical linear layout where, at some point, we inflate a custom view. We don't know how big (and, above all), tall is this view at compile time. The problem is, all or some views below it can disappear because our "big" view takes all available space.

A solution might be to wrap the views in a relative layout. All good, all fine, in that way we can specify that the big view must be below a child, and above another. Then we align the first view to the top, and the last to the bottom.

But the problem is that the height of the relative layout can't be wrapped!
As stated also in the documentation (which should have read before spending a couple of hours in adapting the layout :P)
Note that you cannot have a circular dependency between the size of the RelativeLayout and the position of its children. For example, you cannot have a RelativeLayout whose height is set to WRAP_CONTENT and a child set to ALIGN_PARENT_BOTTOM.
The only way is adjusting the layout at runtime: we must calculate the height of the big view  and if its bottom point is at the end of the layout and there are still views to draw, then we need to calculate their value and resize it accordingly.

A problem is that we can't get simply the height of the offscreen views! Since they are offscreen, Android will give us 0 as the height.

What we can do, is using the class called View.MeasureSpec and calculate the view size like this:



Then, we need to find a way to adjust the size of the big view; again, we can't do that in onCreate() or other similar callbacks, as the view is not rendered yet and we will get 0 as height of the view.
We could override the View.onMeasure() method but as it gets called multiple times by the framework, it is not very reliable.
Best thing is adding a ViewTreeObserver and do all the calculations in its callback:



The code of this example is available here:
https://github.com/nalitzis/TestDynamicViews




Thursday, July 12, 2012

Android fragments for beginners

Fragments have been introduced in Android with the Honeycomb milestone (aka Android 3.0). The main reason was to provide a flexible and powerful solution to deal with different kind of screens (i.e tablets and phone displays).

An activity layout is composed by one or more viewgroups, at the end, by simple views. Every view group is also called layout. An activity has one root layout, usually set with the setContentView(int resId) call.

If a layout is composed by many sublayouts/views, having all the UI code and the application logic code for the activity inside one class might quickly increase the complexity and lower the maintainability of the component.

A solution I've used in the past was to subclass the layout class and create a custom layout where I put all the UI code related to such layout. This approach has a drawback: a view group is not an activity. You've to put the app logic code inside the activity and consiquently to provide some sort of communication channel between the view and the activity when, for instance, you want to react to user actions (tap on a button, typing of a text). Components became tightly coupled and difficult to maintain/change.


With Honeycomb (but also in previous releases of the platform, thanks to the compatibility package), you can use fragments. These are like layouts but they have their own lifecycle, they have been created with the aim to contain both UI and app logic code, and can be used to provide an efficient way to deal with layouts that must be placed in different positions depending on the device the app is running on, or on the orientation of the device itself (portrait, landscape).




I've create a simple test project that shows how a layout can be created by different fragments that place themselves in a different way based on the orientation of the device.


The activity has provided a main layout for portrait and another for landscape. Fragments are placed in the LinearLayout in a different way depending on the orientation of the device:





The app logic that manages the fragment is inside the fragment itself:



As you can see, the fragment has framework methods that allow to save the current state. In the sample, a simple counter value is saved and if you change device orientation its valued isn't lost. As you can see, all the application logic pertaining the fragment is inside the fragment. The activity can handle other higher level tasks, such as fragment repositioning/move or orchestrating tasks affecting different portions of the UI.





The sample code can be found here:

https://github.com/nalitzis/SampleFragments

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).









Saturday, February 25, 2012

NFC, Android, accelerometer and Node.JS can transform your mobile in a brush!

Last saturday I went to Bologna with two colleagues (@robb_casanova as frontend artisan/dev and @emme_giii as mobile UX guru), and together we participated in a contest called HackReality. http://www.whymca.org/evento/whymca-hack-reality-bologna-04-02-2012

Our intent was to mix up some native and web based technologies to transform a mobile phone into a brush, and a wall into a virtual canvas where a user, with its Android phone, could draw multi colored traits.

We used some NFC tags sticked on a paper-made palette:



We used my Galaxy Nexus as a "brush": by tapping the phone on a NFC tag on the palette we changed the paint color. Then we used the accelerometer data to detect accelerations on X and Y axis.
Data were sent to a server running socket.io on top of Node.js. We finally implemented a canvas and we used Processing language to perform the actual drawing.



The source code for the client-side part of project is available at github: https://github.com/nalitzis/hackday_client

If I'll have time, in next posts I will explain how the NFC part works.

Sunday, December 4, 2011

Android PowerManager and wake locks

Today I've tested the new version of my DroidRunner Android app that now relies on a background service to track gps location updates.
To my surprise when I finished my run, I've discovered that the GPS signal had been lost many times.
That was strange since with the previous versions the GPS tracking was much more precise.
After some googling I've found that I had forgot to add a wake lock that prevents the CPU from going asleep. In previous versions that was happening much less frequently maybe because I didin't had any background service and as there was always an activity running, probably would prevent the system from going in a sleep mode.

To add a wakelock just setup a new "use-permission" in AndroidManifest.xml:


<uses-permission android:name="android.permission.WAKE_LOCK"/>

Then, just decide which level of wake lock you want to have (for example, prevent CPU from going to sleep, with a PARTIAL_WAKE_LOCK).
Here is the list of possible options: PowerManager

This is the code snipped that can be used to request a wake lock:

PowerManager powerManager = (PowerManager)this.getApplicationContext().getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Service");

Finally, request the wakelock and release it as soon as you don't use it:

wakeLock.acquire();

...
wakeLock.release();