Android intro

Android dev intro.

Android O Dev course

Preamble

  1. Debug code
  2. Find solutions online:
    1. Google the specific error message
    2. Google “How do I …”
  3. Ask Good question:
    1. Be positive
    2. Be specific
    3. If possible, give a link to the code in action.
    4. Thank someone if they’ve helped you.
    5. 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:
    • onclick action
  • Coding:
    • Create object which defines the onclick action
    • Statements:
      • Log.i method: Log data into the console stream
      • EditTxt object: A text field which accepts user’s input from the UI.
      • Toast object: A fade message shows to the user.
      • getText and toString method: To retrieve enterted data.
      • (EditText) type cast: Convert the View object to EditText object.
      • findViewbyId method: Find View object by element ID.
  • 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:

  1. Copy & Paste the images to the res/drawable folder
  2. Add a “ImageView” element into the layout, rename its id, e.g. catImageView
  3. Add a “Button” element into the layout, rename its id, e.g. catSwitchButton
  4. Then create a onclick action, e.g. swtichCat
  5. In main_activity.xml file, 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:

  1. A display image displays at the top of the screen.
  2. A textView element that indicates the user to enter an amount in pounds.
  3. A plainText element which allows the user to input the amount.
  4. A Button element which trigers the calculation after the user tap the button.
  5. A Toast message that tells the user how much value worth in dollar.
  6. Assume the converter only convert pounds into dollars.

Step:

  1. Download an image, copy & paste it to the res/drawable folder.
  2. Add an imageView element to the layout, and align/modify its position when sees fit in position.
  3. Add a textView element to the layout, and align/modify. Configure the text attribute to indicate the user what should be typed into.
  4. Add a plainText element to the layout, and align/modify, configure the hint attribute.
  5. Add a button element to the layout, and align/modify, configure the onClick & text attribute.
  6. In main_acitivity.xml, do those:
    1. Log the info when the user presses the button.
    2. Get amount which user entered.(Data type should be in double)
    3. Calculate the amount converted to dollars(Data type should be in double too.)
    4. Prints out a Toast message 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:

  1. Add ImageView, and move the images to res/drawable fodler.
  2. Create instance of ImageView and there are bunch of options, e.g. moving(SetTranslation), fade(alpha), rotation(rotation), animation duration(SetDuration).

Video

Too add video:

  1. Add VideoView and move the video to res/raw folder.
  2. Create instance of VideoView, and VideoView.setVideoPath() to refer to the video in the folder.
  3. Add MediaController to 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.

  1. We can apply three functions to an audio, which are:Play, Pause, Stop
  2. To Play the audio, first move the audio files to res/raw fodler.
  3. Instantiate mediaPlayer and audioManager, as well as SeekBar.
  4. Set seekbar progress change listener to mediaplayer progress.
  5. Set seekbar seekto position to audiomanager and timer.

Basic Phrase

Function:

  1. Align 8 Play buttons in a grid layout.
  2. 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:

  1. ListView
  2. Use SeekBar to 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 findViewById if declaring an object before OnCreate() Method.

Timer()

Goal:

  1. Know how the Timer() behaves.
  2. Know how to control SeekBar.
  3. 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) {}
        });
    }
}
Licensed under CC BY-NC-SA 4.0
Powered by Ignorance.