《Android Studio實戰 快速、高效地構建Android應用》--五、備忘錄實驗(1/2)


  • 通過開發App熟悉Android Studio的用法
  • 開發一款用於管理備忘事項列表的App,核心功能:
    • 創建、刪除備忘
    • 將某些備忘標記為重要(左側帶顏色標簽突出顯示)
  • 涉及:操作欄菜單、上下文菜單、用於持久化的本地數據庫、支持多選的設備上的多項選擇

啟動新項目

File|New|New project命名為Reminders,選擇Empty Activity

新建project

初始化Git倉庫

安裝、配置Git:https://www.cnblogs.com/hhhqqq/p/12273696.html

  • 創建Git倉庫

創建本地倉庫

然后選擇項目根目錄來創建Git倉庫

  • 在Version Control工具窗口中右擊Unversioned Files選擇Add to VCS將這些文件添加到Git索引

  • 提交文件(Ctrl+K|將項目的修改記錄到Git版本控制系統的過程)

    提交文件

構建用戶界面

visual designer布局

visual designer布局

可視化設計器

將左側Palette\Legacy中的ListView拖到編輯區域放置該控件,右側Attributes修改該控件各項屬性

listview

編輯布局的原始XML

單擊底部Text,編輯原始XML

編輯XML

更改背景顏色:

 android:background="#181818"	//設置RelativeLayout背景顏色

在XML布局文件中硬編碼顏色值並不是最佳方案,更好的方法:在values資源文件夾下定義colors.xml,在里面定義自己的顏色。這樣便於編輯而且可以很容易地在整個項目中引用。

將代碼修改為:

	android:background="@color/dark_grey"

創建顏色值資源

在錯誤提示中選擇第二項(創建顏色資源)

創建顏色資源2

修改ListView列表項的顯示方式

在res\layout文件夾中新建reminders_row,選擇LinearLayout(LinearLayout是布局中的最外層元素)作為根ViewGroup來為單個列表項行創建布局,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
           android:layout_width="match_parent"
           android:layout_height="50dp"
           android:orientation="vertical"
           android:background="@color/dark_grey">

 <LinearLayout
     android:orientation="horizontal"
     android:layout_width="match_parent"
     android:layout_height="48dp">

 <view
     android:layout_width="10dp"
     android:layout_height="match_parent"
     class="android.view.View"
     android:id="@+id/row_tab"
     android:background="@color/green"/>

 <TextView
     android:layout_width="match_parent"
     android:layout_height="50dp"
     android:text="Reminder Text"
     android:id="@+id/row_text"
     android:textColor="@color/white"
     android:textSize="18sp"
     android:gravity="center_vertical"
     android:padding="10dp"
     android:ellipsize="end"
     android:maxLines="1"/>
</LinearLayout>
 <view
     class="android.view.View"
     android:layout_width="fill_parent"
     android:layout_height="1dp"
     android:background="#000"/>
 <view
     class="android.view.View"
     android:layout_width="fill_parent"
     android:layout_height="1dp"
     android:background="#333"/>
</LinearLayout>

列表項完成

向ListView添加條目

修改相應的Activity文件(RemindersActivity.java),聲明一個ListView成員、修改onCreate()方法

private ListView mListView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_reminders);
     mListView = (ListView)findViewById(R.id.reminders_list_view);
     ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(
             this,   //  當前Activity的Context對象
             R.layout.reminders_row, //使用哪個布局
             R.id.row_text,     //布局中的哪個字段來顯示數據
             new String[]{"first record","second record"}    //示例條目
     );
     mListView.setAdapter(arrayAdapter);
 }

添加條目

添加操作欄溢出菜單

在res文件夾下新建menu文件夾,右鍵New|Menu resource file命名為menu_reminders

menu/menu_reminders.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item android:id="@+id/action_new"
        android:title="New Reminder"
        android:orderInCategory="100"
        app:showAsAction="never"/>
    <item android:id="@+id/action_exit"
        android:title="exit"
        android:orderInCategory="200"
        app:showAsAction="never"/>
</menu>

在RemindersActivity.java中添加創建菜單的方法和菜單的點擊事件

/**
     *創建菜單
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_reminders,menu); //通過getMenuInflater()方法得到MenuInflater對象,再調用它的inflate()方法就可以給當前活動創建菜單了,第一個參數:用於指定我們通過哪一個資源文件來創建菜單;第二個參數:用於指定我們的菜單項將添加到哪一個Menu對象當中。
        return true; // true:允許創建的菜單顯示出來,false:創建的菜單將無法顯示。
    }

    /**
     *菜單的點擊事件
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()){
            case R.id.action_new:
                Log.d(getLocalClassName(),"create new Reminder");
                return true;
            case R.id.action_exit:
                finish();
                return true;
            default:
                return false;
        }
    }

添加溢出菜單

持久化備忘錄

  • SQLite數據庫
  • 數據模型、數據庫代理類、CursorAdapter
    • 數據模型:保存從數據庫讀取以及寫入數據庫的數據
    • 數據庫代理類:適配器類,把來自App的簡單調用轉換為對SQLite數據庫的API調用
    • CursorAdapter:繼承以抽象方式處理數據訪問的標准Android類

數據模型

創建數據模型

com.example.dell.reminders右鍵New Java Class命名為Reminder

Reminder類:

public class Reminder {
    private int mId;
    private String mContent;
    private int mImportant;

    public Reminder(int id, String content, int important) {
        mId = id;
        mContent = content;
        mImportant = important;
    }

    public int getId() {
        return mId;
    }

    public void setId(int id) {
        mId = id;
    }

    public String getContent() {
        return mContent;
    }

    public void setContent(String content) {
        mContent = content;
    }

    public int getImportant() {
        return mImportant;
    }

    public void setImportant(int important) {
        mImportant = important;
    }
}

創建數據庫代理

com.example.dell.reminders右鍵New Java Class命名為RemindersDbAdapter

public class RemindersDbAdapter {
    //定義列名
    public static final String COL_ID = "_id";
    public static final String COL_CONTENT = "content";
    public static final String COL_IMPORTANT = "important";
    //定義索引值
    public static final int INDEX_ID = 0;
    public static final int INDEX_CONTENT = INDEX_ID + 1;
    public static final int INDEX_IMPORTANT = INDEX_ID + 2;
    //用於日志的TAG
    public static final String TAG = "RemindersDbAdapter";

    //兩個數據庫API對象
    private DatabaseHelper mDbHelper;
    private SQLiteDatabase mDb;

    //數據庫名稱、主表名稱、版本的常量
    public static final String DATABASE_NAME = "dba_remdrs";
    public static final String TABLE_NAME = "tbl_remdrs";
    public static final int DATABASE_VERSION = 1;

    //上下文對象
    private final Context mCtx;

    //用於創建數據庫的SQL語句
    private static final String DATABASE_CREATE =
            "CREATE TABLE if not exists " + TABLE_NAME + " ( " +
                    COL_ID + "INTEGER PRIMARY KEY autoincrement, " +
                    COL_CONTENT + " TEXT, " +
                    COL_IMPORTANT + " INTEGER );";
}

SQLite API

DatabaseHelper:用於打開、關閉數據庫的SQLite API類,是一個自定義的類,將其實現為RemindersDbAdapter的內部類

private static class DatabaseHelper extends SQLiteOpenHelper{
        //構造函數完成數據庫初始化
        DatabaseHelper(Context context){
            //將數據庫名和版本號傳給超類,由超類完成建立數據庫的繁重工作
            super(context,DATABASE_NAME,null,DATABASE_VERSION);
        }

        public void onCreate(SQLiteDatabase db){
            Log.w(TAG,DATABASE_CREATE);
            db.execSQL(DATABASE_CREATE);
        }

        public void onUpgrade(SQLiteDatabase db,int oldVersion, int newVersion){
            Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
            db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
            onCreate(db);
        }
    }

使用DatabaseHelper打開和關閉數據庫,RemindersDbAdapter構造函數保存了Context實例,並傳給DatabaseHelper

public RemindersDbAdapter(Context ctx) {
        this.mCtx = ctx;
    }

    public void open() throws SQLException{
        mDbHelper = new DatabaseHelper(mCtx);
        mDb = mDbHelper.getWritableDatabase();
    }

    public void close(){
        if(mDbHelper != null){
            mDbHelper.close();
        }
    }

數據庫的增刪改查:

創建方法使用特殊的ContentValues對象,用於將數據值傳給數據庫對象的insert方法,數據庫會將這些對象轉換為SQL insert語句並執行

//數據庫的增刪改查
    public void createReminder(String name, boolean important){
        ContentValues values = new ContentValues();
        values.put(COL_CONTENT, name);
        values.put(COL_IMPORTANT, important?1:0);
        mDb.insert(TABLE_NAME,null,values);
    }
    
    public long createReminder(Reminder reminder){
        ContentValues values = new ContentValues();
        values.put(COL_CONTENT,reminder.getContent());
        values.put(COL_IMPORTANT,reminder.getImportant());
        return mDb.insert(TABLE_NAME,null,values);
    }
    
    public Reminder fetchReminderById(int id){
        Cursor cursor = mDb.query(TABLE_NAME,new String[]{COL_ID,
                COL_CONTENT,COL_IMPORTANT},COL_ID + "=?",
                new String[]{String.valueOf(id)},null,null,null,null);
        if(cursor != null)
            cursor.moveToFirst();
            
            return new Reminder(
              cursor.getInt(INDEX_ID),
              cursor.getString(INDEX_CONTENT),
                    cursor.getInt(INDEX_IMPORTANT)
            );
    }
    
    public Cursor fetchAllReminders(){
        Cursor mCursor = mDb.query(TABLE_NAME,new String[]{COL_ID,
        COL_CONTENT,COL_IMPORTANT},null,null,null,null,null);
        
        if (mCursor != null){
            mCursor.moveToFirst();
        }
        
        return mCursor;
    }
    
    public void updateReminder(Reminder reminder){
        ContentValues values = new ContentValues();
        values.put(COL_CONTENT,reminder.getContent());
        values.put(COL_IMPORTANT,reminder.getImportant());
        mDb.update(TABLE_NAME,values,
                COL_ID+"=?",new String[]{String.valueOf(reminder.getId())});
    }
    
    public void deleteReminderById(int nId){
        mDb.delete(TABLE_NAME,COL_ID+"=?",new String[]{String.valueOf(nId)});
    }
    
    public void deleteAllReminders(){
        mDb.delete(TABLE_NAME,null,null);
    }

RemindersDbAdapter最終代碼

public class RemindersDbAdapter {
    //定義列名
    public static final String COL_ID = "_id";
    public static final String COL_CONTENT = "content";
    public static final String COL_IMPORTANT = "important";
    //定義索引值
    public static final int INDEX_ID = 0;
    public static final int INDEX_CONTENT = INDEX_ID + 1;
    public static final int INDEX_IMPORTANT = INDEX_ID + 2;
    //用於日志的TAG
    public static final String TAG = "RemindersDbAdapter";

    //兩個數據庫API對象
    private DatabaseHelper mDbHelper;
    private SQLiteDatabase mDb;

    //數據庫名稱、主表名稱、版本的常量
    public static final String DATABASE_NAME = "dba_remdrs";
    public static final String TABLE_NAME = "tbl_remdrs";
    public static final int DATABASE_VERSION = 1;

    //上下文對象
    private final Context mCtx;

    //用於創建數據庫的SQL語句
    private static final String DATABASE_CREATE =
            "CREATE TABLE if not exists " + TABLE_NAME + " ( " +
                    COL_ID + "INTEGER PRIMARY KEY autoincrement, " +
                    COL_CONTENT + " TEXT, " +
                    COL_IMPORTANT + " INTEGER );";

    public RemindersDbAdapter(Context ctx) {
        this.mCtx = ctx;
    }

    public void open() throws SQLException{
        mDbHelper = new DatabaseHelper(mCtx);
        mDb = mDbHelper.getWritableDatabase();
    }

    public void close(){
        if(mDbHelper != null){
            mDbHelper.close();
        }
    }

    //數據庫的增刪改查
    public void createReminder(String name, boolean important){
        ContentValues values = new ContentValues();
        values.put(COL_CONTENT, name);
        values.put(COL_IMPORTANT, important?1:0);
        mDb.insert(TABLE_NAME,null,values);
    }

    public long createReminder(Reminder reminder){
        ContentValues values = new ContentValues();
        values.put(COL_CONTENT,reminder.getContent());
        values.put(COL_IMPORTANT,reminder.getImportant());
        return mDb.insert(TABLE_NAME,null,values);
    }

    public Reminder fetchReminderById(int id){
        Cursor cursor = mDb.query(TABLE_NAME,new String[]{COL_ID,
                COL_CONTENT,COL_IMPORTANT},COL_ID + "=?",
                new String[]{String.valueOf(id)},null,null,null,null);
        if(cursor != null)
            cursor.moveToFirst();

            return new Reminder(
              cursor.getInt(INDEX_ID),
              cursor.getString(INDEX_CONTENT),
                    cursor.getInt(INDEX_IMPORTANT)
            );
    }

    public Cursor fetchAllReminders(){
        Cursor mCursor = mDb.query(TABLE_NAME,new String[]{COL_ID,
        COL_CONTENT,COL_IMPORTANT},null,null,null,null,null);

        if (mCursor != null){
            mCursor.moveToFirst();
        }

        return mCursor;
    }

    public void updateReminder(Reminder reminder){
        ContentValues values = new ContentValues();
        values.put(COL_CONTENT,reminder.getContent());
        values.put(COL_IMPORTANT,reminder.getImportant());
        mDb.update(TABLE_NAME,values,
                COL_ID+"=?",new String[]{String.valueOf(reminder.getId())});
    }

    public void deleteReminderById(int nId){
        mDb.delete(TABLE_NAME,COL_ID+"=?",new String[]{String.valueOf(nId)});
    }

    public void deleteAllReminders(){
        mDb.delete(TABLE_NAME,null,null);
    }
    
    private static class DatabaseHelper extends SQLiteOpenHelper{
        //構造函數完成數據庫初始化
        DatabaseHelper(Context context){
            //將數據庫名和版本號傳給超類,由超類完成建立數據庫的繁重工作
            super(context,DATABASE_NAME,null,DATABASE_VERSION);
        }

        public void onCreate(SQLiteDatabase db){
            Log.w(TAG,DATABASE_CREATE);
            db.execSQL(DATABASE_CREATE);
        }

        public void onUpgrade(SQLiteDatabase db,int oldVersion, int newVersion){
            Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
            db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
            onCreate(db);
        }
    }
}

CursorAdapter

最后,需要一種從數據庫獲取備忘並加入到ListView中的方法,新建java類RemindersSimpleCursorAdapter

public class RemindersSimpleCursorAdapter extends SimpleCursorAdapter {
    public RemindersSimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
        super(context, layout, c, from, to, flags);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        return super.newView(context, cursor, parent);
    }


    //ListView會利用屏幕上的單個View對象反復調用此方法,Adapter的職責就是使用列表項來填充這些視圖
    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        super.bindView(view, context, cursor);//調用超類方法,通過游標cursor獲取到的值映射到View中的元素

        ViewHolder holder = (ViewHolder)view.getTag();
        if(holder == null){ //檢查holder是否綁定到了標簽
            holder = new ViewHolder();
            holder.colImp = cursor.getColumnIndexOrThrow(RemindersDbAdapter.COL_IMPORTANT);
            holder.listTab = view.findViewById(R.id.row_tab);
            view.setTag(holder);
        }

        //使用當前備忘COL_IMPORTANT常量對應的值來決定顏色1:重要 0:次要
        if(cursor.getInt(holder.colImp) > 0){
            holder.listTab.setBackgroundColor(context.getResources().getColor(R.color.orange));
        }
        else{
            holder.listTab.setBackgroundColor(context.getResources().getColor(R.color.green));
        }
    }

    //靜態內部類
    static class ViewHolder{
        int colImp; //Important表列的索引
        View listTab;   //在布局中定義的row_tab視圖
    }
}

調整ReminderActivity

public class RemindersActivity extends AppCompatActivity {

    private ListView mListView;
    private RemindersDbAdapter mDbAdapter;
    private RemindersSimpleCursorAdapter mCursorAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_reminders);
        mListView = (ListView)findViewById(R.id.reminders_list_view);
        mListView.setDivider(null);
        mDbAdapter = new RemindersDbAdapter(this);
        mDbAdapter.open();

        Cursor cursor = mDbAdapter.fetchAllReminders();

        String[] from = new String[]{RemindersDbAdapter.COL_CONTENT};

        int[] to = new int[]{R.id.row_text};

        mCursorAdapter = new RemindersSimpleCursorAdapter(
                RemindersActivity.this,
                R.layout.reminders_row,
                cursor,
                from,
                to,
                0
        );
        mListView.setAdapter(mCursorAdapter);
    }
}

調整完運行app,將不會在列表中看到任何內容,因為最后的修改插入的是SQLite功能而非示例數據

part1最后

Ctrl+K提交備忘錄實驗1的最后一次修改

最后提交


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM