Wednesday, June 5, 2013

Android Adventures - Activity And UI

http://www.i-programmer.info/programming/android/5914-android-adventures-activity-and-ui.html


So you know how to create an Android app, but do you really know how it works? In this second part of our new ebook on getting started with Android using Android Studio we look at how to create a UI and how to hook it up to the code in the Activity.

Android Adventures With Android Studio

Contents
  1. Getting Started With Android Studio
  2. The Activity And The UI
Androidgears

We discovered in Chapter 1 how to use Android Studio  Google's new Android IDE, to build the simplest possible app. On the way we discovered that an Android app consists of two parts - an Activity and a View.
The Activity is the Java code that does something and the View provides the user interface (UI). You can think of this duality as being similar to the HTML page and the JavaScript that runs to make it do something, or as a XAML form and the code behind.
The key idea is that an Activity is the code that works with a UI screen defined by the View. This isn't quite accurate in that an Activity can change its view so that one chunk of Java code can support a number of different views. However, there are advantages to using one Activity per view because, for example, this how the Android back button navigates your app - from Activity to Activity.  A complex app nearly always consists of multiple Activities that the user can move between like web pages but a simple app can manage quite well with just one Activity.
There is no hard and fast rule as to how many Activities your app has to have - but it has to have least one.
If you are wondering if an Activity can exist without a View the answer is that it can, but it doesn't make much sense as this would leave the user with no way to interact with your app. Activities are active when their View is presented to the user.
It really is a great simplification to think in terms of an Activity corresponding to a single screen with a user interface.
If you want something to run without a UI then what you want is a service or a content provider - more of which much later.
It is also worth making clear at this early stage that an Activity has only one thread - the UI thread - and you need to be careful not to perform any long running task because this would block the UI and make your app seem to freeze. Also notice that creating additional Activities doesn't create new threads. Only one Activity is active at any given time - more of this later when we consider the Activity lifecycle in detail.
In other words, Activities are not a substitute for knowing about threading.
For the moment let's concentrate on the single screen UI Activity because it is the most common app building block you will encounter.

The MainActivity

There is of course one activity that is nominated as the one to be launched when you app starts. If you create a new app called SimpleButton and accept all the defaults then the startup Activity is called MainActivity by default.
You can change which Activity starts the app by chaining a line in the app's manifest. The Manifest is a project file we haven't discussed before because if you are using Android Studio you can mostly ignore it but it is better if you know it exists and what it does.
The Manifest is stored in the src/main directory and it is called AndroidManifest.xml. It is an XML file that tells the Android system everything it needs to know about you app. In particular it lists all of the activities and which one is the one to use to start the applications.
If you open the generated Manifest you will see:
 android:name=
 "com.example.simplebutton.MainActivity"
    android:label="@string/app_name" >

This defines the Activity the system has created for you and the lines:
 
      "android.intent.action.MAIN" />
      "android.intent.category.LAUNCHER" />
 

Define it as the startup Activity. Notice which Activity starts the app has nothing to do with what you call it i.e. calling it MainActivity isn't enough.
For the moment you can rely on Android Studio to look after the Manifest for you.

Inside the Activity

The generated Activity has two methods defined but the important one is onCreate. This is an event handler and it is called when you app is first created. This is the place we do all the initializations and setting up for the entire app. It is also generally the place we show the app's main UI screen.
Let's take another look at the generated code for onCreate:
@Override
protected void onCreate(Bundle
                          savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
}

The onCreate event handler is passed a Bundle object called savedInstanceState. This is intended to preserve state information between invocations of you app and we will see how this is used later. In this case no data has been saved and so savedInstanceState is null - but you still have to pass it on to the inherited onCreate method.
The final instruction calls setContentView which is a method that has a number of different overloaded forms. In this case we pass an integer that indicates which XML file describes the layout to be used fro the view. The R object is constructed by the system to reflect the resources you have placed in the resource directories. In this case R.layout.activity_main returns an integer value that allows the setContentView method to find the activity_main XML layout file.

View and ViewGroup

Ok so far so good but it is important to realize that what happens next it that the XML file is rendered as a set of View objects. The entire UI and graphics system is implemented as a hierarchy of components derived from the view class. If you have used almost any GUI framework - AWT, Swing, XAML etc - then this idea will not be new to you.
For example, a button is a class derived from view and to create a button all you have to do is create an instance of the button class.  You can, of course create as many buttons as you like simply by creating more instances.
This leaves open the question of where the button appears in the layout? The answer to this is that there are ViewGroup objects which act as containers for other view objects. You can set the position of the child view objects or just allow them to be controlled by various layout rules - more of which later.
You can opt to create the entire UI in code by creating and working with instances of view objects.
Alternatively you can use XML to define view objects and how they nest one within another and rely on the system to create the view object hierarchy for you.
The simplest way however is to use the designer to create the XML file and then allow the system to create the objects for you from the generated XML file.


Connecting the Activity to the UI

Now we have an event handler hooked up to the button click event we usually want to do something that affects the UI as a result. Let's suppose that when the button is clicked we want to change the text displayed in the Large Text widget to "You Clicked Me!". We can do this by changing the Large Text widget;s text property to the new text. The only problem is how do we find the Large Text widget in code?
This is a fairly standard problem when you use a markup language to define a UI. The markup language defines widgets or other UI objects and the code has to have a way of making the connection to those UI objects. For example in JavaScript you make use of the getElementById method to retrieve a DOM object corresponding to a particular HTML element. In Android we do something similar.
First make sure you follow the idea that all of the XML generated by the designer gets converted into a set of Java objects, one for each widget or view placed on the design surface. These objects have the same range of properties as you saw in the Properties window and methods to get other things done. All you need to do if find a way to reference them.
In the case of the view object that caused the event this is very easy as it is passed to the event handler as the only argument of the call. So if the event handler is:
public void buttonOnClick(View v) {
and the event handler is only hooked up to the button then you can be 100% sure that v is the button object. So if you want to change the button's caption you could just use its setText method to change its value.
Note: if you aren't used to Java then it is worth pointing out that generally all Java frameworks follow the rule that if an object has a property called myProperty, visible as such in the Property window then in code you set it using setMyProperty and retrieve it using getMyProperty. 
So all you have to write is:
v.setText("I've Been Clicked!");
but this doesn't work because v is a general View object which doesn't have a setText method. To call the Button's setText method we have to cast v to its correct type - a Button.
Note: casting is where you tell the system the type fo the object you are working with. If classB is a subclass of classA then you can treat a classB object as a classA - after all it has all of the methods and properties that classA does by inheritance. However if you want to make use of a property or method that only classB has then you need to cast the reference to the  classB object to make its type clear.
For example. assuming classB inherits from classA: 
classA myObject = new classB();
creates an instance of classB but myObject is decleared to be of type classA.  This is fine but you can only access the methods and properties of the classA object. If you try
myObject.classBMethod();
then it will fail if classBMethod only exists in classB. To use the classB method you have to cast myObject to its real type:
(classB) myObject
You can store a reference to the cast in a new variable:
classB myClassBObject = (classB) myObject;
and then call the method
myClassBObject.classBMethod();
or you can just do the cast on the fly at the cost of an extra pair of parentheses:
((classB) myObject).classBMethod();

However if you simply use add the cast to Button you will discover that Android Studio flags an error by showing Button in red.

 cast

If you look at the error message it suggests pressing Alt+Enter which is always good advice because it produces a list of possible fixes for the problem. In this case you have to add the class definition to the start of the program.
import android.widget.Button;

It will also offer to split the declaration and the assignment as being better style - for the moment let's stick with the single line.
Button button=(Button) v;
Now we have the button object we can call its setText method:
button.setText("I've Been Clicked!");
Now if you run the program you will see the button's caption change when you click the button.
Notice that this approach only works if you know the type of the object that caused the event and called the event handler. If you don't then you can use the getId method to discover the view's id and write a switch to deal with each possible type in turn.
Now suppose we want to do something to one of the other widgets in the view. In this case we have to find the object that represents the widget without the help of the event handler's argument. For example how do we find the TextView that we placed below the button?
This is a very standard pattern in working with view objects from the Activity. First you make use of the R object to find the widget's id. In this case the TextView has been given the id textView by default so we can use:
R.id.textView:
and this returns a unique integer that identifies the widget. Next we use the Activities findViewById(int) method which takes the integer id and returns the Java object corresponding to id. Of course this is returned as a View object because findViewById doesn't know the type of the object it returns any more accurately. So we have to cast to make use of the TextView object.
Putting all this together gives:
TextView myTextView=(TextView)
                   findViewById(R.id.textView);

Once again Android Studio will complain about TextView and if you press Alt+Enter it will add the class import to the start of the program for you:
import android.widget.TextView;

Now that you have the TextView object you can use the setText method to change what it displays:
 myTextView.setText("You Clicked Me!");
The complete event handler is:
public void buttonOnClick(View v) {
 Button button=(Button) v;
 button.setText("I've Been Clicked!");
 TextView myTextView=(TextView)
                 findViewById(R.id.textView);
 myTextView.setText("You Clicked Me!");
}

If you now run the program you will see that you are informed twice of the fact that this very important button has been clicked:

buttonfinal

You may think that this is all very small stuff and nothing like a real app but this is how building a UI works in Android. You now know how to design a single screen app using the widgets available in the designer toolbox and how to hook them up to handle their events, find Java object that represent with them and how to call methods that modify them.
Apart from the fine detail of how each of the widgets works - radio buttons, checkboxes and so on you now have the general outline of how to build a single screen app.


Summary 

  • An Activity is the unit of the Android app and it roughly corresponds to one screen full of user interface plus the code to make it work.
  • In most cases you will create an Activity for each UI screen you want to present to your user.
  • Only one Activity from you app is running at any given time.
  • An Activity is single threaded and runs on the UI thread.
  • You can set which Activity starts the app in the Manifest - Android Studio sets this to MainActivity by default.
  • The Activity has events corresponding to different stages in it lifecycle. The onCreate event is called when the app first starts and this is where you perform all initialization.
  • You can restore the apps state from previous runs at this point.
  • The Activity then loads a View or ViewGroup object to create its user interface.
  • You can create View/ViewGroup objects in three possible ways: in code, using XML or using the designer to generate the XML.
  • The designer is far the easiest way to create a UI.
  • By opening the XML file you can use the designer to place widgets corresponding to View objects on the design surface.
  • You can use the property window to set the properties of each widget.
  • The XML file that the designer creates is used by the Activity to set its UI by creating Java object that correspond to each of the View objects placed using the designer.
  • You can hook up event handlers defined within the current Activity to the widgets using the event properties in the Properties window e.g. the onClick.
  • An event handler is just a public function with the signature myEventHandler(View v)
  • The View object parameter is set to the View object that raised the event. This can be used to access the properties/methods of the View object that the user interacted with.
  • To access other View  objects you follow the standard pattern of:
    1. Find the integer id via the string id, set in the designer, of the widget using resource object R.id.stringId.
    2. Use the integer id to retrieve the Java object corresponding to the widget using findViewById(intId)
    3. Work with the object's methods/properties to modify it.

    Androidgears

    Android Adventures With Android Studio

    Contents
    1. Getting Started With Android Studio
    2. The Activity And The UI

    Coming Next

    In the next installment we'll discover how to use layouts and some of the details of the basic widgets.
    Meanwhile if you have any questions on what we've covered so far please let me know using the comments.
    You can download the code for this program from the CodeBin (note you have to register first).

    To be informed about new articles on I Programmer, install the I Programmer Toolbar, subscribe to the RSS feed, follow us on, Twitter,FacebookGoogle+ or Linkedin,  or sign up for our weekly newsletter.


    No comments:

    Post a Comment