Android 纵向双选日历

这个日历的布局分两部分,一部分是显示星期几的LinearLayout,另外就是一个RecyclerView,负责纵向滚动了。

工具类:

bash 复制代码
implementation 'com.blankj:utilcode:1.17.3'

上activity_calendar代码:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:padding="5dp"
            android:text="日" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:padding="5dp"
            android:text="一" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:padding="5dp"
            android:text="二" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:padding="5dp"
            android:text="三" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:padding="5dp"
            android:text="四" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:padding="5dp"
            android:text="五" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:padding="5dp"
            android:text="六" />

    </LinearLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:overScrollMode="never" />
</LinearLayout>

接下来是CalendarActivity

  1. 计算数据
  2. 可以扩展日历的点击事件和范围选择事件
csharp 复制代码
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.blankj.utilcode.constant.TimeConstants;
import com.blankj.utilcode.util.TimeUtils;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;

public class CalendarActivity extends AppCompatActivity {


    private RecyclerView recyclerView;

    private CalendarRangeAdapter calendarRangeAdapter;

    /**
     * 最大月份数
     */
    private int maxMonth = 12;

    private List<DateBean> dateList = new ArrayList<>();

    private List<DateBean> allDateList = new ArrayList<>();


    private Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            initView();
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_calendar);

        recyclerView = findViewById(R.id.recycler_view);

        new Thread() {
            @Override
            public void run() {
                initData();
            }

            ;
        }.start();

    }

    private void initView() {
        GridLayoutManager manager = new GridLayoutManager(CalendarActivity.this, 7);
        manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                return allDateList.get(position).getType() == 0 ? 7 : 1;
            }
        });
        recyclerView.setLayoutManager(manager);
        calendarRangeAdapter = new CalendarRangeAdapter(CalendarActivity.this, allDateList);
        recyclerView.setAdapter(calendarRangeAdapter);
        calendarRangeAdapter.setOnItemSelect(new CalendarRangeAdapter.OnItemSelect() {
            @Override
            public void onItemClick(int position) {

            }


            @Override
            public void onItemRangeSelect(String startDate, String endDate) {
                System.out.println(startDate + "~" + endDate);
            }
        });
    }

    private void initData() {
        for (int i = 0; i < maxMonth; i++) {
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.MONTH, i);

            int year = calendar.get(Calendar.YEAR);
            int month = calendar.get(Calendar.MONTH) + 1;

            int maxDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);

            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
            String dateString = simpleDateFormat.format(calendar.getTime());

            DateBean monthBean = new DateBean();
            monthBean.setDate(dateString.substring(0, 7));
            monthBean.setCanSelect(false);
            monthBean.setType(0);

            dateList.clear();
            dateList.add(monthBean);

            calendar.set(Calendar.DAY_OF_MONTH, 1);

            //当月第一天是周几 0是周日,1是周一 以此类推
            int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
            //填充1号前面的空白
            for (int j = 0; j < dayOfWeek - 1; j++) {
                DateBean dateBean = new DateBean();
                dateBean.setCanSelect(false);
                dateBean.setType(1);
                dateBean.setDate("");
                dateList.add(dateBean);
            }

            for (int j = 0; j < maxDays; j++) {
                DateBean dateBean = new DateBean();
                dateBean.setType(1);
                dateBean.setCenterString(String.valueOf(j + 1));
                dateBean.setSelect(true);
                dateBean.setDate(year + "-" + addZero(month) + "-" + addZero(j + 1));
                //今天之前不可选中
                if (TimeUtils.getTimeSpanByNow(dateBean.getDate()
                        , new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
                        , TimeConstants.DAY) < 0) {
                    dateBean.setCenterString(String.valueOf(j + 1));
                    dateBean.setCanSelect(false);
                } else {
                    if (TimeUtils.getTimeSpanByNow(dateBean.getDate()
                            , new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
                            , TimeConstants.DAY) > 0) {
                        dateBean.setCenterString(String.valueOf(j + 1));
                    } else {
                        dateBean.setCenterString("今天");
                    }
                    dateBean.setCanSelect(true);
                }
                dateList.add(dateBean);
            }

            allDateList.addAll(dateList);
        }

        Message msg = handler.obtainMessage();
        handler.sendMessage(msg);

    }

    private String addZero(int text) {
        if (text < 10) {
            return "0" + text;
        } else {
            return "" + text;
        }
    }
}

上CalendarRangeAdapter代码(根据bean属性操作):

  1. 根据类型,填充月布局和日布局。
  2. 包含点击、范围选择的方法
cpp 复制代码
public class CalendarRangeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private Context context;

    private List<DateBean> list;

    private int[] selectRange = new int[2];


    private final int TYPE_MONTH = 0;
    private final int TYPE_DAY = 1;

    public CalendarRangeAdapter(Context context, List<DateBean> list) {
        this.context = context;
        this.list = list;
        initSelect();
    }

    public void initSelect() {
        clearSelect();
    }


    public void clearSelect() {
        for (int i = 0; i < list.size(); i++) {
            list.get(i).setSelect(false);
            list.get(i).setSelectRange(false);
            list.get(i).setBottomString("");
        }

        selectRange[0] = -1;
        selectRange[1] = -1;

    }

    public void notifySelect() {
        notifyDataSetChanged();
    }


    public void setData(List<DateBean> list) {
        this.list = list;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

        RecyclerView.ViewHolder holder = null;
        if (viewType == 0) {
            //月份
            View view = LayoutInflater.from(context).inflate(R.layout.item_month,
                    parent, false);
            holder = new MonthViewHolder(view);
        } else {
            //日期
            View view = LayoutInflater.from(context).inflate(R.layout.item_day,
                    parent, false);
            holder = new DayViewHolder(view);
        }
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

        final int fPosition = holder.getAdapterPosition();
        if (holder instanceof MonthViewHolder) {
            ((MonthViewHolder) holder).tvMonth.setText(list.get(position).getDate().replace("-", "年") + "月");
        } else {
            final DayViewHolder viewHolder = (DayViewHolder) holder;

            viewHolder.tvCenter.setText(list.get(fPosition).getCenterString());
            viewHolder.tvBottom.setText(list.get(fPosition).getBottomString());
            if (viewHolder.llDay.getTag() instanceof View.OnClickListener){
                viewHolder.llDay.setOnClickListener( null);
            }
            if (list.get(fPosition).isCanSelect()) {
                //今天之前(不可选)
                if (TimeUtils.getTimeSpanByNow(list.get(position).getDate()
                        , new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
                        , TimeConstants.DAY) < 0) {
                    viewHolder.tvCenter.setTextColor(
                            ContextCompat.getColor(context, R.color.color_calendar_can_not_select));
                } else {
                    if (TimeUtils.getTimeSpanByNow(list.get(position).getDate()
                            , new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
                            , TimeConstants.DAY) > 0) {
                        viewHolder.tvCenter.setTextColor(
                                ContextCompat.getColor(context, R.color.color_calendar_can_select));
                    } else {
                        viewHolder.tvCenter.setTextColor(
                                ContextCompat.getColor(context, R.color.color_calendar_today));
                    }
                    if (list.get(fPosition).isSelect()) {
                        viewHolder.llDay.setBackgroundColor(
                                ContextCompat.getColor(context, R.color.color_calendar_background_select));
                        viewHolder.tvCenter.setTextColor(
                                ContextCompat.getColor(context, R.color.color_calendar_select));
                        viewHolder.tvBottom.setTextColor(
                                ContextCompat.getColor(context, R.color.color_calendar_select));
                    } else if (list.get(fPosition).isSelectRange()) {
                        viewHolder.llDay.setBackgroundColor(
                                ContextCompat.getColor(context, R.color.color_calendar_background_select_range));
                        viewHolder.tvCenter.setTextColor(
                                ContextCompat.getColor(context, R.color.color_calendar_select));
                        viewHolder.tvBottom.setTextColor(
                                ContextCompat.getColor(context, R.color.color_calendar_select));
                    } else {
                        viewHolder.llDay.setBackgroundColor(
                                ContextCompat.getColor(context, R.color.color_calendar_background_normal));
                    }


                    if (onItemSelect != null) {
             
                     View.OnClickListener clickListener =    new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                if (selectRange[0] == -1 && selectRange[1] == -1) {
                                    selectRange[0] = fPosition;
                                    list.get(fPosition).setSelect(true);
                                    list.get(fPosition).setBottomString("开始");
                                    onItemSelect.onItemClick(fPosition);
                                    notifyDataSetChanged();

                                } else if (selectRange[0] != -1 && selectRange[1] == -1) {
                                    onItemSelect.onItemClick(fPosition);
                                    if (fPosition < selectRange[0]) {
                                        clearSelect();
                                        selectRange[0] = fPosition;
                                        list.get(fPosition).setSelect(true);
                                        list.get(fPosition).setBottomString("开始");
                                    } else if (fPosition > selectRange[0]) {
                                        selectRange[1] = fPosition;
                                        list.get(fPosition).setSelect(true);
                                        list.get(fPosition).setBottomString("结束");
                                        for (int i = selectRange[0] + 1; i < selectRange[1]; i++) {
                                            list.get(i).setSelectRange(true);
                                            list.get(i).setBottomString("");
                                        }
                                        onItemSelect.onItemRangeSelect(list.get(selectRange[0]).getDate(), list.get(selectRange[1]).getDate());
                                    } else {
                                        clearSelect();
                                    }
                                    notifyDataSetChanged();
                                } else {
                                    clearSelect();
                                    selectRange[0] = fPosition;
                                    list.get(fPosition).setSelect(true);
                                    list.get(fPosition).setBottomString("开始");
                                    onItemSelect.onItemClick(fPosition);
                                    notifyDataSetChanged();
                                }
                            }
                        };
                        viewHolder.llDay.setOnClickListener(clickListener);
                        viewHolder.llDay.setTag(clickListener);
                    }

                }

            } else {
                viewHolder.llDay.setBackgroundColor(ContextCompat.getColor(context, R.color.color_calendar_background_normal));
                viewHolder.tvCenter.setTextColor(ContextCompat.getColor(context, R.color.color_calendar_can_not_select));
            }
        }
    }

    public OnItemSelect onItemSelect;


    public void setOnItemSelect(OnItemSelect onItemSelect) {
        this.onItemSelect = onItemSelect;
    }

    public interface OnItemSelect {
        void onItemClick(int position);

        void onItemRangeSelect(String startDate, String endDate);
    }

    @Override
    public int getItemViewType(int position) {

        return list.get(position).getType() == 0 ? TYPE_MONTH : TYPE_DAY;
    }

    @Override
    public int getItemCount() {
        return list == null ? 0 : list.size();
    }


    class MonthViewHolder extends RecyclerView.ViewHolder {
        TextView tvMonth;

        public MonthViewHolder(View itemView) {
            super(itemView);
            tvMonth = itemView.findViewById(R.id.tv_month);
        }
    }

    class DayViewHolder extends RecyclerView.ViewHolder {
        LinearLayout llDay;
        TextView tvCenter;
        TextView tvBottom;

        public DayViewHolder(View itemView) {
            super(itemView);
            llDay = itemView.findViewById(R.id.ll_day);
            tvCenter = itemView.findViewById(R.id.tv_center);
            tvBottom = itemView.findViewById(R.id.tv_bottom);
        }
    }
}

DateBean代码(中间文字,底部文字,是否可选等属性):

tsx 复制代码
public class DateBean {

    /**
     * 日期
     */
    private String date;
    /**
     * 底部文字
     */
    private String bottomString;
    /**
     * 中间文字
     */
    private String centerString;

    /**
     * 是否可以选择
     */
    private boolean canSelect;
    /**
     * 是否选中
     */
    private boolean isSelect;

    /**是否在选中范围*/
    private boolean isSelectRange;
    /**
     * item type 0月份 1日期
     */
    private int type;


    public String getDate() {
        return date == null ? "" : date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getCenterString() {
        return centerString == null ? "" : centerString;
    }

    public void setCenterString(String centerString) {
        this.centerString = centerString;
    }

    public boolean isCanSelect() {
        return canSelect;
    }

    public void setCanSelect(boolean canSelect) {
        this.canSelect = canSelect;
    }

    public boolean isSelect() {
        return isSelect;
    }

    public void setSelect(boolean select) {
        isSelect = select;
    }

    public boolean isSelectRange() {
        return isSelectRange;
    }

    public void setSelectRange(boolean selectRange) {
        isSelectRange = selectRange;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public String getBottomString() {
        return bottomString == null ? "" : bottomString;
    }

    public void setBottomString(String bottomString) {
        this.bottomString = bottomString;
    }
}

上color.xml(可以自己换):

xml 复制代码
<resources>
    <color name="color_calendar_can_not_select">#dedede</color>
    <color name="color_calendar_can_select">#505050</color>
    <color name="color_calendar_select">#ffffff</color>
    <color name="color_calendar_today">#F67332</color>
    <color name="color_calendar_background_select">#F3BE30</color>
    <color name="color_calendar_background_select_range">#7DF3BE30</color>
    <color name="color_calendar_background_normal">#00000000</color>
</resources>

OK,完毕!

相关推荐
移动开发者1号1 分钟前
剖析 Systrace:定位 UI 线程阻塞的终极指南
android·kotlin
移动开发者1号1 分钟前
深入解析内存抖动:定位与修复实战(Kotlin版)
android·kotlin
whysqwhw2 分钟前
OkHttp深度架构缺陷分析与革命性演进方案
android
Digitally2 小时前
如何将文件从 iPhone 传输到 Android(新指南)
android·ios·iphone
whysqwhw3 小时前
OkHttp深度架构缺陷分析与演进规划
android
用户7093722538513 小时前
Android14 SystemUI NotificationShadeWindowView 加载显示过程
android
木叶丸4 小时前
跨平台方案该如何选择?
android·前端·ios
顾林海4 小时前
Android ClassLoader加载机制详解
android·面试·源码
用户2018792831674 小时前
🎨 童话:Android画布王国的奇妙冒险
android
whysqwhw5 小时前
OkHttp框架的全面深入架构分析
android