diff --git a/README.md b/README.md deleted file mode 100644 index 576c717..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# test - diff --git a/doc/1.pptx b/doc/1.pptx new file mode 100644 index 0000000..acc09ad Binary files /dev/null and b/doc/1.pptx differ diff --git a/doc/文档模板-开源软件维护报告文档 .docx b/doc/文档模板-开源软件维护报告文档 .docx new file mode 100644 index 0000000..2b3a13b Binary files /dev/null and b/doc/文档模板-开源软件维护报告文档 .docx differ diff --git a/src/dairy/src/main/AndroidManifest.xml b/src/dairy/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d2bbc47 --- /dev/null +++ b/src/dairy/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/dairy/src/main/ic_launcher-playstore.png b/src/dairy/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..83d1d54 Binary files /dev/null and b/src/dairy/src/main/ic_launcher-playstore.png differ diff --git a/src/dairy/src/main/java/com/app/diary/Mapp.java b/src/dairy/src/main/java/com/app/diary/Mapp.java new file mode 100644 index 0000000..252b622 --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/Mapp.java @@ -0,0 +1,61 @@ +package com.app.diary; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.room.Room; +import androidx.room.migration.Migration; +import androidx.sqlite.db.SupportSQLiteDatabase; + +import com.app.diary.data.DiaryDataSource; +import com.app.diary.data.impl.DiaryDataSourceImpl; +import com.app.diary.room.database.AppDatabase; + +public class Mapp extends Application { + + private static Mapp instance;//单例 + + private AppDatabase appDatabase;//数据库 + private DiaryDataSource diaryDataSource;//日记数据源 + + @Override + public void onCreate() { + super.onCreate(); + instance = this; + } + + /** + * 获取实例 + */ + public static Mapp getInstance() { + return instance; + } + + /** + * 懒加载获取数据库 + */ + public AppDatabase getAppDatabase() { + if (appDatabase == null) { + appDatabase = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "diary.db").addMigrations(new Migration(1, 2) { + + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + // 将SQLite迁移到Room,数据结构未发生变化,所以这里不做任何处理,保持空实现 + } + + }).build(); + } + return appDatabase; + } + + /** + * 懒加载获取日记数据源 + */ + public DiaryDataSource getDiaryDataSource() { + if (diaryDataSource == null) { + diaryDataSource = new DiaryDataSourceImpl(getAppDatabase()); + } + return diaryDataSource; + } + +} \ No newline at end of file diff --git a/src/dairy/src/main/java/com/app/diary/adapter/DiaryRecyclerAdapter.java b/src/dairy/src/main/java/com/app/diary/adapter/DiaryRecyclerAdapter.java new file mode 100644 index 0000000..6047234 --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/adapter/DiaryRecyclerAdapter.java @@ -0,0 +1,114 @@ +package com.app.diary.adapter; + +import android.annotation.SuppressLint; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; + +import com.app.diary.R; +import com.app.diary.bean.Diary; +import com.app.diary.utils.TimeUtils; + +import java.text.SimpleDateFormat; +import java.util.List; + +public class DiaryRecyclerAdapter extends RecyclerView.Adapter { + + 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() { + + @Override + public void onChanged(Object dataChanged) { + if ((boolean) dataChanged) { + if (savedStateHandle != null) { + savedStateHandle.set(Constant.DATA_CHANGE, true); + } + if (diaryBrowseViewModel != null) { + diaryBrowseViewModel.loadData(diaryId, false); + } + } + } + + }); + + initData(); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_diary_browse, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + diaryBrowseViewModel = new ViewModelProvider(this).get(DiaryBrowseViewModel.class); + savedStateHandle = getNavController().getPreviousBackStackEntry().getSavedStateHandle(); + initView(view); + setView(); + diaryBrowseViewModel.loadData(diaryId, true); + } + + /** + * 初始化数据 + */ + private void initData() { + diaryId = DiaryBrowseFragmentArgs.fromBundle(getArguments()).getDiaryId(); + } + + /** + * 初始化控件 + */ + private void initView(@NonNull View view) { + toolbar = view.findViewById(R.id.toolbar); + scrollView = view.findViewById(R.id.scrollView); + dateTextView = view.findViewById(R.id.date_textView); + weekTextView = view.findViewById(R.id.week_textView); + weatherTextView = view.findViewById(R.id.weather_textView); + titleTextView = view.findViewById(R.id.title_textView); + contentTextView = view.findViewById(R.id.content_textView); + errorTextView = view.findViewById(R.id.error_textView); + } + + /** + * 设置控件 + */ + private void setView() { + //将标题栏关联到页面 + initSupportActionBar(toolbar, true); + + //设置菜单 + requireActivity().addMenuProvider(new MenuProvider() { + + @Override + public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.menu_diary_browse, menu); + } + + @Override + public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { + if (menuItem.getItemId() == R.id.action_update) {//跳转到日记编辑页面 + getNavController().navigate(DiaryBrowseFragmentDirections.diaryEditAction(diaryId)); + + } else if (menuItem.getItemId() == R.id.action_delete) {//弹窗提示是否删除日记 + //弹窗提示是否删除日记 + new AlertDialog.Builder(getContext()).setTitle("提示").setMessage("确定要删除日记吗?").setPositiveButton("确定", new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + diaryBrowseViewModel.deleteDiary(diaryId).observe(getViewLifecycleOwner(), new Observer() { + + @Override + public void onChanged(Boolean success) { + if (success) { + savedStateHandle.set(Constant.DATA_CHANGE, true); + getNavController().navigateUp(); + } + } + + }); + } + + }).setNegativeButton("取消", null).create().show(); + } + return false; + } + + }, getViewLifecycleOwner()); + + //观察日记数据并设置日记控件 + diaryBrowseViewModel.getDiaryLiveData().observe(getViewLifecycleOwner(), new Observer() { + + @Override + public void onChanged(Diary diary) { + setDiaryView(diary); + } + + }); + } + + /** + * 设置日记控件 + */ + private void setDiaryView(@Nullable Diary diary) { + if (diary == null) { + errorTextView.setText("未找到该日记"); + errorTextView.setVisibility(View.VISIBLE); + scrollView.setVisibility(View.GONE); + + } else { + errorTextView.setVisibility(View.GONE); + scrollView.setVisibility(View.VISIBLE); + dateTextView.setText(new SimpleDateFormat("yyyy年MM月dd日").format(diary.getDate())); + weekTextView.setText(new SimpleDateFormat("EEEE").format(diary.getDate())); + weatherTextView.setText(diary.getWeather()); + titleTextView.setText(diary.getTitle()); + //处理内容格式,开头空两格,换行空两格 + String content = "\t\t\t\t" + diary.getContent().replace("\n", "\n\t\t\t\t"); + contentTextView.setText(content); + } + } + +} \ No newline at end of file diff --git a/src/dairy/src/main/java/com/app/diary/ui/DiaryBrowseViewModel.java b/src/dairy/src/main/java/com/app/diary/ui/DiaryBrowseViewModel.java new file mode 100644 index 0000000..bc56588 --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/ui/DiaryBrowseViewModel.java @@ -0,0 +1,86 @@ +package com.app.diary.ui; + +import android.app.Application; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import com.app.diary.Mapp; +import com.app.diary.bean.Diary; +import com.app.diary.data.DiaryDataSource; +import com.app.diary.utils.ToastUtils; +import com.app.diary.utils.rxjava.CompletableObserverUtils; +import com.app.diary.utils.rxjava.SingleObserverUtils; + +import androidx.annotation.NonNull; + +import io.reactivex.rxjava3.observers.DisposableCompletableObserver; +import io.reactivex.rxjava3.observers.DisposableSingleObserver; + +public class DiaryBrowseViewModel extends BaseViewModel { + + private MutableLiveData diaryLiveData = new MutableLiveData<>();//日记的数据容器 + + private DiaryDataSource diaryDataSource;//日记数据来源 + + public DiaryBrowseViewModel(@NonNull Application application) { + super(application); + diaryDataSource = ((Mapp) application).getDiaryDataSource(); + } + + /** + * 删除日记 + */ + public LiveData deleteDiary(long diaryId) { + MutableLiveData liveData = new MutableLiveData<>(); + diaryDataSource.deleteDiary(diaryId).compose(CompletableObserverUtils.applyUIScheduler(this)).subscribe(new DisposableCompletableObserver() { + + @Override + public void onComplete() { + ToastUtils.showShort("删除成功"); + liveData.setValue(true); + } + + @Override + public void onError(@NonNull Throwable e) { + ToastUtils.showShort("删除失败, 原因:" + e.getMessage()); + liveData.setValue(false); + } + + }); + return liveData; + } + + /** + * 获取日记的数据容器 + */ + public LiveData getDiaryLiveData() { + return diaryLiveData; + } + + /** + * 加载数据 + */ + public void loadData(long diaryId, boolean lazy) { + if (lazy && loaded) { + return; + } + loaded = true; + + diaryDataSource.selectOne(diaryId).compose(SingleObserverUtils.applyUIScheduler(this)).subscribe(new DisposableSingleObserver() { + + @Override + public void onSuccess(@NonNull Diary diary) { + diaryLiveData.setValue(diary); + } + + @Override + public void onError(@NonNull Throwable e) { + ToastUtils.showShort("获取失败, 原因:" + e.getMessage()); + diaryLiveData.setValue(null); + } + + }); + } + +} \ No newline at end of file diff --git a/src/dairy/src/main/java/com/app/diary/ui/DiaryEditFragment.java b/src/dairy/src/main/java/com/app/diary/ui/DiaryEditFragment.java new file mode 100644 index 0000000..8eff2db --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/ui/DiaryEditFragment.java @@ -0,0 +1,315 @@ +package com.app.diary.ui; + +import android.app.DatePickerDialog; +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.DatePicker; +import android.widget.EditText; +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.lifecycle.Observer; +import androidx.lifecycle.SavedStateHandle; +import androidx.lifecycle.ViewModelProvider; + +import com.app.diary.R; +import com.app.diary.bean.Constant; +import com.app.diary.bean.Diary; +import com.app.diary.utils.ToastUtils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; + +/** + * 编辑日志 + */ +public class DiaryEditFragment extends BaseFragment { + + private static final String[] WEATHERS = new String[]{"晴天", "雨天", "雪天"}; + + private Toolbar toolbar;//标题栏控件 + private TextView dateTextView;//日期文本控件 + private TextView weatherTextView;//天气文本控件 + private EditText titleEditText;//标题输入框控件 + private EditText contentEditText;//内容输入框控件 + + private DatePickerDialog datePickerDialog;//日期选择对话框 + private AlertDialog weatherPickerDialog;//天气选择对话框 + + private long diaryId;//日记主键 + private DiaryEditViewModel diaryEditViewModel; + private SavedStateHandle savedStateHandle; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + initData(); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_diary_edit, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + diaryEditViewModel = new ViewModelProvider(this).get(DiaryEditViewModel.class); + savedStateHandle = getNavController().getPreviousBackStackEntry().getSavedStateHandle(); + initView(view); + setView(); + if (diaryId > 0) { + diaryEditViewModel.loadData(diaryId, true); + } + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + //在页面销毁前,关闭日期选择对话框,防止对话框未关闭报错 + if (datePickerDialog != null && datePickerDialog.isShowing()) { + datePickerDialog.dismiss(); + } + if (weatherPickerDialog != null && weatherPickerDialog.isShowing()) { + weatherPickerDialog.dismiss(); + } + } + + /** + * 初始化数据 + */ + private void initData() { + diaryId = DiaryEditFragmentArgs.fromBundle(getArguments()).getDiaryId(); + } + + /** + * 初始化控件 + */ + private void initView(@NonNull View view) { + toolbar = view.findViewById(R.id.toolbar); + dateTextView = view.findViewById(R.id.date_textView); + weatherTextView = view.findViewById(R.id.weather_textView); + titleEditText = view.findViewById(R.id.title_editText); + contentEditText = view.findViewById(R.id.content_editText); + } + + /** + * 设置控件 + */ + private void setView() { + //将标题栏关联到页面 + initSupportActionBar(toolbar, true); + + //添加菜单 + requireActivity().addMenuProvider(new MenuProvider() { + + @Override + public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.menu_diary_create, menu); + } + + @Override + public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { + if (menuItem.getItemId() == R.id.action_save) {//保存日记 + saveDiary(); + } + return false; + } + + }, getViewLifecycleOwner()); + + //设置日期文本的点击事件 + dateTextView.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + showDatePickerDialog(); + } + + }); + //设置天气文本的点击事件 + weatherTextView.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + showWeatherPickerDialog(); + } + + }); + + if (diaryId > 0) {//修改日记 + diaryEditViewModel.getDiaryLiveData().observe(getViewLifecycleOwner(), new Observer() { + + @Override + public void onChanged(Diary diary) { + if (diary == null) { + ToastUtils.showShort("未找到该日记"); + getNavController().navigateUp(); + + } else { + //设置该日记数据 + toolbar.setTitle("修改日记"); + dateTextView.setText(new SimpleDateFormat("yyyy年MM月dd日").format(diary.getDate())); + weatherTextView.setText(diary.getWeather()); + titleEditText.setText(diary.getTitle()); + contentEditText.setText(diary.getContent()); + } + } + + }); + + } else {//创建日记 + //设置默认值 + toolbar.setTitle("创建日记"); + dateTextView.setText(new SimpleDateFormat("yyyy年MM月dd日").format(new Date())); + weatherTextView.setText(WEATHERS[0]); + } + } + + /** + * 显示日期选择对话框 + */ + private void showDatePickerDialog() { + //懒加载创建日期选择对话框,取当前日期为默认日期 + if (datePickerDialog == null) { + Calendar calendar = Calendar.getInstance(); + try { + String dateStr = dateTextView.getText().toString().trim(); + Date date = new SimpleDateFormat("yyyy年MM月dd日").parse(dateStr); + calendar.setTime(date); + } catch (ParseException e) { + } + int year = calendar.get(Calendar.YEAR); + int month = calendar.get(Calendar.MONTH); + int day = calendar.get(Calendar.DAY_OF_MONTH); + + datePickerDialog = new DatePickerDialog(getContext(), new DatePickerDialog.OnDateSetListener() { + + @Override + public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { + //将选择的年月日组合成文字,显示在日期文本上 + Date date = getDate(year, monthOfYear, dayOfMonth); + String dateStr = new SimpleDateFormat("yyyy年MM月dd日").format(date); + dateTextView.setText(dateStr); + } + + }, year, month, day); + } + if (!datePickerDialog.isShowing()) { + datePickerDialog.show(); + } + } + + /** + * 显示天气选择对话框 + */ + private void showWeatherPickerDialog() { + if (weatherPickerDialog == null) { + String weather = weatherTextView.getText().toString().trim(); + int position = Arrays.asList(WEATHERS).indexOf(weather); + if (position < 0) { + position = 0; + } + weatherPickerDialog = new AlertDialog.Builder(getContext()).setTitle("选择天气").setSingleChoiceItems(WEATHERS, position, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + weatherTextView.setText(WEATHERS[which]); + dialog.dismiss(); + } + + }).create(); + } + if (!weatherPickerDialog.isShowing()) { + weatherPickerDialog.show(); + } + } + + /** + * 根据年月日获取日期函数 + */ + private Date getDate(int year, int monthOfYear, int dayOfMonth) { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, monthOfYear); + calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth); + return calendar.getTime(); + } + + /** + * 保存日记 + */ + private void saveDiary() { + //检查输入情况 + String date = dateTextView.getText().toString().trim(); + if (date.isEmpty()) { + ToastUtils.showShort("未选择日期"); + return; + } + String weather = weatherTextView.getText().toString().trim(); + if (weather.isEmpty()) { + ToastUtils.showShort("未选择天气"); + return; + } + String title = titleEditText.getText().toString().trim(); + if (title.isEmpty()) { + ToastUtils.showShort("未输入标题"); + return; + } + String content = contentEditText.getText().toString().trim(); + if (content.isEmpty()) { + ToastUtils.showShort("未输入内容"); + return; + } + + Date diaryDate; + try { + diaryDate = new SimpleDateFormat("yyyy年MM月dd日").parse(date); + } catch (ParseException e) { + ToastUtils.showShort("保存失败,时间转换错误"); + return; + } + + if (diaryId > 0) {//修改日记 + diaryEditViewModel.updateDiary(diaryId, diaryDate, weather, title, content).observe(getViewLifecycleOwner(), new Observer() { + + @Override + public void onChanged(Boolean success) { + if (success) { + savedStateHandle.set(Constant.DATA_CHANGE, true); + getNavController().navigateUp(); + } + } + + }); + + } else {//创建日记 + diaryEditViewModel.insertDiary(diaryDate, weather, title, content).observe(getViewLifecycleOwner(), new Observer() { + + @Override + public void onChanged(Boolean success) { + if (success) { + savedStateHandle.set(Constant.DATA_CHANGE, true); + getNavController().navigateUp(); + } + } + + }); + } + } + +} \ No newline at end of file diff --git a/src/dairy/src/main/java/com/app/diary/ui/DiaryEditViewModel.java b/src/dairy/src/main/java/com/app/diary/ui/DiaryEditViewModel.java new file mode 100644 index 0000000..47439fb --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/ui/DiaryEditViewModel.java @@ -0,0 +1,145 @@ +package com.app.diary.ui; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import com.app.diary.Mapp; +import com.app.diary.bean.Diary; +import com.app.diary.data.DiaryDataSource; +import com.app.diary.utils.ToastUtils; +import com.app.diary.utils.rxjava.CompletableObserverUtils; +import com.app.diary.utils.rxjava.SingleObserverUtils; + +import java.util.Date; + +import io.reactivex.rxjava3.core.CompletableSource; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.core.SingleEmitter; +import io.reactivex.rxjava3.core.SingleOnSubscribe; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.observers.DisposableCompletableObserver; +import io.reactivex.rxjava3.observers.DisposableSingleObserver; + +public class DiaryEditViewModel extends BaseViewModel { + + private MutableLiveData diaryLiveData = new MutableLiveData<>();//日记的数据容器 + + private DiaryDataSource diaryDataSource;//日记数据来源 + + public DiaryEditViewModel(@NonNull Application application) { + super(application); + diaryDataSource = ((Mapp) application).getDiaryDataSource(); + } + + /** + * 获取日记的数据容器 + */ + public LiveData getDiaryLiveData() { + return diaryLiveData; + } + + /** + * 加载数据 + */ + public void loadData(long diaryId, boolean lazy) { + if (lazy && loaded) { + return; + } + loaded = true; + + diaryDataSource.selectOne(diaryId).compose(SingleObserverUtils.applyUIScheduler(this)).subscribe(new DisposableSingleObserver() { + + @Override + public void onSuccess(Diary diary) { + diaryLiveData.setValue(diary); + } + + @Override + public void onError(@NonNull Throwable e) { + ToastUtils.showShort("获取失败, 原因:" + e.getMessage()); + diaryLiveData.setValue(null); + } + + }); + } + + /** + * 新增日记 + */ + public LiveData insertDiary(Date date, String weather, String title, String content) { + MutableLiveData liveData = new MutableLiveData<>(); + Single.create(new SingleOnSubscribe() { + + @Override + public void subscribe(@NonNull SingleEmitter emitter) throws Throwable { + Diary diary = new Diary(); + diary.setDate(date); + diary.setWeather(weather); + diary.setTitle(title); + diary.setContent(content); + emitter.onSuccess(diary); + } + + }).flatMapCompletable(new Function() { + + @Override + public CompletableSource apply(Diary diary) throws Throwable { + return diaryDataSource.insertDiary(diary); + } + + }).compose(CompletableObserverUtils.applyUIScheduler(this)).subscribe(new DisposableCompletableObserver() { + + @Override + public void onComplete() { + ToastUtils.showShort("新增成功"); + liveData.setValue(true); + } + + @Override + public void onError(@NonNull Throwable e) { + ToastUtils.showShort("新增失败, 原因:" + e.getMessage()); + liveData.setValue(false); + } + + }); + return liveData; + } + + /** + * 修改日记 + */ + public LiveData updateDiary(long diaryId, Date date, String weather, String title, String content) { + MutableLiveData liveData = new MutableLiveData<>(); + diaryDataSource.selectOne(diaryId).flatMapCompletable(new Function() { + + @Override + public CompletableSource apply(Diary diary) throws Throwable { + diary.setDate(date); + diary.setWeather(weather); + diary.setTitle(title); + diary.setContent(content); + return diaryDataSource.updateDiary(diary); + } + + }).compose(CompletableObserverUtils.applyUIScheduler(this)).subscribe(new DisposableCompletableObserver() { + + @Override + public void onComplete() { + ToastUtils.showShort("修改成功"); + liveData.setValue(true); + } + + @Override + public void onError(@NonNull Throwable e) { + ToastUtils.showShort("修改失败, 原因:" + e.getMessage()); + liveData.setValue(false); + } + + }); + return liveData; + } + +} diff --git a/src/dairy/src/main/java/com/app/diary/ui/DiaryListFragment.java b/src/dairy/src/main/java/com/app/diary/ui/DiaryListFragment.java new file mode 100644 index 0000000..1a0e700 --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/ui/DiaryListFragment.java @@ -0,0 +1,125 @@ +package com.app.diary.ui; + +import android.graphics.Rect; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.NavBackStackEntry; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.app.diary.R; +import com.app.diary.adapter.DiaryRecyclerAdapter; +import com.app.diary.bean.Constant; +import com.app.diary.bean.Diary; +import com.app.diary.utils.SizeUtils; + +import java.util.List; + +/** + * 日志列表 + */ +public class DiaryListFragment extends BaseFragment { + + private Toolbar toolbar;//标题栏控件 + private RecyclerView recyclerView;//列表控件 + + private DiaryRecyclerAdapter diaryRecyclerAdapter; + private DiaryListViewModel diaryListViewModel; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + NavBackStackEntry navBackStackEntry = getNavController().getCurrentBackStackEntry(); + navBackStackEntry.getSavedStateHandle().getLiveData(Constant.DATA_CHANGE).observe(navBackStackEntry, new Observer() { + + @Override + public void onChanged(Object dataChanged) { + if ((boolean) dataChanged) { + if (diaryListViewModel != null) { + diaryListViewModel.loadData(false); + } + } + } + + }); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_diary_list, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + diaryListViewModel = new ViewModelProvider(this).get(DiaryListViewModel.class); + initView(view); + setView(); + diaryListViewModel.loadData(true); + } + + /** + * 初始化控件 + */ + private void initView(@NonNull View view) { + toolbar = view.findViewById(R.id.toolbar); + recyclerView = view.findViewById(R.id.recyclerView); + } + + /** + * 设置控件 + */ + private void setView() { + //将标题栏关联到页面 + initSupportActionBar(toolbar, true); + + //设置列表的布局样式 + LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false); + recyclerView.setLayoutManager(linearLayoutManager); + //设置列表的间隔距离 + recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { + + @Override + public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + int count = parent.getAdapter().getItemCount(); + int index = parent.getChildAdapterPosition(view); + if (index < count - 1) { + outRect.set(0, 0, 0, SizeUtils.dp2px(30)); + } + } + + }); + //设置列表的适配器 + diaryRecyclerAdapter = new DiaryRecyclerAdapter(); + diaryRecyclerAdapter.setOnItemClickListener(new DiaryRecyclerAdapter.OnItemClickListener() { + + @Override + public void onItemClick(Diary diary, int position) { + getNavController().navigate(DiaryListFragmentDirections.diaryBrowseAction(diary.getId())); + } + + }); + recyclerView.setAdapter(diaryRecyclerAdapter); + + //观察日记列表数据并将数据加入适配器 + diaryListViewModel.getDiaryListLiveData().observe(getViewLifecycleOwner(), new Observer>() { + + @Override + public void onChanged(List list) { + diaryRecyclerAdapter.setNewData(list); + } + + }); + } + +} \ No newline at end of file diff --git a/src/dairy/src/main/java/com/app/diary/ui/DiaryListViewModel.java b/src/dairy/src/main/java/com/app/diary/ui/DiaryListViewModel.java new file mode 100644 index 0000000..3b51a0c --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/ui/DiaryListViewModel.java @@ -0,0 +1,62 @@ +package com.app.diary.ui; + +import android.app.Application; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import com.app.diary.Mapp; +import com.app.diary.bean.Diary; +import com.app.diary.data.DiaryDataSource; +import com.app.diary.utils.ToastUtils; +import com.app.diary.utils.rxjava.SingleObserverUtils; + +import java.util.List; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.observers.DisposableSingleObserver; + +public class DiaryListViewModel extends BaseViewModel { + + private MutableLiveData> diaryListLiveData = new MutableLiveData<>();//日记列表的数据容器 + + private DiaryDataSource diaryDataSource;//日记数据来源 + + public DiaryListViewModel(@NonNull Application application) { + super(application); + diaryDataSource = ((Mapp) application).getDiaryDataSource(); + } + + /** + * 获取日记列表的数据容器 + */ + public LiveData> getDiaryListLiveData() { + return diaryListLiveData; + } + + /** + * 加载数据 + */ + public void loadData(boolean lazy) { + if (lazy && loaded) { + return; + } + loaded = true; + + diaryDataSource.selectList().compose(SingleObserverUtils.applyUIScheduler(this)).subscribe(new DisposableSingleObserver>() { + + @Override + public void onSuccess(List list) { + diaryListLiveData.setValue(list); + } + + @Override + public void onError(@NonNull Throwable e) { + ToastUtils.showShort("获取失败, 原因:" + e.getMessage()); + diaryListLiveData.setValue(null); + } + + }); + } + +} \ No newline at end of file diff --git a/src/dairy/src/main/java/com/app/diary/ui/IndexFragment.java b/src/dairy/src/main/java/com/app/diary/ui/IndexFragment.java new file mode 100644 index 0000000..206b6b6 --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/ui/IndexFragment.java @@ -0,0 +1,81 @@ +package com.app.diary.ui; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; + +import com.app.diary.R; +import com.app.diary.utils.AppUtils; + +/** + * 首页 + */ +public class IndexFragment extends BaseFragment { + + private Toolbar toolbar;//标题栏控件 + private Button browseButton;//查看日记按钮控件 + private Button createButton;//创建日记按钮控件 + private TextView versionTextView;//当前版本文本控件 + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_index, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + initView(view); + setView(); + } + + /** + * 初始化控件 + */ + private void initView(@NonNull View view) { + toolbar = view.findViewById(R.id.toolbar); + browseButton = view.findViewById(R.id.browse_button); + createButton = view.findViewById(R.id.create_button); + versionTextView = view.findViewById(R.id.version_textView); + } + + /** + * 设置控件 + */ + private void setView() { + //将标题栏关联到页面 + initSupportActionBar(toolbar, false); + + //设置查看日记按钮的点击事件 + browseButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + getNavController().navigate(IndexFragmentDirections.diaryListAction()); + } + + }); + + //设置创建日记按钮的点击事件 + createButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + getNavController().navigate(IndexFragmentDirections.diaryEditAction(0)); + } + + }); + + //将当前版本名称显示在文本上 + versionTextView.setText("当前版本:v" + AppUtils.getVersionName()); + } + +} \ No newline at end of file diff --git a/src/dairy/src/main/java/com/app/diary/ui/MainActivity.java b/src/dairy/src/main/java/com/app/diary/ui/MainActivity.java new file mode 100644 index 0000000..acdb36c --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/ui/MainActivity.java @@ -0,0 +1,21 @@ +package com.app.diary.ui; + +import android.content.pm.ActivityInfo; +import android.os.Bundle; + +import com.app.diary.R; + +/** + * 主页面 + */ +public class MainActivity extends BaseActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + //设置竖屏 + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + +} \ No newline at end of file diff --git a/src/dairy/src/main/java/com/app/diary/utils/AppUtils.java b/src/dairy/src/main/java/com/app/diary/utils/AppUtils.java new file mode 100644 index 0000000..af3ba9b --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/utils/AppUtils.java @@ -0,0 +1,28 @@ +package com.app.diary.utils; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; + +import com.app.diary.Mapp; + +/** + * app工具 + */ +public class AppUtils { + + /** + * 获取版本名称 + */ + public static String getVersionName() { + try { + Context context = Mapp.getInstance(); + PackageManager pm = context.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0); + return pi == null ? "" : pi.versionName; + } catch (PackageManager.NameNotFoundException e) { + return ""; + } + } + +} diff --git a/src/dairy/src/main/java/com/app/diary/utils/SizeUtils.java b/src/dairy/src/main/java/com/app/diary/utils/SizeUtils.java new file mode 100644 index 0000000..cf4bda2 --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/utils/SizeUtils.java @@ -0,0 +1,42 @@ +package com.app.diary.utils; + +import android.content.res.Resources; + +/** + * 尺寸工具 + */ +public class SizeUtils { + + /** + * dp单位转成px单位 + */ + public static int dp2px(float dpValue) { + float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + /** + * px单位转成dp单位 + */ + public static int px2dp(float pxValue) { + float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (pxValue / scale + 0.5f); + } + + /** + * sp单位转成px单位 + */ + public static int sp2px(float spValue) { + float fontScale = Resources.getSystem().getDisplayMetrics().scaledDensity; + return (int) (spValue * fontScale + 0.5f); + } + + /** + * px单位转成sp单位 + */ + public static int px2sp(float pxValue) { + float fontScale = Resources.getSystem().getDisplayMetrics().scaledDensity; + return (int) (pxValue / fontScale + 0.5f); + } + +} diff --git a/src/dairy/src/main/java/com/app/diary/utils/TimeUtils.java b/src/dairy/src/main/java/com/app/diary/utils/TimeUtils.java new file mode 100644 index 0000000..f7daf9f --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/utils/TimeUtils.java @@ -0,0 +1,50 @@ +package com.app.diary.utils; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +/** + * 时间工具 + */ +public class TimeUtils { + + /** + * 获取简要时间 + */ + public static String getSimpleTime(long millis) { + long now = System.currentTimeMillis(); + long span = now - millis; + if (span < 0) { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(millis)); + } else if (span < 1000) { + return "刚刚"; + } else if (span < 60000) { + return String.format(Locale.getDefault(), "%d秒前", span / 1000); + } else if (span < 3600000) { + return String.format(Locale.getDefault(), "%d分钟前", span / 60000); + } + long wee = getWeeOfToday(); + if (millis >= wee) { + return "今天" + new SimpleDateFormat("HH:mm:ss").format(new Date(millis)); + } else if (millis >= wee - 86400000) { + return "昨天" + new SimpleDateFormat("HH:mm:ss").format(new Date(millis)); + } else { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(millis)); + } + } + + /** + * 获取时分秒归0的当前时间 + */ + private static long getWeeOfToday() { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.MILLISECOND, 0); + return calendar.getTimeInMillis(); + } + +} diff --git a/src/dairy/src/main/java/com/app/diary/utils/ToastUtils.java b/src/dairy/src/main/java/com/app/diary/utils/ToastUtils.java new file mode 100644 index 0000000..265d1b8 --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/utils/ToastUtils.java @@ -0,0 +1,19 @@ +package com.app.diary.utils; + +import android.widget.Toast; + +import com.app.diary.Mapp; + +/** + * 吐司工具 + */ +public class ToastUtils { + + /** + * 短时间吐司 + */ + public static void showShort(CharSequence text) { + Toast.makeText(Mapp.getInstance(), text, Toast.LENGTH_SHORT).show(); + } + +} \ No newline at end of file diff --git a/src/dairy/src/main/java/com/app/diary/utils/rxjava/CompletableObserverUtils.java b/src/dairy/src/main/java/com/app/diary/utils/rxjava/CompletableObserverUtils.java new file mode 100644 index 0000000..b240e0f --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/utils/rxjava/CompletableObserverUtils.java @@ -0,0 +1,41 @@ +package com.app.diary.utils.rxjava; + +import androidx.annotation.Nullable; + +import com.app.diary.ui.BaseViewModel; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.CompletableTransformer; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public class CompletableObserverUtils { + + /** + * UI线程调度器 + */ + public static CompletableTransformer applyUIScheduler(@Nullable BaseViewModel viewModel) { + return upstream -> upstream + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .doOnSubscribe(disposable -> { + if (viewModel != null) { + viewModel.addDisposable(disposable); + } + }); + } + + /** + * IO线程调度器 + */ + public static CompletableTransformer applyIOScheduler(@Nullable BaseViewModel viewModel) { + return upstream -> upstream + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()) + .doOnSubscribe(disposable -> { + if (viewModel != null) { + viewModel.addDisposable(disposable); + } + }); + } + +} \ No newline at end of file diff --git a/src/dairy/src/main/java/com/app/diary/utils/rxjava/SingleObserverUtils.java b/src/dairy/src/main/java/com/app/diary/utils/rxjava/SingleObserverUtils.java new file mode 100644 index 0000000..13dba64 --- /dev/null +++ b/src/dairy/src/main/java/com/app/diary/utils/rxjava/SingleObserverUtils.java @@ -0,0 +1,41 @@ +package com.app.diary.utils.rxjava; + +import androidx.annotation.Nullable; + +import com.app.diary.ui.BaseViewModel; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.SingleTransformer; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public class SingleObserverUtils { + + /** + * UI线程调度器 + */ + public static SingleTransformer applyUIScheduler(@Nullable BaseViewModel viewModel) { + return upstream -> upstream + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .doOnSubscribe(disposable -> { + if (viewModel != null) { + viewModel.addDisposable(disposable); + } + }); + } + + /** + * IO线程调度器 + */ + public static SingleTransformer applyIOScheduler(@Nullable BaseViewModel viewModel) { + return upstream -> upstream + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()) + .doOnSubscribe(disposable -> { + if (viewModel != null) { + viewModel.addDisposable(disposable); + } + }); + } + +} \ No newline at end of file diff --git a/src/dairy/src/main/res/drawable/bg_edit.xml b/src/dairy/src/main/res/drawable/bg_edit.xml new file mode 100644 index 0000000..b175628 --- /dev/null +++ b/src/dairy/src/main/res/drawable/bg_edit.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/dairy/src/main/res/drawable/btn_diary.xml b/src/dairy/src/main/res/drawable/btn_diary.xml new file mode 100644 index 0000000..a6ff890 --- /dev/null +++ b/src/dairy/src/main/res/drawable/btn_diary.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/dairy/src/main/res/drawable/btn_full.xml b/src/dairy/src/main/res/drawable/btn_full.xml new file mode 100644 index 0000000..804166f --- /dev/null +++ b/src/dairy/src/main/res/drawable/btn_full.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/dairy/src/main/res/drawable/btn_hollow.xml b/src/dairy/src/main/res/drawable/btn_hollow.xml new file mode 100644 index 0000000..f295793 --- /dev/null +++ b/src/dairy/src/main/res/drawable/btn_hollow.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/dairy/src/main/res/layout/activity_main.xml b/src/dairy/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..a3dc963 --- /dev/null +++ b/src/dairy/src/main/res/layout/activity_main.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/src/dairy/src/main/res/layout/fragment_diary_browse.xml b/src/dairy/src/main/res/layout/fragment_diary_browse.xml new file mode 100644 index 0000000..cc25615 --- /dev/null +++ b/src/dairy/src/main/res/layout/fragment_diary_browse.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/dairy/src/main/res/layout/fragment_diary_edit.xml b/src/dairy/src/main/res/layout/fragment_diary_edit.xml new file mode 100644 index 0000000..bc37526 --- /dev/null +++ b/src/dairy/src/main/res/layout/fragment_diary_edit.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/dairy/src/main/res/layout/fragment_diary_list.xml b/src/dairy/src/main/res/layout/fragment_diary_list.xml new file mode 100644 index 0000000..705ea29 --- /dev/null +++ b/src/dairy/src/main/res/layout/fragment_diary_list.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/dairy/src/main/res/layout/fragment_index.xml b/src/dairy/src/main/res/layout/fragment_index.xml new file mode 100644 index 0000000..e0168c8 --- /dev/null +++ b/src/dairy/src/main/res/layout/fragment_index.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + +