Calendar (the calendar for accounting is currently plus or minus)

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