Objectives
Here we proceed to explore in detail the creating of a simple activity class + the associated layout. This process will seem quite complex on first contact - but as we proceed through the labs over the next few sessions the deep structure should start to emerge.
Create Project
Create a new Android application as demonstrated in the following 5 screenshots:
Note the 'Minimum Required SDK' which may be different from the default (Figure 3).
The opening project perspective is shown in Figure 6.
The project will look like this:
With the activity_donate open in the visual designer:
It is important to become familiar with the structure and purpose of the three panes surrounding the Donation 'canvas':
Palette:
Component Tree (Outline)
Properties
These views are closely related - and you will need to monitor the information displayed there continually as you evolve the appearance of your activities screens.
Before proceeding, open the file build.gradle in the Editor and modify as shown in Figure 12 and the code below:
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.example.donation"
minSdkVersion 19
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
}
Note that two files named build.gradle are present. Ensure you change the file within the app folder as shown in Figure 12.
- The settings in this file determine the lowest standard device on which the donation app will run.
- In this case it should install and run, at a minimum, on a device specified Android 4.1 API (Jelly Bean).
Layout Donation Activity
For this lab, our objective is to reproduce in Android this feature from the web app:
In Android Studio, delete the current 'Hello World' text, and drag and drop a new'LargeText' form widget onto the canvas.
Note carefully the following features:
- the guides tieing the text to the left, top and right corner
- in Component Tree - the name of the control has been changed from a default to 'donateTitle'. This is changed by selecting the item in outline, and selecting 'Edit ID' from the context menu.
- in Properties - where we entered 'Welcome Homer' into the text field
Recreate the above precisely.
You may toggle between the design and textual representation of the screen using the tabs located towards the foot of the Android Studio perspective as shown in Figure 9.
Closely inspect the following two files:
res/layout/activity_dontate.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".Donate">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/donateTitle"
android:id="@+id/donateTitle"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_marginTop="23dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
/>
</RelativeLayout>
res/values/strings.xml
<resources>
<string name="app_name">Donation</string>
<string name="action_settings">Settings</string>
<string name="donateTitle">Welcome Homer</string>
</resources>
Note the relationship between 'donateTitle' in both files. Also note we have deleted the superfluous 'hello_world' string left over from the generated app as indicated in Figure 8.
Bring in the following string into the donate activity now - (medium text) - and follow the same procedure as above. The designer should look like this:
and our XML files will look like this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".Donate">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/donateTitle"
android:id="@+id/donateTitle"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:layout_marginTop="23dp"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/donateSubtitle"
android:id="@+id/donateSubTitle"
android:layout_marginTop="22dp"
android:layout_below="@+id/donateTitle"
android:layout_alignParentStart="true"
android:layout_alignRight="@+id/donateTitle"
android:layout_alignParentRight="@+id/donateTitle"
android:layout_alignParentLeft="true"
/>
</RelativeLayout>
<resources>
<string name="app_name">Donation</string>
<string name="action_settings">Settings</string>
<string name="donateTitle">Welcome Homer</string>
<string name="donateSubtitle">Please give generously</string>
</resources>
Donate Button
Place a button directly on to the activity - attached to the bottom of the screen as shown:
Following a similar procedure as in the previous step, rename the button and add an id, as shown in Figures 2 and 3.
activity_donate.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".Donate">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/donateTitle"
android:id="@+id/donateTitle"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:layout_marginTop="23dp"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/donateSubtitle"
android:id="@+id/donateSubTitle"
android:layout_marginTop="22dp"
android:layout_below="@+id/donateTitle"
android:layout_alignParentStart="true"
android:layout_alignRight="@+id/donateTitle"
android:layout_alignParentRight="@+id/donateTitle"
android:layout_alignParentLeft="true"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/donateButton"
android:id="@+id/donateButton"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
strings.xml
<resources>
<string name="app_name">Donation</string>
<string name="action_settings">Settings</string>
<string name="donateTitle">Welcome Homer</string>
<string name="donateSubtitle">Please give generously</string>
<string name="donateButton">Donate</string>
</resources>
If there is a deviation from the above - retrace your steps (delete the button) until you can match the above.
We can now switch our attention to the Java Activity class Donate:
package com.example.donation;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.widget.Button;
import android.util.Log;
public class Donate extends AppCompatActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donate);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_donate, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings)
{
return true;
}
return super.onOptionsItemSelected(item);
}
}
The reference to the menu item above requires a new folder in res
named menu
containing a file named menu_donate.xml
with the following content:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".Donate">
<item android:id="@+id/action_settings"
android:title="@string/action_settings"
android:orderInCategory="100"
app:showAsAction="never"/>
</menu>
For any 'controls' a user can interact with we usually find it useful to associate a class member with that object. Currently we have one only on - a Button. The text fields we dont consider 'interactive' as such, so we will not include those.
Insert the following new field into the class:
private Button donateButton;
The class will have to be imported. The class name will always match the name in the Palette:
We are free to call the variable anything we like. However, in order to keep confusion to a minimum, always call the variable by the same name you used in the Component Tree view:
In onCreate - we need to initialise this variable:
donateButton = (Button) findViewById(R.id.donateButton);
We might also add a logging message so we can have some feedback as the app launches:
Log.v("Donate", "got the donate button");
We should check the donate button actually exists before logging our success:
if (donateButton != null)
{
Log.v("Donate", "Really got the donate button");
}
This is the complete activity class:
package com.example.donation;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.widget.Button;
import android.util.Log;
public class Donate extends AppCompatActivity {
private Button donateButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donate);
donateButton = (Button) findViewById(R.id.donateButton);
if (donateButton != null) {
Log.v("Donate", "Really got the donate button");
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_donate, menu);
return true;
}
}
Finding the log message can be very difficult, unless you set a filter.
- LogCat documentation describes how to manage logging.
In the 'LogCat' view in Android Studio, create a filter like this:
If you then select the filter, we should see our message:
Documentation
The android documentation is particularly helpful and well designed. These are the two key starting points:
- http://developer.android.com/guide/components/index.html
- http://developer.android.com/reference/packages.html
The first is designed to be read though as a guide, perhaps independent of any work in Android Studio. You should get into the habit of devoting an hour or two a week just reading this section.
The Reference guide should always be open as you are working on labs or projects, and you should make a serious effort to get to grips with at least some of the information here.
Taking the Button class we have just started using. We can immediately find the reference just by knowing the import statement in our Activity class:
import android.widget.Button;
.. translates to
(note the last three segments match the package name). Open this page now. Read just as far as the "Button Style" heading. There seems to be two ways of learning when an button event occurs. The first method is using the event handler/listener - but a second easier method is also available.
Try this now. Bring in a new method into Donate class:
public void donateButtonPressed (View view)
{
Log.v("Donate", "Donate Pressed!");
}
Import the View class:
import android.view.View;
Then, edit the activity_donate xml file - and add a new attribute into the Button xml fragment:
<Button
android:id="@+id/donateButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="@string/donateButton"
android:onClick="donateButtonPressed"/>
(the very last entry)
Save everything and execute the app, and monitor the log as you press the button:
We now have our first interaction working!
Research + New Control Layout
Recall the UI we are trying to implement:
We need a Radio Buttons, some sort of selection/combo box + a progress bar. These can be found in various locations in the palette:
RadioGroup, ProgressBar and NumberPicker seem likely candidates. The names of these controls are exactly as advertised, and we can find them as illustrated in the preceeding Figures 2, 3 & 4. To verify this, try importing them at the top of the Donate activity class:
import android.widget.RadioGroup;
import android.widget.NumberPicker;
import android.widget.ProgressBar;
... and we can bring in three fields into the class:
private RadioGroup paymentMethod;
private ProgressBar progressBar;
private NumberPicker amountPicker;
We can also open up three pages of documentation - which we can reverse engineer from the package/class names:
- http://developer.android.com/reference/android/widget/RadioGroup.html
- http://developer.android.com/reference/android/widget/ProgressBar.html
- http://developer.android.com/reference/android/widget/NumberPicker.html
Note this time we have gone to the Activity class before actually creating the controls. We should do this now - and remember to use the same names (for the IDs) as we create the controls.
Getting the layout +id names as shown above may take some practice. However, it is an essential skill to get on top of, even it it takes a lot of trial and error.
For reference purposes (try to do it your self), these are the relevant generated xml files:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
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"
tools:context=".Donate" >
<TextView
android:id="@+id/donateTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="@string/donateTitle"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/donateSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/donateTitle"
android:text="@string/donateSubtitle"
android:textAppearance="?android:attr/textAppearanceMedium" />
<RadioGroup
android:id="@+id/paymentMethod"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/progressBar"
android:layout_alignLeft="@+id/donateSubtitle"
android:layout_below="@+id/donateSubtitle"
android:layout_marginLeft="14dp"
android:layout_marginTop="26dp"
android:layout_toLeftOf="@+id/amountPicker" >
<RadioButton
android:id="@+id/PayPal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/PayPal" />
<RadioButton
android:id="@+id/Direct"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/Direct" />
</RadioGroup>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/donateButton"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="67dp" />
<NumberPicker
android:id="@+id/amountPicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/donateSubtitle"
android:layout_alignTop="@+id/paymentMethod" />
<Button
android:id="@+id/donateButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="15dp"
android:onClick="donateButtonPressed"
android:text="@string/donateButton" />
</RelativeLayout>
<resources>
<string name="app_name">Donation</string>
<string name="action_settings">Settings</string>
<string name="donateTitle">Welcome Homer</string>
<string name="donateSubtitle">Please give generously</string>
<string name="donateButton">Donate</string>
<string name="PayPal">PayPal</string>
<string name="Direct">Direct</string>
</resources>
If we have our naming conventions right - then we can bind to these new controls in onCreate:
paymentMethod = (RadioGroup) findViewById(R.id.paymentMethod);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
amountPicker = (NumberPicker) findViewById(R.id.amountPicker);
This is the complete Donate class:
package com.example.donation;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.RadioGroup;
import android.widget.NumberPicker;
import android.widget.ProgressBar;
public class Donate extends AppCompatActivity
{
private Button donateButton;
private RadioGroup paymentMethod;
private ProgressBar progressBar;
private NumberPicker amountPicker;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donate);
donateButton = (Button) findViewById(R.id.donateButton);
paymentMethod = (RadioGroup) findViewById(R.id.paymentMethod);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
amountPicker = (NumberPicker) findViewById(R.id.amountPicker);
amountPicker.setMinValue(0);
amountPicker.setMaxValue(1000);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_donate, menu);
return true;
}
public void donateButtonPressed (View view)
{
Log.v("Donate", "Donate Pressed!");
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings)
{
return true;
}
return super.onOptionsItemSelected(item);
}
}
NumberPicker
This is our reference documentation:
which is a little overwhelming. Back in the guides:
we might find some useful tutorial type introduction to this control - under 'User Interface' - 'Input Controls'
.. and this is the page on 'pickers'
This documentation is concerned with Fragments - a concept that may be difficult to grasp initially, and also explores the usage of date and time pickers.
We can get up and running without this much fuss. Returning to the documentation, these three methods should be sufficient initially:
- http://developer.android.com/reference/android/widget/NumberPicker.html#setMaxValue(int)
- http://developer.android.com/reference/android/widget/NumberPicker.html#setMinValue(int)
- http://developer.android.com/reference/android/widget/NumberPicker.html#getValue()
Observe that in the method onCreate, in the previous step, we have initialized the number picker minium and maxium permitted values:
amountPicker.setMinValue(0);
amountPicker.setMaxValue(1000);
Modify donateButtonPressed as shown here and verify that when Donate button is is pressed the method is invoked, outputting the message + value as shown in the Logcat window:
public void donateButtonPressed (View view)
{
int amount = amountPicker.getValue();
Log.v("Donate", "Donate Pressed! with amount " + amount);
}
Run this now - and verify that it operates as expected (see the actual amounts in the log file).
Radio Button
In donateButtonPressed we need to discover which payment method has been selected. Our RadioGroup documentation is here:
This looks like the method we need:
This is a revised version of donateButtonPressed()
public void donateButtonPressed (View view)
{
int amount = amountPicker.getValue();
int radioId = paymentMethod.getCheckedRadioButtonId();
String method = "";
if (radioId == R.id.PayPal)
{
method = "PayPal";
}
else
{
method = "Direct";
}
Log.v("Donate", "Donate Pressed! with amount " + amount + ", method: " + method);
}
Run it now and verify we are getting the correct logs.
We can simplify it somewhat by reducing the if statement to a singlle line:
String method = radioId == R.id.PayPal ? "PayPal" : "Direct";
This is the Java ternary operator:
This is the complete activity class:
package com.example.donation;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.RadioGroup;
import android.widget.NumberPicker;
import android.widget.ProgressBar;
public class Donate extends AppCompatActivity
{
private Button donateButton;
private RadioGroup paymentMethod;
private ProgressBar progressBar;
private NumberPicker amountPicker;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donate);
donateButton = (Button) findViewById(R.id.donateButton);
paymentMethod = (RadioGroup) findViewById(R.id.paymentMethod);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
amountPicker = (NumberPicker) findViewById(R.id.amountPicker);
amountPicker.setMinValue(0);
amountPicker.setMaxValue(1000);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_donate, menu);
return true;
}
public void donateButtonPressed (View view)
{
int amount = amountPicker.getValue();
int radioId = paymentMethod.getCheckedRadioButtonId();
String method = radioId == R.id.PayPal ? "PayPal" : "Direct";
Log.v("Donate", "Donate Pressed! with amount " + amount + ", method: " + method);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings)
{
return true;
}
return super.onOptionsItemSelected(item);
}
}
Progress
The progress bar documentation:
offers us advice on using the progress bar in multi-threaded application. Not quite what we are ready for yet! (but file it away for future reference).
These two methods are probably what we need:
- http://developer.android.com/reference/android/widget/ProgressBar.html#setMax(int)
- http://developer.android.com/reference/android/widget/ProgressBar.html#setProgress(int)
First we would need to equip our activity with the ability to remember the donation amounts:
private int totalDonated = 0;
Lets set max progress bar to 10000 in onCreate:
progressBar.setMax(10000);
.. and set the progress in donateButtonPressed:
totalDonated = totalDonated + amount;
progressBar.setProgress(totalDonated);
Try this now and observe the progress bar.
This is the complete class:
package com.example.donation;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.RadioGroup;
import android.widget.NumberPicker;
import android.widget.ProgressBar;
public class Donate extends AppCompatActivity
{
private Button donateButton;
private RadioGroup paymentMethod;
private ProgressBar progressBar;
private NumberPicker amountPicker;
private int totalDonated;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donate);
donateButton = (Button) findViewById(R.id.donateButton);
paymentMethod = (RadioGroup) findViewById(R.id.paymentMethod);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
amountPicker = (NumberPicker) findViewById(R.id.amountPicker);
amountPicker.setMinValue(0);
amountPicker.setMaxValue(1000);
progressBar.setMax(10000);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_donate, menu);
return true;
}
public void donateButtonPressed (View view)
{
int amount = amountPicker.getValue();
int radioId = paymentMethod.getCheckedRadioButtonId();
String method = radioId == R.id.PayPal ? "PayPal" : "Direct";
totalDonated = totalDonated + amount;
progressBar.setProgress(totalDonated);
Log.v("Donate", "Donate Pressed! with amount " + amount + ", method: " + method);
Log.v("Donate", "Current total " + totalDonated);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings)
{
return true;
}
return super.onOptionsItemSelected(item);
}
}
Here is another version of exactly the same class:
package com.example.donation;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.RadioGroup;
import android.widget.NumberPicker;
import android.widget.ProgressBar;
public class Donate extends AppCompatActivity
{
private Button donateButton;
private RadioGroup paymentMethod;
private ProgressBar progressBar;
private NumberPicker amountPicker;
private int totalDonated;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donate);
donateButton = (Button) findViewById(R.id.donateButton);
paymentMethod = (RadioGroup) findViewById(R.id.paymentMethod);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
amountPicker = (NumberPicker) findViewById(R.id.amountPicker);
amountPicker.setMinValue(0);
amountPicker.setMaxValue(1000);
progressBar.setMax(10000);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_donate, menu);
return true;
}
public void donateButtonPressed (View view)
{
totalDonated = totalDonated + amountPicker.getValue();
String method = paymentMethod.getCheckedRadioButtonId() == R.id.PayPal ? "PayPal" : "Direct";
progressBar.setProgress(totalDonated);
Log.v("Donate", amountPicker.getValue() + " donated by " + method + "\nCurrent total " + totalDonated);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings)
{
return true;
}
return super.onOptionsItemSelected(item);
}
}
Examine them carefully. What are the differences? Why make these changes?
Note also the careful attention to spacing and alignment in the code. Not just correct indentation, but continual attention to structuring each method carefully, removing duplication and unnecessary code and formatting/aligning the declarations and assignment statements in a table like structure:
Visible here:
private int totalDonated = 0;
private RadioGroup paymentMethod;
private ProgressBar progressBar;
private NumberPicker amountPicker;
and here:
paymentMethod = (RadioGroup) findViewById(R.id.paymentMethod);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
amountPicker = (NumberPicker) findViewById(R.id.amountPicker);
and here:
totalDonated = totalDonated + amountPicker.getValue();
String method = paymentMethod.getCheckedRadioButtonId() == R.id.PayPal ? "PayPal" : "Direct";
Android code can become very verbose and complex. Carefully formatting is essential if you are not to be overwhelmed.
Exercises
Exercise 1:
Here is an archive of lab so far:
You may use this as a reference if you have any unresolved anomalies in your code.
The archive is tagged at end of each lab step. To obtain a snapshot at the end of, for example, step 2, simply run the command git checkout 01_02
. In this case, 01_02 is a tag where 01 represents the first lab in the donation series of labs and 02 represents step 2.
You should now put your Donation application under git version control. You are already familiar with this procedure:
- cd into the project on your computer in a terminal
- create a suitable .gitignore file (sample below)
- run
git add .
- commit with a suitable message
- create a repository on your Bitbucket account named `donation-android-2016'
- follow the Bitbucket documented procedure to establish tracking between local and remote repos
- run the push command to initialize the remote repo with the latest local snapshot.
Use this repository pair to maintain a record of the Android Donation app as you develop it further during the course.
Sample .gitignore file:
#built application files
*.apk
*.ap_
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Windows thumbnail db
Thumbs.db
# OSX files
.DS_Store
# Android Studio
# https://www.jetbrains.com/idea/help/project.html
*.iml
.idea/workspace.xml
.gradle
build/
Exercises 2:
Consider an alternative to the NumberPicker - specifically one of the "Text Fields" controls:
These are mostly EditView objects:
Redesign the activity to take a value from the picker or directly from a text view:
If the number picker is set to zero, then attempt to get a number from the text view.
Here is a hint (a version of donatButonPressed that does what we want):
public void donateButtonPressed (View view)
{
String method = paymentMethod.getCheckedRadioButtonId() == R.id.PayPal ? "PayPal" : "Direct";
progressBar.setProgress(totalDonated);
int donatedAmount = amountPicker.getValue();
if (donatedAmount == 0)
{
String text = amountText.getText().toString();
if (!text.equals(""))
donatedAmount = Integer.parseInt(text);
}
totalDonated = totalDonated + donatedAmount;
Log.v("Donate", amountPicker.getValue() + " donated by " + method + "\nCurrent total " + totalDonated);
}
Exercise 3:
Revise the app such that when the target is achieved (10000) - then no more donations accepted, and the user is made aware of this.
Hint - here is how you can display a simple alert:
Toast toast = Toast.makeText(this, "Target Exceeded!", Toast.LENGTH_SHORT);
toast.show();
Exercise 4:
Show on screen at all times the total amount donated.
You will use standard TextView for this:
You already have a number of these on screen. Your layout could be revised to look like this: