Android中Spinner选年月从SQLite列取值求和的问题排查
解决Android中Spinner选择年月后求和并显示的问题
先看你遇到的核心错误:SQL语法错误,同时数据库操作方法和Activity的事件监听也存在问题,下面一步步帮你修复:
1. 修复SQL语句的语法错误
错误日志里的SQL SELECT CAST(SUM(Jumlah)as Double) FROM tb_periode WHERE strftime('%Y',2010) AND strftime('%M,'01) 有几个关键问题:
- strftime用法错误:strftime的第二个参数应该是表中的日期字段(比如你的
AwalPeriode或AkhirPeriode),而不是直接传年份/月份值 - 月份格式符错误:
%M是分钟,正确的两位月份格式符是%m - WHERE条件逻辑错误:需要明确匹配年月等于选中值,应该用
=连接,同时用参数化查询避免语法错误和SQL注入 - String转数值求和:
COLUMN_JUMLAH是String类型,求和前必须转成数值类型(比如REAL)
2. 修正DatabaseOperations中的方法
修复getAllPeriode方法
你的getAllPeriode方法把cursor.close()放在了while循环内部,导致第一次循环就关闭游标,只能拿到第一条数据,移到循环外面即可:
public ArrayList<Periode> getAllPeriode(){ Cursor cursor = database.query(TABLE_PERIODE, allColumns, null,null,null,null,null); ArrayList<Periode> periodes = new ArrayList<>(); cursor.moveToFirst(); while (!cursor.isAfterLast()){ Periode periode = cursorToPeriode(cursor); periodes.add(periode); cursor.moveToNext(); } cursor.close(); // 移到循环外面 return periodes; }
重写addAllValues方法
完全重构这个方法,正确编写SQL并处理参数:
public double calculateTotalByMonthYear(String bulan, String tahun){ double total = 0.0; // 假设你的日期字段是AwalPeriode,你可以根据实际换成AkhirPeriode String sql = "SELECT SUM(CAST(" + COLUMN_JUMLAH + " AS REAL)) FROM " + TABLE_PERIODE + " WHERE strftime('%Y', " + COLUMN_AWAL_PERIODE + ") = ? " + " AND strftime('%m', " + COLUMN_AWAL_PERIODE + ") = ?"; // 注意:这里要确保COLUMN_AWAL_PERIODE是你表中存储日期的列名,比如"AwalPeriode" Cursor c = database.rawQuery(sql, new String[]{tahun, bulan}); if(c.moveToFirst()){ // 用getDouble避免精度丢失 total = c.getDouble(0); } c.close(); return total; }
注意:如果你的
AwalPeriode存储的不是SQLite支持的日期格式(比如YYYY-MM-DD),strftime可能无法正确解析,需要先确保日期格式符合要求。
3. 修正HitungFinalBulanActivity的逻辑
添加Spinner选择监听
你现在在onCreate里直接调用求和方法,只会执行一次,用户切换Spinner不会更新结果,需要添加OnItemSelectedListener:
public class HitungFinalBulanActivity extends AppCompatActivity { Spinner MonthSpinner; Spinner YearSpinner; TextView totalJumlah; private DatabaseOperations transaksiOperations; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_hitung_final_bulan); transaksiOperations = new DatabaseOperations(this); transaksiOperations.Open(); MonthSpinner = findViewById(R.id.monthSpinner); YearSpinner = findViewById(R.id.yearspinner); totalJumlah = findViewById(R.id.txtViewJumlah); // 初始化月份Spinner ArrayAdapter<CharSequence> MonthAdapter = ArrayAdapter.createFromResource(this, R.array.months_array, android.R.layout.simple_spinner_item); MonthAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); MonthSpinner.setAdapter(MonthAdapter); // 初始化年份Spinner ArrayList<String> years = new ArrayList<String>(); int thisYear = Calendar.getInstance().get(Calendar.YEAR); for (int i = 2010; i <= thisYear; i++) { years.add(Integer.toString(i)); } ArrayAdapter<String> YearAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, years); YearSpinner.setAdapter(YearAdapter); // 添加选择监听 MonthSpinner.setOnItemSelectedListener(spinnerListener); YearSpinner.setOnItemSelectedListener(spinnerListener); // 初始化时计算一次默认选中值的总和 calculateAndDisplayTotal(); } // 共享的Spinner监听 private AdapterView.OnItemSelectedListener spinnerListener = new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { calculateAndDisplayTotal(); } @Override public void onNothingSelected(AdapterView<?> parent) { // 可选:处理未选中的情况 } }; // 计算并显示总和的方法 private void calculateAndDisplayTotal(){ String selectedMonth = MonthSpinner.getSelectedItem().toString(); String selectedYear = YearSpinner.getSelectedItem().toString(); // 注意:如果你的months_array是月份名称(比如"January"),需要转成两位数字("01"),否则strftime无法匹配 // 这里假设你的months_array是两位数字格式,比如"01","02"...,如果是名称需要额外转换 double total = transaksiOperations.calculateTotalByMonthYear(selectedMonth, selectedYear); // 格式化显示结果,保留两位小数 totalJumlah.setText(String.format("Total: %.2f", total)); } @Override protected void onDestroy() { super.onDestroy(); // 关闭数据库连接,避免内存泄漏 transaksiOperations.close(); } }
注意事项:月份格式转换
如果你的months_array是月份名称(比如"January"、"February"),需要把选中的月份名称转换成两位数字(比如"01"、"02")才能和strftime的%m匹配,你可以添加一个转换方法:
private String convertMonthNameToNumber(String monthName){ // 根据你的months_array顺序对应数字 String[] months = getResources().getStringArray(R.array.months_array); for(int i=0; i<months.length; i++){ if(months[i].equals(monthName)){ // 转成两位数字,比如1→"01" return String.format("%02d", i+1); } } return "01"; // 默认返回1月 }
然后在calculateAndDisplayTotal里调用:
String selectedMonth = convertMonthNameToNumber(MonthSpinner.getSelectedItem().toString());
4. 其他潜在问题修复
- 你的
getJumlahPeriode方法的query参数错误,第三个参数是selection,你传了COLUMN_JUMLAH,会导致SQL语法错误,如果这个方法是用来获取所有Jumlah记录,可以改成:
public ArrayList<Periode> getJumlahPeriode(){ Cursor cursor = database.query(TABLE_PERIODE, null, null, null,null,null,null); ArrayList<Periode> periodes = new ArrayList<>(); cursor.moveToFirst(); while (!cursor.isAfterLast()){ Periode periode = cursorToPeriode(cursor); periodes.add(periode); cursor.moveToNext(); } cursor.close(); return periodes; }
这样修改后,应该就能正确根据Spinner选择的年月求和并显示结果了。
内容的提问来源于stack exchange,提问作者XVallerie
相关产品推荐
相关产品推荐

