ExpandableListView as a child of ViewPager

Viewed 29

I am writing an application to control personal budgets. How it is designed now is basically using two different ExpandableListViews, one for incomes, the other for expenses. Inflating Dialogs for setting up the categories of incomes/expenses, as well as the transactions. The application stores the data in an SQLite database.

So far, so good. My questions come when I try to implement some kind of design to organize this data by different months. I thought about ViewPager, because it is the option that I think it is most close to this implementation. I am quite new in Android development so not sure if there is a better option to implement this functionality.

The Activity and Fragment for this view are the followings:

SummaryActivity.java

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;

import com.robin.xbudget.database.TransactionBaseHelper;
import com.robin.xbudget.database.TransactionDbSchema;
import com.robin.xbudget.database.TransactionDbSchema.TransactionTable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SummaryActivity extends SingleFragmentActivity implements
        NewCategoryIncome.NewCategoryActivityListener
        , NewTransactionIncome.NewIncomeActivityListener
        , NewCategoryExpense.NewCategoryActivityListener
        , NewTransactionExpense.NewExpenseActivityListener
        , SummaryFragment.SummaryFragmentActivityListener
        , Statistics.StatisticsListener {

    /**
     * Constants
     */
    private static final String TAG = "XSummaryActivity";
    public static final String mSalaryKey = "salaryKey";
    public static final String mCurrencyKey = "currencyKey";

    /**
     * Members
     */
    private SQLiteDatabase mDatabase;
    private Context mContext;

    /**
     * Incomes
     */
    private List<String> listIncomesDataGroup;
    private Map<String, List<Transaction>> listIncomesDataChild;

    /**
     * Expenses
     */
    private List<String> listExpensesDataGroup;
    private Map<String, List<Transaction>> listExpensesDataChild;

    /**
     * Expense Adapter
     */
    private ExpandableListViewAdapter expandableExpensesListViewAdapter;
    private ExpandableListViewAdapter expandableIncomesListViewAdapter;


    /**
     * Getters / Setters
     */

    @Override
    public List<String> getIncomesDataGroup() {
        return listIncomesDataGroup;
    }

    @Override
    public List<String> getExpensesDataGroup() {
        return listExpensesDataGroup;
    }

    @Override
    public Map<String, List<Transaction>> getExpensesDataChild() {
        return listExpensesDataChild;
    }

    @Override
    public ExpandableListViewAdapter getIncomesAdapter() {
        return expandableIncomesListViewAdapter;
    }

    @Override
    public ExpandableListViewAdapter getExpensesAdapter() {
        return expandableExpensesListViewAdapter;
    }


    @Override
    public double getTotalIncomes() {
        double total = 0;
        for (Map.Entry<String, List<Transaction>> set : listIncomesDataChild.entrySet()) {
            for (Transaction t : set.getValue()) {
                total += t.getQuantity();
            }
        }
        Log.d(TAG, "Total of Income Transactions is: " + total);
        return total;
    }

    @Override
    public double getTotalExpenses() {
        double total = 0;
        for (Map.Entry<String, List<Transaction>> set : listExpensesDataChild.entrySet()) {
            for (Transaction t : set.getValue()) {
                total += t.getQuantity();
            }
        }
        Log.d(TAG, "Total of Expense Transactions is: " + total);
        return total;
    }


    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    protected Fragment createFragment() {
        Log.d(TAG, "onCreateFragment() was called");
        initObject();
        return SummaryFragment.newInstance();
    }


    @Override
    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
        Log.d(TAG, "onCreate() was called");
        super.onCreate(savedInstanceState, persistentState);

    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    public void initObject() {
        Log.d(TAG, "initObject() was called");

        //Starting the databases
        mContext = getApplicationContext();
        mDatabase = new TransactionBaseHelper(mContext).getWritableDatabase();
        // initializing the list of groups
        listIncomesDataGroup = new ArrayList<>();
        listExpensesDataGroup = new ArrayList<>();

        // initializing the list of child
        listIncomesDataChild = new HashMap<>();
        listExpensesDataChild = new HashMap<>();

        // initializing the adapter object
        expandableIncomesListViewAdapter = new ExpandableListViewAdapter(getApplicationContext(), listIncomesDataGroup, listIncomesDataChild);
        expandableExpensesListViewAdapter = new ExpandableListViewAdapter(getApplicationContext(), listExpensesDataGroup, listExpensesDataChild);

        updateCategories();
        updateChilds();

    }

    /***
     * Listeners - (Interface Implementations)
     */

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public void insertIncome(Transaction transaction) {
        //String value = transaction + "   " + mCurrency;
        listIncomesDataChild.get(transaction.getCategory()).add(transaction);
        //updateChilds();
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public void insertExpense(Transaction transaction) {
        ContentValues values = getContentValues(transaction);
        Log.d(TAG, "TRANSACTION TO INSERT IS: " + transaction);
        mDatabase.insert(TransactionTable.mTransactionExpense, null, values);
        updateChilds();
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public void updateExpense(Transaction transaction) {
        ContentValues values = getContentValues(transaction);
        String mIdString = transaction.getId().toString();
        mDatabase.update(TransactionTable.mTransactionExpense, values, TransactionTable.ChildCols.UUID + " = ?", new String[]{mIdString});
        updateChilds();
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public void deleteExpense(Transaction transaction) {
        String mIdString = transaction.getId().toString();
        Log.d(TAG, "mIdString is: " + mIdString);
        Log.d(TAG, "rows deleted are: " + mDatabase.delete(TransactionDbSchema.TransactionTable.mTransactionExpense, TransactionTable.ChildCols.UUID + " = ?", new String[]{mIdString}));
        updateChilds();
    }

    @Override
    public void insertCategoryExpense(String category) {
        if (category != null || !category.trim().isEmpty()) {
            mDatabase.execSQL("INSERT INTO " + TransactionTable.mCategoryExpense + " ('category') " + "VALUES (" + "'" + category + "'" + ")");
            updateCategories();
        }
    }

    
    
    private ContentValues getContentValues(Transaction transaction) {
        ContentValues values = new ContentValues();
        values.put(TransactionTable.ChildCols.CATEGORY, transaction.getCategory());
        values.put(TransactionTable.ChildCols.UUID, transaction.getId().toString());
        values.put(TransactionTable.ChildCols.NAME, transaction.getName());
        values.put(TransactionTable.ChildCols.QUANTITY, transaction.getQuantity());
        values.put(TransactionTable.ChildCols.DATE, transaction.getDate().getTime());

        return values;
    }


    public List<String> getCategoryByTable(String table) {
        List<String> categories = new ArrayList<>();
        String query = "SELECT * from " + TransactionTable.mCategoryExpense;
        Cursor c = mDatabase.rawQuery(query, null);

        if (c.moveToFirst()) {
            while (!c.isAfterLast()) {
                categories.add(c.getString(0));
                c.moveToNext();
            }
        }
        return categories;
    }

    private TransactionCursorWrapper queryTransactions(String table, String whereClause, String[] whereArgs) {
        Cursor cursor = mDatabase.query(
                table
                ,
                null, // columns - null selects all columns
                whereClause,
                whereArgs,
                null, // groupBy
                null, // having
                null // orderBy
        );
        return new TransactionCursorWrapper(cursor);
    }

    @Override
    public List<Transaction> getTransactionsByTable(String table) {
        List<Transaction> transactions = new ArrayList<>();

        TransactionCursorWrapper cursor = queryTransactions(table, null, null);

        try {
            cursor.moveToFirst();
            while (!cursor.isAfterLast()) {
                if (!transactions.contains(cursor.getTransaction()))
                    transactions.add(cursor.getTransaction());
                cursor.moveToNext();
            }
        } finally {
            cursor.close();
        }
        return transactions;
    }

    @Override
    public void updateCategories() {
        List list = getCategoryByTable(TransactionTable.mCategoryExpense);

        Log.d(TAG, "BEFORE: GROUP SIZE" + list.size());
        listExpensesDataGroup.clear();
        listExpensesDataGroup.addAll(list);
        for (String category : listExpensesDataGroup)
            if (!listExpensesDataChild.containsKey(category))
                listExpensesDataChild.put(category, new ArrayList<Transaction>());

        expandableExpensesListViewAdapter.notifyDataSetChanged();
    }


    @RequiresApi(api = Build.VERSION_CODES.N)
    public void updateChilds() {

        //Retrieve data from the DB
        List<Transaction> transactionList = getTransactionsByTable(TransactionTable.mTransactionExpense);

        //Clear lists inside the Map
        for (Map.Entry<String, List<Transaction>> set : getExpensesDataChild().entrySet())
            set.getValue().clear();

        //Add the values to each List from the DB
        for (Transaction transaction : transactionList) {
            Log.d(TAG, "transaction.getCategory() value is: " + transaction.getCategory());
            Log.d(TAG, "listener.getExpensesDataChild().get(transaction.getCategory())" + getExpensesDataChild().get(transaction.getCategory()));
            listExpensesDataChild.get(transaction.getCategory()).add(transaction);
        }

        expandableExpensesListViewAdapter.notifyDataSetChanged();

                /*
        //Remove the category from the view if it has no data
        //Working through a local List to avoid ConcurrentModificationException
        List <String> localListExpensesDataGroup = new ArrayList<>();
        if(!listExpensesDataGroup.isEmpty() || listExpensesDataGroup!=null)
{        for(String group:listExpensesDataGroup){
            if(localListExpensesDataChild.containsKey(group)) {
                localListExpensesDataGroup.add(group);
            }
        }
 */

    }
}

SummaryFragment.java

package com.robin.xbudget;

import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ExpandableListView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;

import java.util.List;
import java.util.Map;

public class SummaryFragment extends Fragment {

    SummaryFragmentActivityListener listener;

    Button newIncomeGroupBtn;
    Button newExpenseGroupBtn;
    Button newIncomeTransactionBtn;
    Button newExpenseTransactionBtn;

    /**
     * Expandable views
     */
    private ExpandableListView expandableIncomesListView;
    private ExpandableListView expandableExpensesListView;

    /**
     * Static members / Constants
     */
    private static final String TAG = "XSecondActivity";

    public static SummaryFragment newInstance() {
        return new SummaryFragment();
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            listener = (SummaryFragmentActivityListener) context;
        } catch (ClassCastException cce) {
            throw new ClassCastException("class has to implement SummaryFragmentActivityListener");
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_summary, container, false);

        Log.d(TAG, "getContext onCreateView es: " + getContext());

        expandableIncomesListView = view.findViewById(R.id.expandableIncomingsListView);
        expandableExpensesListView = view.findViewById(R.id.expandableExpensesListView);

        newIncomeGroupBtn = (Button) view.findViewById(R.id.button_income_group);
        newExpenseGroupBtn = (Button) view.findViewById(R.id.button_expense_group);
        newIncomeTransactionBtn = (Button) view.findViewById(R.id.button_incoming);
        newExpenseTransactionBtn = (Button) view.findViewById(R.id.button_expense_transaction);

        // setting list adapter
        expandableIncomesListView.setAdapter(listener.getIncomesAdapter());
        expandableExpensesListView.setAdapter(listener.getExpensesAdapter());


        initListeners();

        return view;
    }


    /**
     * method to initialize listeners of the views
     */

    private void initListeners() {

        expandableExpensesListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
            @Override
            public void onGroupExpand(int groupPosition) {
                //Log.d(TAG, "Group position is: " + groupPosition);
            }
        });

        expandableIncomesListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {

            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
                //Log.d(TAG, "Group INCOMINGS position is: " + groupPosition + " and Child posititon is: " + childPosition);
                Transaction transaction = new Transaction();
                transaction.getId();
                Bundle bundle = new Bundle();

                NewTransactionIncome incomeDialog = new NewTransactionIncome();
                incomeDialog.show(getFragmentManager(), "Input new income");

                return false;
            }
        });


        expandableExpensesListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {

            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
                NewTransactionExpense expenseDialog = new NewTransactionExpense();
                Bundle bundle = new Bundle();
                bundle.putSerializable(NewTransactionExpense.ARG_EXPENSE_ID, ((Transaction) listener.getExpensesAdapter().getChild(groupPosition, childPosition)).getId());

                expenseDialog.setArguments(bundle);
                expenseDialog.show(getFragmentManager(), "Input new income");

                return false;
            }
        });


        /**
         * Initialize Button listeners
         */

        newIncomeTransactionBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(@Nullable View v) {
                NewTransactionIncome incomeDialog = new NewTransactionIncome();

            }
        });

        newIncomeGroupBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                NewCategoryIncome categoryIncomeDialog = new NewCategoryIncome();
                categoryIncomeDialog.show(getFragmentManager(), "NewIncomeCategory");
            }
        });

        newExpenseTransactionBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(@Nullable View v) {
                NewTransactionExpense expenseDialog = new NewTransactionExpense();
                Transaction transaction = new Transaction();
                Bundle bundle = new Bundle();
                bundle.putSerializable(NewTransactionExpense.ARG_EXPENSE_ID, transaction.getId());
                //expenseDialog.setArguments(bundle);
                expenseDialog.show(getFragmentManager(), "Input new expense");
            }
        });

        newExpenseGroupBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                NewCategoryExpense categoryExpenseDialog = new NewCategoryExpense();
                categoryExpenseDialog.show(getFragmentManager(), "NewExpenseCategory");
            }
        });
    }


    public interface SummaryFragmentActivityListener {
        List<Transaction> getTransactionsByTable(String table);
        Map<String, List<Transaction>> getExpensesDataChild();
        ExpandableListViewAdapter getIncomesAdapter();
        ExpandableListViewAdapter getExpensesAdapter();

        void updateCategories();
    }
}
0 Answers
Related