上一篇我們實現了一個簡單的創建日記記錄的頁面,今天我們就來實現一個展示所有日記記錄的頁面。
回憶一下,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中的代碼。