From 413019cfacc485427b8a4a6305187c23a78d0012 Mon Sep 17 00:00:00 2001 From: 230415 <230415@epvc.pt> Date: Mon, 11 May 2026 15:00:55 +0100 Subject: [PATCH] graficos --- app/build.gradle.kts | 1 + .../example/lifegrid/TelaInicialActivity.java | 3 + .../lifegrid/menu/GraficosFragment.java | 228 +++++++++++++++++- app/src/main/res/layout/fragment_graficos.xml | 79 +++++- settings.gradle.kts | 1 + 5 files changed, 304 insertions(+), 8 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a7a4ad7..dae38c8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -68,4 +68,5 @@ dependencies { androidTestImplementation(libs.ui.test.junit4) debugImplementation(libs.ui.tooling) debugImplementation(libs.ui.test.manifest) + implementation("com.github.PhilJay:MPAndroidChart:v3.1.0") } \ No newline at end of file diff --git a/app/src/main/java/com/example/lifegrid/TelaInicialActivity.java b/app/src/main/java/com/example/lifegrid/TelaInicialActivity.java index f732eb0..ee54eac 100644 --- a/app/src/main/java/com/example/lifegrid/TelaInicialActivity.java +++ b/app/src/main/java/com/example/lifegrid/TelaInicialActivity.java @@ -298,6 +298,9 @@ public class TelaInicialActivity extends AppCompatActivity { ((HomeFragment) currentFragment).carregarDados(mesSelecionado, anoSelecionado, mesNome); } else if (currentFragment instanceof TransacoesFragment) { ((TransacoesFragment) currentFragment).setFiltro(mesSelecionado, anoSelecionado); + } else if (currentFragment instanceof GraficosFragment) { + String mesNome = meses[spinnerMes.getSelectedItemPosition()]; + ((GraficosFragment) currentFragment).setFiltro(mesSelecionado, anoSelecionado, mesNome); } } diff --git a/app/src/main/java/com/example/lifegrid/menu/GraficosFragment.java b/app/src/main/java/com/example/lifegrid/menu/GraficosFragment.java index 42b0f47..6b4e1fd 100644 --- a/app/src/main/java/com/example/lifegrid/menu/GraficosFragment.java +++ b/app/src/main/java/com/example/lifegrid/menu/GraficosFragment.java @@ -2,20 +2,57 @@ package com.example.lifegrid.menu; import android.os.Bundle; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.Toast; import com.example.lifegrid.R; +import com.example.lifegrid.models.Transacao; +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.formatter.IndexAxisValueFormatter; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.database.ValueEventListener; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * GraficosFragment tem como único propósito compilar a informação da aplicação sob uma - * componente puramente matemática exibida de forma ilustrativa. Tarta da visão de Dashboard de estatística. + * componente puramente matemática exibida de forma ilustrativa. Trata da visão de Dashboard de estatística. */ public class GraficosFragment extends Fragment { + private PieChart pieChartMensal; + private BarChart barChartAnual; + private TextView tvTituloMensal; + private TextView tvTituloAnual; + + private List allTransacoesList = new ArrayList<>(); + private int currentMes = -1; + private String currentAno = ""; + private String currentMesNome = ""; + public GraficosFragment() { // Construtor público vazio obrigatório } @@ -25,13 +62,200 @@ public class GraficosFragment extends Fragment { super.onCreate(savedInstanceState); } + @Override + public void onResume() { + super.onResume(); + if (getActivity() instanceof com.example.lifegrid.TelaInicialActivity) { + ((com.example.lifegrid.TelaInicialActivity) getActivity()).atualizarDadosHome(); + } + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflaciona o layout para este fragmento View root = inflater.inflate(R.layout.fragment_graficos, container, false); + pieChartMensal = root.findViewById(R.id.pieChartMensal); + barChartAnual = root.findViewById(R.id.barChartAnual); + tvTituloMensal = root.findViewById(R.id.tvTituloMensal); + tvTituloAnual = root.findViewById(R.id.tvTituloAnual); + + configurarGraficos(); + carregarTransacoes(); return root; + } + + private void configurarGraficos() { + // Config PieChart + pieChartMensal.getDescription().setEnabled(false); + pieChartMensal.setUsePercentValues(true); + pieChartMensal.setEntryLabelColor(android.graphics.Color.BLACK); + pieChartMensal.setEntryLabelTextSize(12f); + pieChartMensal.setCenterText("Despesas"); + pieChartMensal.setCenterTextSize(18f); + + // Config BarChart + barChartAnual.getDescription().setEnabled(false); + barChartAnual.setDrawGridBackground(false); + barChartAnual.getAxisRight().setEnabled(false); + XAxis xAxis = barChartAnual.getXAxis(); + xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); + xAxis.setGranularity(1f); + xAxis.setCenterAxisLabels(true); + + String[] mesesLabel = new String[]{"Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"}; + xAxis.setValueFormatter(new IndexAxisValueFormatter(mesesLabel)); + } + + private void carregarTransacoes() { + FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser(); + if (currentUser == null) return; + + String userId = currentUser.getUid(); + DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference(); + + databaseReference.child("users").child(userId).child("transacoes").addValueEventListener(new ValueEventListener() { + @Override + public void onDataChange(@NonNull DataSnapshot snapshot) { + allTransacoesList.clear(); + for (DataSnapshot ds : snapshot.getChildren()) { + Transacao t = ds.getValue(Transacao.class); + if (t != null) { + t.setId(ds.getKey()); + allTransacoesList.add(t); + } + } + atualizarGraficos(); + } + + @Override + public void onCancelled(@NonNull DatabaseError error) { + if (getContext() != null) { + Toast.makeText(getContext(), "Erro ao carregar transações.", Toast.LENGTH_SHORT).show(); + } + } + }); + } + + public void setFiltro(int mesSelecionado, String anoSelecionado, String mesNome) { + this.currentMes = mesSelecionado; + this.currentAno = anoSelecionado; + this.currentMesNome = mesNome; + + if (tvTituloMensal != null) { + tvTituloMensal.setText("Resumo de " + mesNome + " " + anoSelecionado); } - } \ No newline at end of file + if (tvTituloAnual != null) { + tvTituloAnual.setText("Balanço Anual - " + anoSelecionado); + } + + atualizarGraficos(); + } + + private void atualizarGraficos() { + if (currentMes == -1 || currentAno.isEmpty()) return; + + atualizarPieChart(); + atualizarBarChart(); + } + + private void atualizarPieChart() { + Map categoriasMap = new HashMap<>(); + + for (Transacao t : allTransacoesList) { + if (t.getData() != null && t.getTipo() != null && t.getTipo().equalsIgnoreCase("Despesa")) { + String[] parts = t.getData().split("/"); + if (parts.length == 3) { + try { + int mesTransacao = Integer.parseInt(parts[1]); + String anoTransacao = parts[2]; + if (mesTransacao == currentMes && anoTransacao.equals(currentAno)) { + float valor = Float.parseFloat(t.getValor()); + String cat = t.getCategoria(); + categoriasMap.put(cat, categoriasMap.getOrDefault(cat, 0f) + valor); + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + } + } + + List entries = new ArrayList<>(); + for (Map.Entry entry : categoriasMap.entrySet()) { + if (entry.getValue() > 0) { + entries.add(new PieEntry(entry.getValue(), entry.getKey())); + } + } + + PieDataSet dataSet = new PieDataSet(entries, ""); + dataSet.setColors(ColorTemplate.MATERIAL_COLORS); + dataSet.setSliceSpace(3f); + dataSet.setValueTextSize(14f); + dataSet.setValueTextColor(android.graphics.Color.WHITE); + + PieData data = new PieData(dataSet); + pieChartMensal.setData(data); + pieChartMensal.invalidate(); // refresh + } + + private void atualizarBarChart() { + float[] receitasPorMes = new float[12]; + float[] despesasPorMes = new float[12]; + + for (Transacao t : allTransacoesList) { + if (t.getData() != null) { + String[] parts = t.getData().split("/"); + if (parts.length == 3) { + try { + int mesTransacao = Integer.parseInt(parts[1]) - 1; // 0 a 11 + String anoTransacao = parts[2]; + if (anoTransacao.equals(currentAno) && mesTransacao >= 0 && mesTransacao < 12) { + float valor = Float.parseFloat(t.getValor()); + if (t.getTipo() != null && t.getTipo().equalsIgnoreCase("Receita")) { + receitasPorMes[mesTransacao] += valor; + } else { + despesasPorMes[mesTransacao] += valor; + } + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + } + } + + List entriesReceitas = new ArrayList<>(); + List entriesDespesas = new ArrayList<>(); + + for (int i = 0; i < 12; i++) { + entriesReceitas.add(new BarEntry(i, receitasPorMes[i])); + entriesDespesas.add(new BarEntry(i, despesasPorMes[i])); + } + + BarDataSet setReceitas = new BarDataSet(entriesReceitas, "Receitas"); + setReceitas.setColor(android.graphics.Color.parseColor("#2ECC71")); + setReceitas.setValueTextSize(10f); + + BarDataSet setDespesas = new BarDataSet(entriesDespesas, "Despesas"); + setDespesas.setColor(android.graphics.Color.parseColor("#E74C3C")); + setDespesas.setValueTextSize(10f); + + BarData data = new BarData(setReceitas, setDespesas); + + // agrupar barras + float groupSpace = 0.4f; + float barSpace = 0.05f; + float barWidth = 0.25f; + + data.setBarWidth(barWidth); + barChartAnual.setData(data); + barChartAnual.groupBars(0f, groupSpace, barSpace); + barChartAnual.getXAxis().setAxisMinimum(0f); + barChartAnual.getXAxis().setAxisMaximum(0f + barChartAnual.getBarData().getGroupWidth(groupSpace, barSpace) * 12); + + barChartAnual.invalidate(); // refresh + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_graficos.xml b/app/src/main/res/layout/fragment_graficos.xml index 3488ef0..7275f96 100644 --- a/app/src/main/res/layout/fragment_graficos.xml +++ b/app/src/main/res/layout/fragment_graficos.xml @@ -1,14 +1,81 @@ - - - + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingBottom="100dp" + android:paddingTop="16dp"> - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index a7e3a34..4b1b808 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,7 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + maven { url = uri("https://jitpack.io") } } }