NKq#a(Sk5NA2)%IBCh4yA0w}L^Fu%5JpY_?6
zhzsJUnJqcxQKkp3rCu33Y>({1jd2@N%OSFEwmtRcIOk$>khgFk4UysMXf-Py?m$6)
z0Csx6XzTxH(B@kd&w!cqRbqUqNkO|wGt;rE*U_pmi6yN(G#`+zb9A4dIu2I(Mz|h-
z^X=DE?a&V0k50Kwc|mKOz;aHywP~-Ojl=V^Lg6V3^xoEc1dZ2fj#^Qp(Sb_1W;U}i
z%1W1AG{xqk@z_!!yZ_R>_ubrRRB+MF^;(xuPOAIU8G%dt9zH)LrW$wRH
zx1)lDM=fWKz6wZ|>|;~K(|OaCCRw}qWG)zI3(~y)_2sDl`-0m4@*VgEnE{xemmKMO
z#EQ16ndJ2q+dn)qLjL6#`xhwg`@ts+qRKz5b_tB*<|8^oYQ(}kI>K+9?!E!=|1;TI
z1xPXnZklV!hwwfoFD&oC*v?iDMeI=Fn;lMgo!GietcW#7daW67-_`IC;rm0mp)J0;
z8>(2oaeeib{WTKahU7s0uH{P-p!r-o25iF6oev4YGM0@EjhpB(*LsZ{=b9_|DrDU>
zI+}Nw2_?+;TE;3ENAs6I8!K<)GmuLntN(@M4}yMa-)|%5QuNbJFt_v>2i9o~cU2&w
z+V{Z~g#=$0*>t6WdD1Rlb_MI}%wW;llQ>%WW{&c%!qen?wG?zOjNJ0woP;L%mAN+K
zFbfpg4i55?o`KA=AoO-1($BJE8L_pq0(z6Qk;*!?>gQ(7DI8ce=`e_Ayrws&eoCN*oV&~Gd(O%?T)#9Mi!QxM~*h$K#g*OL1<`^tzhzif!9QK+Q}v3dVn_3Ws-j9&VO4*mgg|+vI5w
z9~ti&a5xl8;Z4-Qs8!u7mX?AnZf?U)
zedtE_K9*uhS(k(l@DB^sOY7)IzAnPr-dHI8cv4Z#rFOA6%YQCaX=doni&RTfz}g`n
zy3OYUFDOKd5`LVZCx)RkELYUz=D{avP(}>Im)$-7b!{;5HL_0q1T@tLwPuJS
zLSp8Gy~VZwYjzlhh^7V$S?C1--2|i?mw*k!w*O>h=Qit9h<859xZWGe@1$iRGMonj
zfN8I}j)4CjQO_2uoe9H3pdJ7cS{NvM4g{i+>(Y+LOS%7~r5fK(m-6F5gjqVnvGNuovicFPIg=RP
z^iWTkweH;Q9%w%wOVC%aEd`
zFmSZ#+v`cIitBEca6ZbLEij}y$JL|sq3fFW$1J*9kP46q7aBKZ<^i9|KMnbdtf~&n
zcz%q6^9`wmC)wzYsrsm{9!&6wYj4hhqJ30B*KFX^>#7P+@_GG7H?pXD`1XnZ&=l;K
zuMzRxY3E(GHP6u-+4o{vdR86$kU_hLcQNQn%Unj|jXDryf##}9O&&{pYlMw^`Xhc5
zSgZzX$J{UQtr7y!!EL8*sjl;SH8IrGyMuqGKG9*F&BgIu*iU3p$SE
zf=x&6hsQ*^Yd3*{SR5wP#_hBB2aE8+e`>t2gEE!B{f?dRR}Wh!V=mRRv!LAF_&0*!UY8S9u
zjw9eD^xyj$jS7pDQSSS{sO35S#IkJ`LmDk(rn=ffD5SdE6Tt0;6f!f~Td7?WTPN1p
z0p9o6>4sFgE>J@4xP}icm3+E?VlXr7lCE;stvrVg-qQX_|V(fi|g4u14>$RS5qE05y4bBa;l#?`+(P=Q|O{nFPY(F5p`T|
zX%A9_9hINBWg*^EEiieg{GGMpye1HDjaF^(a3}BZzhK(=9rQbG6SWQx;{ouMV^qzK
zy=pO|1dUWkaw7LC;aDvN=U=)V6PcA^>|O8o&$z {
+
+ private List list;//日记本列表数据
+ private LayoutInflater inflater;//布局填充器
+
+ private OnItemClickListener onItemClickListener;//点击事件
+
+ @Override
+ public int getItemCount() {
+ return list == null ? 0 : list.size();
+ }
+
+ @NonNull
+ @Override
+ public DiaryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ //布局填充器可以重复使用,所以这里使用懒加载的方式创建
+ if (inflater == null) {
+ inflater = LayoutInflater.from(parent.getContext());
+ }
+ View view = inflater.inflate(R.layout.item_recycler_diary, parent, false);
+ return new DiaryViewHolder(view);
+ }
+
+ @SuppressLint("RecyclerView")
+ @Override
+ public void onBindViewHolder(@NonNull DiaryViewHolder holder, int position) {
+ Diary diary = list.get(position);
+
+ holder.rowLinearLayout.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (onItemClickListener != null) {
+ onItemClickListener.onItemClick(diary, position);
+ }
+ }
+
+ });
+ holder.dateTextView.setText(new SimpleDateFormat("yyyy年MM月dd日").format(diary.getDate()));
+ holder.weatherTextView.setText(diary.getWeather());
+ holder.titleTextView.setText(diary.getTitle());
+ holder.wordCountTextView.setText("字数" + diary.getContent().length() + "个");
+ holder.updateTimeTextView.setText("编辑于" + TimeUtils.getSimpleTime(diary.getUpdateTime().getTime()));
+ }
+
+ /**
+ * 设置新数据
+ */
+ public void setNewData(@Nullable List list) {
+ this.list = list;
+ notifyDataSetChanged();
+ }
+
+ /**
+ * 设置单个日记的点击事件
+ */
+ public void setOnItemClickListener(@Nullable OnItemClickListener listener) {
+ this.onItemClickListener = listener;
+ }
+
+ /**
+ * 视图固定器
+ */
+ public class DiaryViewHolder extends RecyclerView.ViewHolder {
+
+ LinearLayout rowLinearLayout;//行布局控件
+ TextView dateTextView;//日记时间文本控件
+ TextView weatherTextView;//天气文本控件
+ TextView titleTextView;//标题文本控件
+ TextView wordCountTextView;//字数文本控件
+ TextView updateTimeTextView;//修改时间文本控件
+
+ public DiaryViewHolder(@NonNull View itemView) {
+ super(itemView);
+ rowLinearLayout = itemView.findViewById(R.id.row_linearLayout);
+ dateTextView = itemView.findViewById(R.id.date_textView);
+ weatherTextView = itemView.findViewById(R.id.weather_textView);
+ titleTextView = itemView.findViewById(R.id.title_textView);
+ wordCountTextView = itemView.findViewById(R.id.wordCount_textView);
+ updateTimeTextView = itemView.findViewById(R.id.updateTime_textView);
+ }
+
+ }
+
+ /**
+ * 日记的点击事件
+ */
+ public interface OnItemClickListener {
+
+ void onItemClick(Diary diary, int position);
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/dairy/src/main/java/com/app/diary/bean/BaseBean.java b/src/dairy/src/main/java/com/app/diary/bean/BaseBean.java
new file mode 100644
index 0000000..d9ed6dd
--- /dev/null
+++ b/src/dairy/src/main/java/com/app/diary/bean/BaseBean.java
@@ -0,0 +1,9 @@
+package com.app.diary.bean;
+
+import java.io.Serializable;
+
+/**
+ * 数据的基础类
+ */
+public class BaseBean implements Serializable {
+}
diff --git a/src/dairy/src/main/java/com/app/diary/bean/Constant.java b/src/dairy/src/main/java/com/app/diary/bean/Constant.java
new file mode 100644
index 0000000..80656bc
--- /dev/null
+++ b/src/dairy/src/main/java/com/app/diary/bean/Constant.java
@@ -0,0 +1,7 @@
+package com.app.diary.bean;
+
+public class Constant {
+
+ public static String DATA_CHANGE = "data_change";//数据是否改变
+
+}
\ No newline at end of file
diff --git a/src/dairy/src/main/java/com/app/diary/bean/Diary.java b/src/dairy/src/main/java/com/app/diary/bean/Diary.java
new file mode 100644
index 0000000..8137d79
--- /dev/null
+++ b/src/dairy/src/main/java/com/app/diary/bean/Diary.java
@@ -0,0 +1,96 @@
+package com.app.diary.bean;
+
+import androidx.annotation.NonNull;
+import androidx.room.ColumnInfo;
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+import java.util.Date;
+
+/**
+ * 日记
+ */
+@Entity(tableName = "diary")
+public class Diary extends BaseBean {
+
+ @NonNull
+ @PrimaryKey(autoGenerate = true)
+ private Long id;//主键
+
+ @NonNull
+ private Date date;//日期
+
+ @NonNull
+ private String weather;//天气
+
+ @NonNull
+ private String title;//标题
+
+ @NonNull
+ private String content;//内容
+
+ @NonNull
+ @ColumnInfo(name = "create_time")
+ private Date createTime;//创建时间
+
+ @NonNull
+ @ColumnInfo(name = "update_time")
+ private Date updateTime;//修改时间
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getWeather() {
+ return weather;
+ }
+
+ public void setWeather(String weather) {
+ this.weather = weather;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public Date getCreateTime() {
+ return createTime;
+ }
+
+ public void setCreateTime(Date createTime) {
+ this.createTime = createTime;
+ }
+
+ public Date getUpdateTime() {
+ return updateTime;
+ }
+
+ public void setUpdateTime(Date updateTime) {
+ this.updateTime = updateTime;
+ }
+
+}
\ No newline at end of file
diff --git a/src/dairy/src/main/java/com/app/diary/data/DiaryDataSource.java b/src/dairy/src/main/java/com/app/diary/data/DiaryDataSource.java
new file mode 100644
index 0000000..b780a23
--- /dev/null
+++ b/src/dairy/src/main/java/com/app/diary/data/DiaryDataSource.java
@@ -0,0 +1,42 @@
+package com.app.diary.data;
+
+import androidx.annotation.NonNull;
+
+import com.app.diary.bean.Diary;
+
+import java.util.List;
+
+import io.reactivex.rxjava3.core.Completable;
+import io.reactivex.rxjava3.core.Single;
+
+/**
+ * 日记本数据源
+ */
+public interface DiaryDataSource {
+
+ /**
+ * 新增一条日记本
+ */
+ Completable insertDiary(@NonNull Diary diary);
+
+ /**
+ * 删除一条日记本
+ */
+ Completable deleteDiary(long diaryId);
+
+ /**
+ * 修改一条日记本
+ */
+ Completable updateDiary(@NonNull Diary diary);
+
+ /**
+ * 根据主键查询日记本
+ */
+ Single selectOne(long diaryId);
+
+ /**
+ * 查找日记本列表
+ */
+ Single> selectList();
+
+}
\ No newline at end of file
diff --git a/src/dairy/src/main/java/com/app/diary/data/impl/DiaryDataSourceImpl.java b/src/dairy/src/main/java/com/app/diary/data/impl/DiaryDataSourceImpl.java
new file mode 100644
index 0000000..71d19b1
--- /dev/null
+++ b/src/dairy/src/main/java/com/app/diary/data/impl/DiaryDataSourceImpl.java
@@ -0,0 +1,58 @@
+package com.app.diary.data.impl;
+
+import androidx.annotation.NonNull;
+
+import com.app.diary.bean.Diary;
+import com.app.diary.data.DiaryDataSource;
+import com.app.diary.room.database.AppDatabase;
+
+import java.util.Date;
+import java.util.List;
+
+import io.reactivex.rxjava3.core.Completable;
+import io.reactivex.rxjava3.core.Single;
+
+/**
+ * 日志数据源的实现类
+ */
+public class DiaryDataSourceImpl implements DiaryDataSource {
+
+ @NonNull
+ private AppDatabase appDatabase;
+
+ public DiaryDataSourceImpl(@NonNull AppDatabase appDatabase) {
+ this.appDatabase = appDatabase;
+ }
+
+ @Override
+ public Completable insertDiary(@NonNull Diary diary) {
+ Date now = new Date();
+ diary.setCreateTime(now);
+ diary.setUpdateTime(now);
+ return appDatabase.diaryDao().insert(diary);
+ }
+
+ @Override
+ public Completable deleteDiary(long diaryId) {
+ Diary diary = new Diary();
+ diary.setId(diaryId);
+ return appDatabase.diaryDao().delete(diary);
+ }
+
+ @Override
+ public Completable updateDiary(@NonNull Diary diary) {
+ diary.setUpdateTime(new Date());
+ return appDatabase.diaryDao().update(diary);
+ }
+
+ @Override
+ public Single selectOne(long diaryId) {
+ return appDatabase.diaryDao().getOne(diaryId);
+ }
+
+ @Override
+ public Single> selectList() {
+ return appDatabase.diaryDao().getList();
+ }
+
+}
\ No newline at end of file
diff --git a/src/dairy/src/main/java/com/app/diary/room/converter/DateConverter.java b/src/dairy/src/main/java/com/app/diary/room/converter/DateConverter.java
new file mode 100644
index 0000000..16a42ad
--- /dev/null
+++ b/src/dairy/src/main/java/com/app/diary/room/converter/DateConverter.java
@@ -0,0 +1,19 @@
+package com.app.diary.room.converter;
+
+import androidx.room.TypeConverter;
+
+import java.util.Date;
+
+public class DateConverter {
+
+ @TypeConverter
+ public static Date timestampToDate(Long timestamp) {
+ return timestamp == null ? null : new Date(timestamp);
+ }
+
+ @TypeConverter
+ public static Long dateToTimestamp(Date date) {
+ return date == null ? null : date.getTime();
+ }
+
+}
\ No newline at end of file
diff --git a/src/dairy/src/main/java/com/app/diary/room/dao/DiaryDao.java b/src/dairy/src/main/java/com/app/diary/room/dao/DiaryDao.java
new file mode 100644
index 0000000..b9d8559
--- /dev/null
+++ b/src/dairy/src/main/java/com/app/diary/room/dao/DiaryDao.java
@@ -0,0 +1,49 @@
+package com.app.diary.room.dao;
+
+import androidx.room.Dao;
+import androidx.room.Delete;
+import androidx.room.Insert;
+import androidx.room.Query;
+import androidx.room.Update;
+
+import com.app.diary.bean.Diary;
+
+import java.util.List;
+
+import io.reactivex.rxjava3.core.Completable;
+import io.reactivex.rxjava3.core.Single;
+
+@Dao
+public interface DiaryDao {
+
+ /**
+ * 新增日记
+ */
+ @Insert
+ Completable insert(Diary diary);
+
+ /**
+ * 删除日记
+ */
+ @Delete
+ Completable delete(Diary diary);
+
+ /**
+ * 修改日记
+ */
+ @Update
+ Completable update(Diary diary);
+
+ /**
+ * 获取日记列表
+ */
+ @Query("SELECT * FROM diary ORDER BY date DESC")
+ Single> getList();
+
+ /**
+ * 获取日记详情
+ */
+ @Query("SELECT * FROM diary WHERE id = (:diaryId)")
+ Single getOne(long diaryId);
+
+}
\ No newline at end of file
diff --git a/src/dairy/src/main/java/com/app/diary/room/database/AppDatabase.java b/src/dairy/src/main/java/com/app/diary/room/database/AppDatabase.java
new file mode 100644
index 0000000..1553ff3
--- /dev/null
+++ b/src/dairy/src/main/java/com/app/diary/room/database/AppDatabase.java
@@ -0,0 +1,17 @@
+package com.app.diary.room.database;
+
+import androidx.room.Database;
+import androidx.room.RoomDatabase;
+import androidx.room.TypeConverters;
+
+import com.app.diary.bean.Diary;
+import com.app.diary.room.converter.DateConverter;
+import com.app.diary.room.dao.DiaryDao;
+
+@Database(entities = {Diary.class}, version = 2)
+@TypeConverters({DateConverter.class})
+public abstract class AppDatabase extends RoomDatabase {
+
+ public abstract DiaryDao diaryDao();
+
+}
\ No newline at end of file
diff --git a/src/dairy/src/main/java/com/app/diary/ui/BaseActivity.java b/src/dairy/src/main/java/com/app/diary/ui/BaseActivity.java
new file mode 100644
index 0000000..47bcf19
--- /dev/null
+++ b/src/dairy/src/main/java/com/app/diary/ui/BaseActivity.java
@@ -0,0 +1,33 @@
+package com.app.diary.ui;
+
+import android.view.MenuItem;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.navigation.ActivityNavigator;
+
+/**
+ * 页面的基础类
+ */
+public class BaseActivity extends AppCompatActivity {
+
+ /**
+ * 设置菜单点击
+ */
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ //设置标题栏的返回键点击事件
+ if (item.getItemId() == android.R.id.home) {
+ //页面返回
+ getOnBackPressedDispatcher().onBackPressed();
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+ ActivityNavigator.applyPopAnimationsToPendingTransition(this);
+ }
+
+}
diff --git a/src/dairy/src/main/java/com/app/diary/ui/BaseFragment.java b/src/dairy/src/main/java/com/app/diary/ui/BaseFragment.java
new file mode 100644
index 0000000..8d462b4
--- /dev/null
+++ b/src/dairy/src/main/java/com/app/diary/ui/BaseFragment.java
@@ -0,0 +1,38 @@
+package com.app.diary.ui;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.fragment.app.Fragment;
+import androidx.navigation.NavController;
+import androidx.navigation.fragment.NavHostFragment;
+
+/**
+ * Fragment的基础类
+ */
+public class BaseFragment extends Fragment {
+
+ /**
+ * 获取导航控制器
+ */
+ protected NavController getNavController() {
+ return NavHostFragment.findNavController(this);
+ }
+
+ /**
+ * 初始化标题栏
+ *
+ * @param toolbar 标题栏
+ * @param showBack 是否显示返回键
+ */
+ protected void initSupportActionBar(@NonNull Toolbar toolbar, boolean showBack) {
+ if (getActivity() != null && (getActivity() instanceof AppCompatActivity)) {
+ if (toolbar.getTitle() == null) {
+ toolbar.setTitle("");
+ }
+ ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
+ ((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(showBack);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/dairy/src/main/java/com/app/diary/ui/BaseViewModel.java b/src/dairy/src/main/java/com/app/diary/ui/BaseViewModel.java
new file mode 100644
index 0000000..0a06636
--- /dev/null
+++ b/src/dairy/src/main/java/com/app/diary/ui/BaseViewModel.java
@@ -0,0 +1,35 @@
+package com.app.diary.ui;
+
+import android.app.Application;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.AndroidViewModel;
+
+import io.reactivex.rxjava3.disposables.CompositeDisposable;
+import io.reactivex.rxjava3.disposables.Disposable;
+
+public class BaseViewModel extends AndroidViewModel {
+
+ protected boolean loaded;//是否已加载过数据
+ private CompositeDisposable compositeDisposable;
+
+ public BaseViewModel(@NonNull Application application) {
+ super(application);
+ }
+
+ public void addDisposable(@NonNull Disposable disposable) {
+ if (compositeDisposable == null) {
+ compositeDisposable = new CompositeDisposable();
+ }
+ compositeDisposable.add(disposable);
+ }
+
+ @Override
+ protected void onCleared() {
+ super.onCleared();
+ if (compositeDisposable != null) {
+ compositeDisposable.clear();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/dairy/src/main/java/com/app/diary/ui/DiaryBrowseFragment.java b/src/dairy/src/main/java/com/app/diary/ui/DiaryBrowseFragment.java
new file mode 100644
index 0000000..7859676
--- /dev/null
+++ b/src/dairy/src/main/java/com/app/diary/ui/DiaryBrowseFragment.java
@@ -0,0 +1,187 @@
+package com.app.diary.ui;
+
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.view.MenuProvider;
+import androidx.core.widget.NestedScrollView;
+import androidx.lifecycle.Observer;
+import androidx.lifecycle.SavedStateHandle;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.navigation.NavBackStackEntry;
+
+import com.app.diary.R;
+import com.app.diary.bean.Constant;
+import com.app.diary.bean.Diary;
+
+import java.text.SimpleDateFormat;
+
+/**
+ * 日志浏览
+ */
+public class DiaryBrowseFragment extends BaseFragment {
+
+ private Toolbar toolbar;//标题栏控件
+ private NestedScrollView scrollView;//滑动控件
+ private TextView dateTextView;//日期文本控件
+ private TextView weekTextView;//星期文本控件
+ private TextView weatherTextView;//天气文本控件
+ private TextView titleTextView;//标题文本控件
+ private TextView contentTextView;//内容文本控件
+ private TextView errorTextView;//错误文本控件
+
+ private long diaryId;//日记主键
+ private DiaryBrowseViewModel diaryBrowseViewModel;
+ private SavedStateHandle savedStateHandle;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ NavBackStackEntry navBackStackEntry = getNavController().getCurrentBackStackEntry();
+ navBackStackEntry.getSavedStateHandle().getLiveData(Constant.DATA_CHANGE).observe(navBackStackEntry, new Observer