使用SQLite本地数据库

创建数据库

新建RunDatabaseHelper类

package com.huangfei.runtracker;

import java.util.Date;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.location.Location;

/**
 * SQLiteOpenHelper类封装了一些存储应用数据的常用数据库操作,如创建、打开、以及更新数据库等。
 */
public class RunDatabaseHelper extends SQLiteOpenHelper {
    private static final String DB_NAME = "runs.sqlite";// 数据库名
    private static final int VERSION = 1;// 数据库版本号

    private static final String TABLE_RUN = "run";
    private static final String COLUMN_RUN_ID = "_id";
    private static final String COLUMN_RUN_START_DATE = "start_date";

    private static final String TABLE_LOCATION = "location";
    private static final String COLUMN_LOCATION_LATITUDE = "latitude";
    private static final String COLUMN_LOCATION_LONGITUDE = "longitude";
    private static final String COLUMN_LOCATION_ALTITUDE = "altitude";
    private static final String COLUMN_LOCATION_TIMESTAMP = "timestamp";
    private static final String COLUMN_LOCATION_PROVIDER = "provider";
    private static final String COLUMN_LOCATION_RUN_ID = "run_id";

    public RunDatabaseHelper(Context context) {
        super(context, DB_NAME, null, VERSION);
    }

    /**
     * Android内置了操作SQLite的java前端,该前端的SQLiteDatabase类负责提供Cursor类实例形式的结果集。
     * 
     * 在onCreate(SQLiteDatabase)方法中,应为新建数据库创建表结构。
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        // 创建表run
        db.execSQL("create table run ("
                + "_id integer primary key autoincrement, start_date integer)");

        // 创建表location
        db.execSQL("create table location ("
                + " timestamp integer, latitude real, longitude real, altitude real,"
                + " provider varchar(100), run_id integer references run(_id))");
    }

    /**
     * 在onUpgrade(...)方法中,可执行迁移代码,实现不同版本间的数据库结构升级或转换。
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    /**
     * 在表run中插入一条数据
     */
    public long insertRun(Run run) {
        // 通过ContentValues对象标识的栏位名与值得名值对,将long类型的开始日期存入到数据库中。
        ContentValues cv = new ContentValues();
        cv.put(COLUMN_RUN_START_DATE, run.getSartDate().getTime());
        /**
         * SQLiteOpenHelper类有两个访问SQLiteDatabase实例的方法:
         * getWritableDatabase()和getReadableDatabase()方法。这里的使用模式是,需要可写数据库时,
         * 使用getWritableDatabase()方法;需要只读数据库时,使用getReadableDatabase()方法。
         */
        return getWritableDatabase().insert(TABLE_RUN, null, cv);
    }

    /**
     * 在表location中插入一条数据
     */
    public long insertLocation(long runId, Location location) {
        ContentValues cv = new ContentValues();
        cv.put(COLUMN_LOCATION_LATITUDE, location.getLatitude());
        cv.put(COLUMN_LOCATION_LONGITUDE, location.getLongitude());
        cv.put(COLUMN_LOCATION_ALTITUDE, location.getAltitude());
        cv.put(COLUMN_LOCATION_TIMESTAMP, location.getTime());
        cv.put(COLUMN_LOCATION_PROVIDER, location.getProvider());
        cv.put(COLUMN_LOCATION_RUN_ID, runId);
        return getWritableDatabase().insert(TABLE_LOCATION, null, cv);
    }

    /**
     * 查询SQLiteDatabase可返回描述结果的Cursor实例。Cursor API使用简单。且可灵活支持各种类型的查询结果。
     * Cursor将结果集看做是一系列的数据行和数据列,但仅支持String以及原始数据类型的值。
     */
    public RunCursor queryRuns() {
        Cursor wrapped = getReadableDatabase().query(TABLE_RUN, null, null,
                null, null, null, COLUMN_RUN_START_DATE + " asc");
        return new RunCursor(wrapped);
    }

    /**
     * 限制查询只返回一条记录
     */
    public RunCursor queryRun(long id) {
        Cursor cursor = getReadableDatabase().query(TABLE_RUN, null,
                COLUMN_RUN_ID + " = ?", new String[] { String.valueOf(id) },
                null, null, null, "1");
        return new RunCursor(cursor);
    }

    /**
     * 获取指定旅程最近一次地理位置信息
     */
     public LocationCursor queryLastLocationForRun(long runId) {
            Cursor wrapped = getReadableDatabase().query(TABLE_LOCATION, 
                    null, // all columns 
                    COLUMN_LOCATION_RUN_ID + " = ?", // limit to the given run
                    new String[]{ String.valueOf(runId) }, 
                    null, // group by
                    null, // having
                    COLUMN_LOCATION_TIMESTAMP + " desc", // order by latest first
                    "1"); // limit 1
            return new LocationCursor(wrapped);
        }

    /**
     * CursorWrapper是一个内置的Cursor子类。CursorWrapper类设计用于封装当前的Cursor类,并转发所有的方法调用它。
     * CursorWrapper类本身没有多大用途,但作为超类,它为创建适用于模型层对象的定制cursor打下了良好的基础。
     * 
     * RunCursor主要负责将run表中的各个记录转化为Run实例,并按要求对结果进行组织排序。
     */
    public static class RunCursor extends CursorWrapper {

        public RunCursor(Cursor cursor) {
            super(cursor);
        }

        public Run getRun() {
            // 检查确认cursor未越界
            if (isBeforeFirst() || isAfterLast())
                return null;

            Run run = new Run();
            long runId = getLong(getColumnIndex(COLUMN_RUN_ID));
            run.setId(runId);
            long startDate = getLong(getColumnIndex(COLUMN_RUN_START_DATE));
            run.setSartDate(new Date(startDate));
            return run;
        }
    }

     public static class LocationCursor extends CursorWrapper {

            public LocationCursor(Cursor c) {
                super(c);
            }

            public Location getLocation() {
                if (isBeforeFirst() || isAfterLast())
                    return null;
                String provider = getString(getColumnIndex(COLUMN_LOCATION_PROVIDER));
                Location loc = new Location(provider);
                loc.setLongitude(getDouble(getColumnIndex(COLUMN_LOCATION_LONGITUDE)));
                loc.setLatitude(getDouble(getColumnIndex(COLUMN_LOCATION_LATITUDE)));
                loc.setAltitude(getDouble(getColumnIndex(COLUMN_LOCATION_ALTITUDE)));
                loc.setTime(getLong(getColumnIndex(COLUMN_LOCATION_TIMESTAMP)));
                return loc;
            }
        }
}

使用CursorAdapter显示从数据库中查询的结果

package com.huangfei.runtracker;

import com.huangfei.runtracker.RunDatabaseHelper.RunCursor;

import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.TextView;

public class RunListFragment extends ListFragment {
    private static final int REQUEST_NEW_RUN = 0;

    /**
     * 当前Cursor的加载与关闭分别发生在onCreate(Bundle)和onDestroy()中,但我们不推荐这种做法,
     * 因为这不仅强制数据库查询在主线程(UI)上执行,在极端的情况下甚至会引发ANR(应用无响应)。
     * 下一章,我们将使用Loader将数据库查询移到后台运行。
     */
    private RunCursor mCursor;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        mCursor = RunManager.get(getActivity()).queryRuns();
        RunCursorAdapter adapter = new RunCursorAdapter(getActivity(), mCursor);
        setListAdapter(adapter);
    }

    @Override
    public void onDestroy() {
        mCursor.close();
        super.onDestroy();
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.run_list_options, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menu_item_new_run:
            Intent intent = new Intent(getActivity(), RunActivity.class);
            startActivityForResult(intent, REQUEST_NEW_RUN);
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode == REQUEST_NEW_RUN){
            mCursor.requery();//重新查询cursor
            ((RunCursorAdapter)getListAdapter()).notifyDataSetChanged();
        }
    }

    /**
     * 因为我们指定了run_id表中的ID字段,CursorAdapter检测到该字段并将其作为id参数传递给onListItemClick(...)方法。
     * 我们也因此能够直接将其作为附加信息传递给了RunActivity。
     */
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        Intent intent = new Intent();
        intent.putExtra(RunActivity.EXTRA_RUN_ID, id);
        startActivity(intent);
    }

    private static class RunCursorAdapter extends CursorAdapter {

        private RunCursor mRunCursor;

        public RunCursorAdapter(Context context, RunCursor c) {
            /**
             * 为提倡使用loader,大多数flag已被废弃或有一些问题存在,因此,这里传入的值为0
             */
            super(context, c, 0);
            mRunCursor = c;
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            //这里的RunCursor是CursorAdapter已经定位的cursor
            Run run = mRunCursor.getRun();

            //view来自于newView(...)方法的返回值
            TextView startDaTextureView = (TextView) view;
            startDaTextureView.setText("Run at " + run.getSartDate());
        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            return inflater.inflate(android.R.layout.simple_list_item_1,
                    parent, false);
        }

    }
}

代码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值