上一篇我们实现了一个简单的创建日记记录的页面,今天我们就来实现一个展示所有日记记录的页面。
回忆一下,activity_notes_page.xml布局文件并没有特别指定fragment,任何activity托管fragment的场景,都可
以使用它。下面,为了让该布局更加通用,重命名它为activity_fragment.xml。
重命名完成后,Android Studio会自动为我们替换引用该文件的地方。
该项目大多都是采用fragment来实现,所以上篇中如下代码可能在每个Activity中都会出现,所以我们做进一步的抽象:
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
NoteCreateFragment fragment = new NoteCreateFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
新建CurrencyFragmentActivity 类,让其继承基类BaseActivity:
package com.qiushangge.likenotes.base;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
public abstract class CurrencyFragmentActivity extends BaseActivity {
/**
* 重写父类initActivityView,此处用来挂载fragment
*/
@Override
protected void initActivityView() {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(getFragmentContainer());
if (fragment == null) {
fragment = createFragment();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(getFragmentContainer(), fragment);
fragmentTransaction.commit();
}
}
/**
* 返回承载fragment的布局id
* @return
*/
protected abstract int getFragmentContainer();
/**
* 如果fragment布局没有挂载对应的fragment,那么创建对应的fragment
* @return
*/
protected abstract Fragment createFragment();
}
这里我们重写了initActivityView方法,如果其子类仍然需要进行组件相关的初始化工作,那么继续重写即可。如NotePageActivity修改后的实现:
package com.qiushangge.likenotes.activity;
import androidx.fragment.app.Fragment;
import com.qiushangge.likenotes.R;
import com.qiushangge.likenotes.base.CurrencyFragmentActivity;
import com.qiushangge.likenotes.fragment.NoteCreateFragment;
public class NotePageActivity extends CurrencyFragmentActivity {
@Override
protected int getFragmentContainer() {
return R.id.fragment_container;
}
@Override
protected Fragment createFragment() {
return new NoteCreateFragment();
}
@Override
protected void initActivityData() {
}
@Override
protected void initActivityListener() {
}
@Override
protected int getActivityLayout() {
return R.layout.activity_fragment;
}
@Override
protected void initActivityView() {
super.initActivityView();
}
}
接下来我们就应该创建我们的日记列表了,不过在开始之前我们先来学习下怎么在Android 中使用Gson库。
Gson是Google开源的一个JSON库,被广泛应用在Android开发中。
在Android Studio中添加Gson库
然后检查build.gradle文件
接下来我们再来了解一下Android中的 SharedPreferences。
Sharedpreferences是Android平台上一个轻量级的存储类,用来保存应用程序的各种配置信息,其本质是一个以“键-值”对的方式保存数据的xml文件,其文件保存在/data/data//shared_prefs目录下。
使用Sharedpreferences保存数据可以分为下面几个步骤:
1.使用Activity类的getSharedPreferences方法获得SharedPreferences对象
2.使用SharedPreferences接口的edit获得SharedPreferences.Editor对象
3.通过SharedPreferences.Editor接口的putXXX方法保存key-value对
4.通过SharedPreferences.Editor接口的commit方法保存key-value对
获取数据可以通过下面的步骤:
1.使用Activity类的getSharedPreferences方法获得SharedPreferences对象
2.通过SharedPreferences对象的getXXX方法获取数据
其中getSharedPreferences 方法原型如下:
public abstract SharedPreferences getSharedPreferences (String name,
int mode)
mode的可选值为:
MODE_PRIVATE:只能被自己的应用程序访问
原来还有
MODE_WORLD_READABLE:除了自己访问外还可以被其它应该程序读取
MODE_WORLD_WRITEABLE:除了自己访问外还可以被其它应该程序读取和写入
不过这两个参数在API level 17开始就不推荐使用了。
ok,上一篇我们写完日记后,点击保存按钮会提示保存成功,这里我们利用上面提到的知识点保存日记,并且我们的重新创建一个日记列表用来展示所有的日记。
创建NoteListActivity和NoteListFragment类,并将NoteListActivity设置为启动类:
接下来我们实现NoteListActivity和NoteListFragment类:
public class NoteListActivity extends CurrencyFragmentActivity {
@Override
protected int getFragmentContainer() {
return R.id.fragment_container;
}
@Override
protected Fragment createFragment() {
return new NoteListFragment();
}
@Override
protected void initActivityData() {
}
@Override
protected void initActivityListener() {
}
@Override
protected int getActivityLayout() {
return R.layout.activity_fragment;
}
}
public class NoteListFragment extends BaseFragment {
@Override
protected int getFragmentLayout() {
return R.layout.fragment_note_list;
}
@Override
protected void initFragmentData() {
}
@Override
protected void initFragmentListener() {
}
@Override
protected void initFragmentView() {
}
}
接下来我们还需要添加NoteListFragment 对应的布局文件fragment_note_list:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</androidx.constraintlayout.widget.ConstraintLayout>
ok,此时我们运行程序,界面是一片空白什么都没有显示。
我们要显示的是日记的列表,所以这里我们推荐使用目前功能更为齐全的recyclerview。
第一步如同添加Gson一样,添加recyclerview的依赖,这里选择androids.recycleview.网上不少教程还是会推荐大家使用v7支持包,android推出的androidx我还是作为首选项,当然了选择v7也可以的。
在Android Studio中添加recycleview
我们不会详细去介绍recyclerview的具体内容,这里我们就开发中的简单使用为例来实现我们的项目。如果需要更为细致的教程,请大家自行查阅,更高级的使用如果遇到我们在做解释。
在布局文件中使用recycleview
在我们的fragment_note_list.xml使用recycleview很简单:
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
tools:context=".fragment.NoteListFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/note_recycle_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
这里我们为我们的RecyclerView 添加id方便后续的使用。
RecyclerView视图与fragment 关联
NoteListFragment.java:
定义变量:
private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;
private RecyclerView.LayoutManager layoutManager;
关联RecyclerView视图
@Override
@Override
protected void initFragmentView() {
//获取recyclerView组件
recyclerView = fragmentRoot.findViewById(R.id.note_recycle_view);
//使用布局管理器
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
//指定适配器
adapter = new NoteListAdapter();
recyclerView.setAdapter(adapter);
}
其中android为我们提供了常见的三种布局管理器:
1、LinearLayoutManager 线性布局管理器,支持横向、纵向。
2、 GridLayoutManager 网格布局管理器
3、 StaggeredGridLayoutManager 瀑布流式布局管理器
到这里我们的准备工作还没有完成,首先呢还得准备一个列表内容怎么显示的布局文件note_list_item.xml:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_note_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_note_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
app:layout_constraintTop_toBottomOf="@id/tv_note_title" />
<TextView
android:id="@+id/tv_note_create_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="viewEnd"
app:layout_constraintTop_toBottomOf="@id/tv_note_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
讲述的有些乱,不过呢基本上按着流程走的,见谅。
为RecyclerView准备适配器
新建NoteListAdapter类,让这个适配器继承RecyclerView.Adapter,并为其指定泛型为NoteListAdapter.ViewHolder.其中ViewHolder为NoteListAdapter的一个内部类。
public class NoteListAdapter extends RecyclerView.Adapter<NoteListAdapter.ViewHolder> {
private List<NoteItem> noteItemList;
/**
* 通过构造函数传入数据源
* @param noteItems 数据源
*/
public NoteListAdapter(List<NoteItem> noteItems) {
noteItemList = noteItems;
}
/**
* 创建ViewHolder实例
* @param parent
* @param viewType
* @return
*/
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.note_list_item, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
/**
* 对RecyclerView子项进行赋值
* @param holder
* @param position 当前数据项索引
*/
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
NoteItem noteItem = noteItemList.get(position);
holder.noteTitle.setText(noteItem.getNoteTitle());
holder.noteContent.setText(noteItem.getNoteContent());
holder.noteCreateDate.setText(noteItem.getDateCreate().toString());
}
/**
* 获取数据项大小
* @return
*/
@Override
public int getItemCount() {
return noteItemList.size();
}
/**
* 构造函数需要传入view参数,通常为RecyclerView子项的最外层布局,这里就是note_list_item
*/
static class ViewHolder extends RecyclerView.ViewHolder {
TextView noteTitle;
TextView noteContent;
TextView noteCreateDate;
public ViewHolder(@NonNull View itemView) {
super(itemView);
noteTitle = itemView.findViewById(R.id.tv_note_title);
noteContent = itemView.findViewById(R.id.tv_note_content);
noteCreateDate = itemView.findViewById(R.id.tv_note_create_date);
}
}
}
前面我们已经将NoteListFragment.java中关联RecyclerView的代码在initFragmentView方法中已经写过了,这里在来完善下。
首先导入NoteListAdapter,然后为适配器准备数据。
由于我们的日记列表和创建日记的页面还没有整合在一块,所以这里我们先简单模拟下数据。
public class NoteListFragment extends BaseFragment {
private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;
private RecyclerView.LayoutManager layoutManager;
> private List<NoteItem> noteItemList = new ArrayList<>();
@Override
protected int getFragmentLayout() {
return R.layout.fragment_note_list;
}
@Override
> protected void initFragmentData() {
>
> for (int i = 0; i < 20; i++) {
>
> NoteItem noteItem = new NoteItem();
> noteItem.setNoteTitle("标题".concat(String.valueOf(i)));
> noteItem.setNoteContent("内容".concat(String.valueOf(i)));
> noteItem.setDateCreate(new Date());
> noteItemList.add(noteItem);
> }
> }
@Override
protected void initFragmentListener() {
}
@Override
protected void initFragmentView() {
//获取recyclerView组件
recyclerView = fragmentRoot.findViewById(R.id.note_recycle_view);
//使用布局管理器
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
//指定适配器
> adapter = new NoteListAdapter(noteItemList);
recyclerView.setAdapter(adapter);
}
}
运行程序:
好了,到此为止我们就实现了一个最简单的RecyclerView展示列表。
不知道大家有没有注意到,我们在initFragmentView中使用了noteItemList数据,但是该数据却在initFragmentData被初始化,我们看看父类中的顺序
虽然我们的列表显示出来了,但是将一些基础数据的初始化放在组件初始化之前更为符合逻辑,所以我们修改一下两者的顺序,同理还有BaseActivity中的代码。