Calendar (the calendar for accounting is currently plus or minus)
1. Entity class
public class CalendarBean { //0: current month, 1: last month, 2: next month private int dayType; //Week private String weekOfDay; //date private String day; // actual date 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 & amp; & amp; 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<>(); } // get 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; } /** * Directly set the content of TextView, only used by TextView and its subclasses * * @param id needs to set the ID of the View * @param text the content to be set * @return */ public ViewHolder setText(int id, String text) { if (!TextUtils.isEmpty(text) & amp; & amp; id != 0) { View view = findViewById(id); if (view instanceof TextView) { ((TextView) view).setText(text); } } return this; } }
4. Customized calendar view
import android. content. Context; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.widget.FrameLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.example.bookkeeping.R; import com.example.bookkeeping.activity.adapter.BaseRecycerAdapter; import com.example.bookkeeping.activity.adapter.ViewHolder; import com.example.bookkeeping.activity.bean.CalendarBean; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; 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; //Current display month 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 & amp; & amp; upX - downX > FLIP_DISTANCE) { goLastMonth(); } if (downX - upX > suduY & amp; & amp; 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); // TextView tv_state = viewHolder.findViewById(R.id.tv_state); tv_day.setText(itemVO.getDay()); if (itemVO. getDayType() == 1) { // last month's data 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); } } }; } /** * initialization time * * @param indexDate yyyy-MM-dd */ private void initData(CalendarBean indexDate) { Calendar instance = Calendar. getInstance(); if (indexDate != null) { //custom month instance.setTime(indexDate.getDate()); } //Get the largest date in the current month int maxDate = instance.getActualMaximum(Calendar.DATE); instance.set(Calendar.DAY_OF_MONTH, 1); // Get all the days in the current month 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); } //assign the current month mCurrentMonthCalendar = mDatas. get(0); ArrayList<CalendarBean> calendarBeans = processDay(mDatas); mDatas.clear(); mDatas.addAll(calendarBeans); mAdapter. notifyDataSetChanged(); } /** * Get the current month * * @return */ public String getMonth() { return simpleDateFormat. format(mCurrentMonthCalendar. getDate()); } /** * show next month */ 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); } } /** * show previous month */ 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); } } /** * Splicing the data of the previous month and the data of the next month * * @param signDatas * @return */ private ArrayList<CalendarBean> processDay(ArrayList<CalendarBean> signDatas) { ArrayList<CalendarBean> list = new ArrayList<>(); // Get the number of days that need to be completed in the previous month ArrayList<CalendarBean> lastMonth = getLastMonth(signDatas.get(0)); // Get the number of days that need to be completed in the next month ArrayList<CalendarBean> nextMonth = getNextMonth(signDatas.get(0)); if (lastMonth != null & amp; & amp; lastMonth. size() > 0) { //If there are days in the previous month, add them list. addAll(lastMonth); } if (signDatas != null) { //The number of days in the current month list.addAll(signDatas); } if (nextMonth != null & amp; & amp; nextMonth. size() > 0) { //If there are days in the next month, add them list. addAll(nextMonth); } return list; } /** * Get the number of days that need to be completed in the previous month * * @return */ private ArrayList<CalendarBean> getLastMonth(CalendarBean calendarBean) { // Get the first day of the current month Calendar calendarFirstDay = Calendar. getInstance(); calendarFirstDay.setTime(calendarBean.getDate()); calendarFirstDay.set(Calendar.DAY_OF_MONTH, 1); //The first day of the month is the day of the week int firstDayOfWeek = calendarFirstDay. get(Calendar. DAY_OF_WEEK); if (firstDayOfWeek != 1) { /** * When firstDayOfWeek =1, it is Sunday. The first day of the current month is already on Sunday, so there is no need to add the supplementary days of the previous month */ //Number of days that need to be completed. When needAdd is negative, it means that it is Sunday and 6 days need to be completed int needAdd = firstDayOfWeek - 1; ArrayList<CalendarBean> lastMonth = new ArrayList<>(); for (int i = 0; i < needAdd; i ++ ) { CalendarBean dayBean = new CalendarBean(); //0: current month, 1: last month, 2: next month dayBean.setDayType(1); dayBean.setWeekOfDay(getWeekString(i)); //Get the number of days that need to be completed in the previous month //calendarFirstDay.set(Calendar.DAY_OF_MONTH, 0 - i); calendarFirstDay.add(Calendar.DATE, -1); //Number of days in the previous month 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<>(); } /** * Get the number of days that need to be completed in the next month * * @return */ private ArrayList<CalendarBean> getNextMonth(CalendarBean calendarBean) { // Get the last day of the current month Calendar calendarLastDay = Calendar. getInstance(); calendarLastDay.setTime(calendarBean.getDate()); calendarLastDay. add(Calendar. MONTH, 1); calendarLastDay.set(Calendar.DAY_OF_MONTH, 0); //Get the last day of the current month is the day of the week int nextDayOfWeek = calendarLastDay. get(Calendar. DAY_OF_WEEK); if (nextDayOfWeek != 7) { /** * When nextDayOfWeek =7, it is Saturday, and the last day of the current month is already on Saturday, so there is no need to add the supplementary days of the previous month */ int needAdd = 7 - nextDayOfWeek; ArrayList<CalendarBean> nextMonth = new ArrayList<>(); for (int i = 1; i <= needAdd; i ++ ) { CalendarBean dayBean = new CalendarBean(); //0: current month, 1: last month, 2: next month dayBean.setDayType(2); dayBean.setWeekOfDay(getWeekString(i)); //Get the number of days that need to be completed in the previous month calendarLastDay.set(Calendar.DAY_OF_MONTH, i); //Number of days in the previous month 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 "Sunday"; case 2: return "Monday"; case 3: return "Tuesday"; case 4: return "Wednesday"; case 5: return "Thursday"; case 6: return "Friday"; case 7: return "Saturday"; default: return "out of range"; } } 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="56dp" android:layout_height="68dp"> <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>
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" android:paddingLeft="15dp" android:paddingRight="15dp" 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="day" 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="one" 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="two" 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="three" 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="four" 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="five" 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="six" 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:layout_marginLeft="15dp" android:layout_marginRight="15dp" 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. Activity live fragment xml
<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" /> <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.activity.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>
8. In the activity or in the fragment
iv_billing_calendar_finish.setOnClickListener { finish() } //Change the year and month of the title val month = calendarView!!.month tv_date!!.text = month iv_last_month.setOnClickListener { // last month calendarView!!.goLastMonth() } iv_next_month.setOnClickListener { //Next month calendarView!!.goNextMonth() } //Change the year and month of the title calendarView.setOnMonthChangerListener(CustomCalendarView.OnMonthChangerListener { lastMonth, newMonth -> tv_date.text = newMonth })