Android 纵向双选日历

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

纵向双选日历

工具类:

implementation 'com.blankj:utilcode:1.17.3'

上activity_calendar代码:

<?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"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:padding="5dp"android:text="" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:padding="5dp"android:text="" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:padding="5dp"android:text="" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:padding="5dp"android:text="" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:padding="5dp"android:text="" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:padding="5dp"android:text="" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:padding="5dp"android:text="" /></LinearLayout><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recycler_view"android:layout_width="match_parent"android:layout_height="match_parent"android:overScrollMode="never" />
</LinearLayout>

接下来是CalendarActivity

  1. 计算数据
  2. 可以扩展日历的点击事件和范围选择事件
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()) {@Overridepublic void handleMessage(Message msg) {initView();super.handleMessage(msg);}};@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_calendar);recyclerView = findViewById(R.id.recycler_view);new Thread() {@Overridepublic void run() {initData();};}.start();}private void initView() {GridLayoutManager manager = new GridLayoutManager(CalendarActivity.this, 7);manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {@Overridepublic 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() {@Overridepublic void onItemClick(int position) {}@Overridepublic 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. 包含点击、范围选择的方法
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@Overridepublic 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;}@Overridepublic 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() {@Overridepublic 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);}@Overridepublic int getItemViewType(int position) {return list.get(position).getType() == 0 ? TYPE_MONTH : TYPE_DAY;}@Overridepublic 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代码(中间文字,底部文字,是否可选等属性):

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(可以自己换):

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/307076.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【CSS面试题】Flex实现九宫格

考察知识&#xff1a; flex布局 水平垂直居中的实现 初始效果 代码关键&#xff1a;给父盒子添加以下属性 flex-wrap: wrap; /* 允许换行 */justify-content: space-around; /* 主轴对齐方式 */align-content: space-around; /* 多行在侧轴上的对齐方式 */<!DOCTYPE html&…

关于阿里云centos系统下宝塔面板部署django/中pip install mysqlclient失败问题的大总结/阿里云使用oss长期访问凭证

python版本3.12.0 项目的版本依赖 问题1 解决方案 sudo vim /etc/profile export MYSQLCLIENT_CFLAGS"-I/usr/include/mysql" export MYSQLCLIENT_LDFLAGS"-L/usr/lib64/mysql" Esc退出编辑模式 &#xff1a;wq退出并且保存 问题二 说是找不到 mysql.h头…

大数据入门之如何利用Phoenix访问Hbase

在大数据的世界里&#xff0c;HBase和Phoenix可谓是一对黄金搭档。HBase以其高效的列式存储和强大的数据扩展能力&#xff0c;成为大数据存储领域的佼佼者&#xff1b;而Phoenix则以其SQL化的操作方式&#xff0c;简化了对HBase的访问过程。今天&#xff0c;就让我们一起看看如…

LC 515.在每个树行中找最大值

515. 在每个树行中找最大值 给定一棵二叉树的根节点 root &#xff0c;请找出该二叉树中每一层的最大值。 示例1&#xff1a; 输入: root [1,3,2,5,3,null,9] 输出: [1,3,9] 示例2&#xff1a; 输入: root [1,2,3] 输出: [1,3] 提示&#xff1a; 二叉树的节点个数的范围是…

【单片机毕业设计8-基于stm32c8t6的RFID校园门禁系统】

【单片机毕业设计8-基于stm32c8t6的RFID校园门禁系统】 前言一、功能介绍二、硬件部分三、软件部分总结 前言 &#x1f525;这里是小殷学长&#xff0c;单片机毕业设计篇8基于stm32的RFID校园门禁系统 &#x1f9ff;创作不易&#xff0c;拒绝白嫖可私 一、功能介绍 -----------…

【SVN】clean up报错:Cleanup failed to process the following paths 解决方法

报错来源&#xff1a;代码更新有一个文件既不能接受自己的也不能接受别人的&#xff0c;只能取消&#xff0c;再提交提醒clean up&#xff0c;随后报标题错误。 解决方法&#xff1a;参考https://www.cnblogs.com/pinpin/p/11395438.html 注&#xff1a;如果clean up的时候有…

Python(10):常见的4种设计模式(单例/工厂/策略/观察者)

文章目录 一、单例模式二、工厂模式三、策略模式四、观察者模式 程序中设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案&#xff0c;这些解决方案是众多软件按开发人员经过相当长的一段时间的实验和错误总结出来的。使用设计模式是为了重用代码、让代码更容易…

SSL数字证书

SSL数字证书产品提供商主要来自于国外&#xff0c;尤其是美国&#xff0c;原理和使用操作系统一样&#xff0c;区别在于SSL数字证书目前无法替代性&#xff0c;要想达到兼容性99%的机构目前全球才3-4家&#xff0c;目前国内的主流网站主要使用的是国际证书&#xff0c;除了考虑…

文章分享:《二代测序临床报告解读指引》

&#xff3b;摘要&#xff3d; 二代测序&#xff08;next generation sequencing&#xff0c;NGS&#xff09;已成为中国临床肿瘤医生常用检测工具&#xff0c;而中国超 90%临床医生需要 NGS 报告解读支持。因此&#xff0c;为提升临床医生 NGS 报告解读能力&#xff0c;特编写…

django基于python的法院执法案件管理系统

本课题使用Python语言进行开发。代码层面的操作主要在PyCharm中进行&#xff0c;将系统所使用到的表以及数据存储到MySQL数据库中&#xff0c;方便对数据进行操作本课题基于WEB的开发平台&#xff0c;设计的基本思路是&#xff1a; 框架&#xff1a;django/flask 后端&#xff…

AWE2024酷开科技智能家居,让生活从此更智能!

随着科技的飞速发展&#xff0c;智能家居已经成为了人们生活中不可或缺的一部分。在这个领域里&#xff0c;酷开科技品类逐渐丰富&#xff0c;在AWE2024展会上展现出耀眼光芒&#xff0c;将全品类智能家电新品集结亮相&#xff01;让人们的生活更加便捷、舒适和智能化。 酷开K…

C++的stack和queue类(三):适配所有容器的反向迭代器

目录 前言 list的反向迭代器 list.h文件 ReverseIterator.h文件 test.cpp文件 前言 迭代器按性质分类&#xff1a; 单向&#xff1a;forward_list双向&#xff1a;list随机&#xff1a;vector / deque 迭代器按功能分类&#xff1a; 正向反向const list的反向迭代器…

elementUI 下拉框加提示文案

效果如下&#xff1a; 展示文案在最下面&#xff0c;跟选项有个分割线 <el-select v-model"value" placeholder"请选择" clearable popper-class"addNotice" class"addNoticeS" visible-change"(v) >selectNotice(v,展示…

独一无二:探索单例模式在现代编程中的奥秘与实践

设计模式在软件开发中扮演着至关重要的角色&#xff0c;它们是解决特定问题的经典方法。在众多设计模式中&#xff0c;单例模式因其独特的应用场景和简洁的实现而广受欢迎。本文将从多个角度详细介绍单例模式&#xff0c;帮助你理解它的定义、实现、应用以及潜在的限制。 1. 什…

书生·浦语大模型实战营 | 第3次学习笔记

前言 书生浦语大模型应用实战营 第二期正在开营&#xff0c;欢迎大家来学习。&#xff08;参与链接&#xff1a;https://mp.weixin.qq.com/s/YYSr3re6IduLJCAh-jgZqg 第三堂课的视频链接&#xff1a;https://www.bilibili.com/video/BV1QA4m1F7t4/ 本次笔记是学习完第三堂课…

vueRouter动态路由(实现菜单权限控制)

一、权限控制管理&#xff1a; 对于企业级的项目, 我们可能需要对项目做权限控制管理, 实现不同角色的用户登录项目根据所拥有的权限访问不同的页面内容&#xff0c;此时就需要使用到动态路由来对权限页面做限制。 【使用vue-router实现动态路由&#xff0c;达到实现菜单权限…

FFmpeg: 简易ijkplayer播放器实现--06封装打开和关闭stream

文章目录 流程图stream openstream close 流程图 stream open 初始化SDL以允许⾳频输出&#xff1b;初始化帧Frame队列初始化包Packet队列初始化时钟Clock初始化音量创建解复用读取线程read_thread创建视频刷新线程video_refresh_thread int FFPlayer::stream_open(const cha…

Docker 学习笔记(七):介绍 Dockerfile 相关知识,使用 Dockerfile 构建自己的 centos 镜像

一、前言 记录时间 [2024-4-12] 系列文章简摘&#xff1a; Docker学习笔记&#xff08;二&#xff09;&#xff1a;在Linux中部署Docker&#xff08;Centos7下安装docker、环境配置&#xff0c;以及镜像简单使用&#xff09; Docker 学习笔记&#xff08;三&#xff09;&#x…

【MoS2】应变增强的单层MoS2光电探测器

这篇文章的标题是《Strain-Enhanced Large-Area Monolayer MoS2 Photodetectors》&#xff0c;作者是Borna Radatovic等人&#xff0c;发表在《ACS Applied Materials & Interfaces》期刊的2024年第16卷。文章主要研究了应变增强的大面积单层MoS2光电探测器的性能和应用潜力…

【安全】挖矿木马自助清理手册

一、什么是挖矿木马 挖矿木马会占用CPU进行超频运算&#xff0c;从而占用主机大量的CPU资源&#xff0c;严重影响服务器上的其他应用的正常运行。黑客为了得到更多的算力资源&#xff0c;一般都会对全网进行无差别扫描&#xff0c;同时利用SSH爆破和漏洞利用等手段攻击主机。 …