Andorid簡單應用理財工具-實現新增賬單頁面
阿新 • • 發佈:2019-02-14
教程連結:新增賬單頁面
佈局
對於DatePicker沒有怎麼用過,特意去網上找了幾篇介紹
Android DatePickerDialog用法
android java獲取當前時間的總結
java中時間24小時和12小時設定
Form
將表單ui和表單資料獲取的部分,封裝了FORM類進行操作,便於將其他邏輯分離
SimpleCursorAdapter
這個構造器已被標記為棄用(@Deprecated) 。
該方法不推薦使用,Cursor查詢操作是執行在應用程式的UI執行緒當中,那麼會導致無響應的情況。
Model
所有原始碼可以到github上下載,地址:https://github.com/lazyboywu/diligentpiggy/tree/course_4
資料庫有變更,測試之前先更新資料庫,修改了DBHelper.java中
public static void createTable_bills(SQLiteDatabase db) { try { db.execSQL("CREATE TABLE bills(" +" id INTEGER PRIMARY KEY, " +" acctitemid INTEGER," +" fee INTEGER, " +" userid INTEGER, " +" date TEXT, " +" time TEXT, " +" desc TEXT " +");"); Log.v(LOG_TAG, "Create table bills ok"); } catch(Exception e) { Log.v(LOG_TAG, "Create table bills err, table exists"); } }
佈局
ScrollView是一種可以滑動的控制元件。在一個螢幕顯示不了全部內容時,可以使用此類,不過沒有發現在這裡是什麼作用
使用了相對佈局,而不是線性佈局
DatePicker<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:id="@+id/layout" android:layout_width="match_parent" android:layout_height="wrap_content"> <!-- 賬目設定 --> <TextView android:id="@+id/bill_acctitem_tips" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/bill_acctitem_tips" android:minWidth="80dp" android:textAppearance="?android:attr/textAppearanceLarge"/> <EditText android:id="@+id/bill_acctitem_input" android:layout_width="wrap_content" android:layout_height="wrap_content" android:width="200dp" android:hint="" android:maxLines="1" android:inputType="none" android:cursorVisible="false" android:layout_toRightOf="@id/bill_acctitem_tips"/> <!-- 分割線 --> <View android:id="@+id/bill_divider_1" android:layout_width="match_parent" android:layout_height="1dp" android:background="?android:attr/listDivider" android:layout_below="@id/bill_acctitem_input"/> <!-- 費用設定 --> <TextView android:id="@+id/bill_fee_tips" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/bill_fee_tips" android:minWidth="80dp" android:layout_below="@id/bill_divider_1" android:textAppearance="?android:attr/textAppearanceLarge"/> <EditText android:id="@+id/bill_fee_input" android:layout_width="160dp" android:layout_height="wrap_content" android:hint="@string/bill_fee_hint" android:maxLines="1" android:layout_toRightOf="@id/bill_fee_tips" android:layout_below="@id/bill_divider_1"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/bill_fee_after_tips" android:layout_toRightOf="@id/bill_fee_input" android:layout_below="@id/bill_divider_1" android:textAppearance="?android:attr/textAppearanceLarge"/> <!-- 分割線 --> <View android:id="@+id/bill_divider_2" android:layout_width="match_parent" android:layout_height="1dp" android:background="?android:attr/listDivider" android:layout_below="@id/bill_fee_input"/> <!-- 時間設定 --> <TextView android:id="@+id/bill_date_tips" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/bill_date_tips" android:height="24dp" android:fadingEdge="horizontal" android:drawablePadding="2dp" android:layout_below="@id/bill_divider_2"/> <TextView android:id="@+id/bill_date_input" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="120dp" android:layout_below="@id/bill_date_tips" android:textAppearance="?android:attr/textAppearanceLarge"/> <Button android:id="@+id/bill_date_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:width="30dp" android:height="30dp" android:text="@string/bill_date_btn_tips" android:layout_toRightOf="@id/bill_date_input" android:layout_below="@id/bill_date_tips"/> <TextView android:id="@+id/bill_time_input" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="80dp" android:layout_below="@id/bill_date_tips" android:layout_toRightOf="@id/bill_date_btn" android:textAppearance="?android:attr/textAppearanceLarge"/> <Button android:id="@+id/bill_time_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="30dp" android:height="30dp" android:text="@string/bill_date_btn_tips" android:layout_toRightOf="@id/bill_time_input" android:layout_below="@id/bill_date_tips"/> <!-- 分割線 --> <View android:id="@+id/bill_divider_3" android:layout_width="match_parent" android:layout_height="1dp" android:background="?android:attr/listDivider" android:layout_below="@id/bill_time_btn"/> <!-- 賬目型別設定 --> <TextView android:id="@+id/bill_user_tips" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/bill_user_tips" android:minWidth="80dp" android:textAppearance="?android:attr/textAppearanceLarge" android:layout_below="@id/bill_divider_3"/> <Spinner android:id="@+id/bill_user_input" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="200dp" android:layout_toRightOf="@id/bill_user_tips" android:layout_below="@id/bill_divider_3"/> <!-- 分割線 --> <View android:id="@+id/bill_divider_4" android:layout_width="match_parent" android:layout_height="1dp" android:background="?android:attr/listDivider" android:layout_below="@id/bill_user_input"/> <!-- 備註設定 --> <TextView android:id="@+id/bill_desc_tips" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/bill_desc_tips" android:height="24dp" android:fadingEdge="horizontal" android:drawablePadding="2dp" android:layout_below="@id/bill_divider_4"/> <EditText android:id="@+id/bill_desc_input" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="" android:lines="4" android:gravity="top" android:layout_below="@id/bill_desc_tips"/> <!-- 分割線 --> <View android:id="@+id/bill_divider_5" android:layout_width="match_parent" android:layout_height="1dp" android:background="?android:attr/listDivider" android:layout_below="@id/bill_desc_input"/> <!-- 按鈕設定 --> <Button android:id="@+id/bill_save_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:width="160dp" android:text="@string/bill_save_btn" android:textAppearance="?android:attr/textAppearanceLarge" android:layout_below="@id/bill_divider_5"/> <Button android:id="@+id/bill_cancel_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:width="160dp" android:text="@string/bill_cancel_btn" android:textAppearance="?android:attr/textAppearanceLarge" android:layout_below="@id/bill_divider_5" android:layout_alignParentRight="true"/> <!-- 分割線 --> <View android:id="@+id/bill_divider_6" android:layout_width="match_parent" android:layout_height="1dp" android:background="?android:attr/listDivider" android:layout_alignParentBottom="true"/> </RelativeLayout> </ScrollView>
對於DatePicker沒有怎麼用過,特意去網上找了幾篇介紹
Android DatePickerDialog用法
android java獲取當前時間的總結
java中時間24小時和12小時設定
Form
將表單ui和表單資料獲取的部分,封裝了FORM類進行操作,便於將其他邏輯分離
SimpleCursorAdapter
這個構造器已被標記為棄用(@Deprecated) 。
該方法不推薦使用,Cursor查詢操作是執行在應用程式的UI執行緒當中,那麼會導致無響應的情況。
所以使用了android.support.v4.widget.SimpleCursorAdapter
主程式
package com.example.diligentpiggy;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;
public class AddBill extends Activity {
public static final String LOG_TAG = "AddBill";
public static final int BILL_CALL_ACCITTEM = 1;
public static final int MENU_BILL_DETAIL = 1;
public static final int MENU_BILL_TOTAL = 2;
public static final int MENU_BILL_REPORT = 3;
public static final int MENU_BILL_QUIT = 4;
FORM form;
BillModel billModel;
private Cursor cursor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle((String)getString(R.string.app_name)+"-新增賬單");
setContentView(R.layout.add_bill);
billModel = new BillModel(this);
form = new FORM(this);
form.init();
cursor = billModel.getUserCursor();
form.initUserView(cursor);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_BILL_DETAIL, 0, "賬目明細").setIcon(R.drawable.bill_detail);
menu.add(0, MENU_BILL_TOTAL, 0, "賬目統計").setIcon(R.drawable.bill_total);
menu.add(0, MENU_BILL_REPORT, 0, "賬目報表").setIcon(R.drawable.bill_report);
menu.add(0, MENU_BILL_QUIT, 0, "退出").setIcon(R.drawable.quit);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch(item.getItemId()) {
case MENU_BILL_DETAIL:
return true;
case MENU_BILL_TOTAL:
return true;
case MENU_BILL_REPORT:
return true;
case MENU_BILL_QUIT:
confirmQuit();
return true;
}
return true;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch(keyCode) {
case KeyEvent.KEYCODE_BACK:
confirmQuit();
return true;
}
return false;
}
public void confirmQuit() {
AlertDialog.Builder confirm = new AlertDialog.Builder(this);
// 繫結ui內容
confirm.setTitle("提示");
confirm.setMessage("確認退出?");
confirm.setIcon(R.drawable.quit);
// 繫結按鈕
confirm.setPositiveButton("確定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
billModel.close();
finish();
}
});
confirm.setNegativeButton("取消", (DialogInterface.OnClickListener)null);
confirm.show();
}
private void startAcctitemSelector() {
Intent intent = new Intent(this, AcctitemSelector.class);
startActivityForResult(intent, BILL_CALL_ACCITTEM);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
switch(resultCode) {
case RESULT_OK:
// 獲取AcctitemSelector返回的結果
Bundle bundle = new Bundle();
bundle = intent.getExtras(); // 獲取返回結果
form.setAcctitem(bundle.getString(AcctitemSelector.RESULT_ACCITTEM_NAME),
bundle.getString(AcctitemSelector.RESULT_ACCITTEM_ID));
break;
}
}
protected void saveData() {
int acctitemId = form.getAcctitem();
if(acctitemId == -1) {
new AlertDialog.Builder(this)
.setMessage("請首先選擇賬目!")
.show();
return;
}
Log.v(LOG_TAG, "save start:");
BillModel.Bill bill = billModel.createBill();
bill.setAcctitemId(acctitemId);
bill.setFee(form.getFee());
bill.setUserId(form.getUser());
bill.setDate(form.getDate());
bill.setTime(form.getTime());
bill.setDesc(form.getDesc());
if(billModel.insert(bill)) {
Toast.makeText(this, "儲存成功", Toast.LENGTH_SHORT).show();
form.reset();
} else {
Toast.makeText(this, "儲存失敗,請檢查資料。 ", Toast.LENGTH_SHORT).show();
}
Log.v(LOG_TAG, "save end");
}
/**
* 封裝成一個表單操作
*/
public class FORM implements OnClickListener {
int acctitemId = -1;
EditText acctitemInput, descInput, feeInput;
TextView dateInput, timeInput;
Spinner userInput;
Button saveBtn, cancelBtn, dateBtn, timeBtn;
Context context;
DateStuffer dateStuffer;
public static final int DIALOG_SELECT_DATA = 1;
public static final int DIALOG_SELECT_TIME = 2;
DBHelper dbHelper;
public FORM(Context context) {
this.context = context;
}
// 表單初始化
public void init() {
dateStuffer = new DateStuffer();
bindView();
updateDateView();
}
private void bindView() {
acctitemInput = (EditText)findViewById(R.id.bill_acctitem_input);
// 繫結按鈕功能
acctitemInput.setOnClickListener(this);
feeInput = (EditText)findViewById(R.id.bill_fee_input);
dateInput = (TextView)findViewById(R.id.bill_date_input);
dateBtn = (Button)findViewById(R.id.bill_date_btn);
timeInput = (TextView)findViewById(R.id.bill_time_input);
timeBtn = (Button)findViewById(R.id.bill_time_btn);
// 繫結按鈕功能
dateBtn.setOnClickListener(this);
timeBtn.setOnClickListener(this);
userInput = (Spinner)findViewById(R.id.bill_user_input);
descInput = (EditText)findViewById(R.id.bill_desc_input);
saveBtn = (Button)findViewById(R.id.bill_save_btn);
cancelBtn = (Button)findViewById(R.id.bill_cancel_btn);
// 繫結按鈕功能
saveBtn.setOnClickListener(this);
cancelBtn.setOnClickListener(this);
}
private void updateDateView() {
dateInput.setText(dateStuffer.getDateText());
timeInput.setText(dateStuffer.getTimeText());
}
public void initUserView(Cursor cursor) {
SimpleCursorAdapter adapter = new SimpleCursorAdapter(context,
android.R.layout.simple_spinner_item,
cursor,
new String[] {"caption"},
new int[] {android.R.id.text1},
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
userInput.setAdapter(adapter);
}
public int getAcctitem() {
return acctitemId;
}
public void setAcctitem(String name, String id) {
acctitemInput.setText(name);
acctitemId = Integer.parseInt(id);
Log.v(LOG_TAG, "acctitem name = "+name+", id = "+id);
}
public int getFee() {
int result = 0;
String fee = feeInput.getText().toString();
int pos = fee.indexOf(".");
if(pos > 0) {
// 修正 . .x .xx 的正確補位
String fix_zero = "";
for(int i = fee.length() - pos; i < 3; i++) {
fix_zero += "0";
}
fee = fee + fix_zero;
result = Integer.parseInt(fee.substring(0, pos) + fee.substring(pos+1, pos+3));
} else {
result = Integer.parseInt(fee) * 100;
}
return result;
}
public int getUser() {
return (int)userInput.getSelectedItemId();
}
public String getDate() {
return dateInput.getText().toString();
}
public String getTime() {
return timeInput.getText().toString();
}
public String getDesc() {
return descInput.getText().toString();
}
@Override
public void onClick(View v) {
// 賬目選擇觸發
if(v.equals(acctitemInput)) {
// 其實這裡想做一個回撥來著,- -
startAcctitemSelector();
}
// 時間選取按鈕觸發
if(v.equals(dateBtn)) {
createDialog(DIALOG_SELECT_DATA).show();
} else if(v.equals(timeBtn)) {
createDialog(DIALOG_SELECT_TIME).show();
}
// 儲存和取消按鈕
if(v.equals(saveBtn)) {
Log.v(LOG_TAG,"u put save btn");
saveData();
} else if(v.equals(cancelBtn)) {
Log.v(LOG_TAG,"u put cancel btn");
reset();
}
}
private Dialog createDialog(int type) {
switch(type) {
case DIALOG_SELECT_DATA:
return new DatePickerDialog(context,
dateSetListener,
dateStuffer.getData(Calendar.YEAR),
dateStuffer.getData(Calendar.MONTH),
dateStuffer.getData(Calendar.DAY_OF_MONTH));
case DIALOG_SELECT_TIME:
return new TimePickerDialog(context,
timeSetListener,
dateStuffer.getData(Calendar.HOUR_OF_DAY),
dateStuffer.getData(Calendar.MINUTE),
false);
}
return null;
}
private void reset() {
acctitemId = -1;
acctitemInput.setText("");
feeInput.setText("");
dateStuffer.reset();
updateDateView();
userInput.setSelection(0);
descInput.setText("");
}
private DatePickerDialog.OnDateSetListener dateSetListener =
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear,
int dayOfMonth) {
dateStuffer.setDate(year, monthOfYear, dayOfMonth);
updateDateView();
}
};
private TimePickerDialog.OnTimeSetListener timeSetListener =
new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay,
int minute) {
dateStuffer.setTime(hourOfDay, minute);
updateDateView();
}
};
/**
* 封裝form用到的日期時間填充資料
*/
private class DateStuffer {
Calendar calendar;
SimpleDateFormat dateFormat;
SimpleDateFormat timeFormat;
public DateStuffer() {
dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
timeFormat = new SimpleDateFormat("HH:mm", Locale.CHINA);
timeFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
reset();
}
public void reset() {
calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"));
}
public int getData(int type) {
return calendar.get(type);
}
public String getDateText() {
//Log.v(LOG_TAG, "DateStuffer set string="+calendar.getTime().toString());
return dateFormat.format(calendar.getTime());
}
public void setDate(int year, int month, int day) {
//Log.v(LOG_TAG, "DateStuffer set year="+year+", month="+month+", day="+day);
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
}
public String getTimeText() {
//Log.v(LOG_TAG, "DateStuffer set string="+calendar.getTime().toString());
return timeFormat.format(calendar.getTime());
}
public void setTime(int hour, int minute) {
//Log.v(LOG_TAG, "DateStuffer set hour="+hour+", minute="+minute);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
}
}
}
}
Model
package com.example.diligentpiggy;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
public class BillModel {
private DBHelper dbHelper;
private SQLiteDatabase db;
private static final String TABLE = "bills";
public BillModel(Context c) {
dbHelper = new DBHelper(c);
db = dbHelper.getDB();
}
public Cursor getUserCursor() {
return dbHelper.getUserCursor();
}
public Boolean insert(Bill bill) {
ContentValues cv = new ContentValues();
cv.put("acctitemid", bill.getAcctitemId());
cv.put("fee", bill.getFee());
cv.put("userid", bill.getUserId());
cv.put("date", bill.getDate());
cv.put("time", bill.getTime());
cv.put("desc", bill.getDesc());
try {
db.insert(TABLE, null, cv);
return true;
} catch(Exception e) {
Log.v("BillModel", "insert : " + e.getMessage());
return false;
}
}
public Boolean update(Bill bill) {
ContentValues cv = new ContentValues();
cv.put("acctitemid", bill.getAcctitemId());
cv.put("fee", bill.getFee());
cv.put("userid", bill.getUserId());
cv.put("date", bill.getDate());
cv.put("time", bill.getTime());
cv.put("desc", bill.getDesc());
try {
db.update(TABLE, cv, "id=?", new String[] {""+bill.getID()});
return true;
} catch(Exception e) {
Log.v("BillModel", "update : " + e.getMessage());
return false;
}
}
public Boolean delete(Bill bill) {
try {
db.delete(TABLE, "id=?", new String[] {""+bill.getID()});
return true;
} catch(Exception e) {
Log.v("BillModel", "delete : " + e.getMessage());
return false;
}
}
public void close() {
dbHelper.close();
}
public Bill createBill() {
return new Bill();
}
public class Bill {
private int id;
private int acctitemId;
private int fee;
private int userId;
private String date;
private String time;
private String desc;
public void setID(int id) {
this.id = id;
}
public int getID() {
return id;
}
public void setAcctitemId(int acctitemId) {
this.acctitemId = acctitemId;
}
public int getAcctitemId() {
return acctitemId;
}
public void setFee(int fee) {
this.fee = fee;
}
public int getFee() {
return fee;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getUserId() {
return userId;
}
public void setDate(String date) {
this.date = date;
}
public String getDate() {
return date;
}
public void setTime(String time) {
this.time = time;
}
public String getTime() {
return time;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
}
所有原始碼可以到github上下載,地址:https://github.com/lazyboywu/diligentpiggy/tree/course_4