Basics of Android Programming

In this tutorial, you will learn the basics of Android by building an application. The app you are going to create is a Quiz application which tests the user's knowledge of the school and Computer Sciences in general. This tutorial is not meant to be comprehensive. For more information please refer to the slide presentation available on eLearn@USM, reference book or the internet.

Activity and layout

An app consists of an activity and a layout that defines the user interface (UI) of the activity. Activity serves as the entry point for an app's interaction with the user. It provides the window in which the app draws its UI. Layout defines the visual structure for the UI. A layout can be declared in two ways.

To get started, create an Android project. Enter the following values in the create new project window:
Application name: QuizCS
Company Domain: cs.example.com
Choose Empty Activity when you are asked to add an activity.

new project
Fig. 1: A new project window.

Now we will modify the layout file to define the UI. Open the layout file (activity_main.xml) and modify (bold and italic lines) it as follows.

<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/questionTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.25" />

</android.support.constraint.ConstraintLayout>
	

The layout is using ConstraintLayout as the root element. It allows you to position widgets (UI components) in a flexible way.
We add two new lines. The first new line is the android:id attribute which is is to uniquely identify the TextView within the tree. So that later we can reference the TextView in the layout file. The second new line is app:layout_constraintVertical_bias is to tweak the positioning to favor one side over another. In this example we want to position the TextView to be slightly above the middle of the screen.

Now let's add two buttons to the layout to represent True and False buttons. Add the following codes after TextView. Notice the value of app:layout_constraintTop_toBottomOf attribute. We define the question TextView as the value. This is to position the buttons under the question. Similarly for app:layout_constraintRight_toLeftOf and app:layout_constraintLeft_toRightOf.

	...
	
    <Button
        android:id="@+id/trueButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="True"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/falseButton"
        app:layout_constraintTop_toBottomOf="@id/questionTextView"
        app:layout_constraintVertical_bias="0.1" />

    <Button
        android:id="@+id/falseButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="False"
        app:layout_constraintBottom_toBottomOf="parent"
		app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toRightOf="@id/trueButton"
        app:layout_constraintTop_toBottomOf="@id/questionTextView"
        app:layout_constraintVertical_bias="0.1" />
	

Now we have the layout done, we will program them to define their functionality. In the activity file (MainActivity.java), modify it as follows.

public class MainActivity extends AppCompatActivity {

    private Button trueButton;
    private Button falseButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
		
	trueButton = findViewById(R.id.trueButton);
        falseButton = findViewById(R.id.falseButton);
    }
}
	

Now the buttons have resource IDs, we can access them in Activity (MainActivity.java). First we create two variables for the buttons. Then we get the references to the buttons by using findViewById and assign them to the variables.

Setting the event listeners

We want to listen to the event where the buttons are being pressed or clicked, so that we can do something such as checking the answers and inform the user if the answer is correct or not. To implement the event listener, we set a listener to the button using setOnClickListener(OnClickListener) method. The method takes a listener as its argument which implements OnClickListener.

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

        trueButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Does something ...
            }
        });

        falseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Does something ...
            }
        });
    }
	

We can give feedback to users using a pop-up message called a toast. For now, let's make the true button to display "Correct!"and false button to display "Incorrect!".

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

        trueButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "Correct!", Toast.LENGTH_SHORT).show();
            }
        });

        falseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "Incorrect!", Toast.LENGTH_SHORT).show();
            }
        });
    }
	

Now, let's see the new app in action. First we need to create a virtual device. Once you have an AVD, you can run QuizCS on it.

Creating a new class

We are going to upgrade QuizCS to present more than one question. First we are going to add a new class (Question) to encapsulate a single true-false question. Then, we will create an array of Question object in the MainActivity.

To create a new class, File -> New -> Java Class. Then press Ok.

new java class
Fig. 2: Creating a new java class.

Add the following codes; two member variables, a constructor and the 'Getter' of the variables.

public class Question {

	private String mText;
	private boolean mAnswer;

	public Question(String text, boolean answer) {
		mText = text;
		mAnswer = answer;
	}
	
	public String getText() {
		return mText;
	}

	public boolean getAnswer() {
		return mAnswer;
	}
}
	

Updating the activity

Before we update MainActivity.java, we need to make a few modification to the layout. First need to remove the android:text attribute from the TextView. We do not want a hardcoded question to be part of the definition. We will retrieve the questions from an array of questions.

    <TextView
        android:id="@+id/questionTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.25" />
	

Next, we need update the layout to include two new buttons i.e. Prev and Next buttons. Update the layout as follows. You will see the layout as shown in Fig. 3.

    <Button
        android:id="@+id/prevButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Prev"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/nextButton"
        app:layout_constraintTop_toBottomOf="@id/trueButton"
        app:layout_constraintVertical_bias="0.1" />

    <Button
        android:id="@+id/nextButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Next"
        app:layout_constraintBottom_toBottomOf="parent"
		app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toRightOf="@id/prevButton"
        app:layout_constraintTop_toBottomOf="@id/falseButton"
        app:layout_constraintVertical_bias="0.1" />
	
new layout
Fig. 3: Two buttons for navigating the questions.

Now, let's update MainActivity.java. Add variables for the new buttons. We also require a variable to reference the TextView (to display the question). Then, create an array of Question objects and an index for the array.

public class MainActivity extends AppCompatActivity {

    ...
    private Button prevButton;
    private Button nextButton;
    private TextView questionTextView;
	
    private Question[] questionBank = new Question[] {
	new Question("The School of Computer Sciences was established on the 1st March 1995.", true),
	new Question("The minimum total unit requirements is 125-132 units.", true),
	new Question("One of the specialization areas is Game Development.", false),
	new Question("CAT401 research project is a development-oriented project.", false)
    };
    private int currentIndex = 0;
    
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        prevButton = findViewById(R.id.prevButton);
        nextButton = findViewById(R.id.nextButton);
        questionTextView = findViewById(R.id.questionTextView);
        questionTextView.setText(questionBank[currentIndex].getText());
		
    }
}
	

Now, we set a listener to next button such that the next question will be displayed when it is clicked. Similarly for previous button, we set a listener to display the previous question when it is clicked.

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

        nextButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                currentIndex = currentIndex + 1;
                if(currentIndex == questionBank.length) currentIndex = questionBank.length-1;
                questionTextView.setText(questionBank[currentIndex].getText());
            }
        });

        prevButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                currentIndex = currentIndex - 1;
                if(currentIndex < 0) currentIndex = 0;
                questionTextView.setText(questionBank[currentIndex].getText());
            }
        });
    }
	

Defining a method

We have to modify the onClickListener of true and false buttons so that they will compare the user's answer with the question's answer. Let's define a method so that it can be used for both buttons.

    @Override
    protected void onCreate(Bundle savedInstanceState) {	
        
		...
        trueButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                checkAnswer(true);
            }
        });

        falseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                checkAnswer(false);
            }
        });
		
    }
	
    private void checkAnswer(boolean userAnswer) {
        boolean answer = questionBank[currentIndex].getAnswer();
        if(userAnswer == answer)
            Toast.makeText(MainActivity.this, "Correct!", Toast.LENGTH_SHORT).show();
        else {
            Toast.makeText(MainActivity.this, "Incorrect!", Toast.LENGTH_SHORT).show();
        }
    }
	

Second activity

Let's create a new activity. We are going to call this activity CheatActivity because this activity will display the answer. In the MainActivity, we will have a button that will launch the CheatActivity. When we create CheatActivity, Android Studio declares the activity in the AndroidManifest.xml file. Before we start working on this activity, add a button to the layout of MainActivity as shown Fig. 4.

new java class
Fig. 4: Cheat button in Main Activity.

To create a new activity, File -> New -> Activity -> Empty Activity. Then press Finish.

new activity
Fig. 5: Creating a new activity.

Lets define the layout of CheatActivity. Add two TextView and a button as follows.

<android.support.constraint.ConstraintLayout
    ...
    ...>
	<TextView
        android:id="@+id/warnTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Are you sure?"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.4" />

    <TextView
        android:id="@+id/answerTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/warnTextView"
        app:layout_constraintVertical_bias="0.1" />

    <Button
        android:id="@+id/showAnswerButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Show answer"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/answerTextView"
        app:layout_constraintVertical_bias="0.1" />
</android.support.constraint.ConstraintLayout>
	

Starting an activity

Now define the a Button variable as reference to the cheat button. Use findViewById to assign the button to the variable and set OnClickListener to the button. Android provide a method called startActivity(Intent intent) to start an activity. How does the Android knows which Activity to start - that information is in the Intent parameter. An intent is an object that a component can use to communicate with Android. Lets create an Intent that includes the CheatActivity class and specify it as the parameter to start the activity as follows.

        cheatButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, CheatActivity.class);
                startActivity(intent);
            }
        });
	

We need to inform the CheatActivity of the answer to the current question. But how do we pass data between activities? We can use intent extras; extras are data that the calling activity can include with an intent. In the second activity, we can retrieve the data from the extras and use it in the activity. An extra can be added to an intent as follows.

        cheatButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, CheatActivity.class);
				intent.putExtra("QUIZ_ANSWER", questionBank[currentIndex].getAnswer());
                startActivity(intent);
            }
        });
	

Now lets add the necessary codes to CheatActivity.java. Declare the corresponding variables to reference answerTextView and showAnswerButton. Use findViewById to assign the views to the variables. To retrieve the data from extra, use getIntentmethod as follows.

    private boolean quizAnswer;
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cheat);

        ...
        quizAnswer = getIntent().getBooleanExtra("QUIZ_ANSWER", false);
        
    }
	

Now set OnClickListener to showAnswer button. In OnClick method, set the answer to answerTextView as follows.

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

        ...
        showAnswerButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(quizAnswer)
                    answerTextView.setText("True");
                else
                    answerTextView.setText("False");
            }
        });        
    }
	

Run the app on an AVD and launch the new activity.