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,完毕!

相关推荐
二流小码农13 分钟前
鸿蒙开发:上架困难?谈谈我的上架之路
android·ios·harmonyos
Propeller32 分钟前
【Android】动态操作 Window 的背后机制
android·java
张风捷特烈1 小时前
Flutter&TolyUI#12 | 树形组件 toly_tree 重磅推出!
android·前端·flutter
柯南二号1 小时前
【大前端】【Android】一文详解Android MVVM 模式详情解析
android·前端
feathered-feathered1 小时前
Redis【事务】(面试相关)与MySQL相比较,重点在Redis事务
android·java·redis·后端·mysql·中间件·面试
Kapaseker1 小时前
三分钟搞懂 Kotlin Flow 中的背压
android·kotlin
柯南二号1 小时前
【大前端】【Android】把 Activity 重构成 MVVM 的对比示例
android·状态模式
某空m2 小时前
【Android】Glide的缓存机制
android·缓存·glide
某空m2 小时前
【Android】Glide的使用
android·glide
QING6182 小时前
Jetpack Compose 中的 ViewModel 作用域管理 —— 新手指南
android·kotlin·android jetpack