如何在 Android 中构建一个简单的费用计算器应用程序?
先决条件:
- 面向初学者的 Android 应用开发基础知识
- 安装和设置 Android Studio 指南
- 如何在 Android Studio 中创建/启动新项目?
- 运行您的第一个 Android 应用程序
- Android 中的 RecyclerView 示例
- Android 中的共享首选项示例
一个简单的费用计算器可让您以简化的方式添加收入和支出。这是我们将要构建的应用程序的一瞥。该应用程序包含一个带有 RecyclerView 的 Activity、两个 EditText(一个用于输入金额,另一个用于输入交易的简短说明)、1 个可单击的 TextView 以指定损失或收益、1 个可单击的图像以将交易添加到 RecyclerView,以及最后是一个自定义的 ActionBar 来显示余额。它包括在本地存储数据的共享首选项。下面给出了一个示例视频,以了解我们将在本文中做什么。请注意,我们将使用Java语言来实现这个项目。
分步实施
第 1 步:创建一个新项目
要在 Android Studio 中创建新项目,请参阅如何在 Android Studio 中创建/启动新项目。请注意,选择Java作为编程语言。
第2步:
在转到编码部分之前,让我们添加必要的依赖项。我们必须为项目添加的唯一依赖项是Gson 。它是一个Java库,可用于将Java对象转换为其 JSON 表示形式。它还可用于将 JSON字符串转换为等效的Java对象。转到应用级build.gradle文件并添加以下依赖项,然后单击立即同步。
implementation 'com.google.code.gson:gson:2.8.6'
这是一个参考,
第 3 步:
让我们添加必要的矢量资源和可绘制资源文件。转到app > res > drawable并添加以下 xml 文件。
ic_delete.xml (删除图标)
XML
XML
XML
XML
-
-
XML
XML
XML
Java
package com.cs.expensecalculator; public class TransactionClass { private int amount; private String message; private boolean positive; public TransactionClass(int amount, String message, boolean positive) { this.amount = amount; this.message = message; this.positive = positive; } public int getAmount() { return amount; } public void setAmount(int amount) { this.amount = amount; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public boolean isPositive() { return positive; } public void setPositive(boolean positive) { this.positive = positive; }}
Java
package com.cs.expensecalculator; import static com.cs.expensecalculator.MainActivity.calculateBalance;import static com.cs.expensecalculator.MainActivity.checkIfEmpty;import static com.cs.expensecalculator.MainActivity.setBalance; import android.app.AlertDialog;import android.content.Context;import android.content.DialogInterface;import android.graphics.Color;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.TextView; import androidx.annotation.NonNull;import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; public class TransactionAdapter extends RecyclerView.Adapter { Context ctx; // List containing data for recyclerview ArrayList transactionList; // Constructor for TransactionAdapter public TransactionAdapter(Context ctx, ArrayList transactionList) { this.ctx = ctx; this.transactionList = transactionList; } // On Create View Holder to Inflate transaction row layout @NonNull @Override public TransactionAdapter.TViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View v = LayoutInflater.from(ctx).inflate(R.layout.transaction_row_layout,parent,false); return new TransactionAdapter.TViewHolder(v); } @Override public void onBindViewHolder(@NonNull TransactionAdapter.TViewHolder holder, int position) { // Setting Message to a TextView in Row Layout holder.tvMessage.setText(transactionList.get(holder.getAdapterPosition()).getMessage()); // If the transaction is Positive (Received Money) set Text Color to Green if(transactionList.get(holder.getAdapterPosition()).isPositive()) { holder.tvAmount.setTextColor(Color.parseColor("#00c853")); // Setting Amount to a TextView in the row layout holder.tvAmount.setText("+₹"+Integer.toString(transactionList.get(holder.getAdapterPosition()).getAmount())); } // If the transaction is Negative (Spent Money) set Text Color to Red else { holder.tvAmount.setTextColor(Color.parseColor("#F44336")); // Setting Amount to a TextView in the row layout holder.tvAmount.setText("-₹"+Integer.toString(transactionList.get(holder.getAdapterPosition()).getAmount())); } // On Click Listener for Delete Icon holder.ivDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Confirmation Alert to delete a Transaction AlertDialog dialog = new AlertDialog.Builder(ctx) .setCancelable(false) .setTitle("Are you sure? The transaction will be deleted.") .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.dismiss(); } }) .setPositiveButton("Ok", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { transactionList.remove(holder.getAdapterPosition()); dialogInterface.dismiss(); notifyDataSetChanged(); checkIfEmpty(getItemCount()); setBalance(transactionList); } }) .create(); dialog.show(); } }); } // To get size of the list @Override public int getItemCount() { return transactionList.size(); } // View Holder for a Transaction public static class TViewHolder extends RecyclerView.ViewHolder{ TextView tvAmount,tvMessage; ImageView ivDelete; public TViewHolder(@NonNull View itemView) { super(itemView); tvAmount = itemView.findViewById(R.id.tvAmount); tvMessage = itemView.findViewById(R.id.tvMessage); ivDelete = itemView.findViewById(R.id.ivDelete); } }}
Java
package com.cs.expensecalculator; import androidx.appcompat.app.ActionBar;import androidx.appcompat.app.AppCompatActivity;import androidx.recyclerview.widget.LinearLayoutManager;import androidx.recyclerview.widget.RecyclerView; import android.content.SharedPreferences;import android.graphics.Color;import android.os.Bundle;import android.provider.CalendarContract;import android.view.LayoutInflater;import android.view.View;import android.widget.EditText;import android.widget.ImageView;import android.widget.TextView;import android.widget.Toast; import com.google.gson.Gson;import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type;import java.util.ArrayList; public class MainActivity extends AppCompatActivity { TextView tvSign; public static TextView tvEmpty, tvBalance; EditText etAmount, etMessage; ImageView ivSend; boolean positive = true; RecyclerView rvTransactions; TransactionAdapter adapter; ArrayList transactionList; // On create method @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Function to initialize views initViews(); // Function to load data from shared preferences loadData(); // Function to set custom action bar setCustomActionBar(); // To check if there is no transaction checkIfEmpty(transactionList.size()); // Initializing recycler view rvTransactions.setHasFixedSize(true); rvTransactions.setLayoutManager(new LinearLayoutManager(this)); adapter = new TransactionAdapter(this,transactionList); rvTransactions.setAdapter(adapter); // On click sign change tvSign.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { changeSign(); } }); // On click Send ivSend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Input Validation if(etAmount.getText().toString().trim().isEmpty()) { etAmount.setError("Enter Amount!"); return; } if(etMessage.getText().toString().isEmpty()) { etMessage.setError("Enter a message!"); return; } try { int amt = Integer.parseInt(etAmount.getText().toString().trim()); // Adding Transaction to recycler View sendTransaction(amt,etMessage.getText().toString().trim(),positive); checkIfEmpty(transactionList.size()); // To update Balance setBalance(transactionList); etAmount.setText(""); etMessage.setText(""); } catch (Exception e){ etAmount.setError("Amount should be integer greater than zero!"); } } }); } // To set custom action bar private void setCustomActionBar() { this.getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); getSupportActionBar().setDisplayShowCustomEnabled(true); View v = LayoutInflater.from(this).inflate(R.layout.custom_action_bar,null); // TextView to show Balance tvBalance = v.findViewById(R.id.tvBalance); // Setting balance setBalance(transactionList); getSupportActionBar().setCustomView(v); getSupportActionBar().setElevation(0); } // To set Balance along with sign (spent(-) or received(+)) public static void setBalance(ArrayList transactionList){ int bal = calculateBalance(transactionList); if(bal<0) { tvBalance.setText("- ₹"+calculateBalance(transactionList)*-1); } else { tvBalance.setText("+ ₹"+calculateBalance(transactionList)); } } // To load data from shared preference private void loadData() { SharedPreferences pref = getSharedPreferences("com.cs.ec",MODE_PRIVATE); Gson gson = new Gson(); String json = pref.getString("transactions",null); Type type = new TypeToken>(){}.getType(); if(json!=null) { transactionList=gson.fromJson(json,type); } } // To add transaction private void sendTransaction(int amt,String msg, boolean positive) { transactionList.add(new TransactionClass(amt,msg,positive)); adapter.notifyDataSetChanged(); rvTransactions.smoothScrollToPosition(transactionList.size()-1); } // Function to change sign private void changeSign() { if(positive) { tvSign.setText("-₹"); tvSign.setTextColor(Color.parseColor("#F44336")); positive = false; } else { tvSign.setText("+₹"); tvSign.setTextColor(Color.parseColor("#00c853")); positive = true; } } // To check if transaction list is empty public static void checkIfEmpty(int size) { if (size == 0) { MainActivity.tvEmpty.setVisibility(View.VISIBLE); } else { MainActivity.tvEmpty.setVisibility(View.GONE); } } // To Calculate Balance by iterating through all transactions public static int calculateBalance(ArrayList transactionList) { int bal = 0; for(TransactionClass transaction : transactionList) { if(transaction.isPositive()) { bal+=transaction.getAmount(); } else { bal-=transaction.getAmount(); } } return bal; } // Initializing Views private void initViews() { transactionList = new ArrayList(); tvSign = findViewById(R.id.tvSign); rvTransactions = findViewById(R.id.rvTransactions); etAmount = findViewById(R.id.etAmount); etMessage = findViewById(R.id.etMessage); ivSend = findViewById(R.id.ivSend); tvEmpty = findViewById(R.id.tvEmpty); } // Storing data locally // using shared preferences // in onStop() method @Override protected void onStop() { super.onStop(); SharedPreferences.Editor editor = getSharedPreferences("com.cs.ec",MODE_PRIVATE).edit(); Gson gson = new Gson(); String json = gson.toJson(transactionList); editor.putString("transactions",json); editor.apply(); }}
预览:
ic_send.xml (发送图标)
XML
预览:
ic_balance.xml (钱包图标)
XML
预览:
etbg.xml (编辑文本选择器)
XML
-
-
这是一个截图供参考。
第4步:
现在让我们为自定义的 ActionBar 和 RecyclerView 行布局添加布局资源文件。转到app > res > layout并添加以下 xml 文件。下面是custom_action_bar .xml文件的代码。
XML
预览:
下面是transaction_row_layout .xml文件的代码。 (RecyclerView 行布局)
XML
预览:
这是一个截图供参考。
第 5 步:
我们已经为我们正在构建的应用程序添加了必要的资源文件。现在,让我们为我们的应用程序设计 UI。将此 xml 文件添加到app > res > 布局。下面是activity_main.xml文件的代码。