任務(wù)6.1:實(shí)現(xiàn)筆記本(2)

前面提到,筆記本列表頁(yè)面具有以下功能:

  • 展示全部筆記本
  • 創(chuàng)建新的筆記本
  • 選擇筆記本,并向調(diào)用NotebooksActivity的上一級(jí)activity返回對(duì)應(yīng)的筆記本ID

可以做如下設(shè)計(jì):

  • 用RecyclerView來(lái)顯示筆記本列表,其中最上面一項(xiàng)是“全部筆記”,意思是,不選中任何筆記本時(shí),則令筆記列表顯示所有筆記,否則僅顯示選中的筆記本中的內(nèi)容
  • 添加一個(gè)漂浮按鈕來(lái)觸發(fā)新建筆記本操作。點(diǎn)擊漂浮按鈕后,彈出一個(gè)對(duì)話。對(duì)話框包含一個(gè)編輯框,用戶手動(dòng)輸入筆記本名字后確認(rèn),則向數(shù)據(jù)庫(kù)中新插入一條記錄。插入操作完成后刷新筆記本列表
  • 點(diǎn)擊列表中的某一條目之后,關(guān)閉頁(yè)面,并向上一級(jí)頁(yè)面返回:
    a. 選擇“全部筆記”則返回0
    b. 選擇其它筆記本條目則返回筆記本id

1. 創(chuàng)建Notebook類

之前我們用Note類來(lái)表示一條筆記。同樣的道理,我們創(chuàng)建一個(gè)Notebook類來(lái)表示筆記本。
在“model”包下新建Notebook類,并添加如下的屬性:

  • id:筆記本的唯一id
  • name:筆記本名稱

打開新建的Notebook類,編寫代碼如下:


public class Notebook {
    private long id;
    private String name;

    public Notebook(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

2. 向NoteDAO類增加筆記本數(shù)據(jù)訪問(wèn)函數(shù)

打開NoteDAO類,添加以下方法

  • insertNotebook():向數(shù)據(jù)庫(kù)插入一個(gè)筆記本對(duì)象
  • queryAllNotebooks():查詢?nèi)抗P記本
  • queryNotebookById():根據(jù)id查找筆記本對(duì)象

insertNotebook()

    /**
     * 向數(shù)據(jù)庫(kù)中插入一個(gè)筆記本
     * @param noteBook 被添加到數(shù)據(jù)庫(kù)的筆記本對(duì)象
     * @return 插入成功后更新noteBook的id并將note對(duì)象返回;插入失敗則返回null
     */
    public Notebook insertNotebook(Notebook noteBook) {

        if (noteBook == null) {
            return noteBook;
        }

        SQLiteDatabase db = dbHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(COL_NOTEBOOK_NAME, noteBook.getName());

        long id = db.insert(TABLE_NOTEBOOK, null, values);
        if (id <= 0) {
            return null;
        }

        noteBook.setId(id);
        return noteBook;
    }

queryAllNotebooks()

    /**
     * 獲取全部筆記本
     * @return 全部筆記本的列表。如果沒(méi)有任何筆記本,則返回的列表長(zhǎng)度為0
     */
    public ArrayList<Notebook> queryAllNotebooks() {
        ArrayList<Notebook> nbs = new ArrayList<>();

        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = db.query(TABLE_NOTEBOOK, null, null, null, null, null, null);
        if (cursor != null) {
            try {
                while (cursor.moveToNext()) {
                    long id = cursor.getLong(0);
                    String name = cursor.getString(1);
                    Notebook nb = new Notebook(id, name);
                    nbs.add(nb);
                }
            } finally {
                cursor.close();
            }
        }

        return nbs;
    }

queryNotebookById

    /**
     * 根據(jù)id獲取對(duì)應(yīng)的筆記本
     * @param id 筆記本id
     * @return 如果存在id對(duì)應(yīng)的筆記本,則創(chuàng)建對(duì)象并返回,否則返回null
     */
    public Notebook queryNotebookById(long id) {
        if (id <= 0) {
            return null;
        }

        SQLiteDatabase db = dbHelper.getReadableDatabase();
        String selection = COL_ID + "=" + id;
        Cursor cursor = db.query(TABLE_NOTEBOOK, null, selection, null, null, null, null, null);
        if (cursor != null) {
            try {
                if (cursor.moveToFirst()) {
                    String name = cursor.getString(1);
                    Notebook nb = new Notebook(id, name);
                    return nb;
                }
            } finally {
                cursor.close();
            }
        }

        return null;
    }

這樣,對(duì)筆記本數(shù)據(jù)進(jìn)行操作的幾個(gè)方法就完成了。

2. 展示筆記本列表

展示筆記本列表與原來(lái)的全部筆記頁(yè)面是大體相似的。唯一的不同,是我們?cè)诹斜淼淖钋懊姹A粢粋€(gè)“全部筆記”固定項(xiàng)。

在NotebooksActivity的布局文件activity_notebooks.xml中增加RecyclerView:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.jing.app.sn.NotebooksActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/notebook_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

接下來(lái),為筆記本列表項(xiàng)創(chuàng)建布局,新建布局文件notebook_list_item.xml:

在這里,簡(jiǎn)單的讓它只顯示筆記本的名字,因此,只包含一個(gè)TextView,以及一條分隔線:

<?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="72dp"
    android:orientation="vertical"
    android:clickable="true"
    android:background="@drawable/note_list_item_bg">

    <!--筆記本名字-->
    <TextView
        android:id="@+id/tv_nb_name"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:gravity="center_vertical"
        android:text="這里是筆記本名字"
        android:maxLines="2"
        android:textColor="@color/black"
        android:textSize="16sp" />

    <!--分隔線-->
    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="#999999"/>

</LinearLayout>

從布局文件中,我們可以看到,顯示筆記本名字的TextView的id叫做“tv_nb_name”。

現(xiàn)在,我們?cè)偃otebooksActivity中實(shí)現(xiàn)這個(gè)列表。
先創(chuàng)建內(nèi)部類NotebookViewHolder以實(shí)現(xiàn)我們自己的ViewHolder:

    private class NotebookViewHolder extends RecyclerView.ViewHolder {
        // 只有一個(gè)文本視圖用以顯示名字
        private TextView notebookName;

        public NotebookViewHolder(View itemView) {
            super(itemView);
            notebookName = itemView.findViewById(R.id.tv_nb_name);
        }
    }

接下來(lái),創(chuàng)建適配器類NotebookAdapter:

    private class NotebookAdapter extends RecyclerView.Adapter<NotebookViewHolder> {

        // 筆記本列表
        private ArrayList<Notebook> noteBooks = new ArrayList<>();

        // 設(shè)置筆記本列表內(nèi)容
        public void setNotes(ArrayList<Notebook> nbs) {
            // 現(xiàn)將原列表清空,再將傳入的列表元素全部加入
            this.noteBooks.clear();
            if (nbs != null) {
                this.noteBooks.addAll(nbs);
            }
        }

        @Override
        public NotebookViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            // 根據(jù)列表項(xiàng)布局文件創(chuàng)建視圖對(duì)象
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            View view = inflater.inflate(R.layout.notebook_list_item, parent, false);
            // 基于上面的視圖對(duì)象創(chuàng)建ViewHolder對(duì)象并返回
            NotebookViewHolder vh = new NotebookViewHolder(view);
            return vh;
        }

        @Override
        public void onBindViewHolder(NotebookViewHolder holder, int position) {
           // 取對(duì)應(yīng)位置的筆記對(duì)象
            final Notebook nb = noteBooks.get(position);
            // 設(shè)置對(duì)應(yīng)ViewHolder對(duì)象中各視覺(jué)元素的值
            holder.notebookName.setText(nb.getName());

            // 響應(yīng)點(diǎn)擊事件
            // 處理列表項(xiàng)點(diǎn)擊事件
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onSelectNotebook(nb);
                }
            });
        }

        @Override
        public int getItemCount() {
            return noteBooks.size();
        }
    }

可以注意到,在處理列表項(xiàng)點(diǎn)擊事件時(shí),調(diào)用了onSelectNotebook()方法,但是目前并沒(méi)有創(chuàng)建它,因此會(huì)出現(xiàn)報(bào)錯(cuò)。

在NotebooksActivity中(適配器類之外)創(chuàng)建onSelectNotebook()方法,它的參數(shù)就是當(dāng)前點(diǎn)擊的筆記本對(duì)象,它執(zhí)行的操作就是關(guān)閉筆記本列表頁(yè)面,并向上一級(jí)activity返回這個(gè)對(duì)象的id和名字。編寫代碼如下:

   private void onSelectNotebook(Notebook notebook) {
       // 創(chuàng)建一個(gè)Intent對(duì)象,用來(lái)攜帶要返回的參數(shù)
       Intent intent = new Intent();
       intent.putExtra("notebookId", notebook.getId());
       intent.putExtra("notebookName", notebook.getName());

       // 設(shè)置返回結(jié)果,返回碼為1
       setResult(1, intent);

       //關(guān)閉頁(yè)面
       finish();
   }

接下來(lái),我們查詢數(shù)據(jù)庫(kù),獲取筆記本列表,并使其顯示出來(lái)。
首先,為NotebooksActivity添加RecyclerView及其適配器類型的成員:

public class NotebooksActivity extends AppCompatActivity {
    private RecyclerView mRecyclerView;
    private NotebookAdapter mAdapter;
    ...
}

然后找到onCreate()方法,添加代碼以對(duì)RecyclerView進(jìn)行初始化:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_notebooks);

        // 添加代碼:初始化并設(shè)置RecyclerView
        mRecyclerView = findViewById(R.id.notebook_list);
        // 設(shè)定為垂直列表
        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        mRecyclerView.setLayoutManager(layoutManager);

        mAdapter = new NotebookAdapter();
        mRecyclerView.setAdapter(mAdapter);
    }

這里僅僅是初始化了RecyclerView,還沒(méi)有加載數(shù)據(jù)。重寫NotebooksActivity類的onResume()方法,并通過(guò)AsyncTask對(duì)筆記本數(shù)據(jù)進(jìn)行異步加載:

   @Override
   protected void onResume() {
       super.onResume();
       asyncLoadNotebooks();
   }

   private void asyncLoadNotebooks() {
       AsyncTask<Void, Void, ArrayList<Notebook>> task = new AsyncTask<Void, Void, ArrayList<Notebook>>() {
           @Override
           protected ArrayList<Notebook> doInBackground(Void... voids) {
               ArrayList<Notebook> nbs = NoteDAO.getInstance(NotebooksActivity.this.getApplicationContext()).queryAllNotebooks();
               // 在列表最前添加"全部筆記"項(xiàng),id設(shè)為0
               Notebook allNotes = new Notebook(0, getString(R.string.all_notes));
               nbs.add(0, allNotes);
               return nbs;
           }

           @Override
           protected void onPostExecute(ArrayList<Notebook> notebooks) {
               mAdapter.setNotes(notebooks);
               mAdapter.notifyDataSetChanged();
           }
       };

       task.execute();
   }

在這里,我們通過(guò)創(chuàng)建一個(gè)匿名的AsyncTask子類來(lái)實(shí)現(xiàn)筆記本數(shù)據(jù)的異步加載:

  • 在doInBackground()方法中,首先從數(shù)據(jù)庫(kù)中查詢?nèi)康墓P記本,得到一個(gè)列表。然后創(chuàng)建一個(gè)特殊的Notebook對(duì)象,將其id設(shè)置為0(從數(shù)據(jù)庫(kù)中來(lái)的筆記本id不可能為0),名字設(shè)置為“全部筆記”,并插入到列表最前面。
  • 在onPostExecute()方法中,更新適配器類持有的數(shù)據(jù),并通知數(shù)據(jù)集變化。

3. 處理返回結(jié)果

之前,我們講到,從筆記列表頁(yè)面調(diào)用筆記本列表頁(yè)面,選擇筆記本后將會(huì)把它的id返回過(guò)來(lái)。下面來(lái)處理這個(gè)返回值。處理邏輯如下:
分析返回值,將頁(yè)面標(biāo)題設(shè)置為返回的筆記本名字,判斷返回的id:

  • 如果為0,則加載全部筆記,并設(shè)置頁(yè)面標(biāo)題為“全部筆記”
  • 如果大于0,則讀取該筆記本中所有的筆記,顯示在列表中

經(jīng)過(guò)這個(gè)處理,原來(lái)僅僅顯示全部筆記的主頁(yè)面,現(xiàn)在可以根據(jù)我們所選擇的筆記本,動(dòng)態(tài)的變換內(nèi)容了。

進(jìn)行以下修改:

為NoteListActivity類添加notebookId成員:

notebookId成員缺省為0,表明加載全部筆記。當(dāng)我們從筆記本列表中返回時(shí),用返回的新id值將其重新設(shè)置,以切換筆記本:

    private long notebookId;

修改NoteListActivity類的異步加載類:

找到內(nèi)部類LoadAllNotesTask,修改如下:

    private class LoadAllNotesTask extends AsyncTask<Void, Void, ArrayList<Note>> {

        private long notebookId;

        public LoadAllNotesTask(long notebookId) {
            this.notebookId = notebookId;
        }

        public LoadAllNotesTask() {
            this(0);
        }

        @Override
        protected void onPreExecute() {
            mLoadingView.setVisibility(View.VISIBLE);
        }

        @Override
        protected void onPostExecute(ArrayList<Note> notes) {
            // 為適配器設(shè)置新的筆記列表
            adapter.setNotes(notes);
            // 通知RecyclerView刷新
            adapter.notifyDataSetChanged();
            // 關(guān)閉加載等待視圖
            mLoadingView.setVisibility(View.GONE);
        }

        @Override
        protected ArrayList<Note> doInBackground(Void... voids) {
            ArrayList<Note> notes = noteRepository.getAllNotes();
            // 從列表中去掉筆記本id不匹配的筆記對(duì)象
            if (notebookId > 0) {
                for (Iterator<Note> iterator = notes.iterator(); iterator.hasNext();) {
                    if (iterator.next().getNotebookId() != notebookId) {
                        iterator.remove();
                    }
                }
            }
            return notes;
        }
    }

在原來(lái)的基礎(chǔ)上進(jìn)行了如下改動(dòng):

  • 添加了私有成員notebookId,表示當(dāng)前頁(yè)面針對(duì)哪個(gè)筆記本
  • 添加了兩個(gè)構(gòu)造方法,一個(gè)接收指定的筆記本id來(lái)初始化notebookId成員;另一個(gè)缺省的將notebookId設(shè)置為0,即全部筆記
  • 在doInBackground()中,當(dāng)notebookId>0,也就是指定了當(dāng)前頁(yè)面對(duì)應(yīng)的筆記本時(shí),進(jìn)行一次循環(huán)處理,去掉從數(shù)據(jù)庫(kù)中取得的筆記列表中不屬于此筆記本的項(xiàng)目

修改onResume()方法中對(duì)LoadAllNotesTask的創(chuàng)建和調(diào)用

    @Override
    protected void onResume() {
        super.onResume();
        // 修改:創(chuàng)建對(duì)象時(shí)加上筆記本id參數(shù)
        LoadAllNotesTask task = new LoadAllNotesTask(notebookId);
        task.execute();
    }

重寫NoteListActivity類的onActivityResult()方法

onActivityResult()方法專門用于處理被調(diào)用activity的返回值。
打開NoteListActivity類,重寫onActivityResult()方法:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == 1 && resultCode == 1) {
            // 取得返回參數(shù)
            notebookId = data.getLongExtra("notebookId", 0);
            String notebookName = data.getStringExtra("notebookName");
            // 設(shè)置頁(yè)面標(biāo)題
            setTitle(notebookName);
        }
    }

4. 實(shí)現(xiàn)新建筆記本

回到NotebooksActivity,先在它的布局文件中添加漂浮按鈕。可以簡(jiǎn)單的把原來(lái)在筆記列表頁(yè)面中添加的漂浮按鈕拷貝過(guò)來(lái)。完畢后布局內(nèi)容如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.jing.app.sn.NotebooksActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/notebook_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_add_notebook"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|right"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        android:clickable="true"
        android:onClick="onNewNotebook"
        app:srcCompat="@drawable/ic_add" />

</FrameLayout>

這里指定按鈕點(diǎn)擊處理函數(shù)為onNewNotebook(),則在NotebooksActivity類中添加此方法:

    public void onNewNotebook(View view) {
    }

在這個(gè)方法里面,我們彈出一個(gè)對(duì)話框,里面包含一個(gè)編輯框,用來(lái)填寫新建的筆記本的名字,點(diǎn)擊確認(rèn)后將其保存到數(shù)據(jù)庫(kù)。

為onNewNotebook()方法添加代碼如下:

    public void onNewNotebook(View view) {
        // 創(chuàng)建編輯框?qū)ο?        final EditText notebookNameEdit = new EditText(this);
        AlertDialog.Builder builder = new AlertDialog.Builder(this)
                .setTitle(R.string.new_notebook)
                .setView(notebookNameEdit)  // 將編輯框?qū)ο蠹尤雽?duì)話框
                .setNegativeButton("取消", null)
                .setPositiveButton("確認(rèn)", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        String name = notebookNameEdit.getEditableText().toString();
                        if (!TextUtils.isEmpty(name)) {
                            Notebook nb = new Notebook(0, name);
                            nb = NoteDAO.getInstance(NotebooksActivity.this).insertNotebook(nb);
                            if (nb != null) {
                                // 插入成功,刷新筆記本列表
                                asyncLoadNotebooks();
                            } else {
                                // 插入失敗,提示用戶
                                Toast.makeText(NotebooksActivity.this, "保存筆記本失敗", Toast.LENGTH_SHORT).show();
                            }

                        }
                    }
                });
        builder.show();
    }

這樣,到目前階段,可以添加新的筆記本了。選擇新的筆記本之后,UI切回筆記列表頁(yè)面,且標(biāo)題改為當(dāng)前選中的筆記本標(biāo)題:

然而,新的筆記本里面并沒(méi)有任何筆記,這一點(diǎn)要通過(guò)修改新建筆記頁(yè)面來(lái)實(shí)現(xiàn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,644評(píng)論 19 139
  • 設(shè)計(jì)思路 筆記本與筆記是一對(duì)多的關(guān)系: 每條筆記屬于一個(gè)筆記本 一個(gè)筆記本包含多條筆記 1. 數(shù)據(jù)庫(kù)設(shè)計(jì) 首先,我...
    jingz課程閱讀 558評(píng)論 0 0
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 30,262評(píng)論 8 265
  • 今天寶貝真棒,拼音和數(shù)學(xué)測(cè)驗(yàn)都是滿分,我比她都高興,終于上道了,不像前些天那么累了,松了一口氣了,希望不要驕傲...
    李烽熠媽閱讀 235評(píng)論 0 0
  • 效果###
    孟圓的筆記閱讀 275評(píng)論 0 0

友情鏈接更多精彩內(nèi)容