Calendar (the accounting calendar currently has additions and subtractions)
1. Entity class
public class CalendarBean {
//0:当月,1:上月,2:下一个月
private int dayType;
//星期
private String weekOfDay;
//日期
private String day;
//实际日期
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public int getDayType() {
return dayType;
}
public void setDayType(int dayType) {
this.dayType = dayType;
}
public String getWeekOfDay() {
return weekOfDay == null ? "" : weekOfDay;
}
public void setWeekOfDay(String weekOfDay) {
this.weekOfDay = weekOfDay == null ? "" : weekOfDay;
}
public String getDay() {
return day == null ? "" : day;
}
public void setDay(String day) {
this.day = day == null ? "" : day;
}
}
2. Adapter
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public abstract class BaseRecycerAdapter<T> extends RecyclerView.Adapter<ViewHolder> {
private List<T> mData;
private Context mContext;
private int mResId;
private OnItemClickListener<T> mItemClickListener;
private OnItemLongClickListener<T> mItemLongClickListener;
public BaseRecycerAdapter(Context context, List<T> datas, int resId) {
this.mContext = context;
this.mData = datas;
this.mResId = resId;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.from(mContext).inflate(mResId, viewGroup, false);
return new ViewHolder(itemView);
}
@Override
public final void onBindViewHolder(@NonNull ViewHolder viewHolder, final int i) {
final T t = mData.get(i);
onBindViewHolder(viewHolder, t, i);
viewHolder.getRootView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mItemClickListener != null) {
mItemClickListener.onItemClick(t, i);
}
}
});
viewHolder.getRootView().setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if (mItemLongClickListener != null) {
mItemLongClickListener.onItemLongClickListener(t, i);
}
return false;
}
});
}
@Override
public int getItemCount() {
if (mData != null && mData.size() > 0) {
return mData.size();
}
return 0;
}
/**
* @param viewHolder
* @param itemVO
* @param position
*/
public abstract void onBindViewHolder(ViewHolder viewHolder, T itemVO, int position);
public interface OnItemClickListener<T> {
void onItemClick(T t, int position);
}
public interface OnItemLongClickListener<T> {
void onItemLongClickListener(T t, int position);
}
public BaseRecycerAdapter setOnItemClickListener(OnItemClickListener listener) {
if (listener != null) {
this.mItemClickListener = listener;
}
return this;
}
public BaseRecycerAdapter setOnItemLongClickListener(OnItemLongClickListener listener) {
this.mItemLongClickListener = listener;
return this;
}
public List<T> getData() {
if (mData == null) {
mData = new ArrayList<>();
}
return mData;
}
public void setData(List<T> mData) {
this.mData = mData;
}
}
3. Adapter
import android.text.TextUtils;
import android.util.SparseArray;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
public class ViewHolder extends RecyclerView.ViewHolder {
private SparseArray<View> mViews;
private View mRootView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
this.mRootView = itemView;
mViews = new SparseArray<>();
}
//获取view
public <T extends View> T findViewById(int id) {
View view = mViews.get(id);
if (view == null) {
view = itemView.findViewById(id);
mViews.put(id, view);
}
return (T) view;
}
public View getRootView() {
return mRootView;
}
/**
* 直接设置TextView的内容,仅限TextView及它的子类使用
*
* @param id 需要设置View的ID
* @param text 需要设置的内容
* @return
*/
public ViewHolder setText(int id, String text) {
if (!TextUtils.isEmpty(text) && id != 0) {
View view = findViewById(id);
if (view instanceof TextView) {
((TextView) view).setText(text);
}
}
return this;
}
}
4. Customized calendar view
public class CustomCalendarView extends FrameLayout {
protected float FLIP_DISTANCE;
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM");
private RecyclerView listView;
private ArrayList<CalendarBean> mDatas = new ArrayList<>();
private BaseRecycerAdapter mAdapter;
//当前显示月份
private CalendarBean mCurrentMonthCalendar;
public CustomCalendarView(@NonNull Context context) {
super(context);
init(context, null);
}
public CustomCalendarView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CustomCalendarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
FLIP_DISTANCE = ViewConfiguration.get(context).getScaledTouchSlop();
View view = LayoutInflater.from(context).inflate(R.layout.view_calendar, this);
listView = view.findViewById(R.id.list_date);
initAdapter();
initData(null);
listView.setLayoutManager(new GridLayoutManager(context, 7));
listView.setAdapter(mAdapter);
}
private float downX;
private float downY;
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
float upX = event.getX();
float upY = event.getY();
float suduY = Math.abs(downY - upY);
if (upX - downX > suduY && upX - downX > FLIP_DISTANCE) {
goLastMonth();
}
if (downX - upX > suduY && downX - upX > FLIP_DISTANCE) {
goNextMonth();
}
break;
}
return super.dispatchTouchEvent(event);
}
private void initAdapter() {
mAdapter = new BaseRecycerAdapter<CalendarBean>(getContext(), mDatas, R.layout.item_scheduling_day) {
@Override
public void onBindViewHolder(ViewHolder viewHolder, CalendarBean itemVO, int position) {
TextView tv_day = viewHolder.findViewById(R.id.tv_day);
ConstraintLayout cl_calendar_selection = viewHolder.findViewById(R.id.cl_calendar_selection);
// TextView tv_state = viewHolder.findViewById(R.id.tv_state);
tv_day.setText(itemVO.getDay());
//点击切换
if (itemVO.getCheck()) {
cl_calendar_selection.setBackground(getContext().getDrawable(R.drawable.calendar_selection));
} else {
cl_calendar_selection.setBackground(getContext().getDrawable(R.color.white));
}
if (itemVO.getDayType() == 1) {
//上个月数据
tv_day.setTextColor(getResources().getColor(R.color.color_999999));
viewHolder.getRootView().setBackgroundColor(getResources().getColor(R.color.color_ffffff));
// tv_state.setVisibility(INVISIBLE);
} else {
viewHolder.getRootView().setBackgroundColor(getResources().getColor(R.color.color_ffffff));
tv_day.setTextColor(getResources().getColor(R.color.color_434343));
// tv_state.setVisibility(VISIBLE);
}
}
};
}
/**
* 初始化时间
*
* @param indexDate yyyy-MM-dd
*/
private void initData(CalendarBean indexDate) {
Calendar instance = Calendar.getInstance();
if (indexDate != null) {
//自定月份
instance.setTime(indexDate.getDate());
}
//获取当前月最大的日期
int maxDate = instance.getActualMaximum(Calendar.DATE);
instance.set(Calendar.DAY_OF_MONTH, 1);
//获取当前月所有的天数
for (int i = 0; i < maxDate; i++, instance.add(Calendar.DATE, 1)) {
int day = instance.get(Calendar.DAY_OF_MONTH);
CalendarBean calendarBean = new CalendarBean();
calendarBean.setDay(String.valueOf(day));
calendarBean.setDate(instance.getTime());
mDatas.add(calendarBean);
}
//赋值当前月份
mCurrentMonthCalendar = mDatas.get(0);
String format = simpleDateFormat.format(mCurrentMonthCalendar.getDate());
String[] split = format.split("-");
Date date = new Date();
instance.setTime(date);
instance.add(Calendar.DATE, 0);
Date time = instance.getTime();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String format1 = simpleDateFormat.format(time);
String[] split1 = format1.split("-");
String s = split1[2];
int i = Integer.parseInt(s);
for (CalendarBean mData : mDatas) {
int i1 = Integer.parseInt(mData.getDay());
//当前时间
if (split[0].equals(split1[0]) && split[1].equals(split1[1]) && i1 == i) {
mData.setCheck(true);
}
}
//点击传入给适配器
mAdapter.setOnItemClickListener(new BaseRecycerAdapter.OnItemClickListener() {
@Override
public void onItemClick(Object o, int position) {
for (CalendarBean mData : mDatas) {
mData.setCheck(false);
}
mDatas.get(position).setCheck(true);
mAdapter.notifyDataSetChanged();
}
});
ArrayList<CalendarBean> calendarBeans = processDay(mDatas);
mDatas.clear();
mDatas.addAll(calendarBeans);
mAdapter.notifyDataSetChanged();
}
/**
* 获取当前月份
*
* @return
*/
public String getMonth() {
return simpleDateFormat.format(mCurrentMonthCalendar.getDate());
}
/**
* 显示下一个月
*/
public void goNextMonth() {
String lastMonth = simpleDateFormat.format(mCurrentMonthCalendar.getDate());
Calendar instance = Calendar.getInstance();
instance.setTime(mCurrentMonthCalendar.getDate());
instance.add(Calendar.MONTH, 1);
CalendarBean tempBean = new CalendarBean();
tempBean.setDate(instance.getTime());
mDatas.clear();
initData(tempBean);
String newMonth = simpleDateFormat.format(tempBean.getDate());
if (listener != null) {
listener.onMonthChanger(lastMonth, newMonth);
}
}
/**
* 显示上一个月
*/
public void goLastMonth() {
String lastMonth = simpleDateFormat.format(mCurrentMonthCalendar.getDate());
Calendar instance = Calendar.getInstance();
instance.setTime(mCurrentMonthCalendar.getDate());
instance.add(Calendar.MONTH, -1);
CalendarBean tempBean = new CalendarBean();
tempBean.setDate(instance.getTime());
mDatas.clear();
initData(tempBean);
String newMonth = simpleDateFormat.format(tempBean.getDate());
if (listener != null) {
listener.onMonthChanger(lastMonth, newMonth);
}
}
/**
* 拼接上一个月数据和下一个月数据
*
* @param signDatas
* @return
*/
private ArrayList<CalendarBean> processDay(ArrayList<CalendarBean> signDatas) {
ArrayList<CalendarBean> list = new ArrayList<>();
//获取上一个月需要补全的天数
ArrayList<CalendarBean> lastMonth = getLastMonth(signDatas.get(0));
//获取下一个月需要补全的天数
ArrayList<CalendarBean> nextMonth = getNextMonth(signDatas.get(0));
if (lastMonth != null && lastMonth.size() > 0) {
//如果有上一个月的天数,进行添加
list.addAll(lastMonth);
}
if (signDatas != null) {
//当前月天数
list.addAll(signDatas);
}
if (nextMonth != null && nextMonth.size() > 0) {
//如果有下一个月的天数,进行添加
list.addAll(nextMonth);
}
return list;
}
/**
* 获取上一个月需要补全的天数
*
* @return
*/
private ArrayList<CalendarBean> getLastMonth(CalendarBean calendarBean) {
//获取当前月第一天
Calendar calendarFirstDay = Calendar.getInstance();
calendarFirstDay.setTime(calendarBean.getDate());
calendarFirstDay.set(Calendar.DAY_OF_MONTH, 1);
//当月第一天是星期几
int firstDayOfWeek = calendarFirstDay.get(Calendar.DAY_OF_WEEK);
if (firstDayOfWeek != 1) {
/**
* firstDayOfWeek =1时,就是星期日,当前月第一天已经处于星期日,不需要添加上一个月补充天数
*/
//需要补全的天数,当needAdd为负数时候,说明是星期日,需要补6天
int needAdd = firstDayOfWeek - 1;
ArrayList<CalendarBean> lastMonth = new ArrayList<>();
for (int i = 0; i < needAdd; i++) {
CalendarBean dayBean = new CalendarBean();
//0:当月,1:上月,2:下一个月
dayBean.setDayType(1);
dayBean.setWeekOfDay(getWeekString(i));
//取出上一个月需要补全的天数
//calendarFirstDay.set(Calendar.DAY_OF_MONTH, 0 - i);
calendarFirstDay.add(Calendar.DATE, -1);
//上一个月的天数
int lastMonthDay = calendarFirstDay.get(Calendar.DAY_OF_MONTH);
dayBean.setDay(String.valueOf(lastMonthDay));
dayBean.setDate(dayBean.getDate());
lastMonth.add(0, dayBean);
}
return lastMonth;
}
return new ArrayList<>();
}
/**
* 获取下一个月需要补全的天数
*
* @return
*/
private ArrayList<CalendarBean> getNextMonth(CalendarBean calendarBean) {
//获取当前月最后一天
Calendar calendarLastDay = Calendar.getInstance();
calendarLastDay.setTime(calendarBean.getDate());
calendarLastDay.add(Calendar.MONTH, 1);
calendarLastDay.set(Calendar.DAY_OF_MONTH, 0);
//获取当前月最后一天是星期几
int nextDayOfWeek = calendarLastDay.get(Calendar.DAY_OF_WEEK);
if (nextDayOfWeek != 7) {
/**
* nextDayOfWeek =7时,就是星期六,当前月最后一天已经处于星期六,不需要添加上一个月补充天数
*/
int needAdd = 7 - nextDayOfWeek;
ArrayList<CalendarBean> nextMonth = new ArrayList<>();
for (int i = 1; i <= needAdd; i++) {
CalendarBean dayBean = new CalendarBean();
//0:当月,1:上月,2:下一个月
dayBean.setDayType(2);
dayBean.setWeekOfDay(getWeekString(i));
//取出上一个月需要补全的天数
calendarLastDay.set(Calendar.DAY_OF_MONTH, i);
//上一个月的天数
int lastMonthDay = calendarLastDay.get(Calendar.DAY_OF_MONTH);
dayBean.setDay(String.valueOf(lastMonthDay));
dayBean.setDate(dayBean.getDate());
nextMonth.add(dayBean);
}
return nextMonth;
}
return new ArrayList<>();
}
private String getWeekString(int i) {
switch (i) {
case 1:
return "星期日";
case 2:
return "星期一";
case 3:
return "星期二";
case 4:
return "星期三";
case 5:
return "星期四";
case 6:
return "星期五";
case 7:
return "星期六";
default:
return "超出范围";
}
}
private OnMonthChangerListener listener;
public void setOnMonthChangerListener(OnMonthChangerListener listener) {
this.listener = listener;
}
public interface OnMonthChangerListener {
void onMonthChanger(String lastMonth, String newMonth);
}
}
5. Items in the adapter
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="55dp"
android:layout_height="68dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_calendar_selection"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="21"
android:textColor="#434343"
android:textSize="13sp"
android:textStyle="bold"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/appCompatTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="+200.00"
android:textColor="#FF7D7E"
android:textSize="12sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_day" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="-0.00"
android:textColor="#3ED1AE"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appCompatTextView" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
6. Customize the xml in the view
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_week"
android:layout_width="match_parent"
android:layout_height="33dp"
android:orientation="horizontal"
app:layout_constraintLeft_toLeftOf="parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="日"
android:textColor="#999999"
android:textSize="14dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="一"
android:textColor="#999999"
android:textSize="14dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="二"
android:textColor="#999999"
android:textSize="14dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="三"
android:textColor="#999999"
android:textSize="14dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="四"
android:textColor="#999999"
android:textSize="14dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="五"
android:textColor="#999999"
android:textSize="14dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="六"
android:textColor="#999999"
android:textSize="14dp" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bud_get_goal_bg"
tools:itemCount="31"
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:listitem="@layout/item_scheduling_day"
tools:spanCount="7" />
</LinearLayout>
7. xml of activity and fragment
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F4F4F6"
android:orientation="vertical"
tools:context=".activity.BillingCalendarActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="19dp"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_billing_calendar_finish"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="19dp"
android:src="@drawable/ic_back_dt"
app:layout_constraintBottom_toBottomOf="@+id/tv_date"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/tv_date" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_billing_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="19dp"
android:src="@drawable/billing_add_img"
app:layout_constraintBottom_toBottomOf="@+id/tv_date"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tv_date" />
<View
android:id="@+id/view_line1"
android:layout_width="0dp"
android:layout_height="10dp"
android:background="#F6F6F6"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="21dp"
android:text="2020-05"
android:textColor="#171717"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/view_line1" />
<ImageView
android:id="@+id/iv_last_month"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="21dp"
android:src="@drawable/ic_arrow_left"
app:layout_constraintBottom_toBottomOf="@id/tv_date"
app:layout_constraintRight_toLeftOf="@id/tv_date"
app:layout_constraintTop_toTopOf="@id/tv_date" />
<ImageView
android:id="@+id/iv_next_month"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="21dp"
android:src="@drawable/ic_arrow_right"
app:layout_constraintBottom_toBottomOf="@id/tv_date"
app:layout_constraintLeft_toRightOf="@id/tv_date"
app:layout_constraintTop_toTopOf="@id/tv_date" />
<View
android:id="@+id/view_line2"
android:layout_width="0dp"
android:layout_height="0.5dp"
android:background="#EDEDED"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_next_month" />
<com.example.bookkeeping.widget.CustomCalendarView
android:id="@+id/calendarView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:background="@drawable/bud_get_goal_bg"
app:layout_constraintTop_toBottomOf="@id/view_line2" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
8. In activity or fragment
iv_billing_calendar_finish.setOnClickListener {
finish()
}
//更改title的年和月
val month = calendarView!!.month
tv_date!!.text = month
iv_last_month.setOnClickListener { //上一个月
calendarView!!.goLastMonth()
}
iv_next_month.setOnClickListener { //下一个月
calendarView!!.goNextMonth()
}
//更改title的年和月
calendarView.setOnMonthChangerListener(CustomCalendarView.OnMonthChangerListener { lastMonth, newMonth ->
tv_date.text = newMonth
})