Alterei o design para ficar m ais fluido mas falta alterar a cor dos botões e adicionar um botão de voltar no activity_expenses_list.xml e tambem adicionei mais explicações

main
240422 2025-11-03 12:09:35 +00:00
parent 249eb6f6f2
commit f021060585
9 changed files with 145 additions and 153 deletions

View File

@ -8,12 +8,16 @@ import androidx.appcompat.app.AppCompatActivity; // Importa classe base para Act
import com.google.android.material.button.MaterialButton; // Importa botão Material Design
import com.google.android.material.textfield.TextInputEditText; // Importa campo de texto Material Design
import com.google.android.material.textfield.MaterialAutoCompleteTextView; // Campo com menu suspenso
import java.text.SimpleDateFormat; // Importa classe para formatação de datas
import java.util.Date; // Importa classe para trabalhar com datas
import java.util.Locale; // Importa classe para configurações de localização
import java.util.Arrays; // Utilitário para trabalhar com arrays
import java.util.ArrayList; // Lista mutável
import pt.epvc.gestodedespesas.data.DatabaseHelper; // DatabaseHelper movido para pacote data
import android.widget.ArrayAdapter; // Adapter para preencher o dropdown
/**
* AddExpenseActivity - Tela de Adicionar/Editar Despesas
@ -30,7 +34,8 @@ import pt.epvc.gestodedespesas.data.DatabaseHelper; // DatabaseHelper movido par
public class AddExpenseActivity extends AppCompatActivity { // Declara classe que estende AppCompatActivity
// Views da interface - campos de entrada do formulário
private TextInputEditText etDescription, etAmount, etCategory, etDate, etNotes; // Declara campos de texto para entrada de dados
private TextInputEditText etDescription, etAmount, etDate, etNotes; // Declara campos de texto para entrada de dados
private MaterialAutoCompleteTextView etCategory; // Campo de categoria com dropdown
private MaterialButton btnSave, btnCancel; // Declara botões de ação
// Objetos para funcionalidade
@ -59,6 +64,8 @@ public class AddExpenseActivity extends AppCompatActivity { // Declara classe qu
// Para nova despesa, define a data atual como padrão
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()); // Cria formato de data brasileiro
etDate.setText(sdf.format(new Date())); // Define data atual no campo de data
// Categoria padrão opcional (ex.: "Outros") para facilitar
etCategory.setText("Outros", false);
}
}
@ -70,13 +77,26 @@ public class AddExpenseActivity extends AppCompatActivity { // Declara classe qu
// Conecta os campos de entrada de texto
etDescription = findViewById(R.id.etDescription); // Busca campo de descrição por ID e atribui à variável
etAmount = findViewById(R.id.etAmount); // Busca campo de valor por ID e atribui à variável
etCategory = findViewById(R.id.etCategory); // Busca campo de categoria por ID e atribui à variável
etCategory = findViewById(R.id.etCategory); // Busca campo de categoria (dropdown) por ID
etDate = findViewById(R.id.etDate); // Busca campo de data por ID e atribui à variável
etNotes = findViewById(R.id.etNotes); // Busca campo de notas por ID e atribui à variável
// Conecta os botões de ação
btnSave = findViewById(R.id.btnSave); // Busca botão salvar por ID e atribui à variável
btnCancel = findViewById(R.id.btnCancel); // Busca botão cancelar por ID e atribui à variável
// Configura o dropdown de categorias com as mesmas opções do filtro
String[] allCategories = getResources().getStringArray(R.array.expense_categories); // Inclui "Todas"
ArrayList<String> formCategories = new ArrayList<>(Arrays.asList(allCategories));
formCategories.remove("Todas"); // Remove "Todas" do formulário (apenas para filtro)
ArrayAdapter<String> categoryAdapter = new ArrayAdapter<>(
this,
android.R.layout.simple_list_item_1,
formCategories
);
etCategory.setAdapter(categoryAdapter);
etCategory.setOnClickListener(v -> etCategory.showDropDown()); // Abre ao tocar
etCategory.setOnFocusChangeListener((v, hasFocus) -> { if (hasFocus) etCategory.showDropDown(); });
}
/**

View File

@ -15,7 +15,6 @@ import androidx.core.view.WindowInsetsCompat; // Insets de janela
import androidx.recyclerview.widget.LinearLayoutManager; // Layout vertical
import androidx.recyclerview.widget.RecyclerView; // Lista eficiente
import com.google.android.material.floatingactionbutton.FloatingActionButton; // Botão flutuante
import com.google.android.material.button.MaterialButton; // Botão Material
import java.text.DecimalFormat; // Formatar valores monetários
@ -34,13 +33,12 @@ public class ExpensesListActivity extends AppCompatActivity implements ExpenseAd
private RecyclerView recyclerViewExpenses; // Lista de despesas
private TextView tvTotal, tvExpenseCount, tvAverage; // Indicadores de total, contagem e média
private FloatingActionButton fabAddExpense; // Botão flutuante para adicionar
private MaterialButton btnFilter; // Botão para filtrar por categoria
private DatabaseHelper databaseHelper; // Acesso ao SQLite
private ExpenseAdapter expenseAdapter; // Adapter do RecyclerView
private List<Expense> expenseList; // Dados das despesas
private DecimalFormat currencyFormat = new DecimalFormat("€#,##0.00"); // Formatação em euro
private boolean isShowingEmptyState = false; // Controle de qual layout está visível (lista ou estado vazio)
private View emptyStateView; // Container do empty state incluído no layout
/**
* onCreate: configura layout, Edge-to-Edge e listeners de insets, inicializa
@ -50,8 +48,19 @@ public class ExpensesListActivity extends AppCompatActivity implements ExpenseAd
protected void onCreate(Bundle savedInstanceState) { // Ciclo de vida: criação da Activity
super.onCreate(savedInstanceState); // Chama implementação base
EdgeToEdge.enable(this); // Ativa layout de ponta a ponta
databaseHelper = new DatabaseHelper(this); // Instancia acesso ao DB (SQLiteHelper)
renderLayoutByData(); // Decide e aplica o layout conforme existência de despesas
setContentView(R.layout.activity_expenses_list); // Usa layout principal sempre
try {
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
} catch (Exception ignored) {}
databaseHelper = new DatabaseHelper(this); // Instancia acesso ao DB
initializeViews(); // Faz bind das views (inclui empty state)
setupRecyclerView(); // Configura lista
setupClickListeners(); // Configura cliques (filtro e empty state)
loadExpenses(); // Carrega dados iniciais
}
/**
@ -63,8 +72,8 @@ public class ExpensesListActivity extends AppCompatActivity implements ExpenseAd
tvTotal = findViewById(R.id.tvTotal); // Texto do total gasto
tvExpenseCount = findViewById(R.id.tvExpenseCount); // Texto da contagem
tvAverage = findViewById(R.id.tvAverage); // Texto da média
fabAddExpense = findViewById(R.id.fabAddExpense); // FAB adicionar
btnFilter = findViewById(R.id.btnFilter); // Botão filtrar
emptyStateView = findViewById(R.id.emptyStateContainer); // Empty state incluído
} catch (Exception e) {
Toast.makeText(this, "Erro ao inicializar views: " + e.getMessage(), Toast.LENGTH_LONG).show(); // Feedback de erro
}
@ -92,16 +101,6 @@ public class ExpensesListActivity extends AppCompatActivity implements ExpenseAd
*/
private void setupClickListeners() {
try {
if (fabAddExpense != null) { // Verifica se a view foi encontrada
fabAddExpense.setOnClickListener(new View.OnClickListener() { // Clique do FAB
@Override
public void onClick(View v) { // Ação ao clicar
Intent intent = new Intent(ExpensesListActivity.this, AddExpenseActivity.class); // Ir para adicionar
startActivityForResult(intent, 1); // Abrir aguardando resultado
}
});
}
if (btnFilter != null) { // Verifica existência do botão
btnFilter.setOnClickListener(new View.OnClickListener() { // Clique do filtro
@Override
@ -110,6 +109,18 @@ public class ExpensesListActivity extends AppCompatActivity implements ExpenseAd
}
});
}
// Botões do empty state (se presentes no include)
View add = findViewById(R.id.btnAddFirstExpense);
if (add != null) {
add.setOnClickListener(v -> {
Intent intent = new Intent(ExpensesListActivity.this, AddExpenseActivity.class);
startActivityForResult(intent, 1);
});
}
View back = findViewById(R.id.btnBackFromEmpty);
if (back != null) {
back.setOnClickListener(v -> finish());
}
} catch (Exception e) {
Toast.makeText(this, "Erro ao configurar click listeners: " + e.getMessage(), Toast.LENGTH_LONG).show(); // Feedback de erro
}
@ -122,20 +133,15 @@ public class ExpensesListActivity extends AppCompatActivity implements ExpenseAd
private void loadExpenses() {
try {
expenseList = databaseHelper.getAllExpenses(); // Puxa despesas do DB
boolean hasData = expenseList != null && !expenseList.isEmpty();
if (!hasData) {
if (!isShowingEmptyState) {
renderEmptyLayout();
}
return; // Nada para atualizar no layout vazio
}
if (isShowingEmptyState) {
renderMainLayout();
}
if (expenseAdapter != null) { // Garante adapter existente
expenseAdapter.updateExpenses(expenseList); // Atualiza lista na UI
if (expenseAdapter != null) { // Atualiza a lista
expenseAdapter.updateExpenses(expenseList);
}
updateTotal(); // Recalcula indicadores
// Mostra/oculta o empty state no fim da tela
boolean isEmpty = expenseList == null || expenseList.isEmpty();
if (emptyStateView != null) {
emptyStateView.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
}
} catch (Exception e) {
Toast.makeText(this, "Erro ao carregar despesas: " + e.getMessage(), Toast.LENGTH_LONG).show(); // Feedback de erro
}
@ -178,14 +184,11 @@ public class ExpensesListActivity extends AppCompatActivity implements ExpenseAd
} else { // Categoria específica
String category = categories[which]; // Nome da categoria
expenseList = databaseHelper.getExpensesByCategory(category); // Busca filtrada
if (expenseList == null || expenseList.isEmpty()) {
renderEmptyLayout();
} else {
if (isShowingEmptyState) {
renderMainLayout();
}
expenseAdapter.updateExpenses(expenseList); // Atualiza adapter
updateTotal(); // Recalcula totais com filtro
expenseAdapter.updateExpenses(expenseList); // Atualiza adapter (lista pode ficar vazia)
updateTotal(); // Recalcula totais com filtro
boolean isEmpty = expenseList == null || expenseList.isEmpty();
if (emptyStateView != null) {
emptyStateView.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
}
}
})
@ -230,54 +233,5 @@ public class ExpensesListActivity extends AppCompatActivity implements ExpenseAd
.show(); // Exibe diálogo
}
/**
* Decide qual layout usar com base nos dados atuais do DB.
*/
private void renderLayoutByData() { // Decide qual layout mostrar com base nos dados
try {
List<Expense> initial = databaseHelper.getAllExpenses(); // Consulta inicial ao DB
if (initial == null || initial.isEmpty()) { // Sem dados?
renderEmptyLayout(); // Mostra estado vazio
} else { // Há dados
expenseList = initial; // Mantém em memória
renderMainLayout(); // Mostra lista principal
}
} catch (Exception e) { // Qualquer erro
renderEmptyLayout(); // Fallback: evita crash mostrando estado vazio
}
}
/**
* Mostra o layout principal de lista, inicializa views/adapter e listeners.
*/
private void renderMainLayout() { // Exibe layout principal de lista
setContentView(R.layout.activity_expenses_list); // Aplica XML de lista
isShowingEmptyState = false; // Marca que a lista está ativa
try {
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { // Listener de insets
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); // Áreas das barras
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); // Padding seguro
return insets; // Continua propagação
});
} catch (Exception ignored) { } // Se não existir view com id main, ignora
initializeViews(); // Associa views do layout
setupRecyclerView(); // Configura RecyclerView e adapter
setupClickListeners(); // Registra cliques (FAB e filtro)
updateTotal(); // Atualiza totais na UI
}
/**
* Mostra o layout de estado vazio e configura o botão de adicionar.
*/
private void renderEmptyLayout() { // Exibe layout de estado vazio
setContentView(R.layout.empty_state); // Aplica XML vazio
isShowingEmptyState = true; // Marca que estado vazio está ativo
View add = findViewById(R.id.btnAddFirstExpense); // Botão "Adicionar Despesa"
if (add != null) { // Se existir no layout
add.setOnClickListener(v -> { // Ao clicar
Intent intent = new Intent(ExpensesListActivity.this, AddExpenseActivity.class); // Abre tela de adição
startActivityForResult(intent, 1); // Aguarda resultado para recarregar
});
}
}
// Métodos de alternância de layout foram substituídos por um include do empty state no XML
}

View File

@ -112,8 +112,8 @@ public class MainActivity extends AppCompatActivity { // Declara classe que este
btnViewExpenses.setOnClickListener(new View.OnClickListener() { // Define listener de clique para o botão "Ver Despesas"
@Override // Sobrescreve método da interface
public void onClick(View v) { // Método chamado quando botão é clicado
// Navega para a tela simplificada de lista de despesas
Intent intent = new Intent(MainActivity.this, SimpleExpensesActivity.class); // Cria Intent para navegar para SimpleExpensesActivity
// Navega para a tela avançada de lista de despesas (usa empty_state e activity_expenses_list)
Intent intent = new Intent(MainActivity.this, ExpensesListActivity.class); // Cria Intent para ExpensesListActivity
startActivity(intent); // Inicia Activity sem esperar resultado
}
});

View File

@ -6,8 +6,8 @@ import android.database.Cursor; // Onde ficam os resultados de uma consulta
import android.database.sqlite.SQLiteDatabase; // Objeto que permite ler/escrever no banco
import android.database.sqlite.SQLiteOpenHelper; // Classe que ajuda a criar e atualizar o banco
import java.util.ArrayList; // Lista de tamanho variável
import java.util.List; // Tipo "lista" em Java
import java.util.ArrayList; // Lista de tamanho variável (pode crescer conforme necessário)
import java.util.List; // Tipo "lista" em Java (coleção de itens)
import pt.epvc.gestodedespesas.Expense; // Nossa classe que representa uma despesa
@ -23,24 +23,24 @@ public class DatabaseHelper extends SQLiteOpenHelper { // Helper do SQLite
private static final int DATABASE_VERSION = 1; // Versão do banco (muda quando a estrutura mudar)
// Nome da tabela e das colunas (os "campos" da tabela)
private static final String TABLE_EXPENSES = "expenses";
private static final String COLUMN_ID = "id";
private static final String COLUMN_DESCRIPTION = "description";
private static final String COLUMN_AMOUNT = "amount";
private static final String COLUMN_CATEGORY = "category";
private static final String COLUMN_DATE = "date";
private static final String COLUMN_NOTES = "notes";
private static final String TABLE_EXPENSES = "expenses"; // Nome da tabela no banco
private static final String COLUMN_ID = "id"; // Coluna: identificador único da despesa
private static final String COLUMN_DESCRIPTION = "description"; // Coluna: descrição do gasto
private static final String COLUMN_AMOUNT = "amount"; // Coluna: valor do gasto
private static final String COLUMN_CATEGORY = "category"; // Coluna: categoria do gasto
private static final String COLUMN_DATE = "date"; // Coluna: data do gasto (texto formatado)
private static final String COLUMN_NOTES = "notes"; // Coluna: notas/opcional
// Comando que cria a tabela de despesas (rodado somente na primeira vez)
private static final String CREATE_TABLE_EXPENSES =
"CREATE TABLE " + TABLE_EXPENSES + "(" +
COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
COLUMN_DESCRIPTION + " TEXT NOT NULL," +
COLUMN_AMOUNT + " REAL NOT NULL," +
COLUMN_CATEGORY + " TEXT NOT NULL," +
COLUMN_DATE + " TEXT NOT NULL," +
COLUMN_NOTES + " TEXT" +
")";
private static final String CREATE_TABLE_EXPENSES = // Texto do comando para criar a tabela
"CREATE TABLE " + TABLE_EXPENSES + "(" + // Começa criando a tabela "expenses"
COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + // id numérico que cresce sozinho
COLUMN_DESCRIPTION + " TEXT NOT NULL," + // descrição obrigatória
COLUMN_AMOUNT + " REAL NOT NULL," + // valor obrigatório (número)
COLUMN_CATEGORY + " TEXT NOT NULL," + // categoria obrigatória
COLUMN_DATE + " TEXT NOT NULL," + // data obrigatória (guardada como texto)
COLUMN_NOTES + " TEXT" + // notas opcionais
")"; // fecha o comando
public DatabaseHelper(Context context) { // Chamado quando precisamos usar o banco
super(context, DATABASE_NAME, null, DATABASE_VERSION); // Diz o nome e a versão do banco
@ -73,11 +73,11 @@ public class DatabaseHelper extends SQLiteOpenHelper { // Helper do SQLite
public Expense getExpense(int id) { // Busca uma despesa específica pelo id
SQLiteDatabase db = this.getReadableDatabase(); // Abre o banco para leitura
Cursor cursor = db.query(
TABLE_EXPENSES,
new String[]{COLUMN_ID, COLUMN_DESCRIPTION, COLUMN_AMOUNT, COLUMN_CATEGORY, COLUMN_DATE, COLUMN_NOTES},
COLUMN_ID + "=?",
new String[]{String.valueOf(id)}, null, null, null, null
Cursor cursor = db.query( // Faz uma consulta procurando pelo id
TABLE_EXPENSES, // na tabela de despesas
new String[]{COLUMN_ID, COLUMN_DESCRIPTION, COLUMN_AMOUNT, COLUMN_CATEGORY, COLUMN_DATE, COLUMN_NOTES}, // colunas desejadas
COLUMN_ID + "=?", // condição: id igual ao informado
new String[]{String.valueOf(id)}, null, null, null, null // valor do id
);
if (cursor != null && cursor.moveToFirst()) { // Se achou o registro
@ -100,9 +100,9 @@ public class DatabaseHelper extends SQLiteOpenHelper { // Helper do SQLite
public List<Expense> getAllExpenses() { // Busca todas as despesas, mais recentes primeiro
List<Expense> expenseList = new ArrayList<>(); // Cria uma lista vazia
String selectQuery = "SELECT * FROM " + TABLE_EXPENSES + " ORDER BY " + COLUMN_DATE + " DESC"; // Consulta
String selectQuery = "SELECT * FROM " + TABLE_EXPENSES + " ORDER BY " + COLUMN_DATE + " DESC"; // Consulta: tudo, ordenado pela data (mais recente primeiro)
SQLiteDatabase db = this.getWritableDatabase(); // Abre o banco
Cursor cursor = db.rawQuery(selectQuery, null); // Executa a consulta
Cursor cursor = db.rawQuery(selectQuery, null); // Executa a consulta e devolve um "cursor" para navegar
if (cursor.moveToFirst()) { // Se tem pelo menos uma linha
do { // Repete para cada linha
@ -157,10 +157,10 @@ public class DatabaseHelper extends SQLiteOpenHelper { // Helper do SQLite
List<Expense> expenseList = new ArrayList<>(); // Lista vazia
SQLiteDatabase db = this.getReadableDatabase(); // Abre para leitura
Cursor cursor = db.query( // Faz a consulta com filtro de categoria
TABLE_EXPENSES,
new String[]{COLUMN_ID, COLUMN_DESCRIPTION, COLUMN_AMOUNT, COLUMN_CATEGORY, COLUMN_DATE, COLUMN_NOTES},
COLUMN_CATEGORY + "=?",
new String[]{category}, null, null, COLUMN_DATE + " DESC", null
TABLE_EXPENSES, // tabela "expenses"
new String[]{COLUMN_ID, COLUMN_DESCRIPTION, COLUMN_AMOUNT, COLUMN_CATEGORY, COLUMN_DATE, COLUMN_NOTES}, // colunas
COLUMN_CATEGORY + "=?", // condição de filtro
new String[]{category}, null, null, COLUMN_DATE + " DESC", null // valor do filtro e ordenação
);
if (cursor.moveToFirst()) { // Se achou resultados

View File

@ -89,14 +89,17 @@
android:hint="Categoria"
app:startIconDrawable="@drawable/ic_category"
app:boxStrokeColor="@color/primary_color"
app:hintTextColor="@color/primary_color">
app:hintTextColor="@color/primary_color"
app:endIconMode="dropdown_menu">
<com.google.android.material.textfield.TextInputEditText
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/etCategory"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:textColor="@color/text_primary" />
android:inputType="none"
android:textColor="@color/text_primary"
android:hint="Selecione uma categoria"
android:entries="@array/expense_categories" />
</com.google.android.material.textfield.TextInputLayout>

View File

@ -225,23 +225,16 @@
android:layout_height="wrap_content"
android:layout_marginBottom="80dp" />
<!-- Empty state embutido (mostrado quando não há despesas) -->
<include
android:id="@+id/emptyStateContainer"
layout="@layout/empty_state"
android:visibility="gone" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<!-- Floating Action Button -->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAddExpense"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:src="@drawable/ic_add_white"
app:backgroundTint="@color/primary_color"
app:borderWidth="0dp"
app:elevation="12dp"
app:fabSize="normal"
app:tint="@color/white"
app:rippleColor="@color/primary_dark" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -33,40 +33,40 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="32dp"
android:gravity="center">
android:padding="32dp">
<!-- Ícone principal -->
<ImageView
android:layout_width="120dp"
android:layout_height="120dp"
android:src="@drawable/ic_wallet"
android:layout_marginBottom="24dp"
android:background="@drawable/welcome_icon_background"
android:padding="24dp"
android:layout_marginBottom="24dp" />
android:src="@drawable/ic_wallet" />
<!-- Título principal -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="💰 Gestão de Despesas"
android:textSize="32sp"
android:textStyle="bold"
android:textColor="@color/text_primary"
android:layout_marginBottom="12dp"
android:gravity="center"
android:layout_marginBottom="12dp" />
android:text="💰 Gestão de Despesas"
android:textColor="@color/text_primary"
android:textSize="32sp"
android:textStyle="bold" />
<!-- Subtítulo -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Controle seus gastos de forma inteligente e organizada"
android:textSize="16sp"
android:textColor="@color/text_secondary"
android:layout_marginBottom="24dp"
android:gravity="center"
android:lineSpacingExtra="4dp"
android:layout_marginBottom="24dp" />
android:text="Controle seus gastos de forma inteligente e organizada"
android:textColor="@color/text_secondary"
android:textSize="16sp" />
<!-- Botão principal -->
<com.google.android.material.button.MaterialButton
@ -74,14 +74,14 @@
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="🚀 Começar Agora"
android:textSize="18sp"
android:textColor="@color/white"
android:textSize="18sp"
app:backgroundTint="@color/primary_color"
app:cornerRadius="28dp"
app:elevation="6dp"
app:icon="@drawable/ic_rocket"
app:iconTint="@color/white"
app:iconSize="24dp"
app:elevation="6dp" />
app:iconTint="@color/white" />
</LinearLayout>

View File

@ -45,4 +45,16 @@
app:icon="@drawable/ic_add_white"
app:iconTint="@color/white" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnBackFromEmpty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="🔙 Voltar"
android:textColor="@color/primary_color"
app:backgroundTint="@android:color/transparent"
app:strokeColor="@color/primary_color"
app:strokeWidth="2dp"
app:cornerRadius="20dp" />
</LinearLayout>

View File

@ -1,3 +1,13 @@
<resources>
<string name="app_name">Gestão de Despesas</string>
<string-array name="expense_categories">
<item>Todas</item>
<item>Alimentação</item>
<item>Transporte</item>
<item>Entretenimento</item>
<item>Saúde</item>
<item>Compras</item>
<item>Outros</item>
</string-array>
</resources>