test #3

Closed
pe479jhp5 wants to merge 0 commits from ayy_branch into main

@ -1,2 +0,0 @@
# test

Binary file not shown.

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".Mapp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.App.NoActionBar"
tools:targetApi="31">
<activity
android:name=".ui.MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

@ -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;
}
}

@ -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<DiaryRecyclerAdapter.DiaryViewHolder> {
private List<Diary> 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<Diary> 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);
}
}

@ -0,0 +1,9 @@
package com.app.diary.bean;
import java.io.Serializable;
/**
*
*/
public class BaseBean implements Serializable {
}

@ -0,0 +1,7 @@
package com.app.diary.bean;
public class Constant {
public static String DATA_CHANGE = "data_change";//数据是否改变
}

@ -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;
}
}

@ -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<Diary> selectOne(long diaryId);
/**
*
*/
Single<List<Diary>> selectList();
}

@ -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<Diary> selectOne(long diaryId) {
return appDatabase.diaryDao().getOne(diaryId);
}
@Override
public Single<List<Diary>> selectList() {
return appDatabase.diaryDao().getList();
}
}

@ -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();
}
}

@ -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<List<Diary>> getList();
/**
*
*/
@Query("SELECT * FROM diary WHERE id = (:diaryId)")
Single<Diary> getOne(long diaryId);
}

@ -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();
}

@ -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);
}
}

@ -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);
}
}
}

@ -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();
}
}
}

@ -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<Object>() {
@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<Boolean>() {
@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<Diary>() {
@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);
}
}
}

@ -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<Diary> diaryLiveData = new MutableLiveData<>();//日记的数据容器
private DiaryDataSource diaryDataSource;//日记数据来源
public DiaryBrowseViewModel(@NonNull Application application) {
super(application);
diaryDataSource = ((Mapp) application).getDiaryDataSource();
}
/**
*
*/
public LiveData<Boolean> deleteDiary(long diaryId) {
MutableLiveData<Boolean> 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<Diary> 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<Diary>() {
@Override
public void onSuccess(@NonNull Diary diary) {
diaryLiveData.setValue(diary);
}
@Override
public void onError(@NonNull Throwable e) {
ToastUtils.showShort("获取失败, 原因:" + e.getMessage());
diaryLiveData.setValue(null);
}
});
}
}

@ -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<Diary>() {
@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<Boolean>() {
@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<Boolean>() {
@Override
public void onChanged(Boolean success) {
if (success) {
savedStateHandle.set(Constant.DATA_CHANGE, true);
getNavController().navigateUp();
}
}
});
}
}
}

@ -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<Diary> diaryLiveData = new MutableLiveData<>();//日记的数据容器
private DiaryDataSource diaryDataSource;//日记数据来源
public DiaryEditViewModel(@NonNull Application application) {
super(application);
diaryDataSource = ((Mapp) application).getDiaryDataSource();
}
/**
*
*/
public LiveData<Diary> 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<Diary>() {
@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<Boolean> insertDiary(Date date, String weather, String title, String content) {
MutableLiveData<Boolean> liveData = new MutableLiveData<>();
Single.create(new SingleOnSubscribe<Diary>() {
@Override
public void subscribe(@NonNull SingleEmitter<Diary> 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<Diary, CompletableSource>() {
@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<Boolean> updateDiary(long diaryId, Date date, String weather, String title, String content) {
MutableLiveData<Boolean> liveData = new MutableLiveData<>();
diaryDataSource.selectOne(diaryId).flatMapCompletable(new Function<Diary, CompletableSource>() {
@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;
}
}

@ -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<Object>() {
@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<List<Diary>>() {
@Override
public void onChanged(List<Diary> list) {
diaryRecyclerAdapter.setNewData(list);
}
});
}
}

@ -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<List<Diary>> diaryListLiveData = new MutableLiveData<>();//日记列表的数据容器
private DiaryDataSource diaryDataSource;//日记数据来源
public DiaryListViewModel(@NonNull Application application) {
super(application);
diaryDataSource = ((Mapp) application).getDiaryDataSource();
}
/**
*
*/
public LiveData<List<Diary>> getDiaryListLiveData() {
return diaryListLiveData;
}
/**
*
*/
public void loadData(boolean lazy) {
if (lazy && loaded) {
return;
}
loaded = true;
diaryDataSource.selectList().compose(SingleObserverUtils.applyUIScheduler(this)).subscribe(new DisposableSingleObserver<List<Diary>>() {
@Override
public void onSuccess(List<Diary> list) {
diaryListLiveData.setValue(list);
}
@Override
public void onError(@NonNull Throwable e) {
ToastUtils.showShort("获取失败, 原因:" + e.getMessage());
diaryListLiveData.setValue(null);
}
});
}
}

@ -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());
}
}

@ -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);
}
}

@ -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 "";
}
}
}

@ -0,0 +1,42 @@
package com.app.diary.utils;
import android.content.res.Resources;
/**
*
*/
public class SizeUtils {
/**
* dppx
*/
public static int dp2px(float dpValue) {
float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* pxdp
*/
public static int px2dp(float pxValue) {
float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* sppx
*/
public static int sp2px(float spValue) {
float fontScale = Resources.getSystem().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
/**
* pxsp
*/
public static int px2sp(float pxValue) {
float fontScale = Resources.getSystem().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
}

@ -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();
}
}

@ -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();
}
}

@ -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);
}
});
}
}

@ -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 <T> SingleTransformer<T, T> applyUIScheduler(@Nullable BaseViewModel viewModel) {
return upstream -> upstream
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(disposable -> {
if (viewModel != null) {
viewModel.addDisposable(disposable);
}
});
}
/**
* IO线
*/
public static <T> SingleTransformer<T, T> applyIOScheduler(@Nullable BaseViewModel viewModel) {
return upstream -> upstream
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.doOnSubscribe(disposable -> {
if (viewModel != null) {
viewModel.addDisposable(disposable);
}
});
}
}

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--失效-->
<item android:state_enabled="false">
<shape android:shape="rectangle">
<corners android:radius="10dp" />
<stroke android:width="1dp" android:color="@color/pink_200" />
</shape>
</item>
<!--焦点-->
<item android:state_focused="true">
<shape android:shape="rectangle">
<corners android:radius="10dp" />
<stroke android:width="2dp" android:color="@color/pink_500" />
</shape>
</item>
<!--正常-->
<item>
<shape android:shape="rectangle">
<corners android:radius="10dp" />
<stroke android:width="1dp" android:color="@color/pink_300" />
</shape>
</item>
</selector>

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--失效-->
<item android:state_enabled="false">
<shape android:shape="rectangle">
<corners android:radius="6dp" />
<solid android:color="@color/transparent" />
<stroke android:width="2dp" android:color="@color/pink_200" />
</shape>
</item>
<!--按压-->
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="6dp" />
<solid android:color="@color/transparent" />
<stroke android:width="2dp" android:color="@color/pink_500" />
</shape>
</item>
<!--正常-->
<item>
<shape android:shape="rectangle">
<corners android:radius="6dp" />
<solid android:color="@color/transparent" />
<stroke android:width="2dp" android:color="@color/pink_300" />
</shape>
</item>
</selector>

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--失效-->
<item android:state_enabled="false">
<shape android:shape="rectangle">
<corners android:radius="60dp" />
<solid android:color="@color/pink_300" />
</shape>
</item>
<!--按压-->
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="60dp" />
<solid android:color="@color/pink_700" />
</shape>
</item>
<!--正常-->
<item>
<shape android:shape="rectangle">
<corners android:radius="60dp" />
<solid android:color="@color/pink_500" />
</shape>
</item>
</selector>

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--失效-->
<item android:state_enabled="false">
<shape android:shape="rectangle">
<corners android:radius="60dp" />
<solid android:color="@color/transparent" />
<stroke android:width="3dp" android:color="@color/pink_300" />
</shape>
</item>
<!--按压-->
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="60dp" />
<solid android:color="@color/transparent" />
<stroke android:width="3dp" android:color="@color/pink_700" />
</shape>
</item>
<!--正常-->
<item>
<shape android:shape="rectangle">
<corners android:radius="60dp" />
<solid android:color="@color/transparent" />
<stroke android:width="3dp" android:color="@color/pink_500" />
</shape>
</item>
</selector>

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_main" />
</FrameLayout>

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="match_parent"
android:background="@color/pink_200"
android:orientation="vertical">
<!-- 标题栏-->
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.App.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/Theme.App.PopupOverlay"
app:title="日记详情" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- 标题文本-->
<TextView
android:id="@+id/title_textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="20dp"
android:gravity="center_horizontal"
android:textColor="@color/pink_500"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:gravity="center"
android:orientation="horizontal">
<!-- 日期文本-->
<TextView
android:id="@+id/date_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/pink_500"
android:textSize="14sp" />
<!-- 星期文本-->
<TextView
android:id="@+id/week_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:textColor="@color/pink_500"
android:textSize="14sp" />
<!-- 天气文本-->
<TextView
android:id="@+id/weather_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:textColor="@color/pink_500"
android:textSize="14sp" />
</LinearLayout>
<!-- 内容文本-->
<TextView
android:id="@+id/content_textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="30dp"
android:gravity="start|top"
android:textColor="@color/pink_500"
android:textSize="18sp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<!-- 错误文本-->
<TextView
android:id="@+id/error_textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="30dp"
android:gravity="center"
android:textColor="@color/pink_500"
android:textSize="18sp"
android:visibility="gone" />
</LinearLayout>

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="match_parent"
android:background="@color/pink_200"
android:orientation="vertical">
<!-- 标题栏-->
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.App.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/Theme.App.PopupOverlay"
app:title="编辑日记" />
</com.google.android.material.appbar.AppBarLayout>
<!-- 标题输入框-->
<EditText
android:id="@+id/title_editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="20dp"
android:background="@drawable/bg_edit"
android:gravity="start|center_vertical"
android:hint="标题..."
android:padding="20dp"
android:textColor="@color/pink_500"
android:textColorHint="@color/pink_300"
android:textSize="20sp" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="20dp">
<!-- 日期文本框-->
<TextView
android:id="@+id/date_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:background="@drawable/bg_edit"
android:gravity="start|top"
android:hint="日期"
android:padding="10dp"
android:textColor="@color/pink_500"
android:textColorHint="@color/pink_300"
android:textSize="14sp" />
<!-- 天气文本框-->
<TextView
android:id="@+id/weather_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:background="@drawable/bg_edit"
android:gravity="end|top"
android:hint="天气"
android:padding="10dp"
android:textColor="@color/pink_500"
android:textColorHint="@color/pink_300"
android:textSize="14sp" />
</FrameLayout>
<!-- 内容输入框-->
<EditText
android:id="@+id/content_editText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="30dp"
android:background="@drawable/bg_edit"
android:gravity="start|top"
android:hint="内容..."
android:padding="20dp"
android:textColor="@color/pink_500"
android:textColorHint="@color/pink_300"
android:textSize="18sp" />
</LinearLayout>

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="match_parent"
android:background="@color/pink_200"
android:orientation="vertical">
<!-- 标题栏-->
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.App.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/Theme.App.PopupOverlay"
app:title="日记列表" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="30dp" />
</LinearLayout>

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="match_parent"
android:background="@color/pink_200"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.App.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/Theme.App.PopupOverlay"
app:title="@string/app_name" />
</com.google.android.material.appbar.AppBarLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/browse_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/btn_full"
android:paddingStart="30dp"
android:paddingTop="20dp"
android:paddingEnd="30dp"
android:paddingBottom="20dp"
android:text="查看日记"
android:textColor="@color/pink_700"
android:textSize="26sp" />
<Button
android:id="@+id/create_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:background="@drawable/btn_hollow"
android:paddingStart="30dp"
android:paddingTop="20dp"
android:paddingEnd="30dp"
android:paddingBottom="20dp"
android:text="创建日记"
android:textColor="@color/pink_700"
android:textSize="26sp" />
</LinearLayout>
</ScrollView>
<TextView
android:id="@+id/version_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="20dp"
android:textColor="@color/pink_300"
android:textSize="12sp" />
</LinearLayout>

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/row_linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/btn_diary"
android:orientation="vertical"
android:paddingStart="15dp"
android:paddingTop="15dp"
android:paddingEnd="15dp"
android:paddingBottom="10dp">
<TextView
android:id="@+id/title_textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:textColor="@color/pink_500"
android:textSize="16sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/date_textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:gravity="start"
android:textColor="@color/pink_500"
android:textSize="14sp" />
<TextView
android:id="@+id/weather_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:textColor="@color/pink_500"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/wordCount_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:textColor="@color/brown"
android:textSize="12sp" />
<TextView
android:id="@+id/updateTime_textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:gravity="end"
android:textColor="@color/brown"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>

@ -0,0 +1,16 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_update"
android:orderInCategory="100"
android:title="编辑"
app:showAsAction="always" />
<item
android:id="@+id/action_delete"
android:orderInCategory="101"
android:title="删除"
app:showAsAction="never" />
</menu>

@ -0,0 +1,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_save"
android:orderInCategory="100"
android:title="保存"
app:showAsAction="always" />
</menu>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/main_graph"
app:startDestination="@+id/index_fragment">
<fragment
android:id="@+id/index_fragment"
android:name="com.app.diary.ui.IndexFragment"
android:label="首页"
tools:layout="@layout/fragment_index">
<action
android:id="@+id/diary_list_action"
app:destination="@id/diary_list_fragment" />
<action
android:id="@+id/diary_edit_action"
app:destination="@id/diary_edit_fragment" />
</fragment>
<fragment
android:id="@+id/diary_list_fragment"
android:name="com.app.diary.ui.DiaryListFragment"
android:label="日记列表页"
tools:layout="@layout/fragment_diary_list">
<action
android:id="@+id/diary_browse_action"
app:destination="@id/diary_browse_fragment" />
</fragment>
<fragment
android:id="@+id/diary_browse_fragment"
android:name="com.app.diary.ui.DiaryBrowseFragment"
android:label="日记浏览页"
tools:layout="@layout/fragment_diary_browse">
<argument
android:name="diaryId"
app:argType="long" />
<action
android:id="@+id/diary_edit_action"
app:destination="@id/diary_edit_fragment" />
</fragment>
<fragment
android:id="@+id/diary_edit_fragment"
android:name="com.app.diary.ui.DiaryEditFragment"
android:label="日记编辑页"
tools:layout="@layout/fragment_diary_edit">
<argument
android:name="diaryId"
app:argType="long" />
</fragment>
</navigation>

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#000000</color>
<color name="white">#FFFFFF</color>
<color name="brown">#D8C4B6</color>
<color name="transparent">#00000000</color>
<color name="ic_launcher_background">#FF8989</color>
<color name="pink_200">#FFEADD</color>
<color name="pink_300">#FCAEAE</color>
<color name="pink_500">#FF8989</color>
<color name="pink_700">#FF6666</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
</resources>

@ -0,0 +1,3 @@
<resources>
<string name="app_name">日记本</string>
</resources>

@ -0,0 +1,32 @@
<resources>
<style name="Theme.App" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- 主色调 -->
<item name="colorPrimary">@color/pink_300</item>
<item name="colorPrimaryVariant">@color/pink_500</item>
<item name="colorOnPrimary">@color/white</item>
<!-- 次色调 -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- 状态栏颜色 -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
</style>
<style name="Theme.App.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="actionOverflowMenuStyle">@style/OverflowMenuStyle</item>
</style>
<style name="Theme.App.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="Theme.App.PopupOverlay" parent="ThemeOverlay.AppCompat.Light">
<item name="android:textColorPrimary">@color/white</item>
</style>
<style name="OverflowMenuStyle" parent="@style/Widget.MaterialComponents.PopupMenu.Overflow">
<item name="android:popupBackground">@color/pink_300</item>
</style>
</resources>

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older that API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>
Loading…
Cancel
Save