Mehdi Tmimi Mobile

Mobile Development Lab 1 Part 6 of 8

Java takes over the screen

The pill says 0 and the list is empty: your code will fill both.

Your app at the end of Part 5: the styled notes screen, the pill at 0 and an empty list.
Where you start

Before you start

By the end you can

  • set a view’s text and visibility from Java, at the right moment;
  • build a view in Java, or inflate it from XML;
  • say when to use each way.
  • Java MainActivity.java
  • Layout res › layout
  • Resources res › values · res › drawable
  • Emulator
Behind, or a fresh project? Start from here about 4 min
  1. The project: your own from Part 5, or a fresh one: Part 0, steps 0.3 and 0.4 (about 5 minutes).

  2. Your package name, for the code of this page:

    Without JavaScript, the code says com.yourname.notes: replace yourname by the name you chose.

  3. Gradle: check these 3 lines. Add any that is missing, then click Sync Now. Keep your own version numbers.

    Gradle Scripts › libs.versions.tomlAdd the marked lines
    [versions]agp = "9.5.0"junit = "4.13.2"junitVersion = "1.3.0"espressoCore = "3.7.0"appcompat = "1.8.0"material = "1.14.0"constraintlayout = "2.2.2"
    [libraries]junit = { group = "junit", name = "junit", version.ref = "junit" }ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }material = { group = "com.google.android.material", name = "material", version.ref = "material" }constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
    [plugins]android-application = { id = "com.android.application", version.ref = "agp" }
    Gradle Scripts › build.gradle.kts (Module :app)Add the marked lines
    plugins {    alias(libs.plugins.android.application)}
    android { namespace = "com.yourname.notes" compileSdk { version = release(37) }
    defaultConfig { applicationId = "com.yourname.notes" minSdk = 24 targetSdk = 37 versionCode = 1 versionName = "1.0"
    testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" }
    buildTypes { release { optimization { enable = true packageScope = setOf("androidx.**", "kotlin.**", "kotlinx.**") } } } compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 }}
    dependencies { implementation(libs.appcompat) implementation(libs.material) implementation(libs.constraintlayout) testImplementation(libs.junit) androidTestImplementation(libs.espresso.core) androidTestImplementation(libs.ext.junit)}
  4. Restarting this part? First delete res › layout › item_note.xml and every res › drawable › bg_note_*.xml (right-click, Delete…, OK). Red marks in MainActivity until you paste the Java: normal.

  5. res and manifest: download the pack and open it in Explorer. Drag its app folder onto your project folder (C:\Users\<you>\AndroidStudioProjects\Notes), then choose Replace the files in the destination. Not Extract All: it makes a folder inside and replaces nothing. Then, in res › values › themes, delete themes.xml (night) if it is still there.

    lab1-part6-res.zip zip · 6.3 KB
  6. Java: open MainActivity.java, select all, then paste this file. Missing? First right-click kotlin+java › com.yourname.notes, the one without (test), then NewJava Class: type MainActivity, then press Enter.

    kotlin+java › com.yourname.notes › MainActivity.javaReplace the whole file
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate"); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}
  7. ▶. You are ready when the phone shows the notes screen, the pill at 0 and an empty list.

Step 1 of 5

Get your views, then count the cards

7 min

Goal Reach three views from Java; make the pill count the cards.

  • Java kotlin+java › com.yourname.notes › MainActivity.java
  • Emulator
  1. Type Three instance variables, above onCreate().

    1. In MainActivity.java, click at the end of the TAG line.
    2. Press Enter twice: the first makes the marked empty line.
    3. Type the 3 marked lines, with Enter after the first two.
    4. A red name (TextView, LinearLayout)? Click it, press Alt+Enter, then choose Import class in the list that opens.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer;
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
    textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
    updateCount(); }
    private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}
  2. Type Fill them in onCreate(), then call updateCount().

    1. Click at the end of the Log.d(…); line.
    2. Press Enter twice, then type the 3 marked findViewById() lines, with Enter after the first two.
    3. Press Enter twice, then type updateCount();. It stays red until action c writes the method.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer;
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
    textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
    updateCount(); }
    private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}

    Lines 25–27: findViewById() returns the object that setContentView() (line 22) built for that id.

  3. Type The method updateCount(), under onCreate().

    1. Click right after the } that ends onCreate(), at the end of its line.
    2. Press Enter twice: the first makes the marked empty line.
    3. Type private void updateCount() {: Android Studio adds the } right after it.
    4. Press Enter: the } moves down a line.
    5. Type the 3 marked lines between the braces.
    6. View is red? Click it, press Alt+Enter, then choose Import class.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer;
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
    textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
    updateCount(); }
    private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}

    Line 33: getChildCount() is the number of views in notesContainer. Line 34: setText() wants a text, so String.valueOf() turns the number into one. Line 35: a ternary picks VISIBLE at 0 cards, GONE otherwise.

  4. ▶ Run 'app'.

The emulator: the pill says 0, and “No notes yet. Write your first one above.” is centred below the list label.

You should see The empty message, below YOUR NOTES.

Done I see “No notes yet.”

Predict

Without a Run: what if the textCount line came before setContentView()?

See the answer

It builds, then the app closes at launch. Before setContentView(), the screen has no views yet: findViewById() returns null, and calling setText() on null crashes. Logcat says:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
Stuck? 3 symptoms · the file
  • The app closes at launch: NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference A findViewById() line runs before setContentView(), or names an id of another layout. Put the 3 lines after setContentView().
  • Resources$NotFoundException: String resource ID #0x0 at launch setText(count) with an int asks for a string resource. Write String.valueOf(count).
  • TextView, LinearLayout or View stays red Its import is missing. Click on the name, press Alt+Enter, then choose Import class.

The files at the end of this step

kotlin+java › com.yourname.notes › MainActivity.javaReplace the whole file
package com.yourname.notes;
import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Lifecycle";
private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
updateCount(); }
private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
@Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
@Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
@Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
@Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
@Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
@Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}

Step 2 of 5

Build a card by hand

10 min

Goal Way 1: build a card in Java alone, and add it to the list.

  • Resources res › values · res › drawable
  • Java MainActivity.java
  • Emulator
  1. Paste The 8 pastel colours, in colors.xml.

    1. Double-click res › values › colors.xml, in the Project window.
    2. Click Copy 8 lines, beside After line 12.
    3. In colors.xml, click at the end of line 12, press Enter, then Ctrl+V.
    res › values › colors.xmlAdd the marked lines
    <?xml version="1.0" encoding="utf-8"?><resources>    <color name="brand">#C2410C</color>    <color name="on_brand">#FFFFFF</color>    <color name="header_end">#E11D48</color>    <color name="paper">#FFF8F1</color>    <color name="card">#FFFFFF</color>    <color name="field">#FBF6F0</color>    <color name="line">#E7D9CB</color>    <color name="ink">#1F1A17</color>    <color name="muted">#6B6159</color>    <color name="error">#B91C1C</color>    <color name="note_butter">#FFF1C9</color>    <color name="note_butter_line">#F2D27A</color>    <color name="note_mint">#D9F5E6</color>    <color name="note_mint_line">#93D9B4</color>    <color name="note_lavender">#ECE6FF</color>    <color name="note_lavender_line">#C6B8FA</color>    <color name="note_peach">#FFE1D6</color>    <color name="note_peach_line">#F7B29A</color></resources>
  2. Paste The 6 seed strings, in strings.xml.

    1. Double-click res › values › strings.xml.
    2. Click Copy 6 lines, beside After line 13.
    3. In strings.xml, click at the end of line 13, press Enter, then Ctrl+V.
    res › values › strings.xmlAdd the marked lines
    <resources>    <string name="app_name">Notes</string>    <string name="logo_description">Notes logo</string>    <string name="count_start">0</string>    <string name="title_hint">Title</string>    <string name="body_hint">Write something…</string>    <string name="error_no_title">Give your note a title first.</string>    <string name="counter_start">0 / 140</string>    <string name="clear">Clear</string>    <string name="add">Add</string>    <string name="your_notes">YOUR NOTES</string>    <string name="list_hint">Tap to open · hold to delete</string>    <string name="empty">No notes yet.\nWrite your first one above.</string>    <string name="seed_title_1">Saturday</string>    <string name="seed_body_1">Football at 10, then revise ConstraintLayout.</string>    <string name="seed_title_2">Ideas for the app</string>    <string name="seed_body_2">Pastel cards, a Share button, maybe a photo one day. Then save the notes, so that turning the phone loses nothing.</string>    <string name="seed_title_3">Exam: the lifecycle</string>    <string name="seed_body_3">onCreate, onStart, onResume… then onPause, onStop, onDestroy.</string></resources>
  3. Paste The butter card’s background: a new drawable.

    1. Right-click res › drawable, then choose NewDrawable Resource File.
    2. A New Resource File window opens: in File name, type bg_note_butter.
    3. In Root element, press Ctrl+A to select selector, then type shape.
    4. Click OK: bg_note_butter.xml opens in Design: click Code.
    5. Click Copy the file on the panel below.
    6. In bg_note_butter.xml, click in the text, press Ctrl+A, then Ctrl+V.
    res › drawable › bg_note_butter.xmlNew file
    <?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"    android:shape="rectangle">    <solid android:color="@color/note_butter" />    <stroke        android:width="1dp"        android:color="@color/note_butter_line" />    <corners android:radius="16dp" /></shape>
  4. Paste The import and the call, in MainActivity.java.

    1. Click Copy 1 line, beside After line 4.
    2. In MainActivity.java, click at the end of line 4, press Enter, then Ctrl+V.
    3. Click Copy 1 line, beside After line 29.
    4. Click at the end of line 29, press Enter, then Ctrl+V.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;import android.util.TypedValue;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer;
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
    textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
    addCardByHand(getString(R.string.seed_title_1)); updateCount(); }
    private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
    private void addCardByHand(String title) { TextView card = new TextView(this); card.setText(title); card.setTextSize(16); card.setTextColor(getColor(R.color.ink)); card.setBackgroundResource(R.drawable.bg_note_butter); int padding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); card.setPadding(padding, padding, padding, padding); notesContainer.addView(card); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}
  5. Paste The method addCardByHand(), under updateCount().

    1. Click Copy 12 lines, beside After line 38.
    2. Click at the end of line 38, press Enter, then Ctrl+V.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;import android.util.TypedValue;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer;
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
    textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
    addCardByHand(getString(R.string.seed_title_1)); updateCount(); }
    private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
    private void addCardByHand(String title) { TextView card = new TextView(this); card.setText(title); card.setTextSize(16); card.setTextColor(getColor(R.color.ink)); card.setBackgroundResource(R.drawable.bg_note_butter); int padding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); card.setPadding(padding, padding, padding, padding); notesContainer.addView(card); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}

    Line 41: a new view needs a Context: this, the activity. Line 43: setTextSize(16) is already in sp. Line 44: getColor() turns a colour’s id into the colour.

  6. ▶ Run 'app'.

The emulator: one butter card, “Saturday”, right below the list label with no space above it; the pill says 1.

You should see A card built by Java alone:

The hand-built card: 42 phone pixels of padding around “Saturday” on the butter background.
The padding: 42 px on the phone

Done I see a roomy butter card, and 1 in the pill.

Why setPadding() takes pixels; applyDimension() turns 16 dp into pixels on any phone.

Stuck? 4 symptoms · the files
  • cannot find symbol … variable seed_title_1 The seed strings are missing. Paste the 6 lines into strings.xml.
  • Android resource linking failed … bg_note_butter.xml:4: error: resource color/note_butter … not found The pastel colours are missing. Paste the 8 lines into colors.xml.
  • bg_note_butter is red in Java The drawable is missing, or named otherwise. It must be res › drawable › bg_note_butter.xml.
  • TypedValue stays red Its import is missing. Click on the name, press Alt+Enter, then choose Import class.

The files at the end of this step. bg_note_butter.xml is the panel above.

kotlin+java › com.yourname.notes › MainActivity.javaReplace the whole file
package com.yourname.notes;
import android.os.Bundle;import android.util.Log;import android.util.TypedValue;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Lifecycle";
private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
addCardByHand(getString(R.string.seed_title_1)); updateCount(); }
private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
private void addCardByHand(String title) { TextView card = new TextView(this); card.setText(title); card.setTextSize(16); card.setTextColor(getColor(R.color.ink)); card.setBackgroundResource(R.drawable.bg_note_butter); int padding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); card.setPadding(padding, padding, padding, padding); notesContainer.addView(card); }
@Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
@Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
@Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
@Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
@Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
@Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}
res › values › colors.xmlReplace the whole file
<?xml version="1.0" encoding="utf-8"?><resources>    <color name="brand">#C2410C</color>    <color name="on_brand">#FFFFFF</color>    <color name="header_end">#E11D48</color>    <color name="paper">#FFF8F1</color>    <color name="card">#FFFFFF</color>    <color name="field">#FBF6F0</color>    <color name="line">#E7D9CB</color>    <color name="ink">#1F1A17</color>    <color name="muted">#6B6159</color>    <color name="error">#B91C1C</color>    <color name="note_butter">#FFF1C9</color>    <color name="note_butter_line">#F2D27A</color>    <color name="note_mint">#D9F5E6</color>    <color name="note_mint_line">#93D9B4</color>    <color name="note_lavender">#ECE6FF</color>    <color name="note_lavender_line">#C6B8FA</color>    <color name="note_peach">#FFE1D6</color>    <color name="note_peach_line">#F7B29A</color></resources>
res › values › strings.xmlReplace the whole file
<resources>    <string name="app_name">Notes</string>    <string name="logo_description">Notes logo</string>    <string name="count_start">0</string>    <string name="title_hint">Title</string>    <string name="body_hint">Write something…</string>    <string name="error_no_title">Give your note a title first.</string>    <string name="counter_start">0 / 140</string>    <string name="clear">Clear</string>    <string name="add">Add</string>    <string name="your_notes">YOUR NOTES</string>    <string name="list_hint">Tap to open · hold to delete</string>    <string name="empty">No notes yet.\nWrite your first one above.</string>    <string name="seed_title_1">Saturday</string>    <string name="seed_body_1">Football at 10, then revise ConstraintLayout.</string>    <string name="seed_title_2">Ideas for the app</string>    <string name="seed_body_2">Pastel cards, a Share button, maybe a photo one day. Then save the notes, so that turning the phone loses nothing.</string>    <string name="seed_title_3">Exam: the lifecycle</string>    <string name="seed_body_3">onCreate, onStart, onResume… then onPause, onStop, onDestroy.</string></resources>

Step 3 of 5

Inflate the card from a layout

8 min

Goal Way 2: describe the card once in XML; Java makes one per note.

  • Layout res › layout › item_note.xml · Code
  • Java MainActivity.java
  • Emulator
  1. A card Way 1 (6.2): new TextView(this), setText(), setPadding()… Way 2 (6.3): inflate(R.layout.item_note, notesContainer, false).
  2. addView(card, 0) hangs the card in notesContainer; 0 = first place, on top
  3. notesContainer a vertical LinearLayout: one card under the other, newest on top
notesContainer holds the cards, its children: addView() adds one; getChildCount() counts them.
  1. A layout for one card: item_note.xml.

    1. Right-click res › layout, then choose NewLayout Resource File.
    2. New Resource File opens: in File name, type item_note.
    3. In Root element, press Ctrl+A, then type LinearLayout.
    4. A list opens, LinearLayout first: press Enter.
    5. Click OK: item_note.xml opens in Design.
    6. Click Code, top right of the editor.
  2. The card’s look, in item_note.xml.

    1. Click Copy the file on c’s panel.
    2. In item_note.xml, click in the text, press Ctrl+A, then Ctrl+V.

    Its lines 8–9 keep Part 8’s press ripple inside the rounded corners (Android 12+); line 12 tells lint.

  3. Paste The file b pastes.

    res › layout › item_note.xmlNew file
    <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:layout_marginTop="12dp"    android:background="@drawable/bg_note_butter"    android:clipToOutline="true"    android:foreground="?attr/selectableItemBackground"    android:orientation="vertical"    android:padding="16dp"    tools:targetApi="31">
    <TextView android:id="@+id/textNoteTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@color/ink" android:textSize="16sp" android:textStyle="bold" tools:text="Ideas for the app" />
    <TextView android:id="@+id/textNoteBody" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:ellipsize="end" android:maxLines="2" android:textColor="@color/ink" android:textSize="14sp" tools:text="Pastel cards, a Share button, maybe a photo one day." /></LinearLayout>
  4. Type The method addNote(), under addCardByHand().

    1. In MainActivity.java, click right after the } ending addCardByHand(), at the end of its line, then press Enter twice.
    2. Type private void addNote(String title, String body) {: Android Studio adds the } right after it.
    3. Press Enter: the } moves down a line.
    4. Type the 7 marked lines between the braces.
    5. LayoutInflater is red? Click it, press Alt+Enter, then choose Import class.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;import android.util.TypedValue;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer;
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
    textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
    addNote(getString(R.string.seed_title_1), getString(R.string.seed_body_1)); updateCount(); }
    private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
    private void addCardByHand(String title) { TextView card = new TextView(this); card.setText(title); card.setTextSize(16); card.setTextColor(getColor(R.color.ink)); card.setBackgroundResource(R.drawable.bg_note_butter); int padding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); card.setPadding(padding, padding, padding, padding); notesContainer.addView(card); }
    private void addNote(String title, String body) { View card = LayoutInflater.from(this).inflate(R.layout.item_note, notesContainer, false); TextView cardTitle = card.findViewById(R.id.textNoteTitle); TextView cardBody = card.findViewById(R.id.textNoteBody); cardTitle.setText(title); cardBody.setText(body); cardBody.setVisibility(body.isEmpty() ? View.GONE : View.VISIBLE); notesContainer.addView(card, 0); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}

    Line 54 turns item_note.xml into objects, like setContentView(); false: “I add the card myself”. Lines 55–56 find the texts on the card.

  5. Type Call addNote() instead, in onCreate().

    1. Click at the end of the addCardByHand(…); line, then press Shift+Home.
    2. Type the marked line over it.
    3. addCardByHand() turns grey: keep it for 6.5.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;import android.util.TypedValue;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer;
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
    textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
    addNote(getString(R.string.seed_title_1), getString(R.string.seed_body_1)); updateCount(); }
    private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
    private void addCardByHand(String title) { TextView card = new TextView(this); card.setText(title); card.setTextSize(16); card.setTextColor(getColor(R.color.ink)); card.setBackgroundResource(R.drawable.bg_note_butter); int padding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); card.setPadding(padding, padding, padding, padding); notesContainer.addView(card); }
    private void addNote(String title, String body) { View card = LayoutInflater.from(this).inflate(R.layout.item_note, notesContainer, false); TextView cardTitle = card.findViewById(R.id.textNoteTitle); TextView cardBody = card.findViewById(R.id.textNoteBody); cardTitle.setText(title); cardBody.setText(body); cardBody.setVisibility(body.isEmpty() ? View.GONE : View.VISIBLE); notesContainer.addView(card, 0); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}
  6. ▶ Run 'app'.

The emulator: one butter card, “Saturday” in bold and its text line “Football at 10, then revise ConstraintLayout.”, with a space above it; the pill says 1.

You should see A bold title, a text line, space above.

Done I see the card’s new text line.

Stuck? 3 symptoms · the file
  • The new file shows a picture, no XML It opened in Design mode. Click Code, at the top right of the editor.
  • The app closes at launch: NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference The two texts were looked for on the screen. They are on the card: write card.findViewById(…).
  • LayoutInflater stays red Its import is missing. Click on the name, press Alt+Enter, then choose Import class.

The files at the end of this step. item_note.xml is the panel above.

kotlin+java › com.yourname.notes › MainActivity.javaReplace the whole file
package com.yourname.notes;
import android.os.Bundle;import android.util.Log;import android.util.TypedValue;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Lifecycle";
private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
addNote(getString(R.string.seed_title_1), getString(R.string.seed_body_1)); updateCount(); }
private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
private void addCardByHand(String title) { TextView card = new TextView(this); card.setText(title); card.setTextSize(16); card.setTextColor(getColor(R.color.ink)); card.setBackgroundResource(R.drawable.bg_note_butter); int padding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); card.setPadding(padding, padding, padding, padding); notesContainer.addView(card); }
private void addNote(String title, String body) { View card = LayoutInflater.from(this).inflate(R.layout.item_note, notesContainer, false); TextView cardTitle = card.findViewById(R.id.textNoteTitle); TextView cardBody = card.findViewById(R.id.textNoteBody); cardTitle.setText(title); cardBody.setText(body); cardBody.setVisibility(body.isEmpty() ? View.GONE : View.VISIBLE); notesContainer.addView(card, 0); }
@Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
@Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
@Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
@Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
@Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
@Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}

Step 4 of 5

Colour the cards in turn

9 min

Goal Colour each card in turn, and start the app with three notes.

  • Resources res › drawable
  • Java MainActivity.java
  • Emulator
  1. Paste The mint background, a new drawable.

    1. Right-click res › drawable, then choose NewDrawable Resource File.
    2. A New Resource File window opens: in File name, type bg_note_mint.
    3. In Root element, press Ctrl+A to select selector, then type shape.
    4. Click OK: bg_note_mint.xml opens in Design: click Code.
    5. Click Copy the file on the panel below.
    6. In bg_note_mint.xml, click in the text, press Ctrl+A, then Ctrl+V.
    res › drawable › bg_note_mint.xmlNew file
    <?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"    android:shape="rectangle">    <solid android:color="@color/note_mint" />    <stroke        android:width="1dp"        android:color="@color/note_mint_line" />    <corners android:radius="16dp" /></shape>
  2. Paste Lavender and peach, the same way.

    1. Do a’s gestures 1 to 6 again for bg_note_lavender, with the bg_note_lavender.xml panel.
    2. Do them once more for bg_note_peach, with the bg_note_peach.xml panel.
    res › drawable › bg_note_lavender.xmlNew file
    <?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"    android:shape="rectangle">    <solid android:color="@color/note_lavender" />    <stroke        android:width="1dp"        android:color="@color/note_lavender_line" />    <corners android:radius="16dp" /></shape>
    res › drawable › bg_note_peach.xmlNew file
    <?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"    android:shape="rectangle">    <solid android:color="@color/note_peach" />    <stroke        android:width="1dp"        android:color="@color/note_peach_line" />    <corners android:radius="16dp" /></shape>
  3. Paste 4 backgrounds and a counter, at the top of the class.

    1. Click Copy 6 lines, beside After line 15.
    2. In MainActivity.java, click at the end of line 15, press Enter, then Ctrl+V.
    3. Click Copy 1 line, beside After line 25.
    4. Click at the end of line 25, press Enter, then Ctrl+V.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;import android.util.TypedValue;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle"; private static final int[] NOTE_BACKGROUNDS = { R.drawable.bg_note_butter, R.drawable.bg_note_mint, R.drawable.bg_note_lavender, R.drawable.bg_note_peach };
    private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer; private int notesAdded = 0;
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
    textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
    addNote(getString(R.string.seed_title_1), getString(R.string.seed_body_1)); addNote(getString(R.string.seed_title_2), getString(R.string.seed_body_2)); addNote(getString(R.string.seed_title_3), getString(R.string.seed_body_3)); updateCount(); }
    private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
    private void addCardByHand(String title) { TextView card = new TextView(this); card.setText(title); card.setTextSize(16); card.setTextColor(getColor(R.color.ink)); card.setBackgroundResource(R.drawable.bg_note_butter); int padding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); card.setPadding(padding, padding, padding, padding); notesContainer.addView(card); }
    private void addNote(String title, String body) { View card = LayoutInflater.from(this).inflate(R.layout.item_note, notesContainer, false); TextView cardTitle = card.findViewById(R.id.textNoteTitle); TextView cardBody = card.findViewById(R.id.textNoteBody); cardTitle.setText(title); cardBody.setText(body); cardBody.setVisibility(body.isEmpty() ? View.GONE : View.VISIBLE); int background = NOTE_BACKGROUNDS[notesAdded % NOTE_BACKGROUNDS.length]; card.setBackgroundResource(background); notesAdded++;
    notesContainer.addView(card, 0); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}
  4. Paste 2 more seed notes, in onCreate().

    1. Click Copy 2 lines, beside After line 38.
    2. Click at the end of line 38, press Enter, then Ctrl+V.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;import android.util.TypedValue;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle"; private static final int[] NOTE_BACKGROUNDS = { R.drawable.bg_note_butter, R.drawable.bg_note_mint, R.drawable.bg_note_lavender, R.drawable.bg_note_peach };
    private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer; private int notesAdded = 0;
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
    textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
    addNote(getString(R.string.seed_title_1), getString(R.string.seed_body_1)); addNote(getString(R.string.seed_title_2), getString(R.string.seed_body_2)); addNote(getString(R.string.seed_title_3), getString(R.string.seed_body_3)); updateCount(); }
    private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
    private void addCardByHand(String title) { TextView card = new TextView(this); card.setText(title); card.setTextSize(16); card.setTextColor(getColor(R.color.ink)); card.setBackgroundResource(R.drawable.bg_note_butter); int padding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); card.setPadding(padding, padding, padding, padding); notesContainer.addView(card); }
    private void addNote(String title, String body) { View card = LayoutInflater.from(this).inflate(R.layout.item_note, notesContainer, false); TextView cardTitle = card.findViewById(R.id.textNoteTitle); TextView cardBody = card.findViewById(R.id.textNoteBody); cardTitle.setText(title); cardBody.setText(body); cardBody.setVisibility(body.isEmpty() ? View.GONE : View.VISIBLE); int background = NOTE_BACKGROUNDS[notesAdded % NOTE_BACKGROUNDS.length]; card.setBackgroundResource(background); notesAdded++;
    notesContainer.addView(card, 0); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}

    Lines 38–40: the seed notes, three sample notes the app adds at start.

  5. Type Each card takes the next background, in addNote().

    1. In addNote(), click at the end of the cardBody.setVisibility(…); line.
    2. Press Enter, then type the 3 marked lines, with Enter between them.
    3. Press Enter once more: that makes the marked empty line, above addView.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;import android.util.TypedValue;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle"; private static final int[] NOTE_BACKGROUNDS = { R.drawable.bg_note_butter, R.drawable.bg_note_mint, R.drawable.bg_note_lavender, R.drawable.bg_note_peach };
    private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer; private int notesAdded = 0;
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
    textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
    addNote(getString(R.string.seed_title_1), getString(R.string.seed_body_1)); addNote(getString(R.string.seed_title_2), getString(R.string.seed_body_2)); addNote(getString(R.string.seed_title_3), getString(R.string.seed_body_3)); updateCount(); }
    private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
    private void addCardByHand(String title) { TextView card = new TextView(this); card.setText(title); card.setTextSize(16); card.setTextColor(getColor(R.color.ink)); card.setBackgroundResource(R.drawable.bg_note_butter); int padding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); card.setPadding(padding, padding, padding, padding); notesContainer.addView(card); }
    private void addNote(String title, String body) { View card = LayoutInflater.from(this).inflate(R.layout.item_note, notesContainer, false); TextView cardTitle = card.findViewById(R.id.textNoteTitle); TextView cardBody = card.findViewById(R.id.textNoteBody); cardTitle.setText(title); cardBody.setText(body); cardBody.setVisibility(body.isEmpty() ? View.GONE : View.VISIBLE); int background = NOTE_BACKGROUNDS[notesAdded % NOTE_BACKGROUNDS.length]; card.setBackgroundResource(background); notesAdded++;
    notesContainer.addView(card, 0); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}

    % gives the remainder of the division by NOTE_BACKGROUNDS.length, 4: 0, 1, 2, 3, 0… so the colours come back in turn.

  6. ▶ Run 'app'.

The emulator: three cards, newest on top: “Exam: the lifecycle” in lavender, “Ideas for the app” in mint with its text cut by “…”, “Saturday” in butter; the pill says 3.

You should see Exam on top, Saturday at the bottom, 3 in the pill.

Done I see three pastel cards, newest on top.

Stuck? 4 symptoms · the files
  • The cards stick together, with no margin You inflated with null as the parent. Pass notesContainer, false.
  • All three cards are butter setBackgroundResource(background) or notesAdded++ is missing. Type the marked lines again.
  • Saturday is on top addView(card) adds at the end. Write addView(card, 0).
  • bg_note_mint, bg_note_lavender or bg_note_peach is red That drawable is missing, or named otherwise. Create it as actions a and b say.

The files at the end of this step. The 3 drawables are the panels above.

kotlin+java › com.yourname.notes › MainActivity.javaReplace the whole file
package com.yourname.notes;
import android.os.Bundle;import android.util.Log;import android.util.TypedValue;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Lifecycle"; private static final int[] NOTE_BACKGROUNDS = { R.drawable.bg_note_butter, R.drawable.bg_note_mint, R.drawable.bg_note_lavender, R.drawable.bg_note_peach };
private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer; private int notesAdded = 0;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
addNote(getString(R.string.seed_title_1), getString(R.string.seed_body_1)); addNote(getString(R.string.seed_title_2), getString(R.string.seed_body_2)); addNote(getString(R.string.seed_title_3), getString(R.string.seed_body_3)); updateCount(); }
private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
private void addCardByHand(String title) { TextView card = new TextView(this); card.setText(title); card.setTextSize(16); card.setTextColor(getColor(R.color.ink)); card.setBackgroundResource(R.drawable.bg_note_butter); int padding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); card.setPadding(padding, padding, padding, padding); notesContainer.addView(card); }
private void addNote(String title, String body) { View card = LayoutInflater.from(this).inflate(R.layout.item_note, notesContainer, false); TextView cardTitle = card.findViewById(R.id.textNoteTitle); TextView cardBody = card.findViewById(R.id.textNoteBody); cardTitle.setText(title); cardBody.setText(body); cardBody.setVisibility(body.isEmpty() ? View.GONE : View.VISIBLE); int background = NOTE_BACKGROUNDS[notesAdded % NOTE_BACKGROUNDS.length]; card.setBackgroundResource(background); notesAdded++;
notesContainer.addView(card, 0); }
@Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
@Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
@Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
@Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
@Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
@Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}

Step 5 of 5

Compare the two ways, then clean up

3 min

Goal Compare the two ways, then delete Way 1.

  • Java MainActivity.java

Why XML for how it looks; Java for when, and how many.

Way 1, by hand: the look in Java

kotlin+java › com.yourname.notes › MainActivity.javaRead only
package com.yourname.notes;
import android.os.Bundle;import android.util.Log;import android.util.TypedValue;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Lifecycle"; private static final int[] NOTE_BACKGROUNDS = { R.drawable.bg_note_butter, R.drawable.bg_note_mint, R.drawable.bg_note_lavender, R.drawable.bg_note_peach };
private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer; private int notesAdded = 0;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
addNote(getString(R.string.seed_title_1), getString(R.string.seed_body_1)); addNote(getString(R.string.seed_title_2), getString(R.string.seed_body_2)); addNote(getString(R.string.seed_title_3), getString(R.string.seed_body_3)); updateCount(); }
private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
private void addCardByHand(String title) { TextView card = new TextView(this); card.setText(title); card.setTextSize(16); card.setTextColor(getColor(R.color.ink)); card.setBackgroundResource(R.drawable.bg_note_butter); int padding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); card.setPadding(padding, padding, padding, padding); notesContainer.addView(card); }
private void addNote(String title, String body) { View card = LayoutInflater.from(this).inflate(R.layout.item_note, notesContainer, false); TextView cardTitle = card.findViewById(R.id.textNoteTitle); TextView cardBody = card.findViewById(R.id.textNoteBody); cardTitle.setText(title); cardBody.setText(body); cardBody.setVisibility(body.isEmpty() ? View.GONE : View.VISIBLE); int background = NOTE_BACKGROUNDS[notesAdded % NOTE_BACKGROUNDS.length]; card.setBackgroundResource(background); notesAdded++;
notesContainer.addView(card, 0); }
@Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
@Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
@Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
@Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
@Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
@Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}

Way 2, inflated: the look in item_note.xml

kotlin+java › com.yourname.notes › MainActivity.javaRead only
package com.yourname.notes;
import android.os.Bundle;import android.util.Log;import android.util.TypedValue;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Lifecycle"; private static final int[] NOTE_BACKGROUNDS = { R.drawable.bg_note_butter, R.drawable.bg_note_mint, R.drawable.bg_note_lavender, R.drawable.bg_note_peach };
private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer; private int notesAdded = 0;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
addNote(getString(R.string.seed_title_1), getString(R.string.seed_body_1)); addNote(getString(R.string.seed_title_2), getString(R.string.seed_body_2)); addNote(getString(R.string.seed_title_3), getString(R.string.seed_body_3)); updateCount(); }
private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
private void addCardByHand(String title) { TextView card = new TextView(this); card.setText(title); card.setTextSize(16); card.setTextColor(getColor(R.color.ink)); card.setBackgroundResource(R.drawable.bg_note_butter); int padding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); card.setPadding(padding, padding, padding, padding); notesContainer.addView(card); }
private void addNote(String title, String body) { View card = LayoutInflater.from(this).inflate(R.layout.item_note, notesContainer, false); TextView cardTitle = card.findViewById(R.id.textNoteTitle); TextView cardBody = card.findViewById(R.id.textNoteBody); cardTitle.setText(title); cardBody.setText(body); cardBody.setVisibility(body.isEmpty() ? View.GONE : View.VISIBLE); int background = NOTE_BACKGROUNDS[notesAdded % NOTE_BACKGROUNDS.length]; card.setBackgroundResource(background); notesAdded++;
notesContainer.addView(card, 0); }
@Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
@Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
@Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
@Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
@Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
@Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}
  1. Delete Way 1: the grey method, then its import.

    1. In MainActivity.java, click just left of private void addCardByHand (line 50), not on the grey usages hint.
    2. Hold Shift and click just left of private void addNote (line 62).
    3. Lines 50 to 61 are selected: press Delete.
    4. Click in line 5, import android.util.TypedValue;, grey now.
    5. Press Ctrl+Y: the line goes.

Done I see no grey method left.

Stuck? 2 symptoms · the file
  • Red marks after the delete A brace went with the method, or one stayed. Replace the whole file with this one.
  • TypedValue is red The method is still there. Delete it too.

The files at the end of this step

kotlin+java › com.yourname.notes › MainActivity.javaReplace the whole file
package com.yourname.notes;
import android.os.Bundle;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Lifecycle"; private static final int[] NOTE_BACKGROUNDS = { R.drawable.bg_note_butter, R.drawable.bg_note_mint, R.drawable.bg_note_lavender, R.drawable.bg_note_peach };
private TextView textCount; private TextView textEmpty; private LinearLayout notesContainer; private int notesAdded = 0;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate");
textCount = findViewById(R.id.textCount); textEmpty = findViewById(R.id.textEmpty); notesContainer = findViewById(R.id.notesContainer);
addNote(getString(R.string.seed_title_1), getString(R.string.seed_body_1)); addNote(getString(R.string.seed_title_2), getString(R.string.seed_body_2)); addNote(getString(R.string.seed_title_3), getString(R.string.seed_body_3)); updateCount(); }
private void updateCount() { int count = notesContainer.getChildCount(); textCount.setText(String.valueOf(count)); textEmpty.setVisibility(count == 0 ? View.VISIBLE : View.GONE); }
private void addNote(String title, String body) { View card = LayoutInflater.from(this).inflate(R.layout.item_note, notesContainer, false); TextView cardTitle = card.findViewById(R.id.textNoteTitle); TextView cardBody = card.findViewById(R.id.textNoteBody); cardTitle.setText(title); cardBody.setText(body); cardBody.setVisibility(body.isEmpty() ? View.GONE : View.VISIBLE); int background = NOTE_BACKGROUNDS[notesAdded % NOTE_BACKGROUNDS.length]; card.setBackgroundResource(background); notesAdded++;
notesContainer.addView(card, 0); }
@Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
@Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
@Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
@Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
@Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
@Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}
Go deeper Real lists, and View Binding 1 min

A real list of hundreds of notes uses RecyclerView, which reuses a few cards as you scroll; View Binding replaces findViewById. Both build on what you just did.

Now you

A fourth seed

Change nothing: which colour and place would a fourth seed note get?

See the answer

Peach, on top. notesAdded is then 3, and 3 % 4 = 3: the fourth background. addView(card, 0) puts it first. Part 7 shows it: the first note you add there is peach.

If it doesn’t work

  • Android resource linking failed … resource color/note_butter … not found, after a restart The pack replaced colors.xml, but your Part 6 files stayed. Delete them as “Behind?” says, at the top.
  • A grey, ALL CAPS button made in Java new Button(this) is not themed. Inflate the button from XML instead.
  • Alt+Enter offers nothing Android Studio is still indexing: the bar at the bottom of the window. Wait for it to end.

End of the part

Recap

Java now fills the pill and the list. A card comes from Java alone, or from item_note.xml: the app keeps the layout.

  • set a view’s text and visibility from Java, at the right moment;
  • build a view in Java, or inflate it from XML;
  • say when to use each way.
Your app at the end of Part 6: three pastel note cards, newest on top, and 3 in the pill.
Your app now
← Lab 1, all parts