教程链接:
添加账单页面
布局
对于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是一种可以滑动的控件。在一个屏幕显示不了全部内容时,可以使用此类,不过没有发现在这里是什么作用
使用了相对布局,而不是线性布局
<?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
对于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