第一步:为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分别显示笔记创建时间、标题以及正文
编辑阅读页面的布局文件实现上述设计。具体设置可以参考下列代码:
注意其中创建了一些新的参数资源,请在相应的文件里添加,并根据自己思路修改调整:
#909090
#000000
16sp
16dp
8dp
22sp
运行程序查看效果:
目前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()));
...
运行程序查看效果: