在 Android 中使用 SQLite 資料庫

Android - SQLite

在 Android 中使用 SQLite 資料庫來儲存資料。

SQLite簡介

之前介紹的資料儲存方式之一是使用 Preference 來做少量資料的儲存,Preference採用Key-Value的方式來儲存,取資料的時候必須以Key去取得,若資料量大時,並不適合,這時候可以使用關聯式資料庫來做大量結構化資料的儲存。

SQLite是個輕量化的關聯式資料庫,它輕薄短小,無須設定或管理,沒有伺服器及組態檔,不需要資料庫管理員,它只是一個檔案,可以依須求四處移動,對移動裝置來說是非常好用的資料庫。Android把SQLite資料庫儲存在 /data/data/packagename/databases 目錄中,可以使用指令adb或ADT中的FileExplorer視圖來查看。

資料庫在Android中的位置

當你的App建立時,若沒有使用到資料庫,在/data/data/packagename目錄中並不會有databases目錄,如下圖: 當你開始使用資料庫後,資料庫目錄及檔案就會被建立,如下圖:

SQLite使用重點

這裡假設你已經有使用SQL語法及資料庫管理的經驗,這裡只說明在Android中使用SQLite的注意事項。當你在建立資料表時,通常第一個資料欄位會命名為_id,並指定為主鍵,而且使用AUTOINCREMENT自動遞增1。這部份我在測試的時候,把主鍵命名為id,結果在取資料時,會出現找不到_id的錯誤,把主鍵名稱改為_id就正常了,所以應該還是依規定設為_id。

在使用SQLite時,你並不用先去建立資料庫檔案,而由Android幫你建立。你必須繼承SQLiteOpenHelper類別,由它來負責管理資料庫的建立和版本控制。你必須覆寫onCreate及onUpgrade方法,當你第一次使用資料庫時(查詢或新增),若Android找不到資料庫檔案,就會觸發onCreate方法來建立,若已經有檔案了,則不再建立。當資料庫結構有更新時,即版本號有更改,就會觸發onUpgrade方法來更新資料庫,可以砍掉重建,也可直接更新結構。

在程式碼中,對於資料表欄位可以直接用字串寫在SQL語法中,可是一來可能誤打,當欄位要改名時,也必須一個一個去修改,不安全也沒效率。你可以繼承BaseColumns介面,把資料表中的欄位以常數來建立,使用上較方便。使用時記得要先import進來。此外,在BaseColumns中不需再建立_id,因為BaseColumns介面已經預設有了,請看API。

你也可以把資料和view做資料繫結,直接讓查詢結果顯示在view中。

範例執行結果及說明

  • SQLiteDemoActivity.java:主要的顯示畫面,上方有4個文字輸入區,id只有在update及delete時才需輸入。中間有3個按鈕,而別是Add(新增)、Update(修改)、Delete(刪除),下方有2個結果顯示區,上面那個以迴圈取得資料後顯示,下方的ListView則是以資料繫結的方式顯示。
  • DBHelper.java :用來管理資料庫的建立及更新。
  • DbConstants.java :資料庫欄位的常數值。

範例程式碼

SQLiteDemoActivity.java

package com.tonycube.demo;

import static android.provider.BaseColumns._ID;
import static com.tonycube.demo.DbConstants.EMAIL;
import static com.tonycube.demo.DbConstants.NAME;
import static com.tonycube.demo.DbConstants.TABLE_NAME;
import static com.tonycube.demo.DbConstants.TEL;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;

public class SQLiteDemoActivity extends Activity implements OnClickListener {

    private DBHelper dbhelper = null;

    private TextView result = null;
    private ListView listData = null;
    private EditText editName = null;
    private EditText editTel = null;
    private EditText editEmail = null;
    private EditText editId = null;
    private Button btnAdd = null;
    private Button btnDel = null;
    private Button btnUpdate = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        initView();

        openDatabase();
        show();
        showInList();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        closeDatabase();
    }

    private void openDatabase(){
        dbhelper = new DBHelper(this); 
    }

    private void closeDatabase(){
        dbhelper.close();
    }

    private void initView(){
        result = (TextView) findViewById(R.id.txtResult);
        listData = (ListView) findViewById(R.id.listData);
        editName = (EditText) findViewById(R.id.editName);
        editTel = (EditText) findViewById(R.id.editTel);
        editEmail = (EditText) findViewById(R.id.editEmail);
        editId = (EditText) findViewById(R.id.editId);
        btnAdd = (Button) findViewById(R.id.btnAdd);
        btnDel = (Button) findViewById(R.id.btnDel);
        btnUpdate = (Button) findViewById(R.id.btnUpdate);
        btnAdd.setOnClickListener(this);
        btnDel.setOnClickListener(this);
        btnUpdate.setOnClickListener(this);        
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.btnAdd:
            add();
            break;

        case R.id.btnDel:
            del();
            break;

        case R.id.btnUpdate:
            update();
            break;

        default:
            break;
        }

        show();
        showInList();
    }

    private void add(){
        SQLiteDatabase db = dbhelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(NAME, editName.getText().toString());
        values.put(TEL, editTel.getText().toString());
        values.put(EMAIL, editEmail.getText().toString());
        db.insert(TABLE_NAME, null, values);

        cleanEditText();
    }

    private Cursor getCursor(){
        SQLiteDatabase db = dbhelper.getReadableDatabase();
        String[] columns = {_ID, NAME, TEL, EMAIL};

        Cursor cursor = db.query(TABLE_NAME, columns, null, null, null, null, null);
        startManagingCursor(cursor);

        return cursor;
    }

    private void show(){

        Cursor cursor = getCursor();

        StringBuilder resultData = new StringBuilder("RESULT: \n");

        while(cursor.moveToNext()){
            int id = cursor.getInt(0);
            String name = cursor.getString(1);
            String tel = cursor.getString(2);
            String email = cursor.getString(3);

            resultData.append(id).append(": ");
            resultData.append(name).append(": ");
            resultData.append(tel).append(": ");
            resultData.append(email).append(": ");
            resultData.append("\n");
        }

        result.setText(resultData);
    }

    private void showInList(){

        Cursor cursor = getCursor();

        String[] from = {_ID, NAME, TEL, EMAIL};
        int[] to = {R.id.txtId, R.id.txtName, R.id.txtTel, R.id.txtEmail};

        SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.data_item, cursor, from, to);
        listData.setAdapter(adapter);
    }

    private void del(){
        String id = editId.getText().toString();

        SQLiteDatabase db = dbhelper.getWritableDatabase();
        db.delete(TABLE_NAME, _ID + "=" + id, null);

        cleanEditText();
    }

    private void update(){
        String id = editId.getText().toString();

        ContentValues values = new ContentValues();
        values.put(NAME, editName.getText().toString());
        values.put(TEL, editTel.getText().toString());
        values.put(EMAIL, editEmail.getText().toString());

        SQLiteDatabase db = dbhelper.getWritableDatabase();
        db.update(TABLE_NAME, values, _ID + "=" + id, null);

        cleanEditText();
    }

    private void cleanEditText(){
        editName.setText("");
        editTel.setText("");
        editEmail.setText("");
        editId.setText("");
    }

}

DBHelper.java

package com.tonycube.demo;

import static com.tonycube.demo.DbConstants.TABLE_NAME;
import static android.provider.BaseColumns._ID;
import static com.tonycube.demo.DbConstants.NAME;
import static com.tonycube.demo.DbConstants.TEL;
import static com.tonycube.demo.DbConstants.EMAIL;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper {

    private final static String DATABASE_NAME = "demo.db";
    private final static int DATABASE_VERSION = 1;

    public DBHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        final String INIT_TABLE = "CREATE TABLE " + TABLE_NAME + " (" +
                                  _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                                  NAME + " CHAR, " +
                                  TEL + " CHAR, " +
                                  EMAIL + " CHAR);"; 
        db.execSQL(INIT_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLE_NAME;
        db.execSQL(DROP_TABLE);
        onCreate(db);
    }

}

DbConstants.java

package com.tonycube.demo;

import android.provider.BaseColumns;

public interface DbConstants extends BaseColumns {
    public static final String TABLE_NAME = "friends";

    public static final String NAME = "name";
    public static final String TEL = "tel";
    public static final String EMAIL = "email";
}
範例下載:SQLiteDemo.zip(直接匯入Eclipse即可)
本文網址:https://blog.tonycube.com/2011/11/androidsqlite.html
Tony Blog 撰寫,請勿全文複製,轉載時請註明出處及連結,謝謝 😀

172 則留言

  1. 你好,請問一下我有參考您的這篇文章自己寫一個程式,其中list顯示部分跟您的差不多但是我只要加上showInList();執行時就會立即出錯,但不知是哪裡程式有寫錯,導致無法成功執行,只要加上showInList();這個方法,應該是在這個方法顯示宣告部分哪裡有錯

    回覆刪除
    回覆
    1. 你可在DDMS中看到錯誤,或是反正showInList()中也才5行,你可以全部註解掉,從第1行移除註解,執行,看有沒有錯誤,沒有的話再移除1、2行,依序全部移除,就會知道是錯在哪行了。

      我猜可能是SimpleCursorAdapter的參數有錯,檢查一下layout,from,to這幾個部份看看~~~

      刪除
  2. 您好,想請問一下,假如若想要在實際的平板上執行,要把db複製過去平板上的sdcard,那段程式碼要獨立寫在一個java檔裡,還是寫在某個檔案的程式瑪之下就好嗎?

    回覆刪除
    回覆
    1. 你的意思是指把DB存放在SDcard嗎?這我沒試過,不過幫你找到答案,如下

      http://stackoverflow.com/questions/3436434/is-it-possible-to-move-the-internal-db-to-the-sdcard

      應該是要用
      SQLiteDatabase.openDatabase(資料庫在SDcard的路徑, null, SQLiteDatabase.CREATE_IF_NECESSARY);

      這段程式碼來開啟資料庫,然後它就會在你指定的路徑開啟或建立了,實際怎麼做你可能要自己實驗看看~~~ :)

      刪除
  3. 想請問
    我拿到程式碼時 是有錯誤的
    我把 @Override
    public void onClick(View v)
    上面的@Override移除掉 就可以執行
    但是del 跟 update都會無法使用 而跳出程式
    感覺跟被刪除的@Override有關
    會是甚麼一回事啊?

    回覆刪除
    回覆
    1. 移掉 @Override 才能執行,表示你的 onClick() 方法沒有可以 override 的父類別或介面中的方法,請確定你的類別有 implements OnClickListener 。

      使用DDMS你才能知道錯誤發生在哪裡,不然就要"猜猜看"了,
      如果add正常,而del和update會發生錯誤,那表示
      dbhelper.getWritableDatabase()正常、資料庫欄位的取得也正常,
      它們的差別只在del和update中有用到「_ID」 ,所以很可能是錯在這裡,
      可以檢查看看是不是這裡有錯誤。

      但還是真的要去看DDMS比較好,和Android玩1A2B太耗時了 :)

      刪除
    2. 好像有很多人遇到同樣問題,在此回答目前找到的原因,問題不在程式碼,而在於JDK的版本

      在討論區找到的回答,如下:
      You should be using java 1.6 when building android projects. Java 1.7 isn't compatible with the current set of Android tools. Change your projects to use 1.6 and this problem should go away

      In eclipse: Right click project -> properties -> Java Compiler -> switch all references to 1.7 to 1.6

      資料來源:http://stackoverflow.com/questions/9703380/why-i-cant-use-override-annotations-at-android

      看來是JDK1.7對ADT的支援還不夠完整,選擇使用1.6版就可以了~~

      刪除
  4. 想請問...
    如何在同一個"demo.db"
    內建立兩張資料表?

    回覆刪除
    回覆
    1. 在DBHelper中的onCreate()
      有個字串是用來建資料表的,如下:

      final String INIT_TABLE = "CREATE TABLE " ...略
      ...
      db.execSQL(INIT_TABLE);

      重覆以上動作就可建立多個資料表。

      建立資料表的SQL語法,請看 http://www.1keydata.com/tw/sql/sqlcreate.html

      刪除
  5. 您好
    我有照者你的程式執行
    成功了
    但是在沒有輸入任何資料的情況下
    按update 或 delete 鍵
    則會出現錯誤
    我要如何才能讓他出現~列如(無資料可更新 或 無資料可刪除)
    謝謝

    回覆刪除
    回覆
    1. 這裡並不是無資料可更新或刪除的問題,你在ID欄位輸入任意"數字",都不會出現錯誤,程式會依你的要求去資料庫中找到該ID的那筆資料來刪除或更新,如果資料庫是空的,它只會當成是找不到這個ID的資料,回傳0表示什麼資料列都沒有被影響。

      所以問題是出在輸入的資料,當你"沒有輸入資料"的時候,其實在程式碼中還是有資料,只是它是空字串,請看這行程式碼:

      String id = editId.getText().toString();

      當你editId欄位沒有輸入任何東西時,id的值會是""(空字串),送進SQL字串中就會出錯,所以你要做的事是判斷id是否為正整數(通常ID是自動增加的正整數,這裡使用字串是為了給SQL字串使用),除了符合可送進SQL字串的資料外,一律不處理,或出現錯誤訊息。

      至於出現錯誤提醒的方式,大概就兩種 AlertDialog 或 Toast,自己選擇吧~~~

      刪除
    2. 感謝大大指點

      我繼續努力用功

      刪除
  6. 作者已經移除這則留言。

    回覆刪除
  7. 妳好 想請問一下
    在一開始匯入時他會出現
    Invalid project description.
    D:\android\workspace\SQLiteDemo overlaps the location of another project: 'SQLiteDemo'
    她字面上是說有重複的項目 但有看過此資料夾裡並無重複名稱
    想請問一下為何無法匯入

    回覆刪除
    回覆
    1. 可能是你先把 SQLiteDemo 資料夾放在 workspace 裡所發生的錯誤,
      可以這麼做,先把 SQLiteDemo 放在 workspace 目錄之外的其他地方,
      使用 File->Import->Existing Android Code Into Workspace,
      將"Copy projects into workspace"打勾,應該就可以了,
      再把放在 workspace 之外的那個目錄刪掉。

      **記得先檢查 workspace 裡面沒有 SQLiteDemo 資料夾。

      刪除
    2. 謝謝你的指點
      放在以外的資料夾就不會有錯誤了
      我當初竟然沒想到這麼做...
      感謝TONY大的指點

      刪除
  8. 作者已經移除這則留言。

    回覆刪除
  9. 你好~~
    請問這個SQLite是任何一個安卓系統版本解可以使用嗎?
    如果可以想請教如果主頁點進去後才是使用者輸入的介面,該如何著手更改程式?
    麻煩Tony大大

    回覆刪除
    回覆
    1. 是的,Android 已內建 SQLite 資料庫。

      主頁是一個 Activity ,輸入介面是另一另個 Activity
      你可以把下面 "RESULT" 文字框的顯示放在 MainActivity ,只單純顯示資料庫的內容;InputActivity 則是輸入框用來寫入資料庫。

      刪除
    2. 謝謝Tony大大,我會繼續努力的

      刪除
  10. 請問一下
    寫入的資料儲存在哪
    可以打開看嗎

    回覆刪除
    回覆
    1. 文章中已經有寫囉

      可以用 adb 進入資料庫目錄,看到資料庫後輸入
      sqlite3 demo.db
      前面是指令, 後面是你的資料庫名稱
      進入後提示字元會變成 sqlite>
      就可以下sql指令了,結尾要加分號;

      刪除
  11. 請問setContentView(R.layout.main);
    的main是代表什麼?

    回覆刪除
    回覆
    1. 還有後面SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.data_item, cursor, from, to);
      中的data_item

      刪除
    2. 我找到了 不過再請問一下 只有一個顯示介面為何您的layout有兩個數值?

      刪除
    3. 一個是主畫面的 layout,
      一個是 ListView 列的 layout, 給 Adpater 使用。

      刪除
  12. 作者已經移除這則留言。

    回覆刪除
  13. 下方的ListView則是以資料繫結的方式顯示。
    請問為何我的會連表格都會跟著顯示?
    如何修改才能變成單純的顯示資料

    回覆刪除
    回覆
    1. 我不知道你說的表格是指什麼,
      你只要修改 R.layout.data_item 就能改變 ListView 中每一列的內容想要呈現的方式。

      刪除
  14. 那請問tata_item裡面放的是什麼?

    回覆刪除
    回覆
    1. layout
      其實你問這個問題,表示你應該去找一本入門書來看,上面的寫的很詳細。

      刪除
    2. 我大概知道layout是介面
      不過我不太了解data_item這麼介面該放什麼呢?

      刪除
    3. 看你的資料要如何呈現,就怎麼做這個列的layout,
      像這個範例就是放了姓名、電話、信箱等等的資料,可以說等同於資料庫中的一筆資料。

      刪除
    4. 作者已經移除這則留言。

      刪除
    5. 作者已經移除這則留言。

      刪除
  15. 請問一下
    如何開起db的檔案
    檔案室沒有存在電腦是否?
    當我開著模擬器時我有在eclipse ddms找到自己的db
    但一關掉就沒有了
    如何存在電腦裡
    感謝您!!

    回覆刪除
    回覆
    1. 在DDMS視圖中,選擇"File Explorer"頁籤(有個小綠人圖示,如果沒看到,可以在選單Window->Show View中找到)

      你的檔案存在 "data/data/你的套件名稱/databases/你的資料庫名稱.db"
      *套件名稱就是你App的Package名稱
      *資料庫名稱是你在程式中指定的名稱

      找到檔案後,先選擇資料庫檔案,然後按"File Explorer"視窗上方的工具列的"磁片"(pull a file from the device),就可以下載到電腦

      以上動作全部可以用 adb pull 來辦到喔~~

      刪除
  16. 不好意思請問一下 為什麼我現在在製作
    activity_main.XML的時候
    我拉出來的東西在右下角都會有一個黃色小三角形@@?
    然後
    SQLiteDemoActivity.java的
    38、149行會顯示錯誤
    115行顯示警告
    麻煩Tony大大指點迷津

    回覆刪除
    回覆
    1. 在程式碼頁面,把滑鼠指到"黃色警告"或"紅色錯誤"的地方,編輯器會告訴你是什麼問題

      刪除
    2. 按鈕上面的都是[I18N] Hardcoded string "照著程式修改的名稱:", should use @string resource

      紅色的都是data_item 無法解析或不是欄位

      115行的是
      Activity 類型的 startManagingCursor(Cursor) 方法即將棄用

      這些我在GOOGLE 上都沒辦法找到答案,不知道是不是我的基本功不夠
      才會出現這些問題
      謝謝大大:D

      刪除
    3. Hardcoded string 的意思是,你把字串"寫死"在元件上,例如 xml 中的TextView 的 text 屬性,通常來說,如果你的 text 值是程式碼中動態給的,那這裡出現的警告就可以不理它,但如果是直接用來顯示的值,就"建議"把它移到字串資源檔中,這樣在做多語言版本時,才能夠自動去取得相對應的語言文字。總之這只是警告,執行上並不會出錯。

      data_item 無法解析或不是欄位,這不太清楚是什麼問題,請將滑鼠指到 R.layout.data_item,的 R 上,看出現的 Package 是不是自己的 Package 名稱,而不是 android.R 。另外在選單中執行 Project -> Clean 清除看看會不會正常。

      startManagingCursor(Cursor) 方法即將棄用,查 API 是說,在 API 11 之後就被捨棄了,意思就是要換別的方法來做,文件中有說要換成哪個類別來執行,可以看一下。






      刪除
    4. Tony大大

      文件上面是說叫我用LoaderManager和CursorLoader
      這個來做 那如果具體要修改我應該要怎麼做呢@@

      R.layout.data_item 上面R 出現的Package 是我自己的Package名稱沒錯
      而且按清除也沒有用 那還有解決的辦法嗎?

      刪除
    5. 這個我也沒實際用過,你要自己研究看看。Google一下應該有範例。

      檢查看看 res/layout 之下有沒有 data_item.xml ,如果有還出現問題,那我也不知道了...

      刪除
    6. 作者已經移除這則留言。

      刪除
    7. Tony大大 我剛剛看了一下在res/layout真的沒有data_item.xml
      而且在存放資料庫的地方/data/data/packagename/databases
      沒有看到我的資料庫 這個當打開第一個/data之後 後面就沒東西了

      刪除
    8. 應該是沒有 root 權限,輸入 ls 要看東西, 有出現 Permission denied 什麼的訊息嗎?

      模擬器應該是可以進入,手機的話要看有沒有 root。

      刪除
    9. Tony大大 我剛剛試了一下
      我現在只剩下那個要改LoaderManager和CursorLoader
      跟你說的ROOT權限,那個部分我有點聽不懂
      其他的部分都已經正常了

      刪除
  17. 老師你好 請問一下
    迴圈顯示資料的那個資料表
    會一直依序ID顯示下去嗎?
    還是一次只有一筆資料?

    回覆刪除
    回覆
    1. 當你第一次呼叫 cursor.moveToNext() 的時候會取得資料集的第一列,
      cursor.moveToNext() 一次只會取一列資料,
      因為用 while ,所以再次執行 cursor.moveToNext() 就會取第二列,
      依序取到最後一列,之後因為沒資料了,就會回傳 false 跳離迴圈。

      刪除
  18. Tony大大你好
    可不可以請你也分享篇
    SQL搜尋的方法
    目前能寫出資料庫也可使用
    可是我想增加一個搜尋的欄位
    可供使用者去尋找資料
    EX:聯絡人資料搜尋
    該如何去實作
    可以請Tony大大指點一下嗎@@"?

    回覆刪除
    回覆
    1. 把SQL語法學會了你就知道怎麼做了
      http://www.1keydata.com/tw/sql/sqlwhere.html

      例如:SELECT * FROM constact WHERE name='Tony';
      你做一個文字輸入框讓使用者輸入,你的程式會去取得這個輸入的資料(人名),
      填入以上SQL語法的Tony的位置,就會回傳該連絡人的資料給你。

      好好研究一下 SELECT 吧~~~

      刪除
  19. 感謝Tony
    我回去試試看
    請問有無其他管道可以聯絡你
    像是信箱還是FB等等的
    我想多像你詢問一下andorid的東西
    如果不方便也沒關西:"3

    回覆刪除
    回覆
    1. 嗯...其實我時間不夠用了,看我部落格停了那麼久都沒新文章就知道,有什麼就在這問吧,我也只能盡量回:)

      刪除
  20. Tony大 您好
    請問:
    說明startManagingCursor(Cursor) 方法即將棄用 的文件
    是在哪裡可以找得到呢?

    可不可解釋一下startManagingCursor的用法呢?
    謝謝

    回覆刪除
  21. 在這
    http://developer.android.com/reference/android/app/Activity.html#startManagingCursor(android.database.Cursor)

    API Reference 就是程式設計師的說明書,類別或方法是做什麼用的,上面都寫得很清楚,建議還是要自己看一下,先知道它能做什麼,如果不知道怎麼用,再去找範例或教學。

    如果英文不是那麼好,可以查,看久了就懂了,程式常用的單字重覆性很高。

    找資料的搜尋關鍵字可以用
    example, sample, tutorial, 範例, 教學...

    例如:startManagingCursor(Cursor)的替代方法CursorLoader,
    可以搜尋 CursorLoader + example
    就可以找到官方教學 https://developer.android.com/training/load-data-background/setup-loader.html

    試試看吧~~~




    回覆刪除
    回覆
    1. 謝謝Tony大的回覆!
      有問題再請教您 :D

      謝謝唷~!!

      刪除
  22. Tony大~
    現在我有一個問題

    我想要把listview放在另外一個xml裡
    showlist()要怎麼改呢?

    回覆刪除
    回覆
    1. 另外一個xml?是指另外一個Activity嗎?
      如果是的話,只要把ListView相關的程式碼+資料庫相關的程式碼+showInList()這個方法,全部移植過去,應該就可以了吧~~

      刪除
    2. 那在showInList()這個方法中
      有個int[] to = {R.id.txtId, R.id.txtName, R.id.txtTel, R.id.txtEmail};
      這個txtId是什麼啊?

      還有是不是一定要有id
      才能讀取資料庫的值並放到listview上呢?

      謝謝~~

      刪除
    3. 在layout xml中,某個文字輸入框的id設定為 @+id/txtId,
      要取得這個 View 的資源碼,會寫成 R.id.txtId

      要讀取某個 view 會用R.id.xxx
      layout => R.layout.xxx
      drawable => R.drawable.xxx
      可以看這個
      http://developer.android.com/guide/topics/ui/overview.html

      http://developer.android.com/guide/topics/resources/accessing-resources.html

      刪除
    4. 為什麼Tony大要把資料show在listview裡
      卻要to到textview裡呢??
      int[] to = {R.id.txtId, R.id.txtName, R.id.txtTel, R.id.txtEmail};

      刪除
    5. 還有我想個問題
      我現在輸入資料在A頁
      但是我想要把資料從資料庫裡抓出來show在B頁
      要怎麼做呢?
      謝謝tony大~

      刪除
    6. 試過了整個複製過去不行QQ

      刪除
    7. 因為這是範例,示範如何將資料顯示在TextView及ListView的方法。

      現在這個範例是:寫入資料庫,從資料庫取出資料來顯示。
      範例中已經把方法寫的很清楚了,有add也有show,你應該知道該怎麼做:)

      建議你先弄懂每行程式碼是在做什麼的,你得知道你做的每個部驟是在做什麼、會發生什麼事,不然你這樣怎麼也寫不出來。

      刪除
  23. 作者已經移除這則留言。

    回覆刪除
  24. 前幾天提問關於其他app程式如何找到***.db並讀取資料,還沒看到Tony大大的解答.
    從書上看到(我不確定)如要供其他程式存取的話,需以Content Provider方式去建資料庫,
    但外部程式該如何去call它? 請大大幫忙,謝謝.

    回覆刪除
    回覆
    1. 沒看到你的問題啊!?

      如果要讓其他 App 來存取你的 App 的資料,是用 Content Provider 提供方法讓其他 App 來用,當你建立並註冊一個 Content Provider 後,會有一個類似網址的東西,例如: content://com.tonycube.app/xxxx
      其他 App 利用 ContentResolver 去解析它,接著就能用你提供的方法來操作資料。

      你看的書上應該會寫到。以下是官方的說明
      http://developer.android.com/guide/topics/providers/content-providers.html

      或是這裡有人寫的中文教學
      http://blog.changyy.org/2012/05/android-app-content-provider.html

      刪除
    2. 感謝Tony的解答, 我會再用ContentProvider方是再練習一次,
      並試試外部程式的呼叫是否可行! 感恩.

      刪除
    3. 作者已經移除這則留言。

      刪除
  25. 你好,我想詢問如果在版大提供的程式碼裡,想新增三個欄位,是否是改
    DbConstants.java 裡的欄位數;
    DBHelper.java裡增加 import 和 INIT_TABLE 增加 那三個欄位;

    SQLiteDemoActivity.java這要怎麼修改呢?只要在原本就有的欄位在新增那三個就好了嗎?





    回覆刪除
  26. 全對,你已經把答案說出來了,
    基本上,DBConstants是用來讓你避免打錯欄位,而設一個固定名稱來存取,
    要增加欄位,就是依現在的程式碼,再增加而以,沒什麼特別的地方。

    回覆刪除
    回覆
    1. 感謝回覆,

      另問我與版大的專案版本不同,將java檔移置入我的專案裡,會有出現startManagingCursor(刪除線)的情形,請問這要怎麼修改呢?

      刪除
    2. 那是表示這個方法要被棄用了,你滑鼠指上去會有說明。

      這篇文章的其他人的回覆裡我有稍為提到,你可以按Ctrl+F叫出瀏覽器的搜尋,
      搜尋 startManagingCursor 這段文字,就可以找到其他人的留言及回覆。

      這篇文章是2011寫的,有點舊了,很多人提到這個問題,該是要找個時間來寫個新版的教學....

      刪除
  27. 請問一下 當我輸入 sqlite3 demo.db 進去之後 輸入.tables 想查看當前table 卻出現 ubable to open database "demo.db":unable to open database file 這是權限問題嗎? 要怎麼修改

    回覆刪除
    回覆
    1. 這我也不清楚,如果你的adb shell 進得到資料庫目錄,那應該就不是權限問題,你可以直接下 SQL 的 select 去查詢資料看看,還是 .tables 後面接資料表的名稱看會不會出現資料,還是直接 .dump 把資料庫整個倒出來看看。

      刪除
  28. 請問一下 我下載下來直接匯入 只有ADD按鈕可以用 其兩個按鈕按下去 都會出現unfortunately, SQLiteDemo has stopped 這個問題要怎麼解決

    回覆刪除
    回覆
    1. ID欄位你有輸入嗎?
      下面 RESULT:
      7:Tony:....
      那個 7 就是ID

      另外,看DDMS它會告訴你哪一行程式有出錯

      刪除
  29. 大大不好意思喔,請問一下,我自己有創一個資料庫,已經把CSV檔匯入
    這樣我要怎麼樣,才能直接匯入資料庫,把資料庫的資料顯示在手機畫面上,
    並在以後能加入查詢的功能@@

    回覆刪除
    回覆
    1. 沒做過,試看看這個
      http://stackoverflow.com/questions/7162194/how-to-download-an-sqlite-database-from-an-android-device

      刪除
  30. 老師您好;老師你知道有哪邊有教學 從sqlite取出裡面已建好的 經度 緯度,然後算出
    2點間的距離教學嗎?

    回覆刪除
    回覆
    1. 從 sqlite 取資料就用 sql 語法 select 去取,
      計算距離就用距離公式,請參考
      http://www.movable-type.co.uk/scripts/latlong.html
      不過這是兩點間的直線距離,
      如果要計算路徑,要再去找有沒有人提供API

      刪除
    2. 老師您好:若我資料庫表格名稱式 abc 經度欄位叫做 latitude 緯度叫做longitude 我要呼叫出來運算的 sclect 語法 老師可以請您提供一下嗎? 3Q

      刪除
    3. http://www.1keydata.com/tw/sql/sqlselect.html

      刪除
  31. Tony 您好,我想請問如何將資料庫裡的數值相加 (單純數字加減)

    回覆刪除
    回覆
    1. http://www.1keydata.com/tw/sql/sql-sum.html

      刪除
  32. TONY 您好,我有一個問題..就是在重新安裝這個SQLiteDemo在模擬器時,demo.db都會清空一次 重新輸入資料..可不可以在安裝的時候就可以將準備好的demo.db直接安裝在data/data/package/databases而不是再開空的demo.db?就是說我安裝後一開就可以看到準備好的demo.db的資料 謝謝!

    回覆刪除
    回覆
    1. 我原本是覺得不行,但還是查了一下,發現有一個取巧的方法,
      你可以看這篇
      http://stackoverflow.com/questions/513084/how-to-ship-an-android-application-with-a-database
      最下面有打綠色勾勾的回答是可以行的方法。

      方法是,把預先建好的資料庫放在 assets 資料夾,這樣當 buile App 時,會同時把資料庫包在 App 裡,然後在第一次執行時,把這個資料庫複製到 App 放資料的目錄中,這樣就能達到你的要求,試試看吧~~

      刪除
  33. 你好:
    我想要請問一下,要從SQLITE的資料庫中取出同一欄位(staff)中多筆的值,但是每一個值想要呈現於不同的EditText,要怎麼寫迴圈?
    public ArrayList getstaff(String equipment){
    SQLiteDatabase db = getReadableDatabase();
    String sql = "SELECT staff FROM " + TABLE_NAME +
    " WHERE equipment LIKE ?";
    String[] args = {"%" + equipment + "%"};
    Cursor cursor = db.rawQuery(sql, args);
    ArrayList staff = new ArrayList();
    int columnCount = cursor.getColumnCount();
    while(cursor.moveToNext()){
    String equipment_name1 ="";
    for(int i=0; i<columnCount; i++)
    equipment_name1 += cursor.getString(i) +"\n";
    staff.add(equipment_name1);
    }
    cursor.close();
    db.close();
    return staff;
    }

    回覆刪除
    回覆
    1. 假設你有一個 layout 叫做 "@+id/layParent"
      它裡面有 7 個 EditText。

      程式碼中有一個資料陣列
      private ArrayList staff;

      我把它塞假的值
      private void initData() {
      staff = new ArrayList();
      staff.add("A");
      staff.add("B");
      staff.add("C");
      staff.add("D");
      staff.add("E");
      staff.add("F");
      staff.add("G");
      }
      這部份如同你的程式碼中的資料庫的部份,接下來把值放進 EditText 中

      private void setEditTextValue(){

      ViewGroup layParent = (ViewGroup) findViewById(R.id.layParent);
      int count = layParent.getChildCount();
      if (count > 0) {
      for (int i = 0; i < count; i++) {
      EditText edt = (EditText) layParent.getChildAt(i);
      edt.setText(staff.get(i));
      }
      }
      }

      用 getChildAt 去做即可,如果你的 layout 中混著其他不是 EditText 的元件,就必須用 if( layParent.getChildAt(i) instanceof EditText ) 這樣去判斷是不是 EditText。

      刪除
    2. 謝謝你唷^^已經解決了 好感謝~~~

      刪除
  34. 您好,我成功新增到資料庫了,但我目前遇到的問題是:查詢日期(Date1)後,帶出金額(SumSubTotal1),而該金額在帶出時是要加總所有在該日期之下的所有金額(SumSubTotal1):以下是我目前寫的內容,但在SQL那邊不會下語法,因此想請問您要怎麼寫比較好呢? 謝謝您^^。

    public ArrayList getSumSubTotal1(String Date1){
    SQLiteDatabase db = getReadableDatabase();
    String sql = "SELECT SumSubTotal1 FROM " + TABLE_NAME +
    " WHERE Date1 LIKE ?";
    String[] args = {"%" + Date1 + "%"};
    Cursor cursor = db.rawQuery(sql, args);
    ArrayList Add = new ArrayList();
    int columnCount = cursor.getColumnCount();

    while(cursor.moveToNext()){
    int date1_name1 =0;
    for(int i=0; i<columnCount; i++)
    date1_name1 += cursor.getInt(i) +"\n";
    Add.add(date1_name1);
    }
    cursor.close();
    db.close();
    return Add;
    }

    回覆刪除
    回覆
    1. 用sum(http://www.1keydata.com/tw/sql/sql-sum.html)
      會像這樣
      select sum(SumSubTotal1) from TABLE_NAME where date1='2014-01-01';

      sum() 條件的輸出,只會有一筆資料。

      刪除
    2. 謝謝你的快速回覆,成功了 原來是在SumSubTotal1前加上sum,耶耶!!
      非常~非常~的感謝您^^ Orz

      刪除
  35. 您好,我有個問題想提問 ~
    我已在onCreate中建立5筆資料在資料庫中,透過軟體去查看也有成功寫入。但是之後再多寫第6筆資料,透過軟體查看卻沒有第6筆資料。在File Explorer 查看後,也發現databases中的更新時時間,是第一次建立5筆資料的時間。我也在onUpgrade中寫砍掉重建的相關程式碼。請問有什麼問題嗎? 感謝您 ~

    回覆刪除
    回覆
    1. 你的第6筆資料寫入的程式碼有和前面5筆的的一樣嗎,
      檢查看看DDMS有沒有什麼錯誤訊息,

      onUpgrade 只有在資料庫的版本號有變動時才會執行,
      所以除非你有去變動版本號,不然這部份可以忽略。

      刪除
  36. 你好,我想問我匯出了DB檔案,改了資料,但放回去eclipse,但是java指令碼沒有改過??
    http://ppt.cc/cut/preview.php?img=images/790d55062c35e60adc7a58a5a79f72042eb0d31c.jpg&x=0&y=0&w=925&h=548&thumb=1&resize=50

    回覆刪除
    回覆
    1. 這樣應該是不行的(也不好),因為之後如果你要發佈,
      你這樣加入資料庫中的資料還是會不見,
      如果你只是單純要修改資料,就要在 Android 中做,

      如果你是想放預先準備好的資料,可以有兩種做法:
      1.在我的範例的 DBHelper 中的 onCreate,
      在 "Create ..." (SQL語法) 建立資料庫後,
      繼續 "Insert ... "(SQL語法) 新增你所要的所有預設資料。

      2.或是把預先建立好的資料庫檔(例如: abc.db)存放在 "assets"資料夾,
      在使用者第一次打開App時,複製到手機中。可以參考 (http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/)

      刪除
  37. 作者已經移除這則留言。

    回覆刪除
  38. 請問如果要同步資料庫,那甚麼方法比較好??

    回覆刪除
    回覆
    1. 假設你在遠端的資料要送到 Android 裝置,必須透過網路,
      所以你要做的事是,
      裝置向伺服器請求資料,伺服器送出資料,裝置接收資料並儲存到資料庫,
      之後使用者就可以透過存取資料庫來讀寫資料。

      但這樣會稱為"更新資料",而不是"同步",同步的意思是
      A的變動,同時更新到B;B的變動,也會同時更新到A,
      兩邊的資料會在某一個時間點一模一樣。

      刪除
    2. 以下是個人做法

      線上先決:
      先行取得資料庫版本,確認資料庫版本是否相符
      然後再下將資料寫入sqlite

      程式先決:
      向線上提出資料庫版本更新,提出資料
      線上更新

      連線時用AsyncTask
      http://blog.tonycube.com/2011/08/asynctask.html#more

      本地及線上都使用json交換資料

      刪除
  39. 你好,請問點擊新增後LogCat會出現"table sitesInfo has no column named time"的錯誤訊息,該如何修正?是單筆儲存資料不能太多?(約160個字)
    (此錯誤訊息只是第一筆,後面還有一大串錯誤訊息,不知是哪裡出錯@@[初學者])

    回覆刪除
    回覆
    1. 其實錯誤訊息就跟你說了,
      有一個叫 sitesInfo 的資料表(table) 沒有一個叫做 time 的欄位名稱。

      檢查一下你的資料表欄位吧~~

      刪除
    2. 我檢查過了~程式碼裡面有建那個欄位
      但就一直出現這個錯誤訊息
      我手機沒改機無法看到資料庫長什麼樣子@@

      刪除
    3. 用模擬器,就可以查看資料庫了,
      time 應該不是關鍵字,但可能是名稱的問題,
      可以改個名稱試試看,例如 "publish_time",
      也有可能你建資料表的SQL下錯了,
      所以資料表長得和你想的不一樣,
      總之,還是要能直接查看資料庫,
      才能明確知道到底是什麼原因。

      刪除
    4. 感謝您,後來修正後並且改資料庫版本後,欄位就正確了,資料能成功儲存了!!

      刪除
  40. 你好,我想問,如果放密碼,資料庫是明碼還是暗碼,如果是暗碼,怎改為用暗碼,或必須辨別身份,才能連到DB?

    回覆刪除
    回覆
    1. 沒做任何處理當然是明碼,
      如果要加密,可以用SHA1 或 MD5,
      把密碼加密後,再存入資料庫,
      做法可以參考
      http://karanbalkar.com/2013/05/tutorial-28-implement-sha1-and-md5-hashing-in-android/

      刪除
  41. 作者已經移除這則留言。

    回覆刪除
    回覆
    1. 要看DDMS,會有錯誤訊息告訴你出錯在哪一行程式碼

      刪除
  42. 作者已經移除這則留言。

    回覆刪除
    回覆
    1. 參考這篇
      http://stackoverflow.com/questions/11194515/android-get-value-of-the-selected-radio-button
      先檢查 RadioGroup 裡面有哪個項目被選擇了,在去把那個項目取出來。

      刪除
  43. 作者已經移除這則留言。

    回覆刪除
  44. 想請問一下 假設現在可以儲存四個輸入值 如果要再多儲存一個輸入值 是要在DbConstants和DBHelper新增後 我在底下新增value.put(KIND......這段的輸入值寫不進資料庫也無法讀
    請問怎麼解決 謝謝
    SQLiteDatabase db = dbhelper.getWritableDatabase();
    ContentValues values = new ContentValues();
    values.put(NAME, editName.getText().toString());
    values.put(PRICE, editprice.getText().toString());
    values.put(DATE, etdate.getText().toString());
    values.put(KIND, tvkind.getText().toString());
    db.insert(TABLE_NAME, null, values);

    回覆刪除
    回覆
    1. 你有修改 DBHelper 的 DATABASE_VERSION 嗎?
      數字依序往上累加。

      同一支App,在資料庫第一次建立後,
      如果沒有更新版本號,
      它的欄位是不會更動的,
      每次當你更改版本號後,它會先執行 onUpgrade,
      再執行 onCreate,
      這個範例是在 onUpgrade 時把資料表整個刪掉,
      在 onCreate 時重建,
      實務上不一定會這麼做,可能只是修改資料表欄位。

      在實務上可能還要做資料轉移的動作。

      刪除
    2. 我那邊改過後 OK了 非常謝謝你! 摁摁我還是新手正在自己摸索中 感謝你

      刪除
  45. 不好意思我是android的初學者,因為課程需要所以有要用到版主類似的文字輸入框,是要請輸入一些基本資料,但我不清楚這方法是適用在MYSQL?
    可以請教一下如何使用才能顯示我要的資料在DB內?
    不懂請麻煩解惑!!!

    回覆刪除
    回覆
    1. MySQL是伺服器,所以你要連到伺服器才能取資料,
      在 Android 裡面是用 SQLite,它只是一個檔案,存在 App 裡面,
      這篇文章的範例只能在 Android 裡面用,因為這是它提供的方法,
      你如果是要取MySQL的資料,
      那可能會有一個網站伺服器,由它去取MySQL資料庫中的資料,
      然後把資料丟回給App,

      App -> Web Server -> DB Server(MySQL) ↓
      App<- Web Server <- DB Server(MySQL)<-





      刪除
  46. 請問一下 假設我現在有三筆資料是
    1xxxx
    2xxxx
    3xxxx
    我刪除2之後
    3的I'd如何往上變回2

    就是讓ID有連貫性 謝謝

    回覆刪除
    回覆
    1. 通常這個欄位是甪自動增加的,所以無法做任何變動。

      可以在取得資料後,在顯示時利用 for 迴圈自己建立數字,
      也就是不顯示資料庫中的數字(ID),改為顯示 for 建立的數字,
      因為你要做資料庫的更新或刪除時,還是要用到原先的數字(ID),
      所以只是不顯示它而已。

      刪除
  47. 想請問一下 如果我不要用ID刪除用checkbox刪除的話 是把db.delete(TABLE_NAME,_ID+"="+id,null)中間改掉嗎 可是改了之後會閃退

    回覆刪除
    回覆
    1. 感覺怪怪的,看看DDMS給你什麼錯誤訊息

      刪除
  48. 不好意思 我的意思是 我不要用ID刪除 改用CheckBox刪除的話要怎麼使用呢 因為ID是連接資料庫 但CheckBox沒連接 我想把它改成 在listview時不是用id輸入做刪除 而是使用cHECKBOX刪除 謝謝!

    回覆刪除
    回覆
    1. 不是很懂你的意思,
      所謂的用 Checkbox 刪除,應該是指打勾後把該筆資料刪掉(對吧?)

      因為你的資料是由資料庫取出,
      最終你要刪除的還是資料庫中的資料,
      要刪除資料庫中的資料,你就必須指定它某個欄位來比較,
      checkbox 能做的是,指明 listview 中哪筆資料被打勾,並取得它的索引值,
      然後去把資料庫中的該筆刪掉。

      對使用者來說,本來是輸入ID,現在改為使用Checkbox,
      但對開發者(你)來說,刪除的程式碼還是一樣,只是多了一道由Checkbox去取得該筆資料的ID的手續而已。你不可能用一個和資料庫不相甘的東西,去刪除資料庫中的資料。



      刪除
  49. 你好,我們有新增第二個資料表的需求,可是不知道如何建立兩個資料表?是將下列資料庫程式碼在複製一次嗎? 不好意思是新手對這個不太熟悉,再麻煩你了^^
    public class SitesDBHlp extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "sites2";
    private static final int DATABASE_VERSION = 1;
    private static final String TABLE_NAME = "sitesInfo2";
    private static final String TABLE_CREATE =
    "CREATE TABLE " + TABLE_NAME + " ( " +
    " equipment TEXT NOT NULL, " +" date TEXT NOT NULL, "+
    " staff TEXT NOT NULL,"+ " time TEXT NOT NULL," +" backtime TEXT NOT NULL,"+ " note TEXT NOT NULL,PRIMARY KEY (equipment)); ";
    private static final String COL_equipment = "equipment";
    private static final String COL_staff = "staff";
    private static final String COL_date = "date";
    private static final String COL_time = "time";
    private static final String COL_backtime = "backtime";
    private static final String COL_note = "note";

    //資料表2
    private static final String TABLE_NAME2 = "sitesInfo3";
    private static final String TABLE_CREATE1 =
    "CREATE TABLE1 " + TABLE_NAME2 + " ( " +
    " Name1 TEXT NOT NULL, " +" Number1 TEXT NOT NULL,,PRIMARY KEY (Name1)); ";

    private static final String COL_Name1 = "Name1";
    private static final String COL_Number = "Number";

    回覆刪除
    回覆
    1. 是把原本"建立資料表"的程式碼複製一次沒錯,
      但你貼的程式碼我看不太懂,

      以這篇文章中的範例
      你可以在 DbConstants 加入另一個資料表及欄位的常數名,
      或是把它依資料表拆成個別檔案,像是
      TableXXXConstants 對應一個資料表
      TableYYYConstants 對應另一個資料表
      以上是常數的部份,

      在 DBHelper 中的 onCreate()
      裡面現在是建立一個資料表,
      如果要新建另一個表,就是在做一次相同的動作。

      onUpgrade 也是針對新的表在寫一次類似的程式碼。

      刪除
  50. 您好,之前有請教過您SQL語法,沒想到取出其他資料時,又遇到問題了,因此想麻煩您幫忙看看怎麼改語法比較好。

    表格欄位包含: Date1 Name1 Name2 Name3 ,
    取值條件: 在特定之Date1下,計算欄位Name1、Name2、Name3下包含"P"字的 個數。

    目前寫法:
    //使用者輸入某模糊日期後,計算Name1、Name2、Name3欄位中包含P的個數:
    public ArrayList getSubName1Unit(String Date1){
    SQLiteDatabase db = getReadableDatabase();

    String sql = "SELECT COUNT(*) FROM " + TABLE_NAME +
    " WHERE Date1 Like ? and Name1 Like '%P%' ";
    String[] args = {"%" + Date1 + "%" };

    Cursor cursor = db.rawQuery(sql, args);
    ArrayList Add = new ArrayList();
    int columnCount = cursor.getColumnCount();

    while(cursor.moveToNext()){
    String date1_name1 ="";
    for(int i=0; i<columnCount; i++)
    date1_name1 += cursor.getInt(i) +"\n";
    Add.add(date1_name1);
    }
    cursor.close();
    db.close();
    return Add;
    }

    初學中對於SQL不太會運用,目前的寫法只能完成計算欄位Name1中:
    某特定時間下,計算屬於"P"的個數。

    請問要怎麼從不同欄位(Name1、Name2、Name3)的相同條件下(某模糊時間、欄位下包含P)計算符合條件的個數呢? 謝謝您^^

    回覆刪除
    回覆
    1. 首先"Date1 Like ?"是沒有意義的
      如果資料庫的日期是 2014/5/20,但你輸入 2014-5-20,
      並不會符合結果,
      Like 只能用於字串的比對。

      「計算Name1、Name2、Name3欄位中包含P的個數」
      是指 Name1 中會有多個 p 的個數嗎?
      這用SQL寫不出來,只能把欄位值抓出來後去比對有幾個。

      如果是指 Name1 中有p,然後把有p 的所有列數加總,Name2, Name3 也是,可以用 COUNT,不過只能針對一個欄位
      可參考 http://www.1keydata.com/tw/sql/sqlcount.html

      刪除
  51. 老師您好:
    學生有問題請教 因學生android剛入門 煩請老師多點耐心

    private void openDatabase(){
    dbhelper = new DBHelper(this);
    }

    以上是老師sqlite程式一部分
    學生在eclipse上輸入上面這一段code 會出現以下錯誤:
    "The constructor MainActivity.DBHelper(DataList) is undefined"

    學生也上網找了些資料 但還是無法解決問題 只能煩請老師幫忙學生解惑

    回覆刪除
    回覆
    1. undefined 的意思"未定義",
      也就是找不到 MainActivity.DBHelper(DataList) 的建構式
      我的範例寫的是 new DBHelper(this);
      這個 this 是指 SQLiteDemoActivity ,
      你傳入 DataList ,DBHelper 類別中又沒有寫對應的建構式,
      所以編譯器找不到它。

      看你是傳錯參數就修正,還是要多加個建構式。

      刪除
  52. 老師您好:
    我目前是android的初學者 所以要麻煩老師解惑了

    我現在的專案 已經有寫好的SQLite檔案

    但是不知道該擺進去DDMS檔案的哪裡?

    還有我要如何寫出類別 可以連接資料庫&抓資料?

    我看了好多種版本的寫法 好亂 不知道從何下手

    麻煩老師可以點一下 謝謝老師

    回覆刪除
    回覆
    1. 基本上,如果你是透過 adb pull 指令把已經建立好的資料庫放進 app 裡面,
      真正發佈 app 時,這個資料庫是不會跟著發佈的,
      除了用程式在使用者第一次開啟 app 時建立資料庫外,
      唯一的方法,就是把資料庫放到 asset 目錄,讓它一起發佈
      之後再複製到使用者的手機中。
      相關做法請參考之前回覆的兩篇同樣的問題:
      http://blog.tonycube.com/2011/11/androidsqlite.html?showComment=1389706154143#c4389306036394699719

      http://blog.tonycube.com/2011/11/androidsqlite.html?showComment=1394435373536#c736910522297164585

      =======
      我的範例中
      add() 是新增
      del() 是刪除
      update() 是更新
      show() 是查詢
      4個基本功能都有了,研究看看怎麼寫吧~

      刪除
    2. 作者已經移除這則留言。

      刪除
    3. 我的資料庫是從ACCESS轉成SQLite的檔案
      直接inport進去DDMSdata/data/自己新增database的裡面
      因為找不到老師說的/data/data/packagename/databases位置
      可是感覺這樣觀念好像很怪 而且不知道怎麼去使用資料庫
      問題:
      1.我要如何讓我的SQLite檔正確可以放在程式裡?
      2.之後,我該用什麼方法去讀取資料和儲存資料進去?
      3.資料庫的版本要從哪邊去觀察?
      真的麻煩老師了,謝謝您!

      刪除
    4. 正常的資料庫建立流程是,當App被開啟後,第一次使用資料庫時,
      由 SQLiteOpenHelper 類別(我的範例DBHelper是繼承它)的
      onCreate() 去建立,至於要建立什麼資料表,要由你寫在 onCreate 裡。

      資料庫的版本是由你定的,如果版本有更動(通常是往上累加),就會觸發onUpgrade(),要如何更新資料庫也是由你定的。如果沒有要轉存使用者的資料,最快的做法就是資料表刪除重建,否則就要做轉存的工作。

      所以理論上你是無法將已經建立好的資料庫放進/data/data/...之下的目錄,因為 App 在發佈時,並不會包含該目錄裡面的檔案。

      你可以自己寫"Create table"的SQL語法,或應該(我不確定)可以在 Access 裡面找到輸出 "Create table" SQL語法的方法,再將它寫到 onCreate() 裡面。

      如果你的預設資料庫有資料,就在 "Create table..."之後再 "Insert"資料進去,所以在 onCreate() 第一次建立資料庫時可能會做很多事。

      刪除
  53. 想請問老師
    如何用SQLITE存圖片呢,從SD卡讀圖片然後選擇存入,每一筆資料都可以存不同圖片
    可以的話 請賜一些程式碼參考 感謝

    回覆刪除
    回覆
    1. 通常圖片不會直接存在Sqlite裡面,而只是存圖檔名稱,
      圖檔則是直接存在手機上,請參考這篇
      http://blog.tonycube.com/2012/03/android-internal-and-external-storage.html

      儲存圖檔會做到2個動作
      1.把圖檔名稱存入資料庫
      2.將圖檔存在手機空間上
      取出時,
      1.先讀取資料庫中的圖檔名稱
      2.去手機空間將圖檔取回

      刪除
  54. 老師您好我是android的初學者 所以要麻煩老師解答了!

    目前碰到的問題是
    我有一個SQLite的資料庫
    要撰寫一個android使用的app
    要如何將SQLite的資料匯入到ecplies?
    如何可以在執行模擬器時將我的資料顯示出來?

    麻煩了謝謝:)

    回覆刪除
    回覆
    1. 如果你只是要建立一個空的資料庫,可以用範例中的
      DBHelper 的 onCreate(SQLiteDatabase db) 在第一次使用時建立所有的資料表。

      如果你是要把已建好且含有資料的資料庫放進App,那必須先把該資料庫放在 assets 中,再把它複製到 App。
      參考這個回覆
      http://blog.tonycube.com/2011/11/androidsqlite.html?showComment=1389690011293#c1149702333234157678

      怎麼顯示資料在範例中都已經說明。

      刪除
  55. 你好 我想請問一下要如何將以建立的SQL中的數值在eclipse中取出
    我建立一個Attractions的資料庫
    裡面有name longitude latitude summary四個欄位
    我已輸入了5筆資料
    我想取出某一筆資料的longitude latitude欄位的資料作運算
    在eclipse的java檔中做運算
    請問因該要如何取出

    回覆刪除
    回覆
    1. 範例中的 show() 裡面就是取出資料的方法。
      當你去查詢資料以後會取得 Cursor 的資料集合,之後就可以從集合裡面取出資料。

      刪除
  56. 我按照這樣的作法
    但當我開始File Explorer尋找是否有databases裡的.db
    但卻沒有
    我試ㄌ很多個專案都無法
    我想請問是建立資料庫的主要關鍵是哪一部分
    為何我的都無法
    目前正在做畢業專題,很迫切很知道問題所在
    想請老師指教一下謝謝

    回覆刪除
    回覆
    1. 當你建立了一個繼承自 SQLiteOpenHelper 的類別
      例如範例中的 DBHelper
      當你**第一次**要對資料庫存取時,只要 DBHelper 找到不要資料庫
      就會自動呼叫 onCreate ,而執行其中建立資料表的SQL語法,
      這些建立資料表的語法是由你自己去設計的,
      當然,它會先自動建立由你命名的資料庫。

      刪除
  57. 如果我有一個打好的資料庫是用Excel
    請問要如何把黨轉乘SQL並放在eclipse android專案裡

    回覆刪除
    回覆
    1. 1.excel 放在 asset 目錄,
      2.讀取excel (搜尋 "java read excel")
      3.insert 資料到資料庫 (範例都有)

      刪除
  58. 不好意思 我還是不懂
    是說要先講資料用show的方式秀出來
    再用Cursor 的資料集合取出資料是這樣嗎?
    private Cursor getCursor(){

    SQLiteDatabase db = ggs.getReadableDatabase();

    String[] columns = {_ID, NAME, LONGITUDE, LATITUDE, SUMMARY};

    Cursor cursor = db.query(TABLE_NAME, columns, null, null, null, null, null);

    startManagingCursor(cursor);

    return cursor;
    是把這段加到程式哩,之後在我的onClick裡就可以使用了嗎?
    取出特定行資料要用什麼指令?

    回覆刪除
    回覆
    1. db.query 會取出整個資料表的資料,放在 Cursor 裡,
      用 cursorn.moveToNext() 可取出一筆資料。

      特定行用 where 去判斷

      刪除
  59. 那是用
    private Cursor getCursor()
    {

    SQLiteDatabase db = ggs.getReadableDatabase();
    String[] columns = {_ID, NAME, LONGITUDE, LATITUDE, SUMMARY};
    Cursor cursor = db.query(TABLE_NAME, columns, null, null, null, null, null);
    startManagingCursor(cursor);

    }
    再用
    while (Cursor.moveToNext())
    {
    Value += Cursor.getString(0) + "\n";
    }
    方式抓取資料
    還是用
    dbHelper = new SQLite(this);
    Cursor cursor = dbHelper.getAll();
    int rows_num = cursor.getCount();
    if(rows_num != 0) {
    cursor.moveToFirst();
    for(int i=0; i<rows_num; i++) {
    int id = cursor.getInt(0);
    String name = cursor.getString(1);
    int value = cursor.getInt(2);
    cursor.moveToNext();
    }
    }
    的方式來做

    回覆刪除
  60. 你好我參考其他人的方法
    我的程式如下
    protected void onCreate(Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sql4);
    ssg = new SSG(this);
    SQLiteDatabase db = ssg.getReadableDatabase();
    String[] columns = {_ID, NAME, LONGITUDE, LATITUDE, SUMMARY};
    Cursor cursor = db.query(TABLE_NAME, columns, null, null, null, null, null);
    int rows_num = cursor.getCount();
    if(rows_num != 0) {
    cursor.moveToFirst();
    for(int i=0; i<rows_num; i++)
    {
    int id = cursor.getInt(0);
    String longitude = cursor.getString(1);
    String latitude = cursor.getString(2);

    cursor.moveToNext();

    }

    }
    return cursor;
    cursor.close();


    }
    他上面說我的id longitude latitude有!號
    寫說
    The value of the local variable id is not used
    請問我這樣打有錯嗎

    回覆刪除
    回覆
    1. int id = cursor.getInt(0);
      String longitude = cursor.getString(1);
      String latitude = cursor.getString(2);
      因為這三個變數取了值以後什麼事都沒做

      刪除
  61. 請問一下
    如果我用這程式放到手機上後輸入了多筆資料
    我可以將這個的資料庫從手機取出
    在放入新的專案嗎?
    可以請教他的資料庫存放的位置在哪?

    回覆刪除
    回覆
    1. 資料庫的位置,文章裡面就有說了

      刪除
  62. 不好意思 我還是有問題
    在文章上說在新增資料後
    會出現一個在/data/data/packagename中的databases
    可是我在手機上我找不到這個檔案
    在模擬器上在建立第一筆資料後
    在File Explorer底下的
    data/data/com.sql6中看到了databases資料夾裡的demo.db
    可是我單純在手機上搜尋databases它顯示說此資料夾是空的
    如果要匯出在模擬器上的demo.db檔
    請問是要按下Pull a file from the device
    那藥可拉出檔案匯入其他專案是要把這個檔放在哪裡呢

    回覆刪除
    回覆
    1. 在手機上必須要有 root 才能進入 data/data 之下的目錄。

      你可以把資料庫 pull 出來,但是不能放到別的專案,
      在 Android 中,資料庫是由程式碼去建立的,
      即使你把它 push 到 database 目錄之下,
      當你要打包成 apk 檔發佈到商店時,它也不會包進去。
      前面我有回覆過其他人相同的問題,你可以找找。
      如果你有建好的資料庫要打包,唯一的方法就是放在 assets 裡,
      當成組件檔,然後在程式一開啟時,把它複製到app裡。

      刪除
  63. 請問如果NAME要輸入中文的話要如何修改呢?!

    回覆刪除
    回覆
    1. 不能輸入中文嗎? 模擬器可能不行,實機應該是可以的

      刪除
  64. 不好意思,如果已經在PC端建好了SQLite的DB檔,然後隨意放在android的資料夾裡,
    那要讀取DB跟下SQL語法,要怎麼寫會比較好呢?

    回覆刪除
    回覆
    1. 你無法使用在電腦建好的資料庫檔,當你發佈時,這些資料不會一起發佈,
      唯一可能的方法,是放在 asset 目錄中再APP初次開啟時複製資料庫檔,
      前面的回文裡已經有人問過同樣問題,你可以找找。

      刪除
    2. 感謝TONY大

      但假設如果我今天在SD卡裡面放了一個DB檔,我想要讀取他(絕對路徑)且還能對這個資料庫做增刪修改查詢的功能,有甚麼方法啊?這個問題我寫了好久,都無法突破耶

      刪除
  65. 作者已經移除這則留言。

    回覆刪除
  66. 請教一下,如果我要讓儲存的資料讓別的android用戶也看的到(類似共筆的概念),可以透過哪些方式呢?我以下的想法不知道對不對
    1. web server: 要多一台電腦架server?而且要一直開機
    2. 虛擬主機web server: 不用多一台電腦?
    3. 用GCM?就不用學怎麼架web server?

    回覆刪除
    回覆
    1. 你想的就是對的,無論如何都要一台伺服器來做仲介,就看你要選哪個解決方案。

      刪除
  67. Tony大,有辦法做到將剛剛建立的資料表按一個button以後,轉成excel檔匯出嗎?

    回覆刪除
    回覆
    1. 看看這篇 http://javatechig.com/android/how-to-create-excel-file-in-android

      刪除
  68. 您好
    用您的zip下載下來run,可以新增資料,但不能del和update,我也改了jdk1.6的版本,但按了del和update還是會閃退程式。謝謝

    回覆刪除
    回覆
    1. 請確定ID欄位有填入要刪除或更新的那筆資料的id
      然後看logcat也會告訴你錯在哪裡

      刪除
  69. 老師您好:
    請問sqlite 的_id會等於0嗎?因為我創建第一筆資料_id是0,但是查了很久還是找不到問題在哪裡所以來請問您,這樣會影響到往後資料庫運行嗎?

    回覆刪除
    回覆
    1. 應該不會是0,
      先確認 _id 欄位是不是 AUTOINCREMENT,
      並且在新增資料的時候,不要對 _id 欄位做動作,
      只對其他欄位做插入值的動作,這樣 _id 欄位就會自動+1

      刪除

留言小提醒:
1.回覆時間通常在晚上,如果太忙可能要等幾天。
2.請先瀏覽一下其他人的留言,也許有人問過同樣的問題。
3.程式碼請先將它編碼後再貼上。(線上編碼:http://bit.ly/1DL6yog)
4.文字請加上標點符號及斷行,難以閱讀者恕難回覆。
5.感謝您的留言,您的問題也可能幫助到其他有相同問題的人。