本文是参考了网上找到的相应的文章和项目,自己动手实践的记录。

本文的主要目的是:1.复习一下自定义的view 2.利用Androidstudio4.0.2开发对比一下与之前有什么区别,因为在创建新项目的时候,强制性的选中使用android.x包。3.再熟悉一下BaseRecyclerViewAdapterHelper的用法。主要是关于多布局,

item的点击事件等。

先上几张效果图:

其实关键点就是一个自定义的view。先来上布局文件

接下来重点是自定义的日期控件,日期控件的代码如下:

package com.example.mytoolsapp.caldendar;import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Toast;import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.listener.OnItemClickListener;
import com.example.mytoolsapp.R;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;public class CommonCalendarView extends FrameLayout {private ArrayList<MyMultiType> dateBeanArrayList = new ArrayList<>();private CalendarAdapter adapter;private RecyclerView recyclerView;private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");private MyMultiType startDate;//选中的开始日期private MyMultiType endDate;//选中的截止日期private OnDateSelected onDateSelected;public void setOnDateSelected(OnDateSelected onDateSelected) {this.onDateSelected = onDateSelected;}public CommonCalendarView(@NonNull Context context) {super(context);}public CommonCalendarView(@NonNull Context context, @Nullable AttributeSet attrs) {super(context, attrs);LayoutInflater.from(context).inflate(R.layout.layout_view_calendar_outer, this, true);//加载布局initData(context);initClick(context);}public CommonCalendarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}/*** 设置数据初始化*/public void initData(Context context) {recyclerView = findViewById(R.id.recyclerView);dateBeanArrayList.addAll(DateUtils.getDataList(11));adapter = new CalendarAdapter(context, dateBeanArrayList);GridLayoutManager gridLayoutManager = new GridLayoutManager(context, 7);gridLayoutManager.setOrientation(GridLayoutManager.VERTICAL);gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {@Overridepublic int getSpanSize(int i) {//这个方法返回的是当前位置的 item 跨度大小if (MyMultiType.TYPE_MONTH == dateBeanArrayList.get(i).getItemType()) {return 7;} else {return 1;}}});recyclerView.setLayoutManager(gridLayoutManager);recyclerView.setAdapter(adapter);//实现月份标题悬停的效果   测试MyItemDecoration myItemDecoration = new MyItemDecoration();recyclerView.addItemDecoration(myItemDecoration);}/*** 设置点击事件*/public void initClick(Context context) {adapter.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(@NonNull BaseQuickAdapter<?, ?> adapter, @NonNull View view, int position) {MyMultiType myMultiTypeNow = (MyMultiType) adapter.getData().get(position);//当前的数据类if (myMultiTypeNow.getItemType() == MyMultiType.TYPE_MONTH || myMultiTypeNow.getDateBean().getDate() == null) {//如果选中的是月份或者是空白填补的item,则直接returnToast.makeText(context, "该位置不可选", Toast.LENGTH_SHORT).show();return;}//今天之前的日期不可选Date todayDate = new Date();//今天String date = myMultiTypeNow.getDateBean().getMonthStr() + "-" + myMultiTypeNow.getDateBean().getDay();//获取点击item的日期Date beforeToday = new Date();try {beforeToday = simpleDateFormat.parse(date);} catch (ParseException e) {e.printStackTrace();}if (beforeToday.getTime() < todayDate.getTime() - 1000 * 60 * 60 * 24) {//-1000*60*60*24  得到的是昨天的时间  不然今天也不可选//今天之前的日期不可选Toast.makeText(context, "当前日期不可选", Toast.LENGTH_SHORT).show();return;}if (startDate == null) {//从未选择日期setStartDate(myMultiTypeNow);} else if (startDate != null && endDate == null) {//已经选择了开始日期,并未选中结束日期if (startDate.getDateBean().getDate().getTime() > myMultiTypeNow.getDateBean().getDate().getTime()) {//如果选中的是起始日期之前的日期clearStartOrEndState(false);//清除之前的选中状态setStartDate(myMultiTypeNow);} else if (startDate.getDateBean().getDate().getTime() < myMultiTypeNow.getDateBean().getDate().getTime()) {//选择的是合理的结束日期setEndDate(myMultiTypeNow);setState();onDateSelected.hasSelect(true);String startResult = startDate.dateBean.monthStr + "-" + startDate.getDateBean().getDay();String endResult = endDate.dateBean.monthStr + "-" + endDate.getDateBean().getDay();int number = CommonTools.getDayCount(startDate.getDateBean().getDate(), endDate.getDateBean().getDate());onDateSelected.selectedResult(startResult, endResult, number);}} else if (startDate != null && endDate != null) {//开始日期结束日期都已经选中,多余第二次之外的选择clearStartOrEndState(true);//清除之前的选中状态setStartDate(myMultiTypeNow);}adapter.notifyDataSetChanged();}});}/*** @param myMultiType 目前点击item的数据* @description 设置开始日期*/public void setStartDate(MyMultiType myMultiType) {myMultiType.getDateBean().setItemState(DateBean.ITEM_STATE_BEGIN_DATE);startDate = myMultiType;}public void setEndDate(MyMultiType myMultiType) {myMultiType.getDateBean().setItemState(DateBean.ITEM_STATE_END_DATE);endDate = myMultiType;}/*** @description 设置选中状态* @author: pengzhen* @param */private void setState() {if (endDate != null && startDate != null) {int start = dateBeanArrayList.indexOf(startDate);start += 1;int end = dateBeanArrayList.indexOf(endDate);for (; start < end; start++) {MyMultiType myMultiType = dateBeanArrayList.get(start);if (!TextUtils.isEmpty(myMultiType.getDateBean().getDay())) {//如果不是空白选项myMultiType.getDateBean().setItemState(DateBean.ITEM_STATE_SELECTED);}}}}/*** @description 清除开始与结束日期的状态* @author: pengzhen* @param clearBoth 是否全部清除*/private void clearStartOrEndState(boolean clearBoth) {int start = dateBeanArrayList.indexOf(startDate);dateBeanArrayList.get(start).getDateBean().setItemState(DateBean.ITEM_STATE_NORMAL);startDate = null;if (clearBoth) {int end = dateBeanArrayList.indexOf(endDate);start += 1;dateBeanArrayList.get(end).getDateBean().setItemState(DateBean.ITEM_STATE_NORMAL);for (; start < end; start++) {MyMultiType myMultiType = dateBeanArrayList.get(start);myMultiType.getDateBean().setItemState(DateBean.ITEM_STATE_NORMAL);}endDate = null;}}/*** 供页面调用的接口*/public interface OnDateSelected {void selectedResult(String startDate, String endDate,int totalNumber);//将选中的结果回传到Activity页面void hasSelect(boolean select);这个是在Dialog显示的情况下会用到,来判断如期是否已选完,来改变Dialog里面确定按钮的选中状态}}

下面贴上自定义view的布局文件:

<?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="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:id="@+id/linerTop"android:layout_width="match_parent"android:layout_height="40dp"android:orientation="horizontal"tools:ignore="MissingConstraints"><TextViewandroid:id="@+id/tvSunday"style="@style/TextViewStyleForSunday"android:text="@string/sunday"android:textColor="@color/colorForRest" /><TextViewandroid:id="@+id/tvMonday"style="@style/TextViewStyleForSunday"android:text="@string/monday" /><TextViewandroid:id="@+id/tvTuesday"style="@style/TextViewStyleForSunday"android:text="@string/tuesday" /><TextViewandroid:id="@+id/tvWednesday"style="@style/TextViewStyleForSunday"android:text="@string/wednesday" /><TextViewandroid:id="@+id/tvThursday"style="@style/TextViewStyleForSunday"android:text="@string/thursday" /><TextViewandroid:id="@+id/tvFriday"style="@style/TextViewStyleForSunday"android:text="@string/friday" /><TextViewandroid:id="@+id/tvSaturday"style="@style/TextViewStyleForSunday"android:text="@string/saturday"android:textColor="@color/colorForRest" /></LinearLayout><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="0dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintTop_toBottomOf="@+id/linerTop" /></androidx.constraintlayout.widget.ConstraintLayout>

起始从布局就可以看出来,就是简单的一个星期的布局,下面是一个recycleview的搭配。此处的recycleview为多布局,分为月布局,与普通的日期布局。

这个demo我用得是

implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4' 

这个库非常强大,用这个库的本意也就是为了更加熟悉这个库。这个项目主要就是练习了多布局的实现。

下面贴上适配器的代码,以及数据类的代码,以供小伙伴儿参考:

此处是数据类型的类,必须实现多布局接口。

package com.example.mytoolsapp.caldendar;import com.chad.library.adapter.base.entity.MultiItemEntity;/*** 多布局数据类型必须实现接口MultiItemEntity*/
public class MyMultiType implements MultiItemEntity {public static final int TYPE_MONTH = 2;public static final int TYPE_DAY = 1;public int itemType = 1;public DateBean dateBean;public void setItemType(int itemType) {this.itemType = itemType;}public void setDateBean(DateBean dateBean) {this.dateBean = dateBean;}public DateBean getDateBean() {return dateBean;}@Overridepublic int getItemType() {return itemType;}
}

下面是日期的数据类:

package com.example.mytoolsapp.caldendar;import java.util.Date;/*** 日期的数据类*/
public class DateBean  {//item状态public static int ITEM_STATE_BEGIN_DATE = 1;//开始日期public static int ITEM_STATE_END_DATE = 2;//结束日期public static int ITEM_STATE_SELECTED = 3;//选中状态public static int ITEM_STATE_NORMAL = 4;//正常状态public int itemState = ITEM_STATE_NORMAL;Date date;//具体日期String day;//一个月的某天String monthStr;//月份public int getItemState() {return itemState;}public void setItemState(int itemState) {this.itemState = itemState;}public Date getDate() {return date;}public void setDate(Date date) {this.date = date;}public String getDay() {return day;}public void setDay(String day) {this.day = day;}public String getMonthStr() {return monthStr;}public void setMonthStr(String monthStr) {this.monthStr = monthStr;}
}
下面重头戏就是adapter的代码:
package com.example.mytoolsapp.caldendar;import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.ViewGroup;import com.chad.library.adapter.base.BaseMultiItemQuickAdapter;
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
import com.example.mytoolsapp.R;import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;public class CalendarAdapter extends BaseMultiItemQuickAdapter<MyMultiType, BaseViewHolder> {private Context context;public CalendarAdapter(Context context, @Nullable List<MyMultiType> data) {super(data);this.context = context;addItemType(MyMultiType.TYPE_DAY, R.layout.item_for_day_layout);addItemType(MyMultiType.TYPE_MONTH, R.layout.item_for_month_layout);}@Overrideprotected void convert(@NotNull BaseViewHolder baseViewHolder, MyMultiType myMultiType) {switch (myMultiType.itemType){case MyMultiType.TYPE_MONTH://设置月份baseViewHolder.setText(R.id.tvForMonth,myMultiType.getDateBean().getMonthStr());break;case MyMultiType.TYPE_DAY://设置日期SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");Date todayDate = new Date();String todayStr = format.format(todayDate);//获取今天日期DateBean dateBean=myMultiType.getDateBean();String date = dateBean.getMonthStr()+"-"+dateBean.getDay();//获取得到的日期Date beforeToday = new Date();try {beforeToday = format.parse(date);} catch (ParseException e) {e.printStackTrace();}if (date.equals(todayStr)){//如果是今天的日期  则把显示的日期号改为“今天”两个字baseViewHolder.setText(R.id.tv_day,"今天");baseViewHolder.setTextColor(R.id.tv_day,context.getResources().getColor(R.color.blue));} else if (beforeToday.getTime() < todayDate.getTime()){//今天之前的日期  设置成灰色baseViewHolder.setText(R.id.tv_day,dateBean.getDay());baseViewHolder.setTextColor(R.id.tv_day,context.getResources().getColor(R.color.disableChooseColor));} else {baseViewHolder.setText(R.id.tv_day,dateBean.getDay());baseViewHolder.setTextColor(R.id.tv_day,context.getResources().getColor(R.color.black));}//设置item状态if (dateBean.getItemState() == DateBean.ITEM_STATE_BEGIN_DATE || dateBean.getItemState() == DateBean.ITEM_STATE_END_DATE){//开始日期或结束日期baseViewHolder.itemView.setBackgroundColor(context.getResources().getColor(R.color.blue));baseViewHolder.setTextColor(R.id.tv_day,Color.WHITE);baseViewHolder.setVisible(R.id.tv_check_in_check_out,true);if (dateBean.getItemState() == DateBean.ITEM_STATE_BEGIN_DATE){baseViewHolder.setText(R.id.tv_check_in_check_out,"开始");}else {baseViewHolder.setText(R.id.tv_check_in_check_out,"结束");}}else if (dateBean.getItemState() == DateBean.ITEM_STATE_SELECTED){//选中状态baseViewHolder.itemView.setBackgroundColor(context.getResources().getColor(R.color.blue1));baseViewHolder.setTextColor(R.id.tv_day,Color.WHITE);}else {//正常状态baseViewHolder.itemView.setBackgroundColor(Color.WHITE);baseViewHolder.setVisible(R.id.tv_check_in_check_out,false);}break;}}
}

还有一个是月份悬停的一个工具类:

package com.example.mytoolsapp.caldendar;import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;/*** Created by wjy.* Date: 2019/12/31* Time: 12:02* Describe: 实现月份标题悬停的效果*/
public class MyItemDecoration extends RecyclerView.ItemDecoration {Paint paint=new Paint();Paint colorPaint=new Paint();Paint linePaint=new Paint();public MyItemDecoration(){paint.setColor(Color.parseColor("#ffffff"));paint.setStyle(Paint.Style.FILL);colorPaint.setColor(Color.parseColor("#2196F3"));colorPaint.setAntiAlias(true);linePaint.setAntiAlias(true);linePaint.setColor(Color.parseColor("#dddddd"));}@Overridepublic void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {super.onDrawOver(c, parent, state);if(parent.getChildCount()<=0){return;}//头部的高度int height=40;final float scale = parent.getContext().getResources().getDisplayMetrics().density;height= (int) (height*scale+0.5f);//获取第一个可见的view,通过此view获取其对应的月份CalendarAdapter adapter=(CalendarAdapter) parent.getAdapter();View fistView=parent.getChildAt(0);String text=adapter.getData().get(parent.getChildAdapterPosition(fistView)).getDateBean().getMonthStr();String fistMonthStr="";int fistViewTop=0;//查找当前可见的itemView中第一个月份类型的itemfor(int i=0;i<parent.getChildCount();i++){View v=parent.getChildAt(i);if(2==parent.getChildViewHolder(v).getItemViewType()){fistMonthStr=adapter.getData().get(parent.getChildAdapterPosition(fistView)).getDateBean().getMonthStr();fistViewTop=v.getTop();break;}}//计算偏移量int topOffset=0;if(!fistMonthStr.equals(text)&&fistViewTop<height){//前提是第一个可见的月份item不是当前显示的月份和距离的顶部的距离小于头部的高度topOffset=height-fistViewTop;}int t=0-topOffset;//绘制头部区域c.drawRect(parent.getLeft(),t,parent.getRight(),t+height,paint);colorPaint.setTextAlign(Paint.Align.CENTER);colorPaint.setTextSize(15*scale+0.5f);//绘制头部月份文字c.drawText(text,parent.getRight()/2,(t+height)/2,colorPaint);//绘制分割线
//        if(fistViewTop!=height) {
//            linePaint.setStrokeWidth(scale * 0.5f + 0.5f);
//            c.drawLine(parent.getLeft(), t + height, parent.getRight(), t + height, linePaint);
//        }}
}

activity中的代码如下:

package com.example.mytoolsapp;import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;import com.example.mytoolsapp.caldendar.CommonCalendarView;public class MainActivity extends Activity implements CommonCalendarView.OnDateSelected {private TextView textViewTop;private CommonCalendarView commonCalendarView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();}public void init() {textViewTop = findViewById(R.id.tvTop);commonCalendarView = findViewById(R.id.commonCalendarView);commonCalendarView.setOnDateSelected(this);}@Overridepublic void selectedResult(String startDate, String endDate,int totalNumberl) {textViewTop.setText(startDate + "--" + endDate+ "    共"+ totalNumberl+"晚");}@Overridepublic void hasSelect(boolean select) {}
}

讲几个关键点:

就是在选择开始与结束日期的时候有几种情况要考虑全面。具体的我的代码中也有相应的判断。可以参考一下

情况分为几种:1.点击选择日期的时候,判断是否是今天之前的日期 这个作为最外层的一层判断。

2.选择开始日期之后,第二次选择日期的时候,要判断,第二次点击选择的日期是否在开始日期之前,此时就需要重置开始日期,相应之前设置的开始日期的选中状态需要清除。

3.选择正常的结束日期,此时就按照正常情况下设置开始日期与结束日期,并且设置中间日期的一个选中状态

4.就是开始日期结束日期都已经选中,但是又点击按钮,此时就要清除之前所有的选中还有开始,结束日期的状态。重新设置开始日期。

大概就是这些情况,具体的可以参考我的代码,或者有什么疑问,可以给我留言,私信。

自定义的仿美团,飞猪日期选择控件一相关推荐

  1. 【C#】wpf自定义calendar日期选择控件的样式

    原文:[C#]wpf自定义calendar日期选择控件的样式 首先上图看下样式 原理 总览 ItemsControl内容的生成 实现 界面的实现 后台ViewModel的实现 首先上图,看下样式 原理 ...

  2. java界面日期选择控件,JavaFX界面设计之时间选择器(1)

    本章我们主要介绍javafX时间选择器的使用,描述了DatePicker控件的基本特性. javaFX的DatePicker控件可以让我们从一个给定的日历中选择一天,主要用于网站或应用中需要用户输入一 ...

  3. js 跨域的问题 (同一个主域名不同的二级域名下的跨域问题) 解决 WdatePicker.js my97日期选择控件

    js 跨域的问题 (同一个主域名不同的二级域名下的跨域问题) 解决 WdatePicker.js my97日期选择控件 参考文章: (1)js 跨域的问题 (同一个主域名不同的二级域名下的跨域问题) ...

  4. [Ext JS 4] 实战之 带week(星期)的日期选择控件(三)

    前言 在 [Ext JS 4] 实战之 带week(星期)的日期选择控件(二) 的最后,有提到一个解决方案. 不过这方案有一个条件  ==> "2. 每年的周数从(1-52), 如果超 ...

  5. [Ext JS 4] 实战之 带week(星期)的日期选择控件(二)

    前言 JavaScript 中的日期和时间 [Ext JS 4] 实战之 带week(星期)的日期选择控件(一) 如对本篇的一些预备知识需详尽了解,可参考以上两篇. Javascript 有提供Dat ...

  6. extjs 月份选择控件_Ext JS 4实现带week(星期)的日期选择控件(实战二)

    前言 JavaScript 中的日期和时间 Ext JS 4实现带week(星期)的日期选择控件(实战一) 如对本篇的一些预备知识需详尽了解,可参考以上两篇. Javascript 有提供Date 对 ...

  7. Swing的日期选择控件DatePicker

    Swing的日期选择控件 依赖的包 图片示例 示例代码段 设置日期控件的值 验证代码 结论 依赖的包 下载DatePicker.jar 图片示例 示例代码段 JLabel dateJLabel = n ...

  8. ExtJS6.0扩展日期选择控件为也可以选择时间

    PS:ExtJS自带的日期选择控件只能够选择日期,但是现在的需求需要精确到秒,所以在网上搜索了一些例子(大部分是4.0的)作为参考,然后改出了6.0可用的一个日期时间选择控件. 1.找到extjs6. ...

  9. java 的日期选择控件_Java日期选择控件

    一起学习 一次项目研发中需要日期时间选择控件, 网上提供的不多, 且质量一般, 所以只好自己做,参考了 网上某位同学的 作品 Jave 日期选择控件 DateChooser . 目前的代码将日期时间选 ...

最新文章

  1. 第二课.Python编程基础(一)
  2. web中用纯CSS实现筛选菜单
  3. Arduino--1838红外遥控
  4. mysql --max_allowed_packet=32m_mysql 设置max_allowed_packet 大小的办法
  5. oracle生成顺序编号,Oracle排序以及序号的输出 | 学步园
  6. 各类排序算法思想及计算复杂度
  7. 凭证反过账 金蝶k3_金蝶K3总账凭证过账等处理方式
  8. 基于springboot的电影推荐网站管理系统
  9. 【Oracle】批量造测试数据
  10. 流媒体相关资源下载地址(整理)
  11. 用python读取YUV文件 转RGB 8bit/10bit通用
  12. 诺基亚NBU备份文件名片导出程序 vCard助手
  13. PHP如何开发paypal支付插件
  14. python3获取网页天气预报信息并打印
  15. MMA-mathematica数值求解非线性偏微分方程组
  16. Debian apache
  17. Tomcat的基本配置
  18. 一维表转二维表(mysql)
  19. 咕咕机显示服务器请求异常,咕咕机云服务器状态异常
  20. 直播测试-网络延迟和丢包工具设置

热门文章

  1. cad 文件 打印高清效果
  2. Python 基础 之 Pycharm 实现简单的名片管理系统
  3. 内网穿透神器Frps一键安装脚本及设置教程
  4. 微信发朋友圈/评论/点赞/搜索/购物车测试用例
  5. Spring源码——AOP
  6. echarts地图文件json,全国各个省份省市县三级echarts绘制地图所需的json数据。广东、北京、上海、深圳、福建、安徽.....
  7. 关于Aquila使用的create representation问题解决
  8. 小波分析:三、二维离散小波变换
  9. Scala笔记(一)基本简介与基础语法
  10. Vue学习之--------消息订阅和发布、基础知识和实战应用(2022/8/24)