Download two-responsive-screens-U08928-JAN-13

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
SEVERAL COMMUNICATING SCREENS
David Sutton
WHAT WE WILL COVER IN
THIS LECTURE
 ArrayLists (for the benefit of those who have not taken
Data Structures).
 Intents and Multiple Screens
 Adapter Views
THIS WEEK’S TASK
 In week 2 we created an application that selected a
random scale for a students to practice. However it was
selecting the scale from a predetermined list hard coded
as an array in its one and only Activity
 This week we will develop the application so that a
separate screen (Activity) is displayed in which the user
can select the set of scales (s)he is working on.
DESIGN OF THE APPLICATION
Click here
to return
to main
screen
Choose a scale
from list to be
practiced
Shuffle the list
To add a
scale, write it
here, and
then click on
the D-Pad
Scale to be
practised
Launches the
second screen
List of scales to practice.
Clicking on a scale deletes it
from list
PROBLEMS WE HAVE TO SOLVE
 We need a data structure to which we can easily add data
and from which we can easily remove data.
 We need to make on screen launch another.
 We need to transfer data from one screen to another,
 We need to display lists of data (the scales) whose
content can change as we run the application.
STORING DATA
 Arrays are of fixed size and so are not ideal for
representing lists whose content and size can change as
we run an application.
 Fortunately the Java API contains many classes that
represents lists which can dynamically change their size.
 The list implementation that we will use is called the
ArrayList.
LISTS
 Lists are flexible-length data structures where the data
is ordered in a linear fashion.
Each element of the list holds some data.
ARRAYS
VS
Arrays are fixed-length:
once memory is
assigned, the length of
the array remains fixed.
Data structures that are
fixed-size are known as
static data structures.
LISTS
Lists can grow or shrink
in length as a program
runs.
Data structures that can
change in size whilst a
program runs are known
as dynamic data
structures
ARRAYLIST
The Java implementation of lists we will look at is called
ArrayList.
An ArrayList is flexible in length, like a list, but also
offers some of the direct-access advantages of an
array.
For further reading on the ArrayList class, see
Chapter 4 of Horstmann: “Java Concepts” (5th edition)
Chapter 7.2 of Hubbard:
“Schaum's Outline of Data Structures with Java” (2nd edition)
DECLARING ARRAYLIST OBJECTS
When using ArrayList s, at the top of the class, we need
to import the Java utilities package (which is where the
ArrayList class lives):
import java.util.*;
When declaring an ArrayList object, we have to say
what type of objects we want in our ArrayList, e.g.
type of the elements in the ArrayList
ArrayList<String> myStrings;
ArrayList<Balloon> myBalloons;
type
variable name
As with other variables, it is also possible to declare an
ArrayList object and initialise it in the same statement,
e.g.
ArrayList<String> myStrings
= new ArrayList<String>();
ArrayList<Balloon> myBalloons
= new ArrayList<Balloon>();
initialises to
an empty list
with no
elements
USEFUL ARRAYLIST METHODS
There are many methods, but some of the most useful are
Here, E is just referring to the type of elements in the
ArrayList
add(E e) - appends the given element to the end of the list.
get(int index) - returns the element at the given position in
this list.
clear() - removes all of the elements from this list.
remove(int index)
Removes the element at the specified position in this list.
size()
Returns the number of elements in this list.
Full details can be found at the Java API documentation
http://docs.oracle.com/javase/
MAIN ACTIVITY LAYOUT (SLIDE 1)
We will use a vertical LinearLayout for this,
but a RelativeLayout would work too.
<LinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
....
MAIN ACTIVITY LAYOUT (SLIDE 2)
We learnt how to add buttons and
externalise strings in earlier lectures.
<Button
android:id="@+id/choose_scales_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/choose_scale" />
<Button
android:id="@+id/shuffle_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shuffle" />
MAIN ACTIVITY LAYOUT (SLIDE 3)
A “hint” is displayed in a text box before
any real text is entered.
<TextView
android:id="@+id/practice_scale_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/scale_to_practice"
tools:context=".ScalePractice" />
<Button
android:id="@+id/set_scales_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/set_scales" />
</LinearLayout>
WHAT WE SHOULD SEE AT THIS POINT
Running the application
should display a main
activity with the widgets
that we added to the
layout. But there will be
no response from the
buttons yet.
STORING THE LIST OF SCALES
Lines we
have added
are in bold
import java.util.ArrayList;
public class ScalePractice extends Activity {
private final String[] defaultKeys = {"C","D","E","F Sharp",
“B Flat","A Flat"};
We will store
the list of
scales in this
ArrayList
private ArrayList<String> scales = new ArrayList<String>();
Method to
set up initial
content of
ArrayList
private void setDefaultScales() {
for (int i=0;i<defaultKeys.length;i++) {
scales.add(defaultKeys[i] + " Major");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scale_practice);
setDefaultScales();
}
MAKING THE CHOOSE SCALE
BUTTON RESPOND
public class ScalePractice extends Activity
...
int scaleIdx=0;
Button chooseScaleButton;
TextView practiceScale;
@Override
public void onCreate(Bundle savedInstanceState) {
....
chooseScaleButton = (Button) findViewById(R.id.choose_scales_button);
practiceScale = (TextView) findViewById(R.id.practice_scale_text);
chooseScaleButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
if (scaleIdx>=scales.size()) {scaleIdx=0;}
practiceScale.setText(scales.get(scaleIdx));
scaleIdx++;
}
We will get an error here, if the ArrayList is
});
empty. We will fix that later!
}
WHAT WE SHOULD SEE AT THIS POINT
Clicking on the “Choose
Scale” button causes us
to cycle through the
scales, but in a
predetermined order.
There is no response
from the other buttons
yet.
MAKING THE SHUFFLE
BUTTON RESPOND
public class ScalePractice extends Activity {
....
private ArrayList<String> scales = new ArrayList<String>();
int scaleIdx=0;
...
private Button shuffleButton;
@Override
public void onCreate(Bundle savedInstanceState) {
...
shuffleButton = (Button) findViewById(R.id.shuffle_button);
shuffleButton.setOnClickListener( new OnClickListener() {
public void onClick(View v) {
Collections.shuffle(scales);
scaleIdx = 0;
}
});
}
Collections is a class in the
java.util package containing
a set of static methods that
do useful things with
ArrayLists
WHAT WE SHOULD SEE AT THIS POINT
Clicking on the “Choose
Scale” button causes us
to cycle through the
scales.
Clicking on the “Shuffle”
button changes the order
in which scales are
presented.
There is no response
from the “Set Scales”
button yet.
INTENTS AND ACTIVITIES
Android Activities are “loosely coupled”. Activities do not normally access
each others methods or fields (not even public methods).
Intent
data
extras
Intent
data
extras
One activity invokes another by sending a message called an Intent. The
Intent may include information that will be used by the invoked Activity
EXPLICIT AND IMPLICIT INTENTS
 There are two types of Intent:
 Explicit Intents specify that some named Activity should
be launched.
 Implicit Intents specify that some particular action should
be performed, for example dialling a specified number. It
is up to Android to find an Activity capable of performing
that action.
 In this case, we know what Activity we want to invoke, so
we will use an explicit Intent.
 But first we need to create a skeleton implementation of
the Activity we wish to invoke...
SCALE SET ACTIVITY LAYOUT
You can create a new XML
Layout file by right clicking on
the “res/layout” folder and
select “New” and then “Android
XML File”.
A ListView can be dragged in
from the “Composite” section of
the palette.
When we write the code for this
Activity we will arrange for the
ListView to be populated by
Strings drawn from an ArrayList
SCALE SET ACTIVITY LAYOUT (SLIDE 1)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android
="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/finish_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/finish" />
SCALE SET ACTIVITY LAYOUT (SLIDE 2)
<EditText
android:id="@+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/scale_to_add" >
<requestFocus />
</EditText>
<ListView
android:id="@+id/scale_list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
SCALE SET ACTIVITY CODE
package uk.ac.brookes.scalesv2;
import android.app.Activity;
import android.os.Bundle;
public class ScaleSetActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scale_set_layout);
}
You can create a
new class by right
clicking on a
package node in
the package
explorer and
selecting “New”
then “Class”.
}
Inflate the layout file for the scale set activity
STARTING AN ACTIVITY
To start an Activity we must
•
Create an Intent object that specifies the Activity to be
started (or the action to be performed in the case of an
implicit Intent).
•
Pass that intent to the startActivity or
startActivityForResult method of the Activity class.
•
The startActivityForResult method is used when you
expect the invoked Activity to return data to the invoking
Activity. We will want to do that eventually, but for the
time being we will just use startActivity method.
Intent intent = new Intent( getApplicationContext(),
ScaleSetActivity.class);
startActivity(intent);
N.B this code should
be placed in the
main Activity, not in
the Activity we wish
to start.
This is the Activity
we wish to start.
STARTING THE SCALE SET ACTIVITY
public class ScalePractice extends Activity {
....
private Button setScalesButton;
@Override
public void onCreate(Bundle savedInstanceState) {
...
setScalesButton = (Button) findViewById(R.id.set_scales_button);
setScalesButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(
getApplicationContext(),
ScaleSetActivity.class);
startActivity(intent);
}
});
}
THE ANDROID MANIFEST
 In order for our new Activity to be
invoked, we will need to add it to the
Android manifest.
 The manifest is contained in a file
called AndroidManifest.xml and
describes essential information about
the application such as its
permissions and the Activities that it
contains.
 You can find the AndroidManifest file
towards the bottom of the Package
Explorer panel.
EDITING THE MANIFEST
There is a custom editor for the manifest file. To add a new Activity to
the manifest, go to the Application tab, click on Add... and then
specify the name of the Scale Set Activity you just created (that is to
say the name of the class).
You can also create a label for the Activity that will appear in the title
bar when it starts.
THE ANDROID MANIFEST
(ONLY THE APPLICATION NODE SHOWN HERE)
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".ScalePractice"
android:label="@string/title_activity_scale_practice" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="ScaleSetActivity"
android:label="@string/set_scales_title">
</activity>
</application>
WHAT WE SHOULD SEE AT THIS POINT
Clicking on the Set Scales button should now launch the Set
Scales Activity. However clicking on the “Finish” button of
that Activity does nothing.
RETURNING CONTROL
FROM THE SCALE SET ACTIVITY
public class ScaleSetActivity extends Activity {
private Button finishButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scale_set_layout);
finishButton = (Button) findViewById(R.id.finish_button);
finishButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
finish();
}
});
}
}
To return control to the
invoking Activity we simply
call the Activity’s finish
method.
WHAT WE SHOULD SEE AT THIS POINT
Clicking on the Set Scales button should now launch the Set
Scales Activity. Clicking on the Finish button should return
control to the main Activity.
EXCHANGING INFORMATION
BETWEEN ACTIVITIES.
 There are several ways in which Activities can
exchange information.
 The easiest way is to add data to the “extras” bundle
that is attached to an intent.
 This isn’t actually the best solution, but we shall adopt it
for the time being because it is relatively simple.
Intent
data
extras
EXCHANGING INFORMATION
BETWEEN ACTIVITIES
 Here are the modifications that we need to make in order for the list of scales
to be passed between the main activity and the scale set activity.
1.
The list of scales must be attached as an extra to the Intent sent by the
main activity.
2.
We must use the startActivityForResult method, because we now expect
to get information back from the scale set Activity
3.
The Scale Set Activity must retrieve the information from the Intent
4.
The Scale Set Activity must pass an Intent back to the main Activity, and
must attach the list of scales as an extra.
5.
The main Activity class must override the onActivityResult method in order
to retrieve the Intent created by the scale set Activity.
SENDING INFORMATION TO THE
SCALE SET ACTIVITY
public class ScalePractice extends Activity {
private ArrayList<String> scales = new ArrayList<String>();
private final int SCALE_SET_REQUEST=1;
This value will be
...
returned by Scale Set
@Override
Activity.
public void onCreate(Bundle savedInstanceState) {
...
setScalesButton = (Button) findViewById(R.id.set_scales_button);
setScalesButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(
getApplicationContext(),
ScaleSetActivity.class);
We are now using
startActivityForResult
intent.putStringArrayListExtra("Scales", scales);
startActivityForResult(intent, SCALE_SET_REQUEST);
}
});
}
RETRIEVING INFORMATION IN THE
SCALE SET ACTIVITY
public class ScaleSetActivity extends Activity {
private ArrayList<String> scales;
private Button finishButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scale_set_layout);
Intent intent = getIntent();
scales = intent.getStringArrayListExtra("Scales");
RETURNING INFORMATION FROM
THE SCALE SET ACTIVITY
public class ScaleSetActivity extends Activity {
private ArrayList<String> scales;
private Button finishButton;
@Override
public void onCreate(Bundle savedInstanceState) {
...
Intent intent = getIntent();
scales = intent.getStringArrayListExtra("Scales");
finishButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
Intent result = new Intent();
result.putStringArrayListExtra("Scales", scales);
setResult(RESULT_OK, result);
finish();
}
});
}
RETRIEVING INFORMATION IN THE
MAIN ACTIVITY
public class ScalePractice extends Activity {
private ArrayList<String> scales = new ArrayList<String>();
private final int SCALE_SET_REQUEST=1;
...
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
if (resultCode == RESULT_OK) {
if (requestCode == SCALE_SET_REQUEST) {
scales = data.getStringArrayListExtra("Scales");
scaleIdx = 0;
}
}
ADAPTERS AND ADAPTER VIEWS
 We want the Scale Set Activity’s ListVire to display a list
of the currently chosen scales.
 The ListView is an example of an Adapter View. These
are views that can be dynamically bound to a data
structure such as an ArrayList.
 In order to link the ListView to the scales ArrayList, we
need to supply an Adapter.
 We will use a very simple kind of adapter known as an
ArrayAdapter
SETTING UP THE ADAPTER
public class ScaleSetActivity extends Activity {
private ArrayList<String> scales;
private ArrayAdapter<String> scaleAdapter;
private ListView scaleList;
The adapter binds our
ListView to the scales
ArrayList. Each
element of that
ArrayList appears as a
separate element in
the ListView
@Override
public void onCreate(Bundle savedInstanceState) {
...
scaleAdapter = new ArrayAdapter<String>
(this, android.R.layout.simple_list_item_1, scales);
scaleList = (ListView) findViewById(R.id.scale_list_view);
scaleList.setAdapter(scaleAdapter);
....
}
}
WHAT WE SHOULD SEE NOW
When the Set Scales
Activity is launched, we
should now see a list of the
currently selected scales.
OTHER ADAPTER VIEWS
 In addition to the ListView the Android API includes a
GridView that dynamically binds to a 2D data structure.
ADDING SCALES
• We now want to add
code so that if the
user enters a Scale
into the “Scale to
Add” EditText and
then clicks on the D
Pad, the scale will
be added to our list
• In order to do this
we must attach an
OnKeyListener to
the EditText
ADDING SCALES
public class ScaleSetActivity extends Activity {
...
@Override
public void onCreate(Bundle savedInstanceState) {
...
enterScaleEdit = (EditText) findViewById(R.id.enter_scale_edit);
enterScaleEdit.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View view, int keyCode, KeyEvent event) {
boolean result = false;
if (event.getAction() == KeyEvent.ACTION_DOWN)
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
scales.add(0, enterScaleEdit.getText().toString());
scaleAdapter.notifyDataSetChanged();
enterScaleEdit.setText("");
result = true;
}
The result returned should be true if the
return result;
listener has “consumed” the event,
}
which means that it does not want the
});
event to passed to any other listener
}
WHAT WE SHOULD NOW SEE
• If the user types
some text into the
“Scale to Add” box,
and then clicks the
D Pad this will now
be added to the list.
• When you click on
the “Scale To Add”
box, a virtual
keyboard appears.
• You may need to hit
the back button to
hide the keyboard.
We will fix this next
week.
DELETING SCALES
• We now want to add
code so that if the
user clicks on a
Scale it is deleted
from the list.
• In order to do this
we must attach an
OnItemClickListener
to the ListView
DELETING SCALES
public class ScaleSetActivity extends Activity {
...
private ListView scaleList;
@Override
public void onCreate(Bundle savedInstanceState) {
...
scaleList.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent,
View view, int position, long id) {
scales.remove(position);
scaleAdapter.notifyDataSetChanged();
}
});
WHAT WE SHOULD NOW SEE
• If the user clicks on
a scale it is deleted
from the list.
• If the user clicks on
the “Finish” button
they return to the
main Activity, but
now scales are
picked from the
modified list.
SUMMARY
 The ArrayList class is a dynamic data structure allowing
elements to be easily added and deleted.
 One Activity can invoke another by passing an
appropriate Intent.
 AdapterViews display the contents of data structures as
lists of grids. They dynamically update as the content of
the data structures changes.