Pages

Tuesday, October 25, 2011

A New TabHost Perspective

I've been trying to use the TabHost.  If you look it up at the Android API Reference, the entry on any of the associated Views (TabHost or TabWidget) send you to this tutorial.  It begins by briefly mentioning that you can use the Tab set up to flip between views, or entire activities.  Having said this, it chooses the latter, and steps you through a process that includes creating separate Activities and layout files for each tab.

Part way through this process, I found myself wondering why it's such an involved process.  With so much Android UI defined by xml files, why do I have to create all these classes and then hook them up programmatically?

So I went looking for another example.  A Google search for tab demos revealed an alternate example.  Especially surprising is that this simpler example is also on the Android Developer site.  In this approach, you lay out all the tab content in one xml file, and don't have to create any activities for each tab.  Though you still have to create the tabs and connect them to their content in code, which seems un-Androidy.  But overall it's a more straightforward approach if your tab set-up is relatively simple.

Really, I don't know why they link you to the more complex tutorial, but now you know there's an alternative.  Also, whichever approach you use, here's some advice: When you put the TabHost and the FrameLayout in a LinearLayout, remember to set the orientation to vertical.  It would be really embarrassing if you got that wrong and then couldn't figure out where the content went.  I'm guessing.

Wednesday, October 12, 2011

Activity Constructor vs. onCreate

This isn't explicitly stated in many tutorials, but something I've learned from experience.  The onCreate method in Activities is called when the Activity starts, and books show all the set-up work being done in that method.  But what about the Activity constructor?  Can you use it for any initialisation work?

No.  In general you should leave it alone, as much of the Android API will not work if called from the Activity constructor.  For instance, trying to inflate a layout will raise an exception.  So restrict your initialisation to onCreate.

Thursday, July 28, 2011

What does "@+id" mean?

One thing I've discovered recently is how the "@+id/" works in the layout files. 

Most tutorials show you adding android:id="@+id/id_name" to the View element in the layout xml file.  Then if you have to refer to the id in another element (say, to position it in a RelativeLayout) you refer to it without the "+" (i.e. android:id=@id/id_name.)  It gives the impression that the rule is you simply use the + when indicating a View's id, but not when you are referring to another View's id.

Actually, that's not how it works.  Really the rule is that you use the + when you are introducing a new id that you have not used before, then leave out the + when referring to a previously used id.  Most often you'll end up using the id for the first time when defining a View's id, so most people can remain oblivious of how it really works.

Why would you refer to a View before creating it?  You might wish to do that with a RelativeLayout, aligning a View with one you will be defining later in the file, as with this example:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:text="blue"
        android:background="#ff0000ff"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:id="@+id/blue"
        android:layout_toLeftOf="@+id/red"
    />
    <TextView
        android:id="@id/red"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:text="red"
        android:background="#ffff0000"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
    />
</RelativeLayout>

That will produce the following, in which the "blue" View can be positioned to the left of the "red" View, even though "red" isn't defined until later in the file:

Monday, January 31, 2011

Custom XML Attributes for Custom Views

I found there's a lot of help in books and online for creating your own View.  However, one thing I found is not well explained, and that is how to configure my own views with my own XML attributes.  It's well known that you can put your own strings and dimensions and other values in your resource files.  But those are values across the application - what if you want to use a custom view in more than one place, giving it different parameters in each case?

Part of the reason for my difficulty is that the Android documentation assumes you know all the ins and outs of XML.  If, like me, you think of XML as just neater HTML, then you'll need to learn about XML Namespaces.

A Crash Course on XML Namespaces

Anyone can make up their own XML tags and attributes, which leads to the possibility of conflicting tag and attribute names. (In the case of Android, they've created dozens of built-in XML attributes, and now the use can create their own.  Who knows if you're going to accidentally reuse an attribute the Google folks have already used.)

The solution is the Namespace concept.  You simply add a prefix to each XML tag name.  We've all seen one example: you don't get far in Android development without learning that Google's prefix is "android" as in android.layout_width, android.text etc.

So how do we ensure that the prefix is unique?  That's where that mysterious "xmlns" attribute at the start of android resource files comes in.  To refresh your memory, it says this:

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

The purpose is to link the prefix to a URL.  For instance, if I wanted to use the prefix, "tomato" I'd add the attribute:

xmlns:tomato="http://oksmartphone.blogspot.com"

This attribute doesn't have to go at the start of the file - the prefix will be usable in the tag with the xmlns attribute, and all those contained within.  So putting it in the outermost container, along with the android prefix, will ensure it applies to the entire file.  After that, If I want to have an attribute called, say, "depth", I could add it with the tomato prefix

<com.blogspot.oksmartphone.JasonView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tomato:depth="10"/>


An important point to realise is that the value of this attribute just has to be a unique string - there doesn't have to be any file relating to your attributes at that web page.  In fact, it doesn't have to be a URL at all.  I could have used:

xmlns:tomato="http://www.totally-fake-site.com"
or
xmlns:tomato="blah blah blah I hope this is unique"

Using a URL just a custom that  ensures that the string is unique.



Accessing Custom Attributes

Accessing attributes from a View class isn't hard.  When a View is created as part of an XML layout file, the constructor passes an AttributeSet object to the constructor.

The AttributeSet has lots of methods for accessing attributes (such as getAttributeValue.)  Each one has parameters for "namespace" and "attribute."  Note that the "namespace" refers to the unique string from the xmlns attribute, not the prefix associated with it (from my first example, I'd use "http://oksmartphone.blogspot.com/" and not "tomato".  For the attribute, leave off the prefix.  Again using my above example, I'd call:

attrs.getAttributeIntValue("http://oksmartphone.blogspot.com/", "depth", 0);

Assuming I wanted the default value (if the attribute is missing) to be zero.


For a better explanation of XML namespaces, try W3 Schools' page: http://www.w3schools.com/xml/xml_namespaces.asp

Tuesday, January 11, 2011

When the emulator won't start

Last time I talked about the slow starting of the emulator, now here is the related issue of simply getting it to start at all.  It doesn't give any useful error messages (at least by default) so it can be difficult to know what has gone wrong, and when you should start thinking that it's not merely slow, it's stopped all together.

Here are a few things I've found will cause the emulator to fail to start:
No Internet connection
The emulator requires an Internet connection to start, and without one, it will never finish loading.  So if you have an Internet connection that needs to be manually connected, and you haven't bothered because your app doesn't require the Internet, you may be left wondering why it won't start.

No Sound
The emulator also needs sound (it is supposed to be a phone, after all.)  I've tried running the emulator on a Linux box with a wonky sound configuration that befuddles many programs, and I've found I can't get it to start without specifying the -no-audio option.

Aborted Launch
Another problem is caused by an aborted first launch of the emulator.  The first time it runs with a given virtual device (AVD) the emulator needs to construct the memory image for the device.  If the emulator gets shut down before this image is completed, the emulator will not be able to use the unfinished image, nor will it be able to pick up where it left off; it simply won't work.
That's unfortunate since (as mentioned in the last post) the emulator takes longer to load than one would expect.  So a likely scenario is that the user starts the emulator, waits, assumes something is wrong and shuts it down, thus guaranteeing it won't work.
If this has happened, delete the AVD and recreate it. (Use the AVD manager, or delete the directory from the .android/avd directory.)

And if none of this helps, try starting the emulator with one of the debug flags (such as -debug-init.)  Try, emulator -help-debug-tags for all the options.

Friday, January 7, 2011

Speeding up the emulator start-up

One concern I had on first trying the Android emulator was just how long it takes to start.  If, like me, you tend to use the fix-one-thing-try-it-out style of developing, the thought of taking several minutes every time may scare you away.


First, let me reassure you with a point that often isn't spelled-out in Android books and tutorials: You don't have to restart the emulator every time you want to try new code; you can just leave it on, and rely on the tools to place the new package in the already-running emulator.

Also, if you're just starting into Android development, and you're worried that your older, slower computer just can't keep up.  It may at least make you feel less inadequate to know that the Emulator takes a long time to start even on newer, faster equipment.

But if you really want to speed up start up, Android Tutorials a couple of ideas on how to speed it up, a little.