这篇文章主要讲解了如何实现Android自定义日期段选择控件功能,内容清晰明了,对此有兴趣的小伙伴可以学习一下,相信大家阅读完之后会有帮助。
网站建设哪家好,找成都创新互联公司!专注于网页设计、网站建设、微信开发、小程序开发、集团企业网站建设等服务项目。为回馈新老客户创新互联还提供了花垣免费建站欢迎大家使用!
开发中碰到个需求,需要在一个空间中选择完成开始和结束时间。实现的过程走的是程序员开发的老路子,找到轮子后自己改吧改吧就成了。
当时做的时候有几个需求:1.当天为最大的结束日期,2.最大选择范围1年,3.开始时间和结束时间可以为同一天。如有其他需求实现,可以参考代码改进一下。先上效果图:
视频点击后的虚影是屏幕录制的原因。实现步骤:(如有缺失什么资源,请告知。开始时间和结束时间显示自己布局内添加就可以)
1.自定义控件属性
2.自定义控件代码
/** * @Description: 可以选择时间范围的日历控件 * @Author MengXY * @Emil mxy_2012_1@163.com * @Date 2019/1/8 */ public class CalendarView extends LinearLayout implements View.OnClickListener{ private TextView title; private RecyclerView recyclerView; private RelativeLayout layout_calendar_gonext; private RelativeLayout layout_calendar_goup; private LinearLayoutManager linearLayoutManager; private Calendar curDate = Calendar.getInstance(); //从服务器获取的日期 private Date dateFromServer; //外层主recyclerview的adapter private MainRvAdapter mainAdapter; private Listmonths = new ArrayList<>(); private Context context; //相关属性 private int titleColor; private int titleSize; private int enableSelectColor; private int disableSeletColor; private int todayColor; private int todayEmptyColor; private int todayFillColor; /** 初始日期为当前日期前一年*/ private String time; private long timeBefor; private long timeNow; private List titles = new ArrayList<>(); //点击的开始时间与结束时间 private Date sDateTime; private Date eDateTime; private boolean isSelectingSTime = true; private HashMap allAdapters = new HashMap<>(); public CalendarView(Context context) { this(context, null); } public CalendarView(Context context, AttributeSet attrs) { this(context, attrs, 0); } private int maxSelect = 13; public CalendarView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyCalendar); titleColor = ta.getColor(R.styleable.MyCalendar_titleColor, Color.WHITE); titleSize = (int) ta.getDimension(R.styleable.MyCalendar_titleSize, 15); enableSelectColor = ta.getColor(R.styleable.MyCalendar_dayInMonthColor, context.getResources().getColor(R.color.text_lable)); disableSeletColor = ta.getColor(R.styleable.MyCalendar_dayOutMonthcolor, context.getResources().getColor(R.color.text_unenable)); todayColor = ta.getColor(R.styleable.MyCalendar_todayColor, Color.BLUE); todayEmptyColor = ta.getColor(R.styleable.MyCalendar_todayEmptycircleColor, Color.CYAN); todayFillColor = ta.getColor(R.styleable.MyCalendar_todayFillcircleColor, Color.CYAN); ta.recycle(); this.context = context; init(context); } //该方法用于设置从服务器获取的时间,如果没有从服务器获取的时间将使用手机本地时间 private void initTime() { Calendar calendar = Calendar.getInstance(); //得到日历 calendar.setTime(new Date()); calendar.add(Calendar.MONTH,-(maxSelect-1)); time = DateUtils.formatData(calendar.getTime(),Constant.TFORMATE_YMD); timeBefor = DateUtils.getDataTime(time); String now = DateUtils.formatData(new Date(),Constant.TFORMATE_YMD); timeNow = DateUtils.getDataTime(now); // LogUtils.e("之前日期:"+time+"=="+timeBefor); // LogUtils.e("当前日期:"+now+"=="+timeNow); curDate = DateUtil.strToCalendar(time, Constant.TFORMATE_YMD); dateFromServer = DateUtil.strToDate(time, Constant.TFORMATE_YMD); } private void init(Context context) { bindView(context); renderCalendar(); } private void bindView(Context context) { View view = LayoutInflater.from(context).inflate(R.layout.appoint_calendarview, this, false); title = (TextView) view.findViewById(R.id.calendar_title); title.setTextColor(titleColor); title.setTextSize(titleSize); layout_calendar_gonext = view.findViewById(R.id.layout_calendar_gonext); layout_calendar_goup = view.findViewById(R.id.layout_calendar_goup); layout_calendar_gonext.setOnClickListener(this); layout_calendar_goup.setOnClickListener(this); recyclerView = (RecyclerView) view.findViewById(R.id.calendar_rv); linearLayoutManager = new LinearLayoutManager(this.context, LinearLayoutManager.HORIZONTAL, false); recyclerView.setLayoutManager(linearLayoutManager); PagerSnapHelper snapHelper = new PagerSnapHelper(); snapHelper.attachToRecyclerView(recyclerView); addView(view); } public void renderCalendar() { months.clear(); initTime(); for (int i = 0; i < maxSelect; i++) { ArrayList cells = new ArrayList<>(); if (i != 0) { curDate.add(Calendar.MONTH, 1);//后推一个月 } else { curDate.add(Calendar.MONTH, 0);//当前月 } Calendar calendar = (Calendar) curDate.clone(); //将日历设置到当月第一天 calendar.set(Calendar.DAY_OF_MONTH, 1); //获得当月第一天是星期几,如果是星期一则返回1此时1-1=0证明上个月没有多余天数 int prevDays = calendar.get(Calendar.DAY_OF_WEEK) - 1; //将calendar在1号的基础上向前推prevdays天。 calendar.add(Calendar.DAY_OF_MONTH, -prevDays); //最大行数是6*7也就是,1号正好是星期六时的情况 int maxCellcount = 6 * 7; while (cells.size() < maxCellcount) { cells.add(calendar.getTime()); //日期后移一天 calendar.add(calendar.DAY_OF_MONTH, 1); } months.add(new CalendarCell(i, cells)); } for (int i = 0; i < months.size(); i++) { //title格式 2018年6月3日 String title = (months.get(i).getCells().get(20).getYear() + 1900) + "\t-\t" + (months.get(i).getCells().get(20).getMonth() + 1); titles.add(title); } title.setText(titles.get(maxSelect-1)); //只限定3个月,因此模拟给3个数值即可 mainAdapter = new MainRvAdapter(R.layout.appoint_calendarview_item, months); recyclerView.setAdapter(mainAdapter); //recyclerview 的滚动监听 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { title.setText(titles.get(linearLayoutManager.findLastVisibleItemPosition())); super.onScrollStateChanged(recyclerView, newState); } }); recyclerView.scrollToPosition(maxSelect-1); } @Override public void onClick(View v) { int index = linearLayoutManager.findLastVisibleItemPosition(); LogUtils.e("当前项"+index); switch (v.getId()){ case R.id.layout_calendar_gonext: if(index < maxSelect-1){ recyclerView.scrollToPosition(index+1); title.setText(titles.get(index+1)); } break; case R.id.layout_calendar_goup: if(index > 0){ recyclerView.scrollToPosition(index-1); title.setText(titles.get(index-1)); } break; } } /** * 最外层水平recyclerview的adapter */ private class MainRvAdapter extends BaseQuickAdapter { public MainRvAdapter(int layoutResId, @Nullable List data) { super(layoutResId, data); } @Override protected void convert(BaseViewHolder helper, final CalendarCell item) { if (((RecyclerView) helper.getView(R.id.appoint_calendarview_item_rv)).getLayoutManager() == null) { //RecyclerView不能都使用同一个LayoutManager GridLayoutManager manager = new GridLayoutManager(mContext, 7); //recyclerview嵌套高度不固定(wrap_content)时必须setAutoMeasureEnabled(true),否则测量时控件高度为0 manager.setAutoMeasureEnabled(true); ((RecyclerView) helper.getView(R.id.appoint_calendarview_item_rv)).setLayoutManager(manager); } SubRvAdapter subRvAdapter = null; if (allAdapters.get(helper.getPosition()) == null) { subRvAdapter = new SubRvAdapter(R.layout.calendar_text_day, item.getCells()); allAdapters.put(helper.getPosition(), subRvAdapter); ((RecyclerView) helper.getView(R.id.appoint_calendarview_item_rv)).setAdapter(subRvAdapter); } else { subRvAdapter = allAdapters.get(helper.getPosition()); ((RecyclerView) helper.getView(R.id.appoint_calendarview_item_rv)).setAdapter(subRvAdapter); } //item 点击事件响应 subRvAdapter.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { Date date = item.getCells().get(position); if(date.getTime() >= timeBefor && date.getTime()<= timeNow ){ if (isSelectingSTime) { //正在选择开始时间 selectSDate(item.getCells().get(position)); } else { //正在选择结束时间 selectEDate(item.getCells().get(position)); } } //更新所有的adapter,比如今天6月,需要更新6、7、8三个月份不同adapter Iterator iterator = allAdapters.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = (Map.Entry) iterator.next(); ((SubRvAdapter) entry.getValue()).notifyDataSetChanged(); } } }); } } public void selectSDate(Date date) { if (sDateTime != null && eDateTime != null) { sDateTime = date; notifyDateSelectChanged(); } else { sDateTime = date; notifyDateSelectChanged(); } eDateTime = null; isSelectingSTime = false; /** 当前没有选择结束时间*/ if(this.calendaSelListener != null){ calendaSelListener.selectStatus(false); } } public void selectEDate(Date date) { if (sDateTime != null) { if (date.getTime() >= sDateTime.getTime()) { eDateTime = date; isSelectingSTime = true; notifyDateSelectChanged(); }else { eDateTime = sDateTime; sDateTime = date; isSelectingSTime = true; notifyDateSelectChanged(); } /** 选择完成*/ if(this.calendaSelListener != null){ calendaSelListener.selectStatus(true); } } } /** * 通知开始时间跟结束时间均改变 */ public void notifyDateSelectChanged() { if (mETimeSelectListener != null && eDateTime != null) { mETimeSelectListener.onETimeSelect(eDateTime); } if (mSTimeSelectListener != null && sDateTime != null) { mSTimeSelectListener.onSTimeSelect(sDateTime); } } private class SubRvAdapter extends BaseQuickAdapter { public SubRvAdapter(int layoutResId, @Nullable List data) { super(layoutResId, data); } @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) @Override protected void convert(BaseViewHolder helper, Date date) { helper.setIsRecyclable(false);//不让recyclerview进行复用,复用会出问题 ((CalendarDayTextView) helper.getView(R.id.calendar_day_tv)).setEmptyColor(todayEmptyColor); ((CalendarDayTextView) helper.getView(R.id.calendar_day_tv)).setFillColor(todayFillColor); int day = date.getDate(); //设置文本 ((CalendarDayTextView) helper.getView(R.id.calendar_day_tv)).setText(String.valueOf(day)); //设置颜色 if(date.getTime() >= timeBefor && date.getTime()<= timeNow ){ ((CalendarDayTextView) helper.getView(R.id.calendar_day_tv)).setTextColor(enableSelectColor); }else { ((CalendarDayTextView) helper.getView(R.id.calendar_day_tv)).setTextColor(disableSeletColor); } //更改选中文字颜色 if(sDateTime != null && eDateTime != null){ if(date.getTime()>sDateTime.getTime() && date.getTime() cells; public CalendarCell(int position, ArrayList cells) { this.position = position; this.cells = cells; } public int getPosition() { return position; } public void setPosition(int position) { this.position = position; } public ArrayList getCells() { return cells; } public void setCells(ArrayList cells) { this.cells = cells; } } //开始时间的选择监听 public interface CalendarSTimeSelListener { void onSTimeSelect(Date date); } private CalendarSTimeSelListener mSTimeSelectListener; public void setSTimeSelListener(CalendarSTimeSelListener li) { mSTimeSelectListener = li; } //结束时间的监听事件 public interface CalendatEtimSelListener { void onETimeSelect(Date date); } private CalendaSelListener calendaSelListener; /**选择日期完整性*/ public interface CalendaSelListener{ void selectStatus(boolean isOk); } public void setCalendaSelListener(CalendaSelListener calendaSelListener) { this.calendaSelListener = calendaSelListener; } private CalendatEtimSelListener mETimeSelectListener; public void setETimeSelListener(CalendatEtimSelListener li) { mETimeSelectListener = li; } }
3.自定义view用到的布局 appoint_calendarview.xml,对应日历控件如下面图片的部分。
<?xml version="1.0" encoding="utf-8"?>
定义控件选择后的背景部分:CalendarDayRelativeLayout.java
import android.content.Context; import android.graphics.Color; import android.os.Build; import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.widget.RelativeLayout; public class CalendarDayRelativeLayout extends RelativeLayout { public CalendarDayRelativeLayout(Context context) { this(context, null); } public CalendarDayRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) public void isDurationSat(boolean isSaturday) { this.setBackground(getResources().getDrawable(R.drawable.appoint_calendar_sat_bg)); } @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) public void isDurationSun(boolean isSunday) { this.setBackground(getResources().getDrawable(R.drawable.appoint_calendar_sun_bg)); } @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) public void isETime(boolean etime) { // this.setBackgroundResource(getResources().getDrawable(R.drawable.)); this.setBackground(getResources().getDrawable(R.drawable.appoint_calendar_sat_bg)); } @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) public void isSTime(boolean stime) { // this.setBackground(getResources().getDrawable(R.mipmap.appoint_calendar_start_bg)); this.setBackground(getResources().getDrawable(R.drawable.appoint_calendar_sun_bg)); } /** * 同一天 * */ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) public void isSameDay(){ this.setBackground(getResources().getDrawable(R.drawable.appoint_calendar_same_bg)); } }
自定义控件内日期的CalendarDayTextView.java
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import android.util.AttributeSet; /** * @Description: 日历内日期 * @Author MengXY * @Date 2019/1/8 */ public class CalendarDayTextView extends android.support.v7.widget.AppCompatTextView { public boolean isToday; private boolean isSTime; private boolean isETime; private Context context; public void setEmptyColor(int emptyColor) { this.emptyColor = emptyColor; } public void setFillColor(int fillColor) { this.fillColor = fillColor; } private int emptyColor = Color.parseColor("#00ff00"); private int fillColor = Color.parseColor("#00ff00"); private Paint mPaintSTime; private Paint mPaintETime; public CalendarDayTextView(Context context) { super(context); initview(context); } public CalendarDayTextView(Context context, AttributeSet attrs) { super(context, attrs); initview(context); } private void initview(Context context) { this.context=context; // mPaintSTime = new Paint(Paint.ANTI_ALIAS_FLAG); // mPaintSTime.setStyle(Paint.Style.FILL); // mPaintSTime.setColor(context.getResources().getColor(R.color.date_time_bg)); // mPaintSTime.setStrokeWidth(2); // // mPaintETime = new Paint(Paint.ANTI_ALIAS_FLAG); // mPaintETime.setStyle(Paint.Style.FILL); // mPaintETime.setColor(context.getResources().getColor(R.color.date_time_bg)); // mPaintETime.setStrokeWidth(2); } @Override protected void onDraw(Canvas canvas) { //根据当前逻辑开始时间必须先绘制结束时间 // if (isETime) { // canvas.save(); // //移动到当前控件的中心,以中心为圆点绘制实心圆 // canvas.translate(getWidth() / 2, getHeight() / 2); // canvas.drawCircle(0, 0, getWidth() / 2 , mPaintETime); // canvas.restore(); // //此处必须将圆移动回开始位置,否则文本显示会受到影响 // canvas.translate(0, 0); // } // // if (isSTime) { // canvas.save(); // //移动到当前控件的中心,以中心为圆点绘制实心圆 // canvas.translate(getWidth() / 2, getHeight() / 2); // canvas.drawCircle(0, 0, getWidth() / 2 , mPaintSTime); // canvas.restore(); // //此处必须将圆移动回开始位置,否则文本显示会受到影响 // canvas.translate(0, 0); // } super.onDraw(canvas); } public void setToday(boolean today) { isToday = today; this.setTextColor(context.getResources().getColor(R.color.text_main_tab_select)); } public void isETime(boolean etime) { isETime = etime; // this.setTextColor(context.getResources().getColor(R.color.date_time_tv)); // this.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); isSelected(true); } public void isSTime(boolean stime) { isSTime = stime; isSelected(true); // this.setTextColor(context.getResources().getColor(R.color.date_time_tv)); // this.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); } public void isSelected(boolean isSelcted){ if(isSelcted){ this.setTextColor(context.getResources().getColor(R.color.date_time_tv)); this.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); }else { this.setTextColor(context.getResources().getColor(R.color.text_lable)); } } }
appoint_calendarview.xml
<?xml version="1.0" encoding="utf-8"?>
calendar_text_day.xml
<?xml version="1.0" encoding="utf-8"?>
DateUtil.java
import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; public class DateUtil { //Calendar 转化 String public static String calendarToStr(Calendar calendar,String format) { // Calendar calendat = Calendar.getInstance(); SimpleDateFormat sdf = new SimpleDateFormat(format); return sdf.format(calendar.getTime()); } //String 转化Calendar public static Calendar strToCalendar(String str,String format) { // String str = "2012-5-27"; SimpleDateFormat sdf = new SimpleDateFormat(format); Date date = null; Calendar calendar = null; try { date = sdf.parse(str); calendar = Calendar.getInstance(); calendar.setTime(date); } catch (ParseException e) { e.printStackTrace(); } return calendar; } // Date 转化String public static String dateTostr(Date date,String format) { SimpleDateFormat sdf = new SimpleDateFormat(format); // String dateStr = sdf.format(new Date()); String dateStr = sdf.format(date); return dateStr; } // String 转化Date public static Date strToDate(String str,String format) { SimpleDateFormat sdf = new SimpleDateFormat(format); Date date = null; try { date = sdf.parse(str); } catch (ParseException e) { e.printStackTrace(); } return date; } //Date 转化Calendar public static Calendar dateToCalendar(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); return calendar; } //Calendar转化Date public static Date calendarToDate(Calendar calendar) { return calendar.getTime(); } // String 转成 Timestamp public static Timestamp strToTimeStamp(String str) { // Timestamp ts = Timestamp.valueOf("2012-1-14 08:11:00"); return Timestamp.valueOf(str); } //Date 转 TimeStamp public static Timestamp dateToTimeStamp(Date date,String format) { SimpleDateFormat df = new SimpleDateFormat(format); String time = df.format(new Date()); Timestamp ts = Timestamp.valueOf(time); return ts; } }
4.资源文件 /drawableappoint_calendar_sat_bg.xml //开始时间
appoint_calendar_sun_bg.xml //结束时间
appoint_calendar_same_bg.xml //开始时间和结束时间是同一天
<?xml version="1.0" encoding="utf-8"?>
/value
日 一 二 三 四 五 六 #41D2C4
5.在activity中使用 activity_selectdate.xml
<?xml version="1.0" encoding="utf-8"?>
SelectTimeActivity.java
public class SelectTimeActivity extends BaseActivity { @BindView(R.id.tv_title) TextView tvTitle; @BindView(R.id.iv_title_back) ImageView ivTitleBack; @BindView(R.id.tv_title_left) TextView tvTitleLeft; @BindView(R.id.layout_title_left) RelativeLayout layoutTitleLeft; @BindView(R.id.tv_title_right) TextView tvTitleRight; @BindView(R.id.layout_title_right) RelativeLayout layoutTitleRight; @BindView(R.id.layout_line) FrameLayout layoutLine; @BindView(R.id.tv_startime) TextView tvStartime; @BindView(R.id.tv_endtime) TextView tvEndtime; @BindView(R.id.calendarview) CalendarView calendarview; private String starTime; private String endTime; private boolean isSelecgOk = false; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_selectdate); ButterKnife.bind(this); setStatusBar(true); initView(); } private void initView() { tvTitle.setText(getString(R.string.selectTime)); ivTitleBack.setVisibility(View.GONE); tvTitleLeft.setText(getString(R.string.cancel)); tvTitleRight.setText(getString(R.string.confirm)); calendarview.setETimeSelListener(new CalendarView.CalendatEtimSelListener() { @Override public void onETimeSelect(Date date) { if (date != null) { endTime = DateUtils.formatData(date, Constant.TFORMATE_YMD); tvEndtime.setText(endTime); }else { endTime = null; } } }); calendarview.setSTimeSelListener(new CalendarView.CalendarSTimeSelListener() { @Override public void onSTimeSelect(Date date) { if (date != null) { starTime = DateUtils.formatData(date, Constant.TFORMATE_YMD); tvStartime.setText(starTime); }else { starTime = null; } } }); calendarview.setCalendaSelListener(new CalendarView.CalendaSelListener() { @Override public void selectStatus(boolean isOk) { isSelecgOk = isOk; } }); } @OnClick({R.id.tv_title_left, R.id.tv_title_right}) public void onClick(View view) { switch (view.getId()) { case R.id.tv_title_left: finish(); break; case R.id.tv_title_right: if(TextUtils.isEmpty(starTime)){ ToastUtils.showToast(getString(R.string.history_alert1)); return; } if(TextUtils.isEmpty(endTime) || !isSelecgOk){ ToastUtils.showToast(getResources().getString(R.string.history_alert)); return; } Intent intent = new Intent(); intent.putExtra("starTime",starTime); intent.putExtra("endTime",endTime); setResult(RESULT_OK,intent); finish(); break; } } }
RecyclerAdapter引用
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30'
看完上述内容,是不是对如何实现Android自定义日期段选择控件功能有进一步的了解,如果还想学习更多内容,欢迎关注创新互联行业资讯频道。