接入OEM模块,产品想要人家App上的日历控件,只能乖乖自己写一个。。。
需求
实现一个能左右滑动的,带选中日期效果的日历控件。
看到左右滑动,第一反应就是ViewPager,现成的就是方便。接下来要实现的就是每月的日历了,并且可以点击选中某一天。
无限滑动ViewPager
先把简单的实现了,理论上来说,日历应该可以一直往前翻,或者一直向后翻,就是一个可以无限滚动的ViewPager。
1、设置ViewPager的总量为Integer.MAX_VALUE;
2、设置ViewPager最初页数为中间位置Integer.MAX_VALUE/2;
3、为了不浪费太多内存,实际只生成3页,当前页、前一页和后一页;
简单粗暴的代码块
public class CalenderViewAdapter extends PagerAdapter {
private Context context;
private List<CalenderBean> list;
private List<CalenderItemView> viewList;
public CalenderViewAdapter(Context context, List<CalenderBean> list) {
this.context = context;
this.list = list;
initCalenderView();
}
private void initCalenderView() {
//只创建当前页、前一页和后一页
if (viewList == null) {
viewList = new ArrayList<>();
viewList.add(new CalenderItemView(context));
viewList.add(new CalenderItemView(context));
viewList.add(new CalenderItemView(context));
}
for (int i = 0; i < list.size(); i++) {
CalenderBean calenderBean = viewList.get(i).getCalenderBean();
viewList.get(i).setDate(calenderBean.getYear(), calenderBean.getMonth());
}
}
@Override
public int getCount() {
return (list == null || list.size() == 0) ? 0 : Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
position = position % 3;
CalenderItemView calenderItemView = viewList.get(position);
calenderItemView.setDate(list.get(position).getYear(), list.get(position).getMonth());
try {
if (calenderItemView.getParent() != null) {
container.removeView(calenderItemView);
}
container.addView(calenderItemView);
} catch (Exception e) {
e.printStackTrace();
}
return calenderItemView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// super.destroyItem(container, position, object);
// container.removeView((View) object);
}
public void setList(List<CalenderBean> list) {
this.list = list;
notifyDataSetChanged();
}
/**
* 设置当前页
* @param position
*/
public void setCurrentPosition(int position) {
//当前页改变后,把其他两页的数据设置成当前页的前一页和后一页
position = position % 3;
CalenderBean calenderBean = list.get(position);
int tmpPosition = (position + 1) % 3;
list.remove(tmpPosition);
list.add(tmpPosition, CalenderUtil.getNextCalender(calenderBean.getYear(), calenderBean.getMonth()));
tmpPosition = (position - 1 + 3) % 3;
list.remove(tmpPosition);
list.add(tmpPosition, CalenderUtil.getPreCalender(calenderBean.getYear(), calenderBean.getMonth()));
initCalenderView();
notifyDataSetChanged();
}
}
每月日历
一个显示每月数据的日历控件。
1、第一行星期,周日到周六
2、六行具体天数,根据每月1号的星期和每月总天数,每行7天,可能需要5+1(空行)或6行数据
3、点击某日期选中
绘制头部星期和具体天数
/**
* 头部绘制
*
* @param canvas
*/
private void drawHeader(Canvas canvas) {
drawOneText(canvas, "日", itemWidth / 2 + itemWidth * 0, itemWidth / 2, headerPaint);
drawOneText(canvas, "一", itemWidth / 2 + itemWidth * 1, itemWidth / 2, headerPaint);
drawOneText(canvas, "二", itemWidth / 2 + itemWidth * 2, itemWidth / 2, headerPaint);
drawOneText(canvas, "三", itemWidth / 2 + itemWidth * 3, itemWidth / 2, headerPaint);
drawOneText(canvas, "四", itemWidth / 2 + itemWidth * 4, itemWidth / 2, headerPaint);
drawOneText(canvas, "五", itemWidth / 2 + itemWidth * 5, itemWidth / 2, headerPaint);
drawOneText(canvas, "六", itemWidth / 2 + itemWidth * 6, itemWidth / 2, headerPaint);
}
/**
* 绘制一个文字
*
* @param canvas
* @param text
* @param centerX
* @param centerY
* @param paint
*/
private void drawOneText(Canvas canvas, String text, int centerX, int centerY, Paint paint) {
float textWidth = paint.measureText(text);
canvas.drawText(text, centerX - textWidth / 2, centerY - (paint.descent() + paint.ascent()) / 2, paint);
}
/**
* 日期绘制
*
* @param canvas
*/
private void drawDayItem(Canvas canvas) {
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
if (dates[i][j] == preSelectDate) {
drawSelectItem(canvas, String.valueOf(dates[i][j]), itemWidth * j + itemWidth / 2, itemHeight * (i + 1) + itemHeight / 2, false);
}
if (dates[i][j] == selectDate) {
drawSelectItem(canvas, String.valueOf(dates[i][j]), itemWidth * j + itemWidth / 2, itemHeight * (i + 1) + itemHeight / 2, true);
} else if (dates[i][j] > 0) {
drawOneText(canvas, String.valueOf(dates[i][j]), itemWidth * j + itemWidth / 2, itemHeight * (i + 1) + itemHeight / 2, datePaint);
}
}
}
}
绘制选中和未选中日期
/**
* 绘制选中项选中和未选中状态
*
* @param canvas
* @param text
* @param centerX
* @param centerY
* @param isSelect
*/
private void drawSelectItem(Canvas canvas, String text, int centerX, int centerY, boolean isSelect) {
selectItemPaint.setColor(isSelect ? selectBackColor : backColor);
canvas.drawCircle(centerX, centerY, Math.min(itemWidth, itemHeight) / 2, selectItemPaint);
if (isSelect) {
selectItemPaint.setColor(selectTextColor);
drawOneText(canvas, text, centerX, centerY, selectItemPaint);
}
}
点击选中具体日期
根据点击位置是否在某具体日期范围内,绘制选中日期
private PointF startPoint;
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
startPoint = new PointF(event.getX(), event.getY());
// Log.i(TAG,startPoint.toString());
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
float x = event.getX();
float y = event.getY();
// Log.i(TAG,x+","+y);
//选中日期
if (Math.abs(startPoint.x - x) < 20 && Math.abs(startPoint.y - y) < 20) {
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
if (dates[i][j] > 0 && x > itemWidth * j && x < itemWidth * (j + 1) && y > itemHeight * (i + 1) && y < itemHeight * (i + 2)) {
preSelectDate = selectDate;
selectDate = dates[i][j];
if (onItemSelectListener != null) {
onItemSelectListener.onSelect(CalenderUtil.getCalender(year, month, selectDate));
}
invalidate();
}
}
}
return true;
}
}
return super.onTouchEvent(event);
}
ViewPager+日历控件 合体
实现一个继承自ViewPager的自定义控件,具体实现就是将上面两个的代码整合,在继承ViewPager的CalenderView控件中setAdapter无限滚动,每一页都返回一个具体月份的CalenderItemView控件。