Building a Score Tracking Application with Android Architecture Components

Score Tracker Implementation

This application demonstrates the implementation of a score tracking system for two teams using modern Android architecture components. The app features real-time score updates, undo functionality, and data persistence during configuration changes.

Gradle Configuration

Add the following configurations to your app-level build.gradle file:

android {
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
    
    buildFeatures {
        dataBinding true
    }
}

ScoreViewModel Implementation

The ViewModel manages the score data and business logic:

package com.example.scoretracker;

import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class ScoreViewModel extends ViewModel {
    private MutableLiveData<integer> teamAScore;
    private MutableLiveData<integer> teamBScore;
    private int previousTeamA, previousTeamB;

    public MutableLiveData<integer> getTeamAScore() {
        if (teamAScore == null) {
            teamAScore = new MutableLiveData<>();
            teamAScore.setValue(0);
        }
        return teamAScore;
    }

    public MutableLiveData<integer> getTeamBScore() {
        if (teamBScore == null) {
            teamBScore = new MutableLiveData<>();
            teamBScore.setValue(0);
        }
        return teamBScore;
    }

    public void addToTeamA(int points) {
        savePreviousScores();
        teamAScore.setValue(teamAScore.getValue() + points);
    }

    public void addToTeamB(int points) {
        savePreviousScores();
        teamBScore.setValue(teamBScore.getValue() + points);
    }

    public void resetScores() {
        savePreviousScores();
        teamAScore.setValue(0);
        teamBScore.setValue(0);
    }

    public void undoLastAction() {
        teamAScore.setValue(previousTeamA);
        teamBScore.setValue(previousTeamB);
    }

    private void savePreviousScores() {
        previousTeamA = teamAScore.getValue();
        previousTeamB = teamBScore.getValue();
    }
}</integer></integer></integer></integer>

MainActivity Setup

The activity initializes data binding and connecst the ViewModel:

package com.example.scoretracker;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import com.example.scoretracker.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {
    private ScoreViewModel scoreViewModel;
    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        scoreViewModel = new ViewModelProvider(this).get(ScoreViewModel.class);
        binding.setViewModel(scoreViewModel);
        binding.setLifecycleOwner(this);
    }
}

Layout with Data Binding

The layout uses data binding expressions to connect UI elements with the ViewModel:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="viewModel"
            type="com.example.scoretracker.ScoreViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- Team labels -->
        <TextView
            android:id="@+id/teamALabel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Team A"
            android:textSize="24sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/teamBLabel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Team B"
            android:textSize="24sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <!-- Score displays -->
        <TextView
            android:id="@+id/teamAScore"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(viewModel.teamAScore)}"
            android:textSize="48sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/teamALabel" />

        <TextView
            android:id="@+id/teamBScore"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(viewModel.teamBScore)}"
            android:textSize="48sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@id/teamBLabel" />

        <!-- Point buttons for Team A -->
        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="+1"
            android:onClick="@{()->viewModel.addToTeamA(1)}"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/teamAScore" />

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="+2"
            android:onClick="@{()->viewModel.addToTeamA(2)}"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/previousButton" />

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="+3"
            android:onClick="@{()->viewModel.addToTeamA(3)}"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/previousButton2" />

        <!-- Point buttons for Team B -->
        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="+1"
            android:onClick="@{()->viewModel.addToTeamB(1)}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@id/teamBScore" />

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="+2"
            android:onClick="@{()->viewModel.addToTeamB(2)}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@id/previousButton3" />

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="+3"
            android:onClick="@{()->viewModel.addToTeamB(3)}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@id/previousButton4" />

        <!-- Action buttons -->
        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="Undo"
            android:onClick="@{()->viewModel.undoLastAction()}"
            app:srcCompat="@drawable/ic_undo"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" />

        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="Reset"
            android:onClick="@{()->viewModel.resetScores()}"
            app:srcCompat="@drawable/ic_reset"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

The application provides a clean separation of concerns with the ViewModel handling data persistence during configuraton changes and the data binding simplifying the UI updates.

Tags: Android viewmodel livedata DataBinding constraintlayout

Posted on Thu, 10 Sep 2026 16:30:11 +0000 by geaser_geek