Trata-se de um padrão de projeto arquitetural, as camadas são divididas em Model, View e View-Model.
Model: Responsável pela manipulação dos dados. Nesta parte compreendem as classes que representam as entidades e que realizam as persistências no banco de dados.
View: Parte visual do projeto. A View relaciona-se com o ViewModel e não conhece diretamente o Model.
ViewModel: Camada intermediária entre a View e o Model. O ViewModel pode ter acesso ao Model diretamente. O ViewModel atualiza o objeto LiveData através da atualização do Model. O LiveData usa o método observe para atualizar a View.
Para mais detalhes sobre MVVM acesse o material: SOUTO, Thiago. Arquiteturas em Android: MVVM + Kotlin + Android Architecture Components (Databinding, Lifecycle, LiveData, Room). 2019. Disponível em: https://medium.com/android-dev-br/arquiteturas-em-android-mvvm-kotlin-android-architecture-components-databinding-lifecycle-d5e7a9023cf3.Acessao em 25 ago. 2025.
Responsabilidades de alguns componentes dentro do MVVM:
-
DataBinding: permite o vínculo da View (por exemplo, um TextView) com um ViewModel. O vínculo é feito de forma declarativa no XML.
-
LiveData: é um tipo que pode ser observado. Isso significa que os componentes da UI (como Activities e Fragments) podem se inscrever para receber atualizações sobre mudanças nos dados. Notifica apenas os observadores que estão em um estado ativo.
O MVVM garante que a interface do usuário seja sempre atualizada de acordo com o ciclo de vida do componente.
Exemplo prático 01 (com DataBinding e ViewModel):
Inicialmente no projeto criado habilite o DataBinding, no arquivo gradle a nível de app, acrescente as seguintes linhas:
android{ ...
buildFeatures {
dataBinding = true
}
}
-
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="viewModel" type="nomeDoPacote.CalculadoraViewModel" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <EditText android:id="@+id/editTextN1" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Número 1" android:inputType="numberDecimal" /> <EditText android:id="@+id/editTextN2" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Número 2" android:inputType="numberDecimal" /> <EditText android:id="@+id/editTextOperador" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Operador (+, -, *, /)" android:inputType="text" /> <Button android:id="@+id/buttonCalcular" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Calcular" /> <TextView android:id="@+id/textViewResultado" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.resultado}" /> </LinearLayout> </layout>
-
Calculadora.java
public class Calculadora { public double calcular(double value1, double value2, String operator) { switch (operator) { case "+": return value1 + value2; case "-": return value1 - value2; case "*": return value1 * value2; case "/": return value1 / value2; default: return 0; } } }
-
CalculadoraViewModel.java
import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; public class CalculadoraViewModel extends ViewModel { private final Calculadora calculadora; private final MutableLiveData<String> resultado; public CalculadoraViewModel(){ calculadora = new Calculadora(); resultado = new MutableLiveData<>(); } public LiveData<String> getResultado() { return resultado; } public void calcular(double n1, double n2, String op) { double calculationResult = calculadora.calcular(n1, n2, op); resultado.setValue(String.valueOf(calculationResult)); } }
-
MainActivity.java
import android.os.Bundle; import android.view.View; import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.ViewModelProvider; import androidx.databinding.DataBindingUtil; import nomeDoPacote.ActivityMainBinding; public class MainActivity extends AppCompatActivity { private CalculadoraViewModel viewModel; private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, R.layout.activity_main); viewModel = new ViewModelProvider(this).get(CalculadoraViewModel.class); binding.setViewModel(viewModel); binding.setLifecycleOwner(this); binding.buttonCalcular.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { double n1 = Double.parseDouble(binding.editTextN1.getText().toString()); double n2 = Double.parseDouble(binding.editTextN2.getText().toString()); String op = binding.editTextOperador.getText().toString().trim(); viewModel.calcular(n1, n2, op); } }); } }
Exemplo prático 02 (sem DataBinding e com ViewModel):
-
activity_main.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="match_parent" android:orientation="vertical" android:padding="16dp"> <EditText android:id="@+id/editTextN1" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Número 1" android:inputType="numberDecimal" android:padding="8dp" android:textSize="16sp" /> <EditText android:id="@+id/editTextN2" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Número 2" android:inputType="numberDecimal" android:padding="8dp" android:textSize="16sp" /> <EditText android:id="@+id/editTextOp" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Operador (+, -, *, /)" android:inputType="text" android:padding="8dp" android:textSize="16sp" /> <Button android:id="@+id/buttonCalculadora" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Calcular" android:layout_marginTop="12dp" android:padding="8dp" android:textSize="16sp" /> <TextView android:id="@+id/textViewResultado" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Resultado: " android:textSize="18sp" android:paddingTop="16dp" android:textStyle="bold" /> </LinearLayout>
-
Calculadora.java
public class Calculadora { public double calcular(double n1, double n2, String op) { switch (op) { case "+": return n1 + n2; case "-": return n1 - n2; case "*": return n1 * n2; case "/": return n1 / n2; default: return 0; } } }
-
CalculadoraViewModel.java
import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; public class CalculadoraViewModel extends ViewModel { private final Calculadora modelo = new Calculadora(); private final MutableLiveData<String> resultado = new MutableLiveData<>(); public LiveData<String> getResultado() { return resultado; } public void calculate(double n1, double n2, String op) { double calculationResult = modelo.calcular(n1, n2, op); resultado.setValue("Resultado: " + calculationResult); } }
-
MainActivity.java
import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import androidx.appcopat.app.AppCompatActivity; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; public class MainActivity extends AppCompatActivity { private CalculadoraViewModel viewModel; private TextView textViewResultado; private EditText editTextN1, editTextN2, editTextOp; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textViewResultado = findViewById(R.id.textViewResultado); Button buttonCalculadora = findViewById(R.id.buttonCalculadora); editTextN1 = findViewById(R.id.editTextN1); editTextN2 = findViewById(R.id.editTextN2); editTextOp = findViewById(R.id.editTextOp); viewModel = new ViewModelProvider(this).get(CalculadoraViewModel.class); viewModel.getResultado().observe(this, new Observer<String>() { @Override public void onChanged(String result) { textViewResultado.setText(result); } }); buttonCalculadora.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String s1 = editTextN1.getText().toString().trim(); String s2 = editTextN2.getText().toString().trim(); String op = editTextOp.getText().toString().trim(); double n1 = Double.parseDouble(s1); double n2 = Double.parseDouble(s2); viewModel.calculate(n1, n2, op); } }); } }
Exemplo prático 03:
-
Aluno.java (Model)
public class Aluno { private String nome; private int nota; public Aluno(String nome, int nota) { this.nome = nome; this.nota = nota; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public int getNota() { return nota; } public void setNota(int nota) { this.nota = nota; } @Override public String toString() { return "Aluno{" + "nome='" + nome + '\'' + ", nota=" + nota + '}'; } }
-
AlunoRepositorio.java (Model)
import java.util.ArrayList; import java.util.List; public class AlunoRepositorio { public List<Aluno> obterDadosAlunos() { List<Aluno> listaAlunos = new ArrayList<>(); listaAlunos.add(new Aluno("Ana", 6)); listaAlunos.add(new Aluno("Rodrigo", 8)); listaAlunos.add(new Aluno("Paulo", 7)); return listaAlunos; } }
-
AlunoAdapter.java (Controller)
import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; import java.util.List; public class AlunoAdapter extends ArrayAdapter<Aluno> { public AlunoAdapter(Context context, List<Aluno> alunos) { super(context, 0, alunos); } @Override public View getView(int position, View convertView, ViewGroup parent) { Aluno aluno = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.item_aluno, parent, false); } TextView textViewNome = convertView.findViewById(R.id.textViewNome); TextView textViewNota = convertView.findViewById(R.id.textViewNota); if (aluno != null) { textViewNome.setText(aluno.getNome()); textViewNota.setText("Nota: " + aluno.getNota()); } return convertView; } }
-
AlunoViewModel.java (Controller)
import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; import java.util.List; public class AlunoViewModel extends ViewModel { private final MutableLiveData<List<Aluno>> alunos; private final AlunoRepositorio alunoRepositorio; public AlunoViewModel() { alunos = new MutableLiveData<>(); alunoRepositorio = new AlunoRepositorio(); alunos.setValue(alunoRepositorio.obterDadosAlunos()); } public LiveData<List<Aluno>> getAlunos() { return alunos; } }
-
activity_main.xml (View)
<?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="16dp"> <ListView android:id="@+id/listViewAlunos" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
-
item_aluno.xml (View)
<?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="vertical" android:padding="12dp"> <TextView android:id="@+id/textViewNome" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Nome" android:textSize="18sp" android:textStyle="bold" /> <TextView android:id="@+id/textViewNota" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Nota" android:textSize="16sp" /> </LinearLayout>
-
MainActivity.java (View)
import android.os.Bundle; import android.widget.ListView; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private AlunoViewModel alunoViewModel; private ListView listViewAlunos; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listViewAlunos = findViewById(R.id.listViewAlunos); alunoViewModel = new ViewModelProvider(this).get(AlunoViewModel.class); final AlunoAdapter adapter = new AlunoAdapter(this, new ArrayList<Aluno>()); listViewAlunos.setAdapter(adapter); alunoViewModel.getAlunos().observe(this, new Observer<List<Aluno>>() { @Override public void onChanged(List<Aluno> alunos) { adapter.clear(); adapter.addAll(alunos); adapter.notifyDataSetChanged(); } }); } }
Exemplo prático 04:
-
Bolo.java
public class Bolo { private String nome; private String receita; public Bolo(String nome, String receita) { this.nome = nome; this.receita = receita; } public String getNome() { return nome; } public String getReceita() { return receita; } }
-
BoloRepositorio.java
import java.util.ArrayList; import java.util.List; public class BoloRepositorio { public List<Bolo> getBolos() { List<Bolo> bolos = new ArrayList<>(); bolos.add(new Bolo("Bolo de Fubá", "Receita: Fubá, leite, ovos...")); bolos.add(new Bolo("Bolo de Chocolate", "Receita: Chocolate, leite, ovos...")); bolos.add(new Bolo("Bolo de Mandioca", "Receita: Mandioca, leite, ovos...")); return bolos; } }
-
BoloViewModel.java
import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; import java.util.List; public class BoloViewModel extends ViewModel { private final BoloRepositorio repository = new BoloRepositorio(); private final MutableLiveData<List<Bolo>> bolos = new MutableLiveData<>(); public BoloViewModel() { loadBolos(); } private void loadBolos() { bolos.setValue(repository.getBolos()); } public LiveData<List<Bolo>> getBolos() { return bolos; } }
-
BoloAdapter.java
import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; import java.util.List; public class BoloAdapter extends ArrayAdapter<Bolo> { public BoloAdapter(Context context, List<Bolo> bolos) { super(context, 0, bolos); } @Override public View getView(int position, View convertView, ViewGroup parent) { Bolo bolo = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.item_bolo, parent, false); } TextView textViewNome = convertView.findViewById(R.id.textViewNome); if (bolo != null) { textViewNome.setText(bolo.getNome()); } return convertView; } }
-
ReceitaFragment.java
import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class ReceitaFragment extends Fragment { private static final String ARG_RECEITA = "receita"; public static ReceitaFragment newInstance(String receita) { ReceitaFragment fragment = new ReceitaFragment(); Bundle args = new Bundle(); args.putString(ARG_RECEITA, receita); fragment.setArguments(args); return fragment; } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_receita, container, false); TextView textViewReceita = view.findViewById(R.id.textViewReceita);
if (getArguments() != null) { String receita = getArguments().getString(ARG_RECEITA); textViewReceita.setText(receita); } return view; } }
-
item_bolo.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="vertical" android:padding="16dp"> <TextView android:id="@+id/textViewNome" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Nome do bolo" android:textSize="18sp" /> </LinearLayout>
-
fragment_receita.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="match_parent" android:orientation="vertical" android:padding="16dp"> <TextView android:id="@+id/textViewReceita" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Receita" android:textSize="18sp" /> </LinearLayout>
-
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/listViewBolos" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" /> <FrameLayout android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/listViewBolos" /> </RelativeLayout>
-
MainActivity.java
import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private BoloViewModel boloViewModel; private ListView listViewBolos; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listViewBolos = findViewById(R.id.listViewBolos); boloViewModel = new ViewModelProvider(this).get(BoloViewModel.class); final BoloAdapter adapter = new BoloAdapter(this, new ArrayList<Bolo>()); listViewBolos.setAdapter(adapter); boloViewModel.getBolos().observe(this, new Observer<List<Bolo>>() { @Override public void onChanged(List<Bolo> bolos) { adapter.clear(); adapter.addAll(bolos); adapter.notifyDataSetChanged(); } }); listViewBolos.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Bolo bolo = adapter.getItem(position); if (bolo != null) { ReceitaFragment fragment = ReceitaFragment.newInstance(bolo.getReceita()); getSupportFragmentManager().beginTransaction() .replace(R.id.fragment_container, fragment) .addToBackStack(null) .commit(); } } }); } }