2012年4月23日 星期一

說說,關於文件匯出至Excel檔案中。

關於「備份」這件事情,除了雲端備份之外,我們在android中常見的還有Excel備份。
在java中備份是用到「jxl.jar」這個API,
關於這個API的下載可以參考這裡,而文件可以看這裡

網路上有非常多的人實做這個應用,今天我們來為了android來實做,
首先先來試著想想android對於備份的應用怎麼做,使用流程如下:

  1. 產生資料。
  2. 存入SQLite。
  3. 修改資料:
    1. 提取 SQLite中 對應的資料。
    2. 進行資料修改。
    3. 存入SQLite,覆蓋至對應的位置。
  4. 備份資料:
    1. 建立Excel檔案(.xls)。
    2. 建立 Excel檔案的工作表與建立第一列的欄位(通常Excel都會這樣做吧?)
    3. 提取SQLite中所有資料。
    4. 將提取出來的資料分批存入Excel檔案中。
大概是這樣,然後小書實做了一個範例,是修改之前的wifiTest所作,下載位置在這裡

要注意的是,jxl這個API裡面,.write()這個方法必須在最後調用,所以在範例中小書把他放到最後一個,寫完就關閉了這樣。
目前沒有做.xls檔案查詢的功能,有幾個理由:
  1. 要看這檔案的人,會透過M$的專門軟體來開啟,而且也已經有許多人開發了相對應的檢視軟體了。
  2. 我們對SQLite的操作比較瞭解,而這個API提供的方法不能確定其是否適合存取交替運作。
所以我們把.xls檔案的操作專注在「將SQLite的內容整理並複製置.xls檔案中」。

其實做的code如下,首先,我們必須增加能夠外部存取的權限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="exceltest.susan.idea"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name=".ExcelTestActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

接著,這是layout的內容:

<?xml version="1.0" encoding="utf-8"?>



    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <TextView
            android:id="@+id/textView"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:gravity="center"
            android:text="Test" />
    </ScrollView>


然後,我們寫入兩個class,分別是databaase和excel所封裝起來的class:


package exceltest.susan.idea;

import java.sql.Timestamp;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class DatabaseForExcelTest {

    private static final String TAG = DatabaseForExcelTest.class.getSimpleName();

    static final int VERSION = 1;
    static final String DATABASE = android.os.Environment
            .getExternalStorageDirectory().getAbsolutePath()
            + "/ExcelTest/database.db3";
    static final String TABLE = "tableOfthisDataBase";

    /**
     * "date", "bssid", "ssid","level"
     */
    public static final String[] KEY_COLUMNS = { "date", "bssid", "ssid",
            "level" };

    private static class DbHelper extends SQLiteOpenHelper {

        public DbHelper(Context context) {
            super(context, DATABASE, null, VERSION);
        }

        @Override
        // 只會被呼叫一次,在第一次資料庫建立的時候
        public void onCreate(SQLiteDatabase db) {
            Log.i(TAG, "Creating database: " + DATABASE);
            db.execSQL(getCreateSQL());
        }

        public String getCreateSQL() {
            StringBuffer mStringBuffer = new StringBuffer();
            mStringBuffer.append("CREATE TABLE IF NOT EXISTS " + TABLE + " ("
                    + "_id" + " INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
                    + KEY_COLUMNS[0]
                    + " TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,");
            for (int n = 1; n < KEY_COLUMNS.length; n++) {
                mStringBuffer.append(KEY_COLUMNS[n] + " TEXT, ");
            }
            mStringBuffer.delete(mStringBuffer.length() - 2,
                    mStringBuffer.length());// 刪除", "
            mStringBuffer.append(");");
            return mStringBuffer.toString();
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // TODO Auto-generated method stub
            db.execSQL("DROP TABLE IF EXISTS " + TABLE);
            this.onCreate(db);
        }
    }

    private final DbHelper dbHelper;
    private SQLiteDatabase db;

    public DatabaseForExcelTest(Context context) {
        dbHelper = new DbHelper(context);
        Log.i(TAG, "Initialized database in Constructor of DB");
    }

    public DatabaseForExcelTest openToWrite() throws SQLException {
        db = dbHelper.getWritableDatabase(); // 若資料庫存在則開啟;若不存在則建立一個新的
        return this;
    }

    public DatabaseForExcelTest openToRead() throws SQLException {
        db = dbHelper.getReadableDatabase(); // 若資料庫存在則開啟;若不存在則建立一個新的
        return this;
    }

    public void close() {
        dbHelper.close();
    }

    /** 添加資料 */
    public void insert(ContentValues values) throws SQLException {
        db.insertOrThrow(TABLE, null, values);
        Log.i(TAG, "insert data into db");
    }

    /**
     * 更新資料(測試)
     * 
     * @param column
     *            輸入欄位名稱
     * @param oldValue
     *            輸入欄位內欲查詢的變數
     * @param newValue
     *            輸入要更新的值
     */
    public void update(String column, String oldValue, ContentValues newValue)
            throws SQLException {
        db.update(TABLE, newValue, column + "=" + oldValue, null);
    }

    /**
     * 刪除資料(測試)
     * 
     * @param column
     *            輸入欄位名稱
     * @param value
     *            輸入欄位內欲查詢的變數
     */
    public void deleteData(String column, String value) throws SQLException {
        db.delete(TABLE, column + "=" + value, null);
    }

    /**
     * 獲得資料(測試)
     * 
     * @param column
     *            輸入欄位名稱
     * @param value
     *            輸入欄位內欲查詢的變數
     */
    public Cursor getData(String column, String value) {
        String cmd = "SELECT * FROM " + TABLE + " WHERE " + column + "="
                + value;
        return db.rawQuery(cmd, null);
    }

    /** 獲得全部資料 */
    public Cursor getAll() {
//         return db.rawQuery("SELECT * FROM " + TABLE, null);
        return db.query(TABLE, null, null, null, null, null, null);
    }

    /** 依照時間間隔獲取資料 */
    public Cursor getIntervalData(Timestamp from, Timestamp to) {
        String cmd = "SELECT * FROM " + TABLE + " WHERE (" + KEY_COLUMNS[0]
                + " >= '" + from.toString() + "') AND (" + KEY_COLUMNS[0]
                + " <= '" + to.toString() + "')";

        return db.rawQuery(cmd, null);
    }

    /** 清除全部的資料 */
    public boolean clearAll() {
        return db.delete(TABLE, null, null) > 0;
    }
    
    

}




package exceltest.susan.idea;

import java.io.File;
import java.io.IOException;

import jxl.Workbook;
import jxl.format.Colour;
import jxl.read.biff.BiffException;
import jxl.write.Label;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import jxl.write.biff.RowsExceededException;

/** 參考:http://jexcelapi.sourceforge.net/resources/javadocs/current/docs/ */
public class ExcelHelper {

    String path, fileName;
    String[] labelNames;
    WritableWorkbook mWritableWorkbook;
    Workbook mWorkbook;

    String EXCEL_PATH;

    static WritableCellFormat mWritableCellFormat;

    /** 開啟一個可寫入的excel檔案,注意最後必須調用closeExcelWritableWorkbook以完成寫入。 */
    public boolean createExcelWritableWorkbook(String path, String fileName
            ) {
        this.path = path;
        this.fileName = fileName + ".xls";
        
        EXCEL_PATH = this.path + "/" + this.fileName;
        try {
            // 打开文件
            File dir = new File(path);
            if (!dir.exists()) // true if this file exists, false otherwise.
            {
                dir.mkdir();// true if the directory has been created, false
                            // otherwise
            }// 如果資料夾不存在則創立一個資料夾..

            mWritableWorkbook = Workbook.createWorkbook(new File(EXCEL_PATH));
            return true;
        } catch (Exception e) {
            System.out.println(e);
            return false;
        }
    }

    
//    public boolean getExcelBook() {
//        
//        if(mWritableWorkbook!=null){
//            try {
//                mWritableWorkbook.close();
//            } catch (WriteException e) {
//                e.printStackTrace();
//                return false;
//            } catch (IOException e) {
//                e.printStackTrace();
//                return false;
//            }
//        }
//        
//        try {
//            mWorkbook = Workbook.getWorkbook(new File(EXCEL_PATH));
//            return true;
//        } catch (BiffException e) {
//            e.printStackTrace();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return false;
//    }
    
    /** 關閉excel檔案,很重要!最後一定要執行 */
    public boolean close() {
        if (mWritableWorkbook != null) {
            try {
                wrriteExcelWritableWorkbook();
                mWritableWorkbook.close();
                return true;
            } catch (WriteException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(mWorkbook!=null){
            mWorkbook.close();
        }
        return false;
    }

    public WritableSheet getOrCreateExcelSheet(String sheetName) {
        // 產生excel中的工作表
        if (mWritableWorkbook != null) {
            WritableSheet sheet = mWritableWorkbook.getSheet(sheetName);
            if (sheet == null) {
                sheet = mWritableWorkbook.createSheet(sheetName, 0);
            }
            return sheet;
        }
        return null;
    }
    
    /**放置文字至指定工作表中。*/
    public boolean putData(WritableSheet sheet, int c, int r,
            String lableString, WritableCellFormat mWritableCellFormat) {
        Label label;
        if (mWritableCellFormat != null) {
            label = new Label(c, r, lableString, mWritableCellFormat);
        } else {
            label = new Label(c, r, lableString);
        }
        try {
            sheet.addCell(label);

            return true;
        } catch (RowsExceededException e) {
            e.printStackTrace();

        } catch (WriteException e) {
            e.printStackTrace();
        }

        return false;
    }

    

    /**放置數字至指定工作表中。*/
    public boolean putData(WritableSheet sheet, int c, int r,
            double numberDouble, WritableCellFormat mWritableCellFormat) {
        jxl.write.Number number;
        if (mWritableCellFormat != null) {
            number = new jxl.write.Number(c, r, numberDouble,
                    mWritableCellFormat);
        } else {
            number = new jxl.write.Number(c, r, numberDouble);
        }
        try {
            sheet.addCell(number);

            return true;
        } catch (RowsExceededException e) {
            e.printStackTrace();

        } catch (WriteException e) {
            e.printStackTrace();

        }
        return false;
    }

    private boolean wrriteExcelWritableWorkbook() {
        try {
            mWritableWorkbook.write();
            return true;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 設定表格的樣式。參考:http://jexcelapi.sourceforge.net/resources/javadocs/current/
     * docs/
     */
    public static WritableCellFormat putWritableCellFormat(
            WritableFont.FontName writableFont, int paintSize, Colour mColour) {
        if (mWritableCellFormat == null) {
            mWritableCellFormat = new WritableCellFormat();
        }
        WritableFont chFont11w = new WritableFont(writableFont, paintSize);
        /*
         * WritableCellFormat cellFormat1 = new WritableCellFormat ();
         * cellFormat1.setFont(chFont11w);
         * cellFormat1.setBackground(Colour.DARK_GREEN);
         * cellFormat1.setAlignment(Alignment.CENTRE);
         * cellFormat1.setBorder(Border.ALL, BorderLineStyle.THIN,
         * Colour.GRAY_80);
         */

        try {
            chFont11w.setColour(mColour);
        } catch (WriteException e) {
            e.printStackTrace();
        }
        mWritableCellFormat.setFont(chFont11w);
        return mWritableCellFormat;
    }

}


最後,是main class,其中的流程是這樣的:

  1. 增新資料,存入SQLite。
  2. 顯示資料,這邊是多餘的動作。
  3. 將資料從SQLite讀出來,再置入Excel當中。



package exceltest.susan.idea;

//生成Excel的类
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.TimeZone;

import jxl.format.Colour;
import jxl.write.WritableFont;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.os.Bundle;
import android.widget.TextView;

public class ExcelTestActivity extends Activity {
    TextView mTextView;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mTextView = (TextView) this.findViewById(R.id.textView);
        createTestingDataToSQLite(50);//增新n筆資料
        showDataBase();//顯示資料,這邊顯示的資料應該會被蓋過去。
        saveDataToExcelFromDatabase();//儲存資料
    }

    /** 建立測試檔案 */
    private void createTestingDataToSQLite(int number) {
        DatabaseForExcelTest db = new DatabaseForExcelTest(this);
        db.openToWrite();
        db.clearAll();//先做一個清空的動作。
        Calendar c = Calendar.getInstance();
        c.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));// 設定時區為台灣時間。
        Timestamp timestamp;
        ContentValues contentValues = new ContentValues();
        for (int n = 0; n < number; n++) {
            timestamp = new Timestamp(c.getTimeInMillis());// 先行建立,於輸入database時置入
            putDatabaseValue(db, contentValues, timestamp,
                    "BSSID" + n, "SSID" + n, "LEVEL" + n);
        }
        db.close();
    }

    /** 顯示database中的檔案內容 */
    private void showDataBase() {
        DatabaseForExcelTest db = new DatabaseForExcelTest(this);
        db.openToRead();
        Cursor mCursor = db.getAll();
        mCursor.moveToFirst();
        do {
            mTextView.append("顯示database資訊:");
            mTextView
                    .append("\nBSSID:"
                            + mCursor.getString(mCursor
                                    .getColumnIndex(DatabaseForExcelTest.KEY_COLUMNS[1])));
            mTextView
                    .append("\nSSID:"
                            + mCursor.getString(mCursor
                                    .getColumnIndex(DatabaseForExcelTest.KEY_COLUMNS[2])));
            mTextView
                    .append("\nLEVEL:"
                            + mCursor.getString(mCursor
                                    .getColumnIndex(DatabaseForExcelTest.KEY_COLUMNS[3])));
        } while (mCursor.moveToNext());

        db.close();
    }

    /** 顯示database中的檔案內容 */
    private void saveDataToExcelFromDatabase() {
        mTextView.setText("儲存資料...");
        final String path = android.os.Environment
                .getExternalStorageDirectory().getAbsolutePath() + "/ExcelTest";// 路徑名稱
        final String name = "ExcelTest";// 檔案名稱,不用加上副檔名
        final String nameOfSheet = "第一頁";// 工作表名稱
        ExcelHelper mExcelHelper = new ExcelHelper();
        if (mExcelHelper.createExcelWritableWorkbook(path, name)) {//如果開啟成功
            for (int n = 0; n < DatabaseForExcelTest.KEY_COLUMNS.length-1; n++) {
                mExcelHelper.putData(mExcelHelper
                        .getOrCreateExcelSheet(nameOfSheet), n, 0,
                        DatabaseForExcelTest.KEY_COLUMNS[n+1], ExcelHelper
                                .putWritableCellFormat(WritableFont.TIMES, 16,
                                        Colour.BLACK));
                //這個for裡面,注意為了不要獲取time的資料欄位所做了+1-1等更動。
            }
        }
        //打開database
        DatabaseForExcelTest db = new DatabaseForExcelTest(this);
        db.openToRead();
        Cursor mCursor = db.getAll();//獲得所有內容
        mCursor.moveToFirst();
        String[] showInfo = { "\nBSID:", "\nSSID:", "\nLEVEL:" };
        int inSheetRow=1;//因為第0列顯示的是欄位名稱
        do {
            mTextView.append("\n顯示database資訊:");
            for (int n = 0; n < DatabaseForExcelTest.KEY_COLUMNS.length-1; n++) {
                //顯示資訊
                mTextView
                        .append(showInfo[n]
                                + mCursor.getString(mCursor
                                        .getColumnIndex(DatabaseForExcelTest.KEY_COLUMNS[n+1])));
                //將資訊儲存至excel
                mExcelHelper
                        .putData(
                                mExcelHelper.getOrCreateExcelSheet(nameOfSheet),
                                n,
                                inSheetRow,
                                mCursor.getString(mCursor
                                        .getColumnIndex(DatabaseForExcelTest.KEY_COLUMNS[n+1])),
                                ExcelHelper.putWritableCellFormat(
                                        WritableFont.TIMES, 30, Colour.BLACK));
                //這個for裡面,注意為了不要獲取time的資料欄位所做了+1-1等更動。
            }
            inSheetRow++;//移到下一個row
        } while (mCursor.moveToNext());
        
        mExcelHelper.close();//寫入,關閉excel。
        mTextView.append("\n完畢!");
        db.close();
    }

    /**添加一筆資料至database*/
    private void putDatabaseValue(DatabaseForExcelTest database,
            ContentValues contentValues, Timestamp timestamp, String bssid,
            String ssid, String level) {

//        contentValues.clear();
        contentValues.put(DatabaseForExcelTest.KEY_COLUMNS[0],
                timestamp.toString());
        contentValues.put(DatabaseForExcelTest.KEY_COLUMNS[1], bssid);
        contentValues.put(DatabaseForExcelTest.KEY_COLUMNS[2], ssid);
        contentValues.put(DatabaseForExcelTest.KEY_COLUMNS[3], level);
        database.insert(contentValues);
    }

}


其檔案可以從SD卡的ExcelTest資料夾中找到.db3和.xls檔。




7 則留言:

  1. 您好
    請問用您的ExcelTest.apk就可以將資料匯出至exceltest內
    但是用上面的程式...就會顯示很抱歉...已停止...就不能執行了...請問這是什麼地方我沒有注意到

    回覆刪除
  2. 那可能是被我玩壞了。XD

    你可以依照下面步驟檢查:
    1.專案裡面的libs有沒有jxl.jar檔
    2.檢查專專案沒有寫入讀取sd卡的權限
    3.檢查看有沒有lose掉上面有的程式碼

    如果是程式碼直接copy的話,沒有error的話,
    可能就要請你給我看一下logcat顯示的錯誤訊息是什麼了。

    回覆刪除
    回覆
    1. 謝謝您的回覆

      1.有的(用您的連結下戴)
      2.這樣子對嗎?
      在AndroidManifest.xml內
      android:versionName="1.0" android:installLocation="auto">
      3.只有第二個與您不同
      第一個是AndroidManifest.xml
      第二個是layout->activity_main.xml....這兒與您不同
      第三個是DatabaseForExcelTest.java
      第四個是ExcelHelper.java
      第五個是ExcelTestActivity.java
      網頁上面共提供了五個

      我試了三次都是這樣子
      顯示很抱歉...已停止...就不能執行了
      所以才很甩冒昧請教您

      刪除
    2. 我想...可以給我看error 的log訊息嗎??
      可能要知道比較詳細的訊息會有辦法debug
      話說..
      我剛剛想到, libs的jxl.jar有import進去嗎??

      刪除
    3. 您好:
      我是自行將jxl.jar檔案直接copy到libs內
      您說的import是如何操作才是
      感謝您的回覆

      刪除
    4. 有機會應該來做一篇imprort的方法,謝謝你的提醒。

      你可以先看這個:
      http://cw1057.blogspot.tw/2011/12/android-jar-3rd-party-library.html

      希望對你有幫助。

      刪除
    5. 我做了一份整理:
      http://androidsusan.blogspot.tw/2013/04/import-jar.html

      希望對你有幫助。

      刪除

你好,我是小書,如果文章內容有錯誤,或是看到有建議以及任何感想時,歡迎提出分享,我們一起學習一起努力。

追蹤者