任务4:实现笔记的阅读

第一步:为ActionBar添加回退按钮

参考任务3,首先修改阅读页onCreate()函数,将回退按钮显示出来:

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

        ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);
        ...

然后,实现回退操作。实际上Activity基类本身默认存在一个onBackPressed()方法。此方法用来响应用户点击Android手机的回退键,缺省的操作就是关闭当前页面。所以,我们可以直接调用onBackPressed()来实现操作。这样的做的好处是使ActionBar回退按钮和手机的回退键操作一致:

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                onBackPressed();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

第二步:创建布局

根据设计图,我们做如下布局实现:

  • 将布局根元素设为最基本的FrameLayout
  • 然后包含一个ScrollView,使得视图可以整体滚动
  • 在滚动视图中添加一个垂直方向的LinearLayout
  • 再在LinearLayout中从上到下,用三个TextView分别显示笔记创建时间、标题以及正文

编辑阅读页面的布局文件实现上述设计。具体设置可以参考下列代码:

<?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.ReadNoteActivity">
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
            <!-- 显示笔记创建时间 -->
            <TextView
                android:id="@+id/tv_read_time"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:paddingLeft="@dimen/read_padding_horizontal"
                android:paddingRight="@dimen/read_padding_horizontal"
                android:paddingTop="@dimen/read_padding_vertical"
                android:textSize="@dimen/read_time_font_size"
                android:textColor="@color/dark_grey"
                android:text="2018/03/13"/>
            <!-- 显示笔记标题 -->
            <TextView
                android:id="@+id/tv_read_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:paddingLeft="@dimen/read_padding_horizontal"
                android:paddingRight="@dimen/read_padding_horizontal"
                android:paddingTop="@dimen/read_padding_vertical"
                android:paddingBottom="@dimen/read_padding_vertical"
                android:textSize="@dimen/read_title_font_size"
                android:textColor="@color/black"
                android:text="标题"/>
            <!-- 显示笔记正文 -->
            <TextView
                android:id="@+id/tv_read_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:paddingLeft="@dimen/read_padding_horizontal"
                android:paddingRight="@dimen/read_padding_horizontal"
                android:paddingTop="@dimen/read_padding_vertical"
                android:paddingBottom="@dimen/read_padding_vertical"
                android:textSize="@dimen/edit_content_font_size"
                android:textColor="@color/black"
                android:text="正文"/>
        </LinearLayout>
    </ScrollView>
</FrameLayout>

注意其中创建了一些新的参数资源,请在相应的文件里添加,并根据自己思路修改调整:

    <color name="dark_grey">#909090</color>
    <color name="black">#000000</color>
    <dimen name="read_time_font_size">16sp</dimen>
    <dimen name="read_padding_horizontal">16dp</dimen>
    <dimen name="read_padding_vertical">8dp</dimen>
    <dimen name="read_title_font_size">22sp</dimen>

运行程序查看效果:


目前UI中各元素显示的内容是缺省设置的。接下来我们解决如何根据用户在笔记列表中的选择显示对应笔记内容的问题。


第三步:加载显示指定笔记内容

这一步我们分成两个阶段:

第一阶段:用户在全部笔记页面点击笔记条目启动阅读页面

在这一阶段,我们的程序不但要触发阅读页面(已经实现),还要设法通知阅读页面具体打开哪一条笔记。Android SDK为Intent对象提供附加参数(extra)的功能。我们借助这一功能来达到我们的目的。

首先,我们修改全部笔记页面中RecyclerView相关的实现。找到实现选择笔记条目操作的代码,在NoteViewHolder内部类中:

这段代码放在此处有一个问题——访问数据集中对应的笔记对象不方便。我们将其迁移到NoteAdapter类的onBindViewHolder()方法的最后面:

        @Override
        public void onBindViewHolder(NoteViewHolder holder, int position) {
            ...
            // 响应点击事件
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onReadNote();
                }
            });
        }

对于笔记条目,我们可以通过它的id属性来唯一标识它,所以我们要增加一些操作:

  • 获取当前条目的id
  • 在创建启动阅读页面的Intent对象时,将id作为附加参数插入其中
    那么,我们修改代码如下:
        public void onBindViewHolder(NoteViewHolder holder, int position) {
            // 取对应位置的笔记对象
            Note note = notes.get(position);
           ...
            // 1. 获取note对象的id
           final long noteId = note.getId();

            // 响应点击事件
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // 2. 将note对象的id作为参数传递
                    onReadNote(noteId);
                }
            });
        }

然后修改onReadNote()的定义,为其增加一个表示笔记id参数。不然程序会被报错:

    private void onReadNote(long noteId) {
        ...
    }

接下来,修改onReadNote()方法内部的代码,调用Intent类的putExtra()方法,将noteId参数添加到Intent对象。这里,我们需要提供一个字符串作为参数的键(key),将来仍然通过这个来读取noteId。修改完毕后,代码如下:

    private void onReadNote(long noteId) {
        // 启动阅读页面
        Intent intent = new Intent(this, ReadNoteActivity.class);
        intent.putExtra("note_id", noteId);
        startActivity(intent);
    }

这样,在阅读页面启动之后,我们根据键(“note_id”)来读取对应的值,即可获知到底要打开哪条笔记。

第二阶段:在阅读页面加载指定的笔记内容

在第一阶段,我们通过一个Intent对象要求系统开启阅读页面,并同时通过这个Intent对象捎来要阅读的笔记的id。那么在阅读页面开启过程中,我们按顺序实现以下步骤,即可获得当前阅读的笔记对象:

  • 获取开启阅读页面的Intent对象
  • Intent对象中读取附加的id值
  • 通过数据仓库接口,根据id值查找到对应的笔记对象

我们在阅读页面的onCreate()方法中依次编写代码实现以下步骤:

获取开启阅读页面的Intent对象并读取附加的id值:

可通过Activity基类自带的getIntent()方法直接获取Intent对象:

        Intent intent = getIntent();
        // 根据插入附加参数时指定的键查找对应的值,如果不存在则取默认值0
        long noteId = intent.getLongExtra(“note_id”, 0);

通过数据仓库接口,根据id值查找到对应的笔记对象

不过目前还没有这样的接口,我们为INoteRepository接口添加这个方法:

public interface INoteRepository {
    ...
    /**
     * 从存储中获取指定的笔记对象
     * @param noteId
     * @return
     */
    Note getNote(long noteId);
}

然后在TestNoteRepository里面实现它:

public class TestNoteRepository implements INoteRepository {
    ...    
    @Override
    public Note getNote(long noteId) {
        // 在所有对象中逐个比对,如果id相等,则返回此对象
        for (Note note : notes) {
            if (note.getId() == noteId) {
                return note;
            }
        }
        // 若数据集中没有能够匹配的对象,则返回null
        return null;
    }
}

接口添加完毕后,我们就可以调用它来获取笔记对象了。如果获取到的对象为null,说明指定要阅读的笔记根本不存在,无法阅读,所以直接关闭页面:

        Note note = TestNoteRepository.getInstance().getNote(noteId);
        if (note == null) {
            finish();
            return;
        }

如果获取到不为空的笔记,则接下来可以将其显示到UI上了。分别为时间标题正文三个文本域设置内容。整体代码如下:

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

        ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);

        Intent intent = getIntent();
        long noteId = intent.getLongExtra(EXTRA_NOTE_ID, 0);
        Note note = TestNoteRepository.getInstance().getNote(noteId);
        if (note == null) {
            finish();
            return;
        }

        TextView timeView = findViewById(R.id.tv_read_time);
        TextView titleView = findViewById(R.id.tv_read_title);
        TextView contentView = findViewById(R.id.tv_read_content);

        timeView.setText(Utils.formatTime(note.getCreateTime()));
        titleView.setText(note.getTitle());
        contentView.setText(note.getContent());

    }

注意,在设置时间的时候,出现了以下调用:

        Utils.formatTime(note.getCreateTime());

没错,因为我们在许多地方都要执行将毫秒值转换成可读的时间格式的操作,因此单独定义一个工具类Utils,并且将之前在全部笔记页面中定义的formatTime()方法迁移到其中,作为公有的静态方法,以便需要的类都可以调用:

package com.jing.app.sn.utils;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Utils {

    public static String formatTime(long time) {
        // 按照给定的格式串("yyyy/MM/dd")转换
        SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
        return format.format(new Date(time));
    }
}

当然,以后如果有类似的功能性操作函数需要供全局使用,同样可以在Utils工具类里面添加。

为了保持代码简洁,我们去全部笔记页面代码中,将原来私有的formatTime()方法删除,并修改对应的调用:

        @Override
        public void onBindViewHolder(NoteViewHolder holder, int position) {
            ...
            holder.createTimeView.setText(Utils.formatTime(note.getCreateTime()));
            ...

运行程序查看效果:


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,195评论 25 707
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,432评论 0 17
  • 5月20日,某商务酒会。由于时间尚早,美女达莲觉得无聊就打开微信。一个红包发了过来,备注是前几天在一竟标会...
    寒塘1840阅读 255评论 0 0
  • 在大部分人所受的教育中,低调是一种好的品质,不论是父母还是老师,都曾教育我们“做人要低调”、“骄兵必败”,可是,在...
    古傲君阅读 1,122评论 0 1
  • 因为这副画造型比较散,型起的太紧了不容易发挥,所以就简单的定一下水平线和水面边缘。 天空亮部:淡黄+多水 暗部:...
    泡沫姑娘阅读 986评论 5 16