Building Your First Android App From Scratch Using Java With Full Source Code

Building Your First Android App From Scratch Using Java With Full Source Code

Ever wondered how those apps on your phone actually get made? Today, I’m going to show you exactly how to build a real Android app from start to finish. No fluff, no shortcuts – just practical, working code.


What We’re Building Today

We’re going to create a fully functional To-Do List app called “TaskMaster”. By the end of this tutorial, you’ll have an app that:

  • Lets users add new tasks
  • Displays all tasks in a scrollable list
  • Allows users to mark tasks as complete
  • Deletes tasks with a long press
  • Saves data so tasks persist when you close the app
  • Looks professional with Material Design

Why a To-Do List app? Because it teaches you the fundamentals every Android developer needs: layouts, user input, lists, data persistence, and event handling. Master this, and you can build almost anything.

Time Required: 45-60 minutes (including setup)

Prerequisites:

  • A computer (Windows, Mac, or Linux)
  • Basic Java knowledge (variables, classes, methods)
  • Patience and coffee ☕

Part 1: Setting Up Your Development Environment

Before we write any code, we need the right tools. Think of this like setting up a workshop before building furniture.

Step 1.1: Download and Install Android Studio

Android Studio is Google’s official tool for building Android apps. It’s free and available for all platforms.

  1. Go to https://developer.android.com/studio
  2. Click “Download Android Studio”
  3. Accept the terms and download (around 1GB)
  4. Install it like any other program
    • Windows: Run the .exe file
    • Mac: Drag to Applications folder
    • Linux: Extract and run studio.sh

First launch will take 5-10 minutes as it downloads additional components. Let it finish. Grab that coffee now.

Step 1.2: Configure Android Studio

When Android Studio opens for the first time:

  1. Choose “Standard” installation (recommended)
  2. Select your preferred theme (I like Darcula, but Light works too)
  3. Click “Finish” and let it download SDK components
  4. Wait for “Downloading Components” to complete (10-15 minutes on first install)

Pro tip: If you see any errors about virtualization (Intel HAXM), don’t panic. We’ll test on a real device instead of an emulator.


Part 2: Creating Your First Android Project

Alright, tools are ready. Time to create something!

Step 2.1: Start a New Project

  1. Click “New Project” on the welcome screen
  2. Select “Empty Activity” (this gives us a clean slate)
  3. Click “Next”

Step 2.2: Configure Your Project

Fill in these details:

  • Name: TaskMaster
  • Package name: com.yourname.taskmaster (use your actual name, lowercase)
  • Save location: Choose where you want to save your project
  • Language: Java (not Kotlin)
  • Minimum SDK: API 21: Android 5.0 (Lollipop)
    • This covers 98%+ of Android devices

Click “Finish”.

Android Studio will now build your project. You’ll see “Gradle Build Running” at the bottom. This takes 2-5 minutes the first time. Go stretch your legs.

Step 2.3: Understanding the Project Structure

Once Gradle finishes, you’ll see a bunch of folders. Here’s what matters:

app/
├── manifests/
│   └── AndroidManifest.xml     (App configuration)
├── java/
│   └── com.yourname.taskmaster/
│       └── MainActivity.java    (Your main code)
└── res/
    ├── layout/
    │   └── activity_main.xml    (Your UI design)
    ├── values/
    │   ├── strings.xml          (Text strings)
    │   └── colors.xml           (Color definitions)
    └── drawable/                 (Images and icons)

Think of it like this:

  • manifests/ = Birth certificate of your app
  • java/ = The brain (logic)
  • res/layout/ = The face (UI)
  • res/values/ = The vocabulary (text and colors)

Part 3: Designing the User Interface

Time to make our app look good. We’ll use XML to design the interface.

Step 3.1: Open the Layout File

  1. In the left panel (Project view), navigate to: app → res → layout → activity_main.xml
  2. You’ll see two tabs at the top: “Code” and “Design”
  3. Click “Code” (we’ll write XML directly – it’s easier to understand)

Step 3.2: Create the Main Layout

Replace everything in activity_main.xml with this code:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:background="#F5F5F5"
    tools:context=".MainActivity">

    <!-- Title at the top -->
    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TaskMaster"
        android:textSize="28sp"
        android:textStyle="bold"
        android:textColor="#1976D2"
        android:gravity="center"
        android:paddingTop="16dp"
        android:paddingBottom="24dp" />

    <!-- Input area for new tasks -->
    <LinearLayout
        android:id="@+id/inputLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/tvTitle"
        android:orientation="horizontal"
        android:background="@android:color/white"
        android:elevation="4dp"
        android:padding="12dp">

        <EditText
            android:id="@+id/etNewTask"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="Enter a new task..."
            android:textSize="16sp"
            android:background="@null"
            android:padding="8dp" />

        <Button
            android:id="@+id/btnAddTask"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Add"
            android:textColor="@android:color/white"
            android:backgroundTint="#1976D2"
            android:paddingLeft="24dp"
            android:paddingRight="24dp" />
    </LinearLayout>

    <!-- List of tasks -->
    <ListView
        android:id="@+id/lvTasks"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/inputLayout"
        android:layout_marginTop="16dp"
        android:divider="#E0E0E0"
        android:dividerHeight="1dp"
        android:background="@android:color/white"
        android:elevation="4dp" />

    <!-- Empty state message -->
    <TextView
        android:id="@+id/tvEmptyState"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="No tasks yet!\nTap 'Add' to create your first task"
        android:textSize="18sp"
        android:textColor="#9E9E9E"
        android:gravity="center"
        android:visibility="gone" />

</RelativeLayout>

What’s happening here?

  • RelativeLayout: A flexible container that positions elements relative to each other
  • TextView: Displays text (our title and empty state message)
  • EditText: Input field where users type their tasks
  • Button: The “Add” button to create tasks
  • ListView: Displays our list of tasks (we’ll populate this with Java code)

Color codes:

  • #1976D2 = Material Blue (professional look)
  • #F5F5F5 = Light gray background
  • #E0E0E0 = Divider lines between tasks

Step 3.3: Create Custom List Item Layout

We need to define how each task looks in the list. Let’s create a new layout file:

  1. Right-click on res → layout
  2. Select New → Layout Resource File
  3. Name it: task_item.xml
  4. Click OK

Now paste this code into task_item.xml:

<?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="wrap_content"
    android:orientation="horizontal"
    android:padding="16dp"
    android:background="?attr/selectableItemBackground">

    <CheckBox
        android:id="@+id/cbTask"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:buttonTint="#1976D2" />

    <TextView
        android:id="@+id/tvTaskName"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_marginLeft="12dp"
        android:textSize="16sp"
        android:textColor="#212121"
        android:layout_gravity="center_vertical" />

</LinearLayout>

What this does:

  • Each task item has a checkbox and text
  • Users can check off completed tasks
  • The background has a ripple effect when tapped (Material Design)

Part 4: Writing the Java Code (The Brain)

Now comes the fun part – making everything actually work!

Step 4.1: Create the Task Class

First, we need a class to represent a task. This is basic object-oriented programming.

  1. Right-click on your package (com.yourname.taskmaster)
  2. Select New → Java Class
  3. Name it: Task
  4. Click OK

Add this code to Task.java:

package com.yourname.taskmaster;

public class Task {
    private String taskName;
    private boolean isCompleted;

    // Constructor
    public Task(String taskName, boolean isCompleted) {
        this.taskName = taskName;
        this.isCompleted = isCompleted;
    }

    // Getters and setters
    public String getTaskName() {
        return taskName;
    }

    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }

    public boolean isCompleted() {
        return isCompleted;
    }

    public void setCompleted(boolean completed) {
        isCompleted = completed;
    }

    // Convert task to string for storage
    @Override
    public String toString() {
        return taskName + "|" + isCompleted;
    }

    // Create task from stored string
    public static Task fromString(String taskString) {
        String[] parts = taskString.split("\\|");
        return new Task(parts[0], Boolean.parseBoolean(parts[1]));
    }
}

Why we need this:

  • Organizes our data (every task has a name and completion status)
  • Makes it easy to convert tasks to/from strings for storage
  • Follows good programming practices (encapsulation)

Step 4.2: Create Custom Adapter

The adapter connects our data (tasks) to the ListView (visual list). It’s like a translator.

  1. Create new Java class: TaskAdapter
  2. Add this code:
package com.yourname.taskmaster;

import android.content.Context;
import android.graphics.Paint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.TextView;

import java.util.ArrayList;

public class TaskAdapter extends ArrayAdapter<Task> {
    
    private Context context;
    private ArrayList<Task> tasks;

    public TaskAdapter(Context context, ArrayList<Task> tasks) {
        super(context, 0, tasks);
        this.context = context;
        this.tasks = tasks;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Get the task for this position
        Task task = getItem(position);

        // Reuse view if possible (performance optimization)
        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.task_item, parent, false);
        }

        // Find the views in task_item.xml
        CheckBox cbTask = convertView.findViewById(R.id.cbTask);
        TextView tvTaskName = convertView.findViewById(R.id.tvTaskName);

        // Set the task name
        tvTaskName.setText(task.getTaskName());

        // Set checkbox state
        cbTask.setChecked(task.isCompleted());

        // Add strikethrough if task is completed
        if (task.isCompleted()) {
            tvTaskName.setPaintFlags(tvTaskName.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
            tvTaskName.setTextColor(context.getResources().getColor(android.R.color.darker_gray));
        } else {
            tvTaskName.setPaintFlags(tvTaskName.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
            tvTaskName.setTextColor(context.getResources().getColor(android.R.color.black));
        }

        // Handle checkbox clicks
        cbTask.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                task.setCompleted(cbTask.isChecked());
                notifyDataSetChanged(); // Refresh the list
            }
        });

        return convertView;
    }
}

What this adapter does:

  • Takes our list of tasks and displays each one
  • Handles the checkbox toggle
  • Adds strikethrough text for completed tasks
  • Reuses views for better performance (important for long lists)

Step 4.3: Write the Main Activity Code

Now let’s bring everything together in MainActivity.java. This is the heart of the app.

Open MainActivity.java and replace EVERYTHING with this:

package com.yourname.taskmaster;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AlertDialog;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

public class MainActivity extends AppCompatActivity {

    // UI Elements
    private EditText etNewTask;
    private Button btnAddTask;
    private ListView lvTasks;
    private TextView tvEmptyState;

    // Data
    private ArrayList<Task> taskList;
    private TaskAdapter taskAdapter;

    // For saving data
    private SharedPreferences sharedPreferences;
    private static final String PREFS_NAME = "TaskMasterPrefs";
    private static final String TASKS_KEY = "tasks";

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

        // Initialize views
        etNewTask = findViewById(R.id.etNewTask);
        btnAddTask = findViewById(R.id.btnAddTask);
        lvTasks = findViewById(R.id.lvTasks);
        tvEmptyState = findViewById(R.id.tvEmptyState);

        // Initialize data
        taskList = new ArrayList<>();
        sharedPreferences = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);

        // Load saved tasks
        loadTasks();

        // Set up adapter
        taskAdapter = new TaskAdapter(this, taskList);
        lvTasks.setAdapter(taskAdapter);

        // Update empty state visibility
        updateEmptyState();

        // Add task button click
        btnAddTask.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addTask();
            }
        });

        // Long press to delete task
        lvTasks.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                showDeleteConfirmation(position);
                return true;
            }
        });
    }

    /**
     * Adds a new task to the list
     */
    private void addTask() {
        String taskName = etNewTask.getText().toString().trim();

        // Validate input
        if (taskName.isEmpty()) {
            Toast.makeText(this, "Please enter a task", Toast.LENGTH_SHORT).show();
            return;
        }

        // Create new task
        Task newTask = new Task(taskName, false);
        taskList.add(newTask);

        // Update UI
        taskAdapter.notifyDataSetChanged();
        etNewTask.setText(""); // Clear input field
        updateEmptyState();

        // Save tasks
        saveTasks();

        // Show confirmation
        Toast.makeText(this, "Task added!", Toast.LENGTH_SHORT).show();
    }

    /**
     * Shows confirmation dialog before deleting a task
     */
    private void showDeleteConfirmation(final int position) {
        Task task = taskList.get(position);

        new AlertDialog.Builder(this)
                .setTitle("Delete Task")
                .setMessage("Are you sure you want to delete \"" + task.getTaskName() + "\"?")
                .setPositiveButton("Delete", (dialog, which) -> {
                    // Remove task
                    taskList.remove(position);
                    taskAdapter.notifyDataSetChanged();
                    updateEmptyState();
                    saveTasks();
                    Toast.makeText(MainActivity.this, "Task deleted", Toast.LENGTH_SHORT).show();
                })
                .setNegativeButton("Cancel", null)
                .show();
    }

    /**
     * Shows/hides empty state message
     */
    private void updateEmptyState() {
        if (taskList.isEmpty()) {
            tvEmptyState.setVisibility(View.VISIBLE);
            lvTasks.setVisibility(View.GONE);
        } else {
            tvEmptyState.setVisibility(View.GONE);
            lvTasks.setVisibility(View.VISIBLE);
        }
    }

    /**
     * Saves tasks to SharedPreferences
     */
    private void saveTasks() {
        Set<String> taskStrings = new HashSet<>();
        for (Task task : taskList) {
            taskStrings.add(task.toString());
        }
        sharedPreferences.edit().putStringSet(TASKS_KEY, taskStrings).apply();
    }

    /**
     * Loads tasks from SharedPreferences
     */
    private void loadTasks() {
        Set<String> taskStrings = sharedPreferences.getStringSet(TASKS_KEY, new HashSet<>());
        for (String taskString : taskStrings) {
            try {
                taskList.add(Task.fromString(taskString));
            } catch (Exception e) {
                // Skip corrupted task data
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        // Save tasks when app goes to background
        saveTasks();
    }
}

Let me explain the key parts:

  1. onCreate(): Runs when the app starts
    • Connects Java code to XML views using findViewById()
    • Loads saved tasks from storage
    • Sets up the adapter to display tasks
    • Configures button clicks
  2. addTask(): Handles adding new tasks
    • Validates that user entered something
    • Creates new Task object
    • Updates the list
    • Saves to storage
    • Shows confirmation message
  3. showDeleteConfirmation(): Handles task deletion
    • Shows a dialog to confirm deletion
    • Removes task if confirmed
    • Saves changes
  4. saveTasks() / loadTasks(): Data persistence
    • Uses SharedPreferences (lightweight key-value storage)
    • Converts tasks to strings and back
    • Ensures tasks survive app restarts
  5. updateEmptyState(): UI polish
    • Shows helpful message when list is empty
    • Hides it when tasks exist

Part 5: Testing Your App

Time for the moment of truth! Let’s run this thing.

Option A: Test on Real Device (Recommended)

Why real device? It’s faster, more reliable, and shows you exactly how users will experience your app.

Steps:

  1. Enable Developer Mode on your Android phone:
    • Go to Settings → About Phone
    • Tap “Build Number” 7 times (yes, really)
    • You’ll see “You are now a developer!”
  2. Enable USB Debugging:
    • Go to Settings → Developer Options
    • Turn on “USB Debugging”
    • Accept the security warning
  3. Connect phone to computer:
    • Use a USB cable
    • On your phone, tap “Allow USB Debugging” when prompted
  4. Run the app in Android Studio:
    • Click the green “Run” button (or press Shift+F10)
    • Select your device from the dropdown
    • Click OK

First run takes 1-2 minutes as Gradle builds everything. Subsequent runs are faster.

Testing Checklist

Once your app launches, test these features:

✅ Type a task and tap “Add” – does it appear?
✅ Tap the checkbox – does it strikethrough?
✅ Long press a task – does delete dialog appear?
✅ Delete a task – does it disappear?
✅ Close app and reopen – do tasks persist?
✅ Try adding an empty task – does it show error?


Part 6: Building and Exporting Your App (APK)

Generate Signed APK

  1. Click Build → Generate Signed Bundle / APK
  2. Select APK → Click Next
  3. Click Create new… (for Key store path)

Create Keystore

Fill in these details:

  • Key store path: Choose where to save it
  • Password: Create a strong password (write it down!)
  • Alias: taskmaster-key
  • Validity: 25 years (default)
  • First and Last Name: Your name

Click OK, then Next, select release, and click Finish.

Your APK will be at: app/release/app-release.apk

Congratulations! This is your finished app.


Troubleshooting Common Issues

“Cannot resolve symbol R”

  • Click Build → Clean Project
  • Then Build → Rebuild Project

App crashes on launch

  • Check Logcat for error messages
  • Verify package names match

Tasks don’t save

  • Check that saveTasks() is called in onPause()
  • Clear app data and test again

Next Steps

Congratulations! You’ve built a real Android app. Here are some enhancements to try:

  1. Add Task Priority (High, Medium, Low)
  2. Add Due Dates using DatePicker
  3. Categories/Tags for organizing tasks
  4. Dark Mode Support
  5. Cloud Sync with Firebase

Full Source Code Here!

Final Thoughts

You just built a real Android app from scratch. That’s legitimately impressive.

What you learned:
✅ Android Studio setup
✅ XML layout design
✅ Java programming for Android
✅ Data persistence
✅ User interface patterns
✅ App lifecycle management
✅ Building and exporting APKs

The best way to learn is by building. Take this app and modify it. Break it. Fix it. Add features. Make it yours.

Every expert was once a beginner who didn’t give up.

Keep coding. Keep learning. Keep building.


Did this tutorial help you? Share it with other aspiring Android developers!

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *