实际是圆的,由于录制视频视频尺寸小于实际宽度,所以看着有点像椭圆;
1、需求左右点击可以切换月份,不需要左右滑动切换,时间紧迫就按需求完成功能;
2、打卡日期选中如图,单个选中、左右相邻选中和换行样式如图;
3、当前日期选中,点击也需要选中效果;
4、月份可能会显示5行或6行,控制总高度不变,动态设置单行日期的高度,选中要是的宽高;
5、根据年月获取当月有多少天,再根据第一天在星期几,当月数据从此处开始添加
星期根据控件的宽均分显示,高度居中显示
public class WeekBarView extends AppCompatTextView {
public String[] days = {"日", "一", "二", "三", "四", "五", "六"};
private TextPaint textPaint;
public WeekBarView(Context context) {
this(context, null);
}
public WeekBarView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
textPaint = getPaint();
textPaint.setTextAlign(Paint.Align.CENTER);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int width = getMeasuredWidth() - paddingRight - paddingLeft;
int height = getMeasuredHeight() - paddingTop - paddingBottom;
for (int i = 0; i < days.length; i++) {
Rect rect = new Rect(paddingLeft + (i * width / days.length), paddingTop, paddingLeft + ((i + 1) * width / days.length), paddingTop + height);
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float top = fontMetrics.top;
float bottom = fontMetrics.bottom;
int baseLineY = (int) (rect.centerY() - top / 2 - bottom / 2);
canvas.drawText(days[i], rect.centerX(), baseLineY, textPaint);
}
}
}
日期的工具类
/**
* 获取对应年月的天数
*
* @param year
* @param month
* @return
*/
public int getMonthLastDay(int year, int month) {
Calendar a = Calendar.getInstance();
a.set(Calendar.YEAR, year);
a.set(Calendar.MONTH, month - 1);
a.set(Calendar.DATE, 1);//把日期设置为当月第一天
a.roll(Calendar.DATE, -1);//日期回滚一天,也就是最后一天
return a.get(Calendar.DATE);
}
//获取日历相关数据
Calendar calendar = Calendar.getInstance();
//当天在当月中第几天 2、12、23
int currDay = calendar.get(Calendar.DAY_OF_MONTH);
//当月是第几个月 8、11
int currMouth = calendar.get(Calendar.MONTH) + 1;
//当前年份 2019
int currYear = calendar.get(Calendar.YEAR);
/**
* 对应年月的一号是星期几
*
* @param year
* @param month
* @return
*/
public int getFirstDayOfWeek(int year, int month) {
Calendar calendar = Calendar.getInstance();
calendar.set(year, month - 1, 0);
int currYear = calendar.get(Calendar.YEAR);
int currMouth = calendar.get(Calendar.MONTH) + 1;
int currDay = calendar.get(Calendar.DAY_OF_MONTH);
int i1 = calendar.get(Calendar.DAY_OF_WEEK);//这就是星期几
if (i1 == 7) {
i1 = 0;
}
return i1;
}
设置日历的数据核心代码:
//获取日历布局的宽高,后面用于item的宽高和选中宽高设置
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
totalHeight = getMeasuredHeight();
totalWidth = getMeasuredWidth();
// Log.e("snow_m", "=========" + totalHeight);
}
/**
* 重新设置日历数据
*
* @param year
* @param month
*/
public void resetCalendarData(int year, int month, List clockInList) {
calendarDataList.clear();
int monthLastDay = CalendarUtils.init().getMonthLastDay(year, month);
int dayOfWeek = CalendarUtils.init().getFirstDayOfWeek(year, month);
int dayOfMonth = CalendarUtils.init().getTodayOfMonth();
//当前页面显示上月的数据,或者空
for (int i = 0; i < dayOfWeek; i++) {
CalendarData calendarData = new CalendarData();
calendarData.setSolarNum("");
calendarDataList.add(calendarData);
}
//设置当月数据
for (int i = 0; i < monthLastDay; i++) {
CalendarData calendarData = new CalendarData();
calendarData.setYearMD(year + "-" + month + "-" + CalendarUtils.init().addZeroBefore(i + 1));
calendarData.setSolarNum(String.valueOf(i + 1));
calendarDataList.add(calendarData);
}
// calendarAdapter.setTodayPosition(dayOfMonth + dayOfWeek, CalendarUtils.init().getTodayMonth());
//计算共几行
int rowNum = (int) Math.ceil((monthLastDay + dayOfWeek) / 7f);
calendarAdapter.setItemWidthHeight(totalHeight / rowNum, rowNum, totalWidth / 7);
CalendarUtils.init().addClockInInfoToCalendarInfo(year, month, dayOfWeek, calendarDataList, clockInList);
calendarAdapter.setDataList(calendarDataList);
}
适配器实现功能核心代码:
public class AdapterCalendarItem extends RecyclerView.Adapter {
private Context mContext;
private List dataList;
private int itemHeight;
private int itemWidth;
private int onlySelectWH;
CalendarItemClickListener itemClickListener;
//当前点击日期 2019-11-02
private String currentClickDate;
//当前日期 2019-11-02
private String todayDate;
public AdapterCalendarItem(Context mContext, CalendarItemClickListener itemClickListener) {
this.mContext = mContext;
this.itemClickListener = itemClickListener;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.item_calendar_adapter, null);
return new ViewHolder(view);
}
public void setDataList(List dataList) {
this.dataList = dataList;
notifyDataSetChanged();
}
/**
* 设置今天日期
*
* @param todayPosition
*/
public void setTodayPosition(String todayPosition) {
this.todayDate = todayPosition;
}
/**
* 设置item的高度 总行数
*
* @param itemHeight
* @param rowNum
*/
public void setItemWidthHeight(int itemHeight, int rowNum, int itemWidth) {
this.itemHeight = itemHeight;
this.itemWidth = itemWidth;
onlySelectWH = (int) (itemHeight - TextViewUtils.init().dp2px(mContext, 12) * (5 / 6f));
if (onlySelectWH > itemWidth) {
onlySelectWH = itemWidth;
}
// Log.e("snow_m", "====itemHeight=====" + itemHeight);
// Log.e("snow_m", "====itemWidth=====" + itemWidth);
}
public void setCurrentClickDate(String clickDate) {
this.currentClickDate = clickDate;
notifyDataSetChanged();
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
final CalendarData data = dataList.get(position);
ViewHolder vh = (ViewHolder) holder;
//设置item的高
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) vh.root_view.getLayoutParams();
layoutParams.height = itemHeight;
vh.root_view.setLayoutParams(layoutParams);
//设置选中的宽高
RelativeLayout.LayoutParams onlySelectViewParams = (RelativeLayout.LayoutParams) vh.onlySelectView.getLayoutParams();
onlySelectViewParams.height = onlySelectWH;
onlySelectViewParams.width = onlySelectWH;
vh.onlySelectView.setLayoutParams(onlySelectViewParams);
vh.tv_num.setText(data.getSolarNum());
//设置选中的===已签到状态
if (data.getClockInBean() != null) {
vh.tv_num.setTextColor(mContext.getResources().getColor(R.color.tt_FF8820));
setSelectedView(vh.selectedView, position);
vh.ivRemind.setVisibility(View.VISIBLE);
} else {
vh.tv_num.setTextColor(mContext.getResources().getColor(R.color.tt_33333));
vh.ivRemind.setVisibility(View.INVISIBLE);
vh.selectedView.setBackground(null);
}
//设置当前点击的效果
if (!TextUtils.isEmpty(currentClickDate) && currentClickDate.equals(data.getYearMD())) {
vh.ivRemind.setColorFilter(mContext.getResources().getColor(R.color.tt_white));
vh.onlySelectView.setBackgroundResource(R.drawable.bg_ff8820_circle);
vh.tv_num.setTextColor(mContext.getResources().getColor(R.color.tt_white));
} else {
vh.ivRemind.setColorFilter(mContext.getResources().getColor(R.color.tt_FF8820));
vh.onlySelectView.setBackground(null);
}
//设置今天对应的效果
if (!TextUtils.isEmpty(data.getYearMD()) && data.getYearMD().equals(todayDate)) {
vh.ivRemind.setColorFilter(mContext.getResources().getColor(R.color.tt_white));
vh.onlySelectView.setBackgroundResource(R.drawable.bg_ff8820_circle);
vh.tv_num.setTextColor(mContext.getResources().getColor(R.color.tt_white));
}
//item点击
vh.root_view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (itemClickListener != null) {
itemClickListener.itemClick(data, position);
}
}
});
}
/**
* 根据情况显示已选中(打卡)的样式
*
* @param selectedView
* @param position
*/
private void setSelectedView(View selectedView, int position) {
if (position % 7 == 0) {//最左边的数字
drawSelectView(selectedView, nextIsSelected(position, dataList) ? 3 : 0);
} else if (position % 7 == 6) {//最右边的数字
drawSelectView(selectedView, lastIsSelected(position, dataList) ? 2 : 0);
} else {//中间数据
boolean left = lastIsSelected(position, dataList);
boolean right = nextIsSelected(position, dataList);
if (left && right) {
drawSelectView(selectedView, 1);
} else if (left) {
drawSelectView(selectedView, 2);
} else if (right) {
drawSelectView(selectedView, 3);
} else {
drawSelectView(selectedView, 0);
}
}
}
/**
* 绘制选中的样式
*
* @param selectedView
* @param flag
*/
private void drawSelectView(View selectedView, int flag) {
//设置选中的宽高
RelativeLayout.LayoutParams onlySelectViewParams = (RelativeLayout.LayoutParams) selectedView.getLayoutParams();
onlySelectViewParams.height = onlySelectWH;
if (flag == 1) {//画全整个矩形
selectedView.setBackgroundResource(R.color.tt_ffe2c9);
onlySelectViewParams.width = itemWidth + 5;
} else if (flag == 2) { //左矩形 右圆==靠左
selectedView.setBackgroundResource(R.drawable.bg_ffe2c9_right_circle);
onlySelectViewParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
onlySelectViewParams.width = itemWidth / 2 + onlySelectWH / 2;
} else if (flag == 3) {//右矩形 左圆====靠右
selectedView.setBackgroundResource(R.drawable.bg_ffe2c9_left_circle);
onlySelectViewParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
onlySelectViewParams.width = itemWidth / 2 + onlySelectWH / 2;
} else { //圆形
selectedView.setBackgroundResource(R.drawable.bg_ffe2c9_circle);
onlySelectViewParams.width = onlySelectWH;
onlySelectViewParams.addRule(RelativeLayout.CENTER_IN_PARENT);
}
selectedView.setLayoutParams(onlySelectViewParams);
}
/**
* 上一条数据是否被选中
*
* @param position
* @return
*/
private boolean lastIsSelected(int position, List dataList) {
if (dataList == null) {
return false;
}
CalendarData lastData = null;
if (position > 0) {
lastData = dataList.get(position - 1);
}
if (lastData == null || lastData.getClockInBean() == null) {
return false;
}
return true;
}
/**
* 下一条数据是否被选中
*
* @param position
* @return
*/
private boolean nextIsSelected(int position, List dataList) {
if (dataList == null) {
return false;
}
CalendarData nextData = null;
if (position < dataList.size() - 1) {
nextData = dataList.get(position + 1);
}
if (nextData == null || nextData.getClockInBean() == null) {
return false;
}
return true;
}
@Override
public int getItemCount() {
return dataList == null ? 0 : dataList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView tv_num;
RelativeLayout root_view;
//点击或者单个选中
View onlySelectView;
//已签到选中
View selectedView;
ImageView ivRemind;
public ViewHolder(@NonNull View itemView) {
super(itemView);
tv_num = itemView.findViewById(R.id.tv_num);
root_view = itemView.findViewById(R.id.root_view);
onlySelectView = itemView.findViewById(R.id.only_select_view);
selectedView = itemView.findViewById(R.id.selected_view);
ivRemind = itemView.findViewById(R.id.iv_remind);
}
}
public interface CalendarItemClickListener {
void itemClick(CalendarData data, int position);
}
}
对应的适配器xml代码:
其实github也有很多好的日历依赖库,但项目样式和需求问题只能自定义实现,由于时间问题,临时是只实现左右切换,没有做左右滑动切换,可以使用ViewPager进行包装实现左右滑动切换,对数据还需要整理下才能更好适用自己的项目;
demo代码