Android dev intro.

Android O Dev course
Preamble
- Debug code
- Find solutions online:
- Google the specific error message
- Google “How do I …”
- Ask Good question:
- Be positive
- Be specific
- If possible, give a link to the code in action.
- Thank someone if they’ve helped you.
- Answer someone else’s question
Intro to Android Studio
Download & Install Android Studio @: https://developer.android.com/studio
Follow with the default settigns and components, let it do its thing, and wait for the installation is complete.
Dive into AS
The breadcrumb at the top of the dev window indicates the file structure and the path we are in.
The buttons at the top-right corner are debugging & running section.
Buttons on the left pane has a “project” pane and it lists the directory and a “Gradle Scipts”.
“Gradle Scripts” are used to build the app we’re developing. Later in the course we will modify those scripts.
Inside the “App” folder, there is a “manifest” folder, open it, it contains a “xml” file: “AndroidManifest.xml” which contains information about the app. Later in the course we will modify this file to add features. But for now just leave it as it is.
The “Java” folder contains all of the codes. It has two “test” folders. Our point of interset should be the top-most folder which contains our code.
The “res” folder includes a “layout” folder, within this folder we can see a “activity_main.xml” file and we will spend a lot of time in this file.
AS overview
- Create new project
- Activity settings
- Layouts:
- margin settings
- background
- hint
- color
- Open a existed project
AS Basics
- Text:
- textfield
- password
- Button:
onclickaction
- Coding:
- Create object which defines the
onclickaction - Statements:
Log.imethod: Log data into the console streamEditTxtobject: A text field which accepts user’s input from the UI.Toastobject: A fade message shows to the user.getTextandtoStringmethod: To retrieve enterted data.(EditText)type cast: Convert theViewobject toEditTextobject.findViewbyIdmethod: FindViewobject by elementID.
- Create object which defines the
- Debugging
Example Interactivity Demo app:
public void clickFunction(View view) { //The Click action
EditText editText = (EditText) findViewById(R.id.usernameEditText) //Creates a EditText object which can retrieve what the user has just typed into the text field.
Log.i(log:"info", editText.getText().toString()) //Logger which record the activities into the cnosole.
}
PS: CLOSE PROXIFIER BEFORE DEBUGGING
How to use Toast:
Inside the AS Mainactivity.java, type in Toast and autocomplete to get the following line of code.
Toast.makeText(context:this, text:"This is the message displayed in the pop up.", Toast.LENGTH_SHORT.show())
Toast will display a pop up message at the bottom of the screen.
Images
To update image using a button:
- Copy & Paste the images to the
res/drawablefolder - Add a “ImageView” element into the layout, rename its id, e.g.
catImageView - Add a “Button” element into the layout, rename its id, e.g.
catSwitchButton - Then create a
onclickaction, e.g.swtichCat - In
main_activity.xmlfile, follow the following code:
public void switchCat(View view) {
Log.i(tag:"info", msg:"Button pressed.";
ImageView image = (ImageView) findViewById(R.id.catImageView);
// 1.Create an ImageView object "image".
// 2.Use findViewById to find the imageView element in the layout, then cast it into ImageView object.
image.setImageResource(R.drawable.cat2);
// 3.Update the image resource to the second image.
}
Currency Converter
To build a currency converter app, we need those elements:
- A display image displays at the top of the screen.
- A
textViewelement that indicates the user to enter an amount in pounds. - A
plainTextelement which allows the user to input the amount. - A
Buttonelement which trigers the calculation after the user tap the button. - A
Toastmessage that tells the user how much value worth in dollar. - Assume the converter only convert pounds into dollars.
Step:
- Download an image, copy & paste it to the
res/drawablefolder. - Add an
imageViewelement to the layout, and align/modify its position when sees fit in position. - Add a
textViewelement to the layout, and align/modify. Configure thetextattribute to indicate the user what should be typed into. - Add a
plainTextelement to the layout, and align/modify, configure thehintattribute. - Add a
buttonelement to the layout, and align/modify, configure theonClick&textattribute. - In
main_acitivity.xml, do those:- Log the info when the user presses the button.
- Get amount which user entered.(Data type should be in
double) - Calculate the amount converted to dollars(Data type should be in
doubletoo.) - Prints out a
Toastmessage that tells how much dollars worth by the value of pounds he/she just typed.
Codes:
public void onClick(View view) {
EditText amountInPoundEditText = (EditText) findViewById(R.id.id_of_plainText)
Double poundValue = Double.parseDouble(amountInPoundText.getText().toString());
Double dollarValue = poundValue * 1.3;
Toast.makeText(context:this, String.format("Pound in %.2f is %.2f dollars", poundValue, dollarValue), Toast.LENGTH_SHORT.show());
}
Dive into Java
HelloWorld.java:
public class HelloWorld {
// `public` means this class is accessable throughout the file.
// `clas` means its a chunk of code which defines what this class is.
public static void main(String[] args) {
// `static` defines a specific behavior of an instance of this class.
// `void` means it does not return anythin.
// `main` is a function that each Java program must have.
// `String[]` defines a string array.
// `args` means the parameter this function needs to complete its work.
System.out.println("Hello World!");
// `System.out.println` output the results to the system console.
}
}
Primitive Data types:
String // a set of characters
byte // integers, range from -128 to 127
short // integers, range from 32**** to -32***
int // integers, range from 2^32 to -2^32
float // floating point numbers
double // floating point numbers that stores a number double the memory
long // floating point numbers that takes up to 8 bytes.
boolean // true or false
Basic methods:
string.length()
// operations
+
-
*
/
...
Basic constructs:
Array: similar to tuple in Python.(Immutable after creation)
List: similar to list in Python.
PS: call import java.util.*; at the begining of the java program to use List type.
Map: similar to dict in Python.
Usage:
Array
public class HelloWorld {
public static void main(String[] args) {
int[] primeNums = {1,2,3,4,5};
// integer array;
String[] names = {"Max", "Gwen", "Kevin", "Michale", "Jack"};
// string array;
System.out.println(names);
System.out.println(primeNums.toString);
}
}
List
import java.util.*;
public class HelloWorld {
public static void main(String[] args) {
List countries = new ArrayList();
countries.add("China");
countries.add("USA");
countries.add("Japan");
countries.remove(2); // Remove "Japan" from the list.
countries.remove("USA"); // Remove "USA" from the list.
System.out.println(countries.toString());
}
}
Hash Map
import java.util.*;
public class HelloWorld {
public static void main(String[] args) {
Map family = new HashMap();
map.put("Father", "Max");
map.put("Mother", "Gwen");
System.out.println(map.get("Father"));
// output is: Max
System.out.println(map.get("Mother"));
// output is: Gwen
System.out.println(map.toString());
// output is: {Father=Max, Mother=Gwen}
System.out.println(map.size());
// output is: 2
}
}
If statement
import java.util.*;
public class HelloWorld {
public static void main(String[] args) {
int[] compare_nums = {1, 1};
if (compare_nums[1] == compare_nums[0]) {
System.out.println("The second number is equal to the first number.");
} else if (compare_nums[1] > compare_nums[0]) {
System.out.println("The second number is greater than the first number.");
} else {
System.out.println("The second number is less than the first number.");
}
}
}
Loops
/*************************************************************************
> File Name: HelloWorld.java
> Author: xuanchengpan
> Mail: [email protected]
> Created Time: Wed, Sep 23, 2020 5:52:20 PM
************************************************************************/
public class HelloWorld {
public static void main(String[] args) {
// 1. first ten 3 multiples using while loop.
System.out.println("*******************First ten multiples of three using while loop.");
int x = 1;
while (x<=10) {
System.out.println(x*3);
x++;
}
// 2. first ten 3 multiples using for loop.
System.out.println("*******************First ten multiples of three using for loop.");
for (int y = 1; y <= 10; y++) {
System.out.println(y*3);
}
// 3. Triangular numbers using while loop.
System.out.println("*******************First ten triangular numbers using while loop.");
int m = 0;
int count = 0;
while (count < 10) {
System.out.println(m);
m += count + 1;
count++;
}
// 4. Triangular numbers using for loop.
System.out.println("*******************First ten triangular numbers using while loop.");
int foo = 0;
for (int n = 0; n < 10; n++) {
System.out.println(foo);
foo += n + 1;
}
}
}
Output:
*******************First ten multiples of three using while loop.
3
6
9
12
15
18
21
24
27
30
*******************First ten multiples of three using for loop.
3
6
9
12
15
18
21
24
27
30
*******************First ten triangular numbers using while loop.
0
1
3
6
10
15
21
28
36
45
*******************First ten triangular numbers using while loop.
0
1
3
6
10
15
21
28
36
45
real 0m0.912s
user 0m0.000s
sys 0m0.046s
Hit any key to close this window...
Classes and objects
/*************************************************************************
> File Name: HelloWorld.java
> Author: xuanchengpan
> Mail: [email protected]
> Created Time: Wed, Sep 23, 2020 5:52:20 PM
************************************************************************/
import java.util.*;
public class HelloWorld {
public static void main(String[] args) {
class Number {
int value;
public boolean isPositive() {
if (value > 0) {
return true;
} else {
return false;
}
}
}
Number a_num = new Number();
a_num.value = 0;
if (a_num.isPositive()) {
System.out.println(a_num.value + " is positive.");
} else {
System.out.println(a_num.value + " is not positive.");
}
}
}
Output:
"C:\Program Files\Git\usr\bin\bash.exe" -c "time java HelloWorld.java"
0 is not positive.
real 0m0.758s
user 0m0.000s
sys 0m0.031s
Hit any key to close this window...
Number Shapes app
Create an app that determine whether a number entered by the user is a square number, a triangular number, or none of the above.
package com.xuanchengpan.numbershapes;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
class Number {
int value;
public boolean isSquare() {
double square = Math.sqrt(value);
String num_string = String.valueOf(square);
return num_string.length() <= 3;
}
public boolean isTriangular() {
int remainder = value % 9;
int[] triangular = {0, 1, 3, 6};
for (int i : triangular) {
if (remainder == i) {
return true;
}
}
return false;
}
}
public void checkNumber(View view) {
EditText numberEditText = (EditText) findViewById(R.id.numberEditText);
Number number = new Number();
number.value = Integer.parseInt(numberEditText.getText().toString());
String message;
Log.i("info", "Button pressed.");
if (number.isSquare() && number.isTriangular()) {
message = "The number you entered are both square and triangular number!";
} else if (number.isSquare() && !number.isTriangular()) {
message = "The number you entered is a square number!";
} else if (!number.isSquare() && number.isTriangular()) {
message = "The number you entered is a triangular number!";
} else {
message = "Sorry, the number you entered is neither a triangular number nor a square number.";
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Mistake:
Since I found that all triangular numbers has a remainder of 0, 1, 3, 6 when divded by 9, so a number can be determiend as a triangular number if it has a remainder of 0, 1, 3, 6 divided by 9.
But this is wrong.
E.g. 9 has a remainder of 0 when divided by 9, but it is not a triangular number.
A correct way to approach this is to iterate each triangular number until it is not less than the target number.
After the iteration, check if the number is equal to a triangular number, if it is, then it is triangular.
Numer shape app 2.0
- Fixed that when the user tap the button with nothing entered the app crashes.
package com.xuanchengpan.numbershapes;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
public void checkNumber(View view) {
Log.i("info", "Button pressed");
EditText numberEditText = (EditText) findViewById(R.id.numberEditText);
String message = numberEditText.getText().toString();
if (message.isEmpty()) {
message = "Please enter a number to start!";
} else {
Number number = new Number();
number.value = Integer.parseInt(numberEditText.getText().toString());
if (number.isTriangular() && number.isSquare()) {
message += " is both a triangular number and a square number!";
} else if (number.isSquare()) {
message += " is a square number!";
} else if (number.isTriangular()) {
message += " is a triangular number!";
} else {
message += " is neither a square number nor a triangular number!";
}
}
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
class Number {
int value;
public boolean isTriangular() {
int triangular_num = 0;
for (int i = 0; triangular_num < value; i++) {
triangular_num += i + 1;
}
if (triangular_num == value) {
return true;
} else {
return false;
}
}
public boolean isSquare() {
double square_num = Math.sqrt(value);
if (square_num == Math.floor(square_num)) {
return true;
} else {
return false;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Tic Tac Toe
package com.xuanchengpan.tictactoe;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import org.w3c.dom.Text;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
// 1: Player 1, 2: Player 2
int player = 1;
int[] state = {0, 0, 0, 0, 0, 0, 0, 0, 0};
int[][] win_pos = {
{0, 1, 2}, {3, 4, 5},
{6, 7, 8}, {0 ,3 ,6},
{1, 4, 7}, {2, 5, 8},
{0, 4, 8}, {2, 4, 6}
};
String winning_message;
boolean game_state = true;
public void dropIn(View view) {
int counter = 0;
// player 1: red, player 2: purple
Button play_again_button = (Button) findViewById(R.id.playAgainButton);
ImageView imageView = (ImageView) view;
TextView winnerTextView = (TextView) findViewById(R.id.winningStateTextView);
// 1.Initiatives
int tag = Integer.parseInt(imageView.getTag().toString());
if (state[tag] == 0 && game_state) {
counter++;
if (counter == state.length){
game_state = false;
winning_message = "No one wins!";
}
imageView.setTranslationY(-1000);
// 2.Player turns
if (player == 1) {
imageView.setImageResource(R.drawable.red);
tag = Integer.parseInt(imageView.getTag().toString());
state[tag] = 1;
player = 2;
} else if (player == 2) {
imageView.setImageResource(R.drawable.purple);
tag = Integer.parseInt(imageView.getTag().toString());
state[tag] = 2;
player = 1;
}
imageView.animate().translationY(0).setDuration(200);
// 3.Check winning state
for (int[] list : win_pos) {
if (state[list[0]] == state[list[1]] && state[list[1]] == state[list[2]] && state[list[2]] != 0) {
String winner;
if (player == 1) {
winner = "Purple";
} else {
winner = "Red";
}
winning_message = "Player " + winner + " wins!";
game_state = false;
}
}
} else {
winnerTextView.setText(winning_message);
play_again_button.setVisibility(View.VISIBLE);
winnerTextView.setVisibility(View.VISIBLE);
}
}
public void playAgain(View view){
Log.i("info", "Button pressed.");
TextView winner_text_view = (TextView) findViewById(R.id.winningStateTextView);
Button play_again_button = (Button) findViewById(R.id.playAgainButton);
play_again_button.setVisibility(View.INVISIBLE);
winner_text_view.setVisibility(View.INVISIBLE);
androidx.gridlayout.widget.GridLayout grid_layout = findViewById(R.id.gridLayOut);
for(int i=0; i<grid_layout.getChildCount(); i++) {
ImageView child = (ImageView) grid_layout.getChildAt(i);
child.setImageDrawable(null);
}
Arrays.fill(state, 0);
game_state = true;
player = 1;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Section 4: Media, Images, Video & Audio
Add images
To add images:
- Add
ImageView, and move the images tores/drawablefodler. - Create instance of
ImageViewand there are bunch of options, e.g. moving(SetTranslation), fade(alpha), rotation(rotation), animation duration(SetDuration).
Video
Too add video:
- Add
VideoViewand move the video tores/rawfolder. - Create instance of
VideoView, andVideoView.setVideoPath()to refer to the video in the folder. - Add
MediaControllerto control the playback of the video.
Audio
To add audio:
Notice: There are no viewports for audio, audio shall be created when the app starts as a background resource.
- We can apply three functions to an audio, which are:
Play,Pause,Stop - To
Playthe audio, first move the audio files tores/rawfodler. - Instantiate
mediaPlayerandaudioManager, as well asSeekBar. - Set seekbar progress change listener to mediaplayer progress.
- Set seekbar
seektoposition toaudiomanagerandtimer.
Basic Phrase
Function:
- Align 8 Play buttons in a grid layout.
- Play the corresponding audio when press each button.
package com.xuanchengpan.basicphrase;
import androidx.appcompat.app.AppCompatActivity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
MediaPlayer mPlayer;
public void play(View v) {
Button button = (Button) v;
if (mPlayer == null) {
mPlayer = MediaPlayer.create(this, getResources().getIdentifier(button.getTag().toString(), "raw", getPackageName()));
mPlayer.start();
} else {
mPlayer.release();
mPlayer = null;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Advanced Features
Times Tables app
Goal:
ListView- Use
SeekBarto display the time multiples.(e.g. 1, 2, 3, 4…)
Code:
package com.xuanchengpan.timestable;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.*;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
ListView timeTableListView;
public void timeTableGenerate(int timeTableNumber) {
ArrayList<String> timeTableNumbers = new ArrayList<String>();
for (int i = 1; i < 10; i++) {
timeTableNumbers.add(Integer.toString(i * timeTableNumber));
}
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, timeTableNumbers);
timeTableListView.setAdapter(arrayAdapter);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final SeekBar timeTableSeekBar = findViewById(R.id.timeTableSeekBar);
timeTableListView = findViewById(R.id.timeTableListView);
timeTableSeekBar.setMax(20);
timeTableSeekBar.setProgress(10);
timeTableGenerate(1);
timeTableSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int timeTableNumber;
int min = 1;
if (progress < min) {
timeTableNumber = min;
timeTableSeekBar.setProgress(min);
} else {
timeTableNumber = progress;
}
Log.i("SeekBar moved to: ", Integer.toString(timeTableNumber));
timeTableGenerate(timeTableNumber);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
}
Mistake:
Cannot
findViewByIdif declaring an object beforeOnCreate()Method.
Timer()
Goal:
- Know how the
Timer()behaves. - Know how to control
SeekBar. - Update
Timer()
package com.example.eggtimer;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
// Define class property as primitive types.
int time; // Time position.
int minutes = 0; // Minute.(Time remainder divided by 1 minute, ie. 60000 miliseconds.)
int seconds = 0; // Second.(Time ifference with minutes, divided by 1 second, ie. 1000 miliseconds.)
int timeMax = 600 * 1000; // Upper bound of timer.
int timeMin = 3 * 1000; // Lower bound of timer.
int time_interval = 1000; // One second.
TextView minutesTextView;
TextView secondsTextView;
Button countDownTimerButton;
// Compute minutes and seconds.
public void calculateMinutesAndSeconds() {
minutes = time / (60 * 1000);
seconds= (time - (60 * 1000 * minutes)) / 1000;
}
// Update the text view corresponding to updated minutes and seconds.
public void setMinutesAndSecondsText() {
minutesTextView.setText(String.valueOf(minutes));
secondsTextView.setText(String.valueOf(seconds));
}
// Count down timer start after the button is pressed.
public void countDownTimerStart(View view) {
Log.i("Debug", "countDownTimerStart");
new CountDownTimer(time, time_interval) {
@Override
public void onTick(long millisUntilFinished) {
Log.i("Count Down Timer:", minutes + " : " + seconds);
time -= time_interval;
calculateMinutesAndSeconds();
setMinutesAndSecondsText();
}
@Override
public void onFinish() {
Log.i("Count Timer Done.","No count down.");
}
}.start();
}
// OnCreate.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize visual elements.
minutesTextView = findViewById(R.id.minutesTextView);
secondsTextView = findViewById(R.id.secondsTextView);
setMinutesAndSecondsText();
countDownTimerButton = findViewById(R.id.countDownTimerButton);
final SeekBar countDownTimerSeekBar = findViewById(R.id.countDownTimerSeekBar);
// countDownTimerSeekBar settings.
countDownTimerSeekBar.setMax(timeMax);
countDownTimerSeekBar.setProgress(timeMin);
// countDownTimerSeekBar behavior definition.
countDownTimerSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
Log.i("Debug", "The user moved the SeekBar. Now the time is: " + time);
// Track the user movement and define the minimum value of the timer could count.
if (progress < timeMin) {
countDownTimerSeekBar.setProgress(timeMin);
time = timeMin;
} else {
time = progress;
}
calculateMinutesAndSeconds();
Log.i("Minute:", String.valueOf(minutes));
Log.i("Second:", String.valueOf(seconds));
setMinutesAndSecondsText();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
}
}