Browse Source

feat: 🌰 打卡

master
niushuai233 1 year ago
parent
commit
f3ed0059ac
  1. 170
      app/schemas/cc.niushuai.dididone.biz.roomx.Database/1.json
  2. 6
      app/src/main/java/cc/niushuai/dididone/biz/entity/Record.java
  3. 175
      app/src/main/java/cc/niushuai/dididone/ui/calendar/CalendarFragment.java
  4. 50
      app/src/main/res/layout/fragment_calendar.xml

170
app/schemas/cc.niushuai.dididone.biz.roomx.Database/1.json

@ -1,170 +0,0 @@ @@ -1,170 +0,0 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "3b066031a2741d47363ad506910e0a80",
"entities": [
{
"tableName": "t_record",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER, `name` TEXT, `check_date` TEXT, `project_id` INTEGER, `description` TEXT, `create_date` INTEGER, `deleted` INTEGER, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "checkDate",
"columnName": "check_date",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "projectId",
"columnName": "project_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "description",
"columnName": "description",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "createDate",
"columnName": "create_date",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "deleted",
"columnName": "deleted",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "t_project",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER, `name` TEXT, `icon` TEXT, `icon_color` INTEGER, `create_date` INTEGER, `deleted` INTEGER, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "icon",
"columnName": "icon",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "iconColor",
"columnName": "icon_color",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "createDate",
"columnName": "create_date",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "deleted",
"columnName": "deleted",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "t_icon",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER, `icon` TEXT, `color` TEXT, `size` INTEGER, `create_date` INTEGER, `deleted` INTEGER, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "icon",
"columnName": "icon",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "color",
"columnName": "color",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "size",
"columnName": "size",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "createDate",
"columnName": "create_date",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "deleted",
"columnName": "deleted",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3b066031a2741d47363ad506910e0a80')"
]
}
}

6
app/src/main/java/cc/niushuai/dididone/biz/entity/Record.java

@ -22,7 +22,7 @@ public class Record { @@ -22,7 +22,7 @@ public class Record {
private String name;
@ColumnInfo(name = "check_date")
private String checkDate;
private Long checkDate;
@ColumnInfo(name = "project_id")
private Long projectId;
@ -52,11 +52,11 @@ public class Record { @@ -52,11 +52,11 @@ public class Record {
this.name = name;
}
public String getCheckDate() {
public Long getCheckDate() {
return checkDate;
}
public void setCheckDate(String checkDate) {
public void setCheckDate(Long checkDate) {
this.checkDate = checkDate;
}

175
app/src/main/java/cc/niushuai/dididone/ui/calendar/CalendarFragment.java

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
package cc.niushuai.dididone.ui.calendar;
import android.graphics.Color;
import android.content.Context;
import android.icu.text.DateFormat;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
@ -15,7 +16,8 @@ import androidx.annotation.NonNull; @@ -15,7 +16,8 @@ import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.mikepenz.fontawesome_typeface_library.FontAwesome;
import com.github.clans.fab.FloatingActionButton;
import com.github.clans.fab.FloatingActionMenu;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.view.IconicsImageView;
import com.necer.calendar.BaseCalendar;
@ -26,14 +28,29 @@ import com.necer.painter.InnerPainter; @@ -26,14 +28,29 @@ import com.necer.painter.InnerPainter;
import com.necer.utils.CalendarUtil;
import org.joda.time.LocalDate;
import org.reactivestreams.Subscription;
import java.util.List;
import cc.niushuai.dididone.R;
import cc.niushuai.dididone.biz.BizGlobal;
import cc.niushuai.dididone.biz.entity.Project;
import cc.niushuai.dididone.biz.entity.Record;
import cc.niushuai.dididone.biz.roomx.DBManager;
import cc.niushuai.dididone.databinding.FragmentCalendarBinding;
import cc.niushuai.dididone.util.IntSnowflake;
import cc.niushuai.dididone.util.Toasts;
import cc.niushuai.dididone.util.XLog;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import io.reactivex.Completable;
import io.reactivex.CompletableObserver;
import io.reactivex.Flowable;
import io.reactivex.FlowableSubscriber;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public class CalendarFragment extends Fragment {
@ -41,6 +58,8 @@ public class CalendarFragment extends Fragment { @@ -41,6 +58,8 @@ public class CalendarFragment extends Fragment {
private String lastDate;
private List<Project> floatingActionProjectList;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
CalendarViewModel calendarViewModel =
@ -59,6 +78,9 @@ public class CalendarFragment extends Fragment { @@ -59,6 +78,9 @@ public class CalendarFragment extends Fragment {
private void init() {
// initPointList();
// 构建弹出框内容
fetchNewestActionButtonData();
}
private void initPointList(List<String> pointList) {
@ -68,13 +90,85 @@ public class CalendarFragment extends Fragment { @@ -68,13 +90,85 @@ public class CalendarFragment extends Fragment {
private void setListeners() {
// 日历选择变更事件
setCalendarChangeListener();
addCalendarChangeListener();
// floatAction
addFloatActionMenuToggleListener();
}
private void addFloatActionMenuToggleListener() {
binding.didiCheckMenu.setOnMenuToggleListener(new FloatingActionMenu.OnMenuToggleListener() {
@Override
public void onMenuToggle(boolean opened) {
if (opened) {
buildFloatActionButtons(floatingActionProjectList, getContext(), binding.didiCheckMenu);
} else {
// 关闭时清除所有button
binding.didiCheckMenu.removeAllMenuButtons();
}
}
});
}
private void buildFloatActionButtons(List<Project> floatingActionProjectList, Context context, FloatingActionMenu didiCheckMenu) {
for (Project project : floatingActionProjectList) {
FloatingActionButton actionButton = new FloatingActionButton(context);
actionButton.showButtonInMenu(true);
actionButton.show(true);
actionButton.setTag(project.getName() + project.getId());
actionButton.setLabelText(project.getName());
actionButton.setImageDrawable(new IconicsDrawable(context).icon(project.getIcon()).color(project.getIconColor()).sizeDp(24));
actionButton.setButtonSize(FloatingActionButton.SIZE_NORMAL);
actionButton.setColorNormal(getResources().getColor(R.color.white));
actionButton.setColorPressed(getResources().getColor(R.color.white));
actionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 打卡完成 关闭动画
didiCheckMenu.close(true);
// 添加数据
// tabsPagerAdapter.getHomeFragment().add2Top(CheckTypeEnum.QUICK_ADD, actionButton.getLabelText());
Record record = new Record();
record.setProjectId(project.getId());
record.setName(project.getName());
record.setCheckDate(DateUtil.parseDate(lastDate).getTime());
// 插入数据库
Completable completable = DBManager.INSTANCE.recordDao().insertAll(record);
completable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onComplete() {
XLog.i("[{}]打卡成功", project.getName());
Toasts.shortShow(getActivity(), "[{}]打卡成功", project.getName());
// 更新视图
add2Top(project, record);
}
@Override
public void onError(Throwable e) {
XLog.e("[{}]打卡失败, {}", project.getName(), e);
Toasts.shortShow(getActivity(), "[{}]打卡失败, {}", project.getName(), e.getMessage());
}
});
}
});
didiCheckMenu.addMenuButton(actionButton);
}
}
/**
* 日历选择变更事件
*/
private void setCalendarChangeListener() {
private void addCalendarChangeListener() {
binding.miui10Calendar.setOnCalendarChangedListener(new OnCalendarChangedListener() {
@Override
@ -87,17 +181,57 @@ public class CalendarFragment extends Fragment { @@ -87,17 +181,57 @@ public class CalendarFragment extends Fragment {
Lunar lunar = CalendarUtil.getCalendarDate(localDate).lunar;
String lunarDate = lunar.chineseEra + "年" + lunar.lunarMonthStr + lunar.lunarDayStr;
// 当前选中的日期放到最上面
add2Top(new Record(), localDate.toString(), lunarDate);
// 重置上次选中日期
lastDate = localDate.toString();
// 重组日期
rebuildRecordListViewByDate(lastDate);
}
});
}
private void add2Top(Record record, String localDate, String lunarDate) {
private void rebuildRecordListViewByDate(String date) {
DateTime dateTime = DateUtil.parseDate(date);
// 移除所有旧内容项
binding.cCalendarCheckInList.removeAllViews();
// 添加新内容项
Flowable<List<Record>> flowable = DBManager.INSTANCE.recordDao().queryByDate(dateTime.getTime());
flowable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new FlowableSubscriber<List<Record>>() {
@Override
public void onSubscribe(Subscription s) {
// 3s超时
s.request(1);
}
@Override
public void onNext(List<Record> recordList) {
XLog.d("DataListOnNext 查询到数据: {}", recordList.size());
for (Record record : recordList) {
Project project = BizGlobal.CACHE_PROJECT.get(record.getProjectId());
if (null == project) {
Toasts.longShow(getContext(), "ProjectId: {}不存在, 可能存在BUG", String.valueOf(record.getProjectId()));
return;
}
add2Top(project, record);
}
}
@Override
public void onError(Throwable t) {
Toasts.shortShow(getActivity(), "DataList根据时间拉取历史数据失败: ", t.getLocalizedMessage());
}
@Override
public void onComplete() {
}
});
}
private void add2Top(Project project, Record record) {
// 外层layout 白色背景 存在边距
RelativeLayout itemLayout = new RelativeLayout(getContext());
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 175);
@ -113,28 +247,30 @@ public class CalendarFragment extends Fragment { @@ -113,28 +247,30 @@ public class CalendarFragment extends Fragment {
RelativeLayout.LayoutParams iconLayout = new RelativeLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
iconLayout.setMargins(10, 5, 0, 0);
IconicsImageView iconView = new IconicsImageView(getContext());
iconView.setId(IntSnowflake.next_id());
int iconViewId = IntSnowflake.next_id();
iconView.setId(iconViewId);
iconView.setLayoutParams(iconLayout);
IconicsDrawable icon = new IconicsDrawable(getContext());
icon.icon(FontAwesome.Icon.faw_swimmer).color(Color.BLUE).sizeDp(50);
icon.icon(project.getIcon()).color(project.getIconColor()).sizeDp(50);
iconView.setIcon(icon);
itemLayout.addView(iconView);
RelativeLayout.LayoutParams projectLayout = new RelativeLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
projectLayout.setMargins(20, 5, 0, 0);
projectLayout.addRule(RelativeLayout.RIGHT_OF, 111);
TextView projectTextView = this.getTextView(IntSnowflake.next_id(), localDate, projectLayout, R.color._333333, 16, Gravity.CENTER_VERTICAL);
projectLayout.addRule(RelativeLayout.RIGHT_OF, iconViewId);
int projectNameViewId = IntSnowflake.next_id();
TextView projectTextView = this.getTextView(projectNameViewId, record.getName(), projectLayout, R.color._333333, 16, Gravity.CENTER_VERTICAL);
itemLayout.addView(projectTextView);
RelativeLayout.LayoutParams dateLayout = new RelativeLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
dateLayout.setMargins(20, 25, 0, 0);
dateLayout.addRule(RelativeLayout.RIGHT_OF, 111);
dateLayout.addRule(RelativeLayout.BELOW, 222);
TextView dateTextView = this.getTextView(IntSnowflake.next_id(), DateUtil.now(), dateLayout, R.color._666666, 12, Gravity.TOP);
dateLayout.addRule(RelativeLayout.RIGHT_OF, iconViewId);
dateLayout.addRule(RelativeLayout.BELOW, projectNameViewId);
TextView dateTextView = this.getTextView(IntSnowflake.next_id(), DateUtil.date(record.getCreateDate()).toString(DatePattern.NORM_DATETIME_PATTERN), dateLayout, R.color._666666, 12, Gravity.TOP);
itemLayout.addView(dateTextView);
binding.cCalendarCheckInList.removeAllViews();
// binding.cCalendarCheckInList.removeAllViews();
binding.cCalendarCheckInList.addView(itemLayout, 0);
binding.cCalendarCheckInList.requestLayout();
}
@ -156,4 +292,11 @@ public class CalendarFragment extends Fragment { @@ -156,4 +292,11 @@ public class CalendarFragment extends Fragment {
super.onDestroyView();
binding = null;
}
private void fetchNewestActionButtonData() {
Flowable<List<Project>> listFlowable = DBManager.INSTANCE.projectDao().listAll();
listFlowable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(projectList -> floatingActionProjectList = projectList);
}
}

50
app/src/main/res/layout/fragment_calendar.xml

@ -41,11 +41,57 @@ @@ -41,11 +41,57 @@
android:id="@id/c_calendar_check_in_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
android:orientation="vertical"></LinearLayout>
</androidx.core.widget.NestedScrollView>
</com.necer.calendar.Miui10Calendar>
</LinearLayout>
<com.github.clans.fab.FloatingActionMenu
android:id="@+id/didi_check_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|end"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:menu_animationDelayPerItem="50"
app:menu_backgroundColor="@android:color/transparent"
app:menu_buttonSpacing="0dp"
app:menu_colorNormal="#DA4336"
app:menu_colorPressed="#E75043"
app:menu_colorRipple="#99FFFFFF"
app:menu_fab_hide_animation="@anim/fab_scale_down"
app:menu_fab_label=""
app:menu_fab_show_animation="@anim/fab_scale_up"
app:menu_fab_size="normal"
app:menu_icon="@drawable/fab_add"
app:menu_labels_colorNormal="#333333"
app:menu_labels_colorPressed="#444444"
app:menu_labels_colorRipple="#66FFFFFF"
app:menu_labels_cornerRadius="3dp"
app:menu_labels_ellipsize="none"
app:menu_labels_hideAnimation="@anim/fab_slide_out_to_right"
app:menu_labels_margin="0dp"
app:menu_labels_maxLines="-1"
app:menu_labels_padding="8dp"
app:menu_labels_paddingBottom="4dp"
app:menu_labels_paddingLeft="8dp"
app:menu_labels_paddingRight="8dp"
app:menu_labels_paddingTop="4dp"
app:menu_labels_position="left"
app:menu_labels_showAnimation="@anim/fab_slide_in_from_right"
app:menu_labels_showShadow="true"
app:menu_labels_singleLine="false"
app:menu_labels_style="@style/Animation.Design.BottomSheetDialog"
app:menu_labels_textColor="#FFFFFF"
app:menu_labels_textSize="14sp"
app:menu_openDirection="up"
app:menu_shadowColor="#66000000"
app:menu_shadowRadius="4dp"
app:menu_shadowXOffset="1dp"
app:menu_shadowYOffset="3dp"
app:menu_showShadow="true"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Loading…
Cancel
Save