Vous êtes sur la page 1sur 109

Building User Interfaces and Basic

Applications
Ren-Shio Liu
Dept. of Industrial and Information Management
National Cheng Kung University
Overview
All user interface elements in an Android app are built using View
and ViewGroup objects
A View is an object that draws something on the screen that the
user can interact with
A ViewGroup is an invisible container that holds other View (and
ViewGroup) objects in order to define the layout of the interface
Layouts
User Interface Layout
To declare your layout, you can
instantiate View objects in code and start building a tree
but the easiest and most effective way to define your layout is with an XML file
The name of an XML element for a view is respective to the Android
class it represents
So a <TextView> element creates a TextView widget in your UI, and a
<LinearLayout> element creates a LinearLayout view group
User Interface Layout (cont.)
The advantage to declaring your UI in XML is that it enables you to
better separate the presentation of your application from the code
that controls its behavior
Your UI descriptions are external to your application code, which
means that you can modify or adapt it without having to modify
your source code and recompile.
For example, you can create XML layouts for different screen
orientations, different device screen sizes, and different languages.
Additionally, declaring the layout in XML makes it easier to visualize
the structure of your UI, so it's easier to debug problems
Working with XML
Using Android's XML vocabulary, you can quickly design UI layouts and
the screen elements they contain, in the same way you create web
pages in HTML with a series of nested elements.
Each layout file must contain exactly one root element, which must be a
View or ViewGroup object.
Once you've defined the root element, you can add additional layout
objects or widgets as child elements to gradually build a View hierarchy
that defines your layout.
Root element
<ViewGroup>
A container for other View elements.
There are many different kinds of ViewGroup objects and each one lets
you specify the layout of the child elements in different ways.
Different kinds of ViewGroup objects include LinearLayout,
RelativeLayout, and FrameLayout, etc.
Attributes
android:id A unique resource name for the element, which you can use to
obtain a reference to the ViewGroup from your application.
android:layout_height Required. The height for the group, as a dimension
value (or dimension resource) or a keyword (match_parent" or "wrap_content")
android:layout_width Required. The width for the element, as a dimension
value (or dimension resource) or a keyword (match_parent" or "wrap_content")
Common Layouts
Each subclass of the ViewGroup class provides a unique way to display
the views you nest within it
Some of the more common layout types that are built into the Android
platform:

Can be either
vertical or
horizontal
<View>
An individual UI component, generally referred to as a "widget".
Different kinds of View objects include TextView, Button, and CheckBox.
Attributes
android:id A unique resource name for the element, which you can use to
obtain a reference to the ViewGroup from your application.
android:layout_height Required. The height for the group, as a dimension
value (or dimension resource) or a keyword (match_parent" or "wrap_content")
android:layout_width Required. The width for the element, as a dimension
value (or dimension resource) or a keyword (match_parent" or "wrap_content")
<?xml version="1.0" encoding="utf-8"?>

Linear Layout
<LinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:text="Name"
android:ems="10"
android:layout_weight="1"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:id="@+id/editText" />
</LinearLayout>
Relative Layout
A relative layout is used for screen designs that require control elements
to be positioned in relation to one another
Relavtive Layout attributes
android:layout_above Positions the bottom edge of this view above the given anchor view ID.
android:layout_below Positions the top edge of this view below the given anchor view ID.

android:layout_alignBottom Makes the bottom edge of this view match the bottom edge of the given anchor
view ID.
android:layout_alignLeft Makes the left edge of this view match the left edge of the given anchor view ID.

android:layout_alignStart Makes the start edge of this view match the start edge of the given anchor view ID.

android:layout_alignParentTop If true, makes the top edge of this view match the top edge of the parent.

android:layout_alignParentLeft If true, makes the left edge of this view match the left edge of the parent.

SDK8 padding
<?xml version="1.0" encoding="utf-8"?> match_pfill_p
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" Note: fill_parent is deprecated.
android:paddingLeft="16dp" Use match_parent for SDK 8+
android:paddingRight="16dp" > and higher
<EditText
android:id="@+id/name" density p:
android:layout_width="fill_parent" view,
android:layout_height="wrap_content"
android:hint="Reminder" />
sp:
<Button
android:id="@+id/cancel"
android:layout_width="96dp" px:
android:layout_height="wrap_content"
android:layout_below="@id/name"
android:layout_alignParentRight="true"
android:text="Cancel" />
<Button
android:id="@+id/ok" ("Cancel")
android:layout_width="96dp"
android:layout_height="wrap_content"
android:layout_below="@id/name"
android:layout_toLeftOf="@id/cancel"
android:text="OK" />
</RelativeLayout>
FrameLayout
FrameLayout is designed to block out an area on the screen to display a
single item
Generally, FrameLayout should be used to hold a single child view,
because it can be difficult to organize child views in a way that's scalable
to different screen sizes without the children overlapping each other
Child views are drawn in a stack, with the most recently added child on
top
The size of the FrameLayout, if not specified, is the smallest rectangle
area that encompasses all the child views (plus padding)
<FrameLayout
xmlns:android="http://schemas.android.com/apk/r
es/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<ImageView
android:src="@mipmap/ic_launcher" Size of the
android:scaleType="fitCenter" FrameLayout
android:layout_gravity="center"
android:layout_height="350px"
android:layout_width="350px"/>

<TextView
android:text="Frame Demo"
android:textSize="60px"
android:textStyle="bold"
android:textColor="@color/colorAccent"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:gravity="center"/>
</FrameLayout>
GridLayout
A layout that places its children in a rectangular grid
Children occupy one or more contiguous cells, as defined by their
rowSpec and columnSpec layout parameters
Each rowSpec/columnSpec defines the set of rows or columns that are
to be occupied; and how children should be aligned within the resulting
group of cells
If a child does not specify the row and column indices of the cell it
wishes to occupy, GridLayout assigns cell locations automatically using
its: orientation, rowCount and columnCount properties
Space between children may be specified either by using instances of the
dedicated Space view or by setting the leftMargin, topMargin,
rightMargin and bottomMargin layout parameters
GridLayout (cont.)
When the useDefaultMargins property is set, default margins around
children are automatically allocated based on the prevailing UI style
guide for the platform
As of API 21, GridLayout's distribution of excess space accommodates
the principle of weight
<GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:rowCount="6"
android:columnCount="4"
android:layout_margin="4dp"
android:orientation="horizontal">

<TextView
android:id="@+id/textView"
android:text="@string/zero"
android:textSize="70sp"
android:backgroundTint="@color/orange_cream"
android:layout_columnSpan="4"
android:layout_gravity="fill_horizontal"
android:gravity="end"/>
<Button
android:id="@+id/buttonPercent"
android:layout_gravity="fill_horizontal"
android:text="@string/percent"
android:minHeight="70dp"/>

<Button
android:id="@+id/buttonDivide"
android:text="@string/div"
android:minHeight="70dp"/>
A Side Note on DP and SP
dp
Density-independent Pixels - an abstract unit that is based on the physical
density of the screen
These units are relative to a 160 dpi screen, so one dp is one pixel on a 160 dpi
screen
The ratio of dp-to-pixel will change with the screen density, but not necessarily
in direct proportion.
Note: The compiler accepts both "dip" and "dp", though "dp" is more consistent
with "sp"
A Side Note on DP and SP (cont.)
sp
Scale-independent Pixels - this is like the dp unit, but it is also scaled by the
user's font size preference.
It is recommended you use this unit when specifying font sizes, so they will be
adjusted for both the screen density and user's preference.
Load the XML Resource
When you compile your application, each XML layout file is compiled
into a View resource.
You should load the layout resource from your application code, in your
Activity.onCreate() callback implementation.
Do so by calling setContentView(), passing it the reference to your
layout resource in the form of: R.layout.<layout_file_name> :

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
...
Attributes
Every View and ViewGroup object supports their own variety of XML
attributes. Some attributes are specific to a View object (for example,
TextView supports the textSize attribute), but these attributes are also
inherited by any View objects that may extend this class.
Some are common to all View objects, because they are inherited from
the root View class (e.g. the id attribute).
And, other attributes are considered "layout parameters, which are
attributes that describe certain layout orientations of the View object, as
defined by that object's parent ViewGroup object.
The ID Attribute
Any View object may have an integer ID associated with it, to uniquely
identify the View within the view hierarchy
When the application is compiled, this ID is referenced as an integer,
but the ID is typically assigned in the layout XML file as a string, in the
id attribute.
This is an XML attribute common to all View objects (defined by the
View class) and you will use it very often.
The syntax for an ID, inside an XML tag is:
android:id=@+id/my_button
The at-symbol (@) at the beginning of the string indicates that the
XML parser should parse and expand the rest of the ID string and
identify it as an ID resource.
The plus-symbol (+) means that this is a new resource name that
must be created and added to our resources (in the R.id file).

<Button
android:id="@+id/button1"
android:text="@string/button1"
android:onClick="onButton1Clicked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
Layout Parameters
Layout Parameters are used by views to tell their parents how they
want to be laid out
For example, you may specify the size of a view as follows:

<Button
android:id="@+id/button1" wrap_content tells your view to size itself to the
android:text="@string/button1" dimensions required by its content.
android:onClick="onButton1Clicked"
match_parent tells your view to become as big as
android:layout_width="wrap_content"
android:layout_height="wrap_content" its parent view group will allow
/> Alternatively, you can also specify size using
density-independent pixel units (dp)
There are also position related attributes (e.g.,
For more details, see this link left and top), but hard coding position should be
avoided
Connecting the XML with the code
In order to create views and reference them from the application, a
common pattern is to:
1. Define a view/widget in the layout file and assign it a unique ID:
<Button
android:id="@+id/button1"
android:text="@string/button1"
android:onClick="onButton1Clicked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

2. Then create an instance of the view object and capture it from the layout
(typically in the onCreate() method):

Button myButton = (Button) findViewById(R.id.button1);


<RequestFocus>
Any element representing a View object can include this empty element,
which gives its parent initial focus on the screen
You can have only one of these elements per file
Input Controls
Input controls are the interactive components in your app's user
interface
Android provides a wide variety of controls you can use in your UI, such
as buttons, text fields, seek bars, checkboxes, zoom buttons, toggle
(switch) buttons, and many more
Textfields
An editable text field
Every text field expects a certain type of text input, such as an email
address, phone number, or just plain text
You should always declare the input method for your text fields by
adding the android:inputType attribute to the <EditText> element.
Textfields Specify the Keyboard Type

<EditText
android:id="@+id/phone"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/phone_hint"
android:inputType="phone" />

<EditText
android:id="@+id/password"
android:hint="@string/password_hint"
android:inputType="textPassword"
... />
Texfields Enable Spelling Suggestions
In addition, if your text field is intended for basic text input, you should
enable auto spelling correction with the "textAutoCorrect" value

<EditText
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType=
"textCapSentences|textAutoCorrect"
... />
Textfields Specify the Input Method Action
Most soft input methods provide a user action button in the bottom
corner
By default, the system uses this button for either a Next or Done action
However, you can specify additional actions that might be more
appropriate for your text field, such as Send or Go

<EditText
android:id="@+id/search"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/search_hint"
android:inputType="text"
Click here for the complete list of
android:imeOptions="actionSend" />
android:imeOptions
The Observer Pattern

The Observer is known as a behavioural pattern, as it's used to form


relationships between objects at runtime.

Define a one-to-many dependency between objects


so that when one object changes state, all its
dependents are notified and updated automatically.
The Observer Pattern (cont.)
The idea behind the pattern is simple:
One of more Observers are interested in the state of a Subject and
register their interest with the Subject by attaching themselves
When something changes in our Subject that the Observer may be
interested in, a notify message is sent, which calls the update
method in each Observer.
When the Observer is no longer interested in the Subject's state,
they can simply detach themselves.
To pass data onto the observers, the
subject doesn't need to know who
needs to know.
Instead, everything is done through a
common interface, and the notify
method just calls all the objects out
there that have registered their
interest.
This is a very powerful decoupling -
meaning that any object can simply
implement the Observer interface and
get updates from the Subject.
Textfields Specify the Input Method Action
(cont.)
You can then listen for presses on the action button by defining a
TextView.OnEditorActionListener for the EditText element
EditText editText = (EditText) findViewById(R.id.search);
editText.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
boolean handled = false;
if (actionId == EditorInfo.IME_ACTION_SEND) {
sendMessage();
handled = true;
}
return handled;
}
});
Textfields Watching Text Changes
We can also get notification when text changes through the use of the
TextWatcher interface
The TextWatcher interface is actually an implementation of the Observer
design pattern
Textfields Watching Text Changes (cont.)
1. In general, your can either choose to create a TextWatcher objects (see
textbook p.117) or
2. Implement the TextWatcher interface in your Activity class (see next
slide)

Note: Textfields offer many more useful features, such as auto-complete


suggestions, see here for more info
public class MainActivity extends AppCompatActivity implements TextWatcher {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText textEdit = (EditText) findViewById(R.id.editText);
textEdit.addTextChangedListener(this);
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
... // The text is uneditable
}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
... // The text is uneditable
}

@Override
public void afterTextChanged(Editable s) {
... // The text is editable
}
}
Textfields Other Events
You can build other flexibility into EditText control in similar
ways:
OnFocusChangeListener: identify when the focus status of EditText
changes

Checkboxes
Checkboxes allow the user to select one or more options from a set
Typically, you should present each checkbox option in a vertical list.
Checkboxes (cont.)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<CheckBox android:id="@+id/checkbox_meat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/meat"
android:onClick="onCheckboxClicked"/>
<CheckBox android:id="@+id/checkbox_cheese"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cheese"
android:onClick="onCheckboxClicked"/>
</LinearLayout>
Checkboxes (cont.)
public void onCheckboxClicked(View view) {
// Is the view now checked?
boolean checked = ((CheckBox) view).isChecked();

// Check which checkbox was clicked


switch(view.getId()) {
case R.id.checkbox_meat:
if (checked)
// Put some meat on the sandwich
else
// Remove the meat
break;
case R.id.checkbox_cheese:
if (checked)
// Cheese me
else
// I'm lactose intolerant Note: The method you declare in the
break; android:onClick attribute must have a
// TODO: Veggie sandwich
} signature exactly as shown in the figure
}
Other Controls
Android offers many other useful controls, such as SeekBar, Switch
Button, Spinner (drop down list), and RadioGroup.
Read chapter 2 for examples
Adaptive Design Concept
Screens and Orientations
Landscape Layout
The landscape layout is also named
activity_my.xml
To differentiate between the two layouts, the
landscape layout is stored in a separate
folder, res/layout-land
The easiest way to create this folder is to
choose Create Landscape Variation from the
Configuration Icon in the Graphical Layout
Editor in the design mode
<GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:rowCount="4"
android:columnCount="6"
android:layout_gravity="center"
android:layout_margin="4dp"
android:orientation="horizontal">

<TextView
android:id="@+id/textView"
android:text="@string/zero"
android:textSize="70dip"
android:backgroundTint="@color/orange_cream"
android:layout_columnSpan="4"
android:layout_gravity="fill_horizontal"
android:gravity="start"/>
<Button
android:id="@+id/buttonAC" <Button
android:layout_columnSpan="2" android:id="@+id/buttonMinus"
android:layout_gravity="fill_horizontal" android:text="@string/minus"
android:backgroundTint="@color/dusk_blue" android:minHeight="70dip"/>
android:text="@string/ac"
android:minHeight="70dip" <Button
android:onClick="onButtonClick"/> android:id="@+id/buttonPlus"
android:text="@string/plus"
<Button android:minHeight="70dip"/>
android:id="@+id/buttonPercent"
android:layout_gravity="fill_horizontal" <Button
android:text="@string/percent" android:id="@+id/button5"
android:minHeight="70dip"/> android:text="@string/five"
android:minHeight="70dip"/>
<Button
android:id="@+id/buttonDivide" <Button
android:text="@string/div" android:id="@+id/button6"
android:minHeight="70dip"/> android:layout_width="wrap_content"
android:layout_height="wrap_content"
<Button android:text="@string/six"
android:id="@+id/buttonMultiply" android:minHeight="70dip"/>
android:text="@string/multiply"
android:minHeight="70dip"/>
<Button
<Button
android:id="@+id/button7"
android:id="@+id/button1"
android:text="@string/seven"
android:text="@string/one"
android:minHeight="70dip"/>
android:minHeight="70dip"/>
<Button
<Button
android:id="@+id/button8"
android:id="@+id/button2"
android:text="@string/eight"
android:text="@string/two"
android:minHeight="70dip"/>
android:minHeight="70dip"/>
<Button
<Button
android:id="@+id/button9"
android:id="@+id/button3"
android:text="@string/nine"
android:text="@string/three"
android:minHeight="70dip"/>
android:minHeight="70dip"/>
<Button
<Button
android:id="@+id/button0"
android:id="@+id/button4"
android:layout_columnSpan="1"
android:text="@string/four"
android:text="@string/zero"
android:minHeight="70dip"/>
android:minHeight="70dip"/>
<Button
android:id="@+id/buttonPoint"
android:text="@string/point"
android:minHeight="70dip"/>

<Button
android:id="@+id/buttonEqual"
android:layout_columnSpan="2"
android:layout_gravity="fill_horizontal"
android:backgroundTint="@color/dusk_blue"
android:text="@string/equal"
android:minHeight="70dip"/>
</GridLayout>
The landscape orientation has
an extra . button
How can we deal with the
button? More specifically, how
can we tell if we are in portrait
or landscape layout?
Determine the Current Layout
Since your implementation of each layout will be a little different, one of
the first things you will probably have to do is determine what layout the
user is currently viewing
For example, you might want to know whether the user is in "single
pane" mode or "dual pane" mode. You can do that by querying if a given
view exists and is visible:

(adap-
tive design)

public class NewsReaderActivity extends FragmentActivity {


boolean mIsDualPane;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);

View articleView = findViewById(R.id.article);


mIsDualPane = articleView != null &&
articleView.getVisibility() == View.VISIBLE;
}
}

Now you can react according to the current layout


Container Views
Container views are simply ViewGroups, which are Views
Android categorizes this group of Views as containers because its sole
function is to act as containers for other views
Container Views (cont.)
Once a container is created, it can be queried for values
The method for accessing values from a container will depend on the
type of container being used
Typically, containers are used with Adapters, which map data to a View
For now, you can think of an Adapter as an object that acts as a bridge
between the container and the data items with which it will be
populated
When the content of an application is dynamic or not predetermined,
an Adapter is used to populate the layout with Views at runtime
Overview
Very common to want to show a list -- choices, email headers,
etc.
Two ways to create a list in Android
Create a layout containing a ListView with the special id
@android:id/list
OR Create a ListActivity which hosts a ListView object that can be bound to
different data sources, typically either an array or a Cursor holding query results.
Either way, you will need an Adapter
Screen Layout
ListActivity has a default layout that consists of a single, full-screen list
in the center of the screen
However, if you desire, you can customize the screen layout by setting
your own view layout with setContentView() in onCreate(). To do this,
your own view MUST contain a ListView object with the id
"@android:id/list" (or list if it's in code)
Optionally, your custom view can contain another view object of any
type to display when the list view is empty. This "empty list" notifier
must have an id "android:id/empty".
Note that when an empty view is present, the list view will be hidden
when there is no data to display.
Adapter Design Pattern
In the real world, an adapter helps two incompatible interfaces to work
together.
Adapter Design Pattern (cont.)
In computer programming, the adapter design pattern is used when
you want two different classes with incompatible interfaces to work
together.

In this example, the client class can


work with any class that
implements the Adaptor interface
Row Layout
You can specify the layout of individual rows in the list.
You do this by specifying a layout resource in the ListAdapter object
hosted by the activity (the ListAdapter binds the ListView to the data;
more on this later).
Android provides some standard row layout resources. These are in the
R.layout class, and have names such as simple_list_item_1,
simple_list_item_2, and simple_list_item_checked
Row Layout (cont.)

You can find more standard


layout here

simple_list_item_1 simple_list_item_2 simple_list_item_checked


Binding to Data
You bind the ListActivity's ListView object to data using a class
that implements the ListAdapter interface.
Android provides a few standard list adapters:
ArrayAdapter for static arrays
SimpleAdapter for static maps
and SimpleCursorAdapter for Cursor query results.
Demo: Basic ListActivity
Basic ListActivity
Create a new project called BasicListActivity
Name your activity BasicListActivity
Name your layout activity_basic_list
Delete the content of your activity layout file and change it to that listed
below
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_basic_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="tw.edu.ncku.iim.rsliu.basiclistactivity.BasicListActivity">

<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal" />
</LinearLayout>
Basic ListActivity (cont.)
Modify your activity class so that it extends ListActivity
Now, to display a list of names, do
public class BasicListActivity extends ListActivity {
private ArrayAdapter<String> adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_basic_list);

List<String> data = new ArrayList<String>();


data.add("Jeremy Lin");
data.add("Pete Wang");
data.add("Aaron Liu");
data.add("Emma Liu");

adapter = new ArrayAdapter<String>(


this, android.R.layout.simple_list_item_1, data);

setListAdapter(adapter);
}
}

Note: Be sure to import ListActivity, List, ArrayList classes


To do
See what happens if you
1. Comment out all the data.add() lines
2. Comment out setContentView() function call
Basic ListActivity (cont.)
To get notified whenever an item in the list is selected,
override onListItemClick() method (Code > Override
Methods > onListItemClick)

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);

String content = (String) getListAdapter().getItem(position);


Toast.makeText(this, content, Toast.LENGTH_LONG).show();
}
onListItemClick Parameters

Parameters
l ListView: The ListView where the click happened
v View: The view that was clicked within the ListView
position int: The position of the view in the list
id long: The row id of the item that was clicked
Exercise
Modify the iTuneListener App we did last week so that is uses a
ListAcitivty to display the top 20 songs on iTune
No need to turn in, but youll need this for the next demo!
Solution
Make iTuneListener extend ListActivity and add the following variables to
iTuneListener class

public class iTuneListener extends ListActivity {

LinearLayout layout;
ArrayList<String> titles;
ArrayAdapter<String> adapter;

Note: be sure to import ListActivity, ArrayList, ArrayAdapter


protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//setContentView(R.layout.activity_i_tune_listener);
titles = new ArrayList<String>();
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, titles);
setListAdapter(adapter);

try {
URL url = new URL("http://ax.phobos.apple.com.edgesuite.net/WebObjects/" +
"MZStore.woa/wpa/MRSS/topsongs/limit=20/rss.xml");
new iTuneSAX(url, new ParserListener() {
@Override
public void setTitle(final String s) {
runOnUiThread(new Runnable() {
@Override
public void run() {
titles.add(s);
adapter.notifyDataSetChanged();
}
});
}
});
} catch (Exception e) {
e.printStackTrace();
Update onCreate() of iTuneListener
} as shown above
}
Demo: iTuneListener with Album
Cover Thumbnails
Demo: iTuneListener with Cover Thumbnail
Need to get URLs of the thumbnails first!
Modify iTuneSAX class as follows
Define a new boolean member variable imageFound

public class iTuneSAX extends DefaultHandler {


Boolean itemFound = false;
ParserListener delegate;
String element = "";
Boolean imageFound = false;
Demo: iTuneListener with Cover Thumbnail (cont.)

Update startElement and endElement methods as follows:

@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
super.startElement(uri, localName, qName, attributes);
if (localName.equals("item")) {
itemFound = true;
}
if (itemFound && localName.equals("coverArt") && Note: Size of cover art can be 53, 60 or
attributes.getValue("height").equals(100")) { 100
imageFound = true;
}
element = "";
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
if (itemFound && localName.equals("title")) {
delegate.setTitle(element);
} else if (imageFound && localName.equals("coverArt")) {
Log.i("URL", element);
imageFoud = false;
}
}
Demo: iTuneListener with Cover Thumbnail (cont.)
Now, for each URL, we need to create a thread to download its
corresponding image Better use ExecutorService!
However, before that, lets add another array in iTuneListener class to
hold the images
public class iTuneListener extends ListActivity {
LinearLayout layout;
iTuneListener listener = this;
ArrayList<String> titles;
ArrayList<Bitmap> covers;
ArrayAdapter<String> adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

titles = new ArrayList<String>();


covers = new ArrayList<Bitmap>();
Demo: iTuneListener with Cover Thumbnail (cont.)
Also, whenever an image is downloaded, we need an API to store it
into this array and refresh the list
So, add another method in the ParserListener interface
interface ParserListener {
void setTitle(final String s);
void setCover(final Bitmap b);
}

Implement that method in the iTuneListener class


try {
URL url = new URL("http://ax.phobos.apple.com.edgesuite.net/WebObjects/" +
"MZStore.woa/wpa/MRSS/topsongs/limit=20/rss.xml");
new iTuneSAX(url, new ParserListener() {
@Override
public void setTitle(final String s) {
// Only UI thread can update the UI
runOnUiThread(new Runnable() {
@Override
public void run() {
titles.add(s);
adapter.notifyDataSetChanged();
}
});
}

@Override
public void setCover(final Bitmap b) {
runOnUiThread(new Runnable() {
@Override
public void run() { Note: You can click Code > Override Methods
covers.add(b); > setCover to add the method
adapter.notifyDataSetChanged();
}
});
}
});
Demo: iTuneListener with Cover Thumbnail (cont.)
OK, we are ready to download the album cover thumbnails
As each thumbnail has a different URL, we need to define a
runnable that connect to a distinct url and perform the
download
Right click on your package and create a new Java class called
iTuneRunnable that implements the Runnable interface
public class iTuneRunnable implements Runnable {
String mURL;
ParserListener mListener;

iTuneRunnable(String url, ParserListener listener) {


mURL = url;
mListener = listener;
Note: You may have to delete
}
the line which says import
Runnable;
public void run() {
try {
URL url = new URL(mURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream is = connection.getInputStream();
final Bitmap bitmap = BitmapFactory.decodeStream(is);
mListener.setCover(bitmap);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Demo: iTuneListener with Cover Thumbnail (cont.)
Its better to use ExecutorService to manage the worker threads
So, define another member variable in iTuneSAX

public class iTuneSAX extends DefaultHandler {


Boolean itemFound = false;
ParserListener delegate;
String element = "";
Boolean imageFoud = false;
ExecutorService service;

public iTuneSAX(final URL url, ParserListener listener) {


delegate = listener;
service = Executors.newFixedThreadPool(1);
Demo: iTuneListener with Cover Thumbnail (cont.)

Now, when an URL is retrieved/parsed, create a new iTuneRunnable

@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
super.endElement(uri, localName, qName);
if (itemFound && localName.equals("title")) {
delegate.setTitle(element);
} else if (imageFoud && localName.equals("coverArt")) {
Log.i("URL", element);
service.execute(new iTuneRunnable(element, delegate));
imageFoud = false;
}
}
Demo: iTuneListener with Cover Thumbnail (cont.)

Now, we have to create a custom adapter to display the images as


Android doesnt provide a built-in layout for this
Right click on src/main/Java/<your package>/ > new > Java Class
Name the class iTuneListAdapter
Make the new class a subclass of BaseAdapter
Note if Android studio import the wrong class, remove it and import by
ALT+ENTER again
Create a constructor for the class:
public class iTuneListAdapter extends BaseAdapter {
ArrayList<String> mTitles;
ArrayList<Bitmap> mCovers;
Context mContext;

public iTuneListAdapter(
Context context, ArrayList<String> titles, ArrayList<Bitmap> covers) {
mContext = context;
mTitles = titles;
mCovers = covers;
}
Demo: iTuneListener with Cover Thumbnail (cont.)

Add a new layout file for the item view


Right click on res/layout > New > Layout resource file
Name the file itune_list_item
Set the root element as LinearLayout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp">

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageView"/>

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView"
android:layout_weight="1"
android:paddingLeft="8dp"
/>
</LinearLayout>
Demo: iTuneListener with Cover Thumbnail (cont.)
Implement the getView, getItem, getItemId and getCount method in
the custom adapter

@Override
public int getCount() {
@Override
return mCovers.size();
public long getItemId(int i) {
} return i;
}

@Override
public Object getItem(int i) {
return mCovers.get(i);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater =
(LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View itemView = inflater.inflate(R.layout.itune_list_item, null);

TextView textView = (TextView) itemView.findViewById(R.id.textView);


textView.setText(mTitles.get(position));

ImageView imageView = (ImageView) itemView.findViewById(R.id.imageView);


imageView.setImageBitmap(mCovers.get(position));

return itemView;
}
Demo: iTuneListener with Cover Thumbnail (cont.)

Update your iTuneListener activity class to use the new adapter


public class iTuneListener extends ListActivity {

LinearLayout layout;
iTuneListener listener = this;
ArrayList<String> titles;
ArrayList<Bitmap> covers;
iTuneListAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

titles = new ArrayList<String>();


covers = new ArrayList<Bitmap>();
adapter = new iTuneListAdapter(this, titles, covers);
setListAdapter(adapter);
Demo: iTuneListener with Cover Thumbnail (cont.)

To handle item click event, override onListItemClick() method

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Toast.makeText(this, titles.get(position), Toast.LENGTH_LONG).show();
}
Lab Assignment
Add another activity to iTuneListener and do the following:
When a song is selected from the list, start the new activity and play the preview
of the song.
The new activity need to display a larger album cover thumbnail and its title
Pause the music when your app is paused
Resume the music when it is resumed
Upload your code to moodle no later than 11/17

PS. See this example of streaming mp3 media file from URL using Android
MediaPlayer class
Swipe to Refresh
Define the swipe to refresh layout and list view to show the feed
response
Create a new layout file inside res/layout folder (right click > Layout
Resource File), name it as activity_swipe_refresh.xml and paste the
below code snippets
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</android.support.v4.widget.SwipeRefreshLayout>
How dose it work?
The ListView is placed as a child view to SwipeRefreshLayout
This allows user the ability to show the loading spinner when user swipes
the ListView edge.
All the functionality of displaying loading bar is encapsulated inside
SwipeRefreshLayout class
When user swipes down, the OnRefreshListener events gets fired. You
can handle this event to write the logic for downloading or refreshing
data
Note that, once data is downloaded, user has to manually call
setRefreshing(false) to hide the refresh spinner
Swipe to Refresh (cont.)
Add a listView variable in the iTuneListener class

public class iTuneListener extends ListActivity {

LinearLayout layout;
iTuneListener listener = this;
ArrayList<String> titles;
ArrayList<Bitmap> covers;
iTuneListAdapter adapter;
ListView listView;
SwipeRefreshLayout mSwipeRefreshLayout;

Update onCreate() as follows:


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_swipe_refresh);

titles = new ArrayList<String>();


covers = new ArrayList<Bitmap>();
adapter = new iTuneListAdapter(this, titles, covers);
setListAdapter(adapter);

listView = (ListView) findViewById(android.R.id.list);


mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
titles.clear();
covers.clear();
downloadList();
}
});

downloadList();
}
Swipe to Refresh (cont.)
Before implementing the downloadList() method, add two new methods
to the ParserListener interface:

interface ParserListener {
void start();
void setTitle(final String s);
void setCover(final Bitmap b);
void finish();
}
protected void downloadList() {
//Refreshing data on server
try {
URL url = new URL("http://ax.phobos.apple.com.edgesuite.net/WebObjects/" +
"MZStore.woa/wpa/MRSS/topsongs/limit=20/rss.xml");
new iTuneSAX(url, new ParserListener() {
@Override
public void setTitle(final String s) {
titles.add(s);
// Only UI thread can update the UI
runOnUiThread(new Runnable() {
@Override
public void run() {
// Be sure to import TextView
adapter.notifyDataSetChanged();
}
});
}
@Override
public void setCover(Bitmap b) {
covers.add(b);
runOnUiThread(new Runnable() {
@Override
public void run() {
// Be sure to import TextView
adapter.notifyDataSetChanged();
}
});
}

@Override
public void start() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(true);
}
});
}
@Override
public void finish() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(false);
}
});
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
Swipe to Refresh (cont.)
Override startDocument() and endDocument() methods in iTuneSAX
class:

@Override
public void startDocument() throws SAXException {
super.startDocument();
delegate.start();
}

@Override
public void endDocument() throws SAXException {
super.endDocument();
delegate.finish();
}
Animation
Overview
The Android framework provides two animation systems: property
animation (introduced in Android 3.0) and view animation
Both animation systems are viable options, but the property animation
system is the preferred method to use, because it is more flexible and
offers more features
In addition to these two systems, you can utilize Drawable animation,
which allows you to load drawable resources and display them one frame
after another.
Drawable animation
Drawable animation involves displaying Drawable resources one after
another, like a roll of film. This method of animation is useful if you want to
animate things that are easier to represent with Drawable resources, such
as a progression of bitmaps
View Animation
View Animation is the older system and can only be used for Views. It is
relatively easy to setup and offers enough capabilities to meet many
application's needs.
Property Animation
Introduced in Android 3.0 (API level 11), the property animation system lets
you animate properties of any object, including ones that are not rendered
to the screen. The system is extensible and lets you animate properties of
custom types as well