Android Fundamentals with Java

Your First Activity & Layout

18 min Lesson 5 of 12

Your First Activity & Layout

An Android screen is the marriage of two things: an Activity — a Java class that owns the screen's logic and lifecycle — and a layout XML file that describes what the screen looks like. Neither piece works without the other. This lesson walks you through building a real, functional screen from scratch so that pattern becomes second nature.

The Two Files That Make a Screen

When Android Studio generates a new project it creates exactly this pair for you:

  • app/src/main/java/…/MainActivity.java — the Java class that receives lifecycle callbacks and controls behaviour.
  • app/src/main/res/layout/activity_main.xml — the XML resource that declares every widget and how they are arranged on screen.

The Activity inflates the layout at runtime: it reads the XML, instantiates the corresponding View objects, and attaches them to the window. The call that triggers this is setContentView(R.layout.activity_main) inside onCreate().

What is inflation? Android's layout inflater parses the XML tree and constructs a matching tree of Java View objects in memory. After setContentView returns, every widget you declared in XML exists as a live Java object you can find and manipulate.

A Minimal Activity

Here is the smallest Activity that compiles and shows a screen:

package com.example.myfirstapp; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Tell Android which layout XML to inflate for this screen setContentView(R.layout.activity_main); } }

Two things are mandatory here: calling super.onCreate(savedInstanceState) — the framework requires this to set up its internal state — and calling setContentView before you try to access any view. Calling findViewById before setContentView will return null and cause a NullPointerException the moment you use the result.

Building the Layout XML

Open res/layout/activity_main.xml. A layout file has exactly one root view group. The most flexible root for a beginner screen is ConstraintLayout, but LinearLayout is easier to reason about at first. Here is a practical screen with a heading, a text field, and a button arranged vertically:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="24dp"> <TextView android:id="@+id/tvGreeting" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/greeting" android:textSize="24sp" android:layout_marginBottom="16dp" /> <EditText android:id="@+id/etName" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hint_name" android:inputType="textPersonName" android:layout_marginBottom="16dp" /> <Button android:id="@+id/btnSayHello" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/btn_hello" /> </LinearLayout>

Several concepts worth understanding here:

  • android:id="@+id/tvGreeting" — the @+id/ prefix tells the build tool to create a new integer constant in the generated R.id class. You use that constant in Java to locate the view: R.id.tvGreeting.
  • match_parent — fill all available space from the parent view group.
  • wrap_content — shrink to just big enough to fit the content.
  • @string/greeting — a reference to a string resource defined in res/values/strings.xml. Hard-coding text directly in XML works, but using string resources is required for localisation and is enforced by lint warnings.
  • dp and sp — density-independent pixels for margins/padding; scale-independent pixels for font sizes. Always use these units, never raw pixels (px).

Wiring Java to the Layout: findViewById

Once the layout is inflated, you locate views by their ID and store them as fields so you can respond to events:

package com.example.myfirstapp; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends AppCompatActivity { // Declare view fields at class scope private TextView tvGreeting; private EditText etName; private Button btnSayHello; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // inflate FIRST // Locate the live View objects by their XML id constants tvGreeting = findViewById(R.id.tvGreeting); etName = findViewById(R.id.etName); btnSayHello = findViewById(R.id.btnSayHello); // Attach a click listener to the button btnSayHello.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String name = etName.getText().toString().trim(); if (name.isEmpty()) { tvGreeting.setText("Hello, stranger!"); } else { tvGreeting.setText("Hello, " + name + "!"); } } }); } }
ViewBinding (recommended alternative to findViewById): Modern projects enable ViewBinding in build.gradle (viewBinding { enabled = true }). The build tool generates a typed binding class per layout, eliminating all findViewById calls and making NullPointerException from a missing ID a compile-time error rather than a runtime crash. For this introductory lesson findViewById makes the inflating mechanism more visible, but plan to switch to ViewBinding as soon as you are comfortable.

String Resources

Add the strings referenced in your XML to res/values/strings.xml:

<resources> <string name="app_name">MyFirstApp</string> <string name="greeting">What is your name?</string> <string name="hint_name">Enter your name</string> <string name="btn_hello">Say Hello</string> </resources>

The layout references these as @string/greeting etc. Android resolves the reference at runtime, choosing the correct locale-specific values folder if translations exist. This is the foundation of Android's resource qualifier system — the same mechanism later handles screen density, night mode, and RTL layouts.

The R Class — The Bridge Between XML and Java

Every time you build the project, the Android build tools scan all files under res/ and auto-generate a class called R. It contains nested static integer classes: R.id, R.layout, R.string, R.drawable, and so on. Each integer is a unique handle the runtime uses to locate the corresponding resource. You never write or modify R.java — you only read from it.

Do not import the wrong R. Android Studio sometimes auto-imports android.R (the system resource class) instead of your app's com.example.myfirstapp.R. If your IDs are suddenly unresolved, check the import statements at the top of the file — it must import your package's R, not Android's.

Running the App

Hit the green Run button (or press Shift+F10). Android Studio compiles the project, packages it into an APK, and installs it on the selected emulator or connected device. You will see your layout appear, type a name, tap the button, and watch the greeting update — all driven by the onClick lambda you just wrote.

Summary

Every Android screen is built from the same two-file pattern: a Java class extending AppCompatActivity that calls setContentView(R.layout.activity_main) inside onCreate(), and an XML layout file that declares the widget tree. After inflation, findViewById bridges the two by returning the live View object for any ID you defined in XML. String resources decouple text from layouts and enable localisation from day one. In the next lesson you will study the AndroidManifest.xml and understand how Android knows which Activity to launch when the app starts.