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

相关推荐
2401_8979078640 分钟前
10天学会flutter DAY2 玩转dart 类
android·flutter
m0_748233641 小时前
【PHP】部署和发布PHP网站到IIS服务器
android·服务器·php
Yeats_Liao2 小时前
Spring 定时任务:@Scheduled 注解四大参数解析
android·java·spring
雾里看山4 小时前
【MySQL】 库的操作
android·数据库·笔记·mysql
水瓶丫头站住12 小时前
安卓APP如何适配不同的手机分辨率
android·智能手机
xvch13 小时前
Kotlin 2.1.0 入门教程(五)
android·kotlin
xvch17 小时前
Kotlin 2.1.0 入门教程(七)
android·kotlin
望风的懒蜗牛17 小时前
编译Android平台使用的FFmpeg库
android
浩宇软件开发17 小时前
Android开发,待办事项提醒App的设计与实现(个人中心页)
android·android studio·android开发
ac-er888818 小时前
Yii框架中的多语言支持:如何实现国际化
android·开发语言·php