1. 程式人生 > >Andorid簡單應用理財工具-實現新增賬單頁面

Andorid簡單應用理財工具-實現新增賬單頁面

教程連結:新增賬單頁面

資料庫有變更,測試之前先更新資料庫,修改了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