日历控件

用了两种方式实现日历控件

先来看看效果图
效果图
第一种就是用gridview简单点

class CalendarAdapter extends BaseAdapter {

        AbsListView.LayoutParams params;
        Context context;
        Calendar today;
        Calendar calendar;
        List<String> list;
        int select = -1;

        public CalendarAdapter(Context context, int year, int month) {
            this.context = context;
            int width = context.getResources().getDisplayMetrics().widthPixels;
            params = new AbsListView.LayoutParams(-1, width / 7);//让每个块的高度等于屏幕宽的1/7,宽度就自动平分了
            today = Calendar.getInstance();//今天日期
            calendar = Calendar.getInstance();
            calendar.set(year, month - 1, 1);//这月的一号
            int sc = calendar.get(Calendar.DAY_OF_WEEK);//一号是周几
            list = new ArrayList<String>();
            for (int i = 0; i < sc - 1; i++) {
                list.add("");
            }//然后把一号前的数据都设为空串
            int count = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
            for (int i = 0; i < count; i++) {
                list.add(i + 1 + "");
            }
        }

        private long getTime(int position) {
            return getCalendar(position).getTimeInMillis();
        }

//获取点击的日期
        private Calendar getCalendar(int position) {
            int day = Integer.valueOf(getItem(position));
            calendar.set(Calendar.DAY_OF_MONTH, day);
            return calendar;
        }

//判断是今天前,今天还是今天以后
        private int isToday(int position) {
            if (getTime(position) > today.getTimeInMillis()) {
                return 1;
            } else if (getTime(position) < today.getTimeInMillis()) {
                return -1;
            } else {
                return 0;
            }
        }

        private void select(int position) {
            this.select = position;
            notifyDataSetChanged();
        }

        public int getCount() {
            if (list == null) {
                return 0;
            }
            return list.size();
        }

        public String getItem(int position) {
            return list.get(position);
        }

        public long getItemId(int position) {
            return position;
        }
//是不是周末
        private boolean isWeekday(int position) {
            if (position % 7 == 0 || position % 7 == 6) {
                return true;
            }
            return false;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            TextView textView = new TextView(context);
            textView.setGravity(Gravity.CENTER);
            textView.setText(list.get(position));
            textView.setTextSize(18);
            textView.setLayoutParams(params);
            if (list.get(position).equals("")) {
                return textView;
            }
            if (isToday(position) < 0) {
                textView.setTextColor(0xff999999);
            } else {
                if (isToday(position) == 0) {
                    textView.setTextColor(0xff333333);
                    textView.setText("今天");
                } else {
                    textView.setTextColor(0xff666666);
                }
                if (isWeekday(position)) {
                    textView.setTextColor(0xffffa417);
                }
                if (select == position) {
                    textView.setTextColor(0xffffffff);
                    textView.setBackgroundResource(R.drawable.circle_blue_bg);
                }
            }
            return textView;
        }

    }

然后就写item点击事件选择日期等。
不过感觉这样写逼格不够高,就直接继承view画一个月历

    private int year;
    private int month;
    private Calendar calendar;
    private Calendar today;
    private List<Position> list;

    private Paint paint;
    private int width;
    private float perWidth;//每块的宽高

    private int textSize = 30;
    private int textColor = 0xff000000;
    private int textColorSelect = 0xffffffff;
    private int selectRectColor = 0xff04ade5;
    private int weekendTextColor = 0xffffa417;

    private float touchSlop;//手指移动距离小于这个判定它触发点击事件
    private float startX = 0;
    private float startY = 0;
    private double distance = 0;//手指移动距离
    private Position selectPosition; //选中的位置
    private OnDayClickListener onDayClickListener;//日点击事件

    public RItemCalendarView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public RItemCalendarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RItemCalendarView(Context context) {
        super(context);
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setTextSize(textSize);
        calendar = Calendar.getInstance();
        today = Calendar.getInstance();
        touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    }

    public void setData(int year, int month) {
        LogUtil.i(year + "-" + month);
        this.year = year;
        this.month = month;
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month - 1);
        int maxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);//本月一共多少天
        if (list == null) {
            list = new ArrayList<>();
        } else {
            list.clear();
        }
        for (int i = 1; i <= maxDay; i++) {
            calendar.set(Calendar.DAY_OF_MONTH, i);
            int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);//当天是周几
            int weekOfMonth = calendar.get(Calendar.WEEK_OF_MONTH);//是第几周
            list.add(new Position(dayOfWeek - 1, weekOfMonth + 1, i, dayOfWeek, calendar.getTimeInMillis()));//position记录每天是第几行第几列,是几号,还有具体时间,第0行放了日期,第一行放了周,所以weekOfMonth+1
        }
    }

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int height;
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = textSize * 20;//wrap时计算一个大致的宽度
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            int maxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
            calendar.set(Calendar.DAY_OF_MONTH, maxDay);//计算最后一天是本月的第几周,就知道一共该有多少行,来计算高度
            int weekOfMonth = calendar.get(Calendar.WEEK_OF_MONTH);
            height = width / 7 * (weekOfMonth + 2);//多加两行显示日期和周
        }
        perWidth = width * 1.0f / 7;
        setMeasuredDimension(width, height);
    }

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawDate(canvas);
        drawWeek(canvas);
        drawSelect(canvas);
        drawDay(canvas);
        selectPosition = null;
    }

//第一行画xxxx年xx月
    private void drawDate(Canvas canvas) {
        paint.setColor(textColor);
        String time = TimeUtil.getFormatTime(calendar.getTime(), "yyyy-MM");
        float x = width / 2 - paint.measureText(time) / 2;
        float y = perWidth * 0.5f - (paint.ascent() + paint.descent()) / 2;
        canvas.drawText(time, x, y, paint);
    }

    private void drawWeek(Canvas canvas) {
        String[] weeks = new String[]{"日", "一", "二", "三", "四", "五", "六"};
        paint.setColor(textColor);
        float x, y;
        for (int i = 0; i < weeks.length; i++) {
            x = perWidth * (i + 0.5f) - paint.measureText(weeks[i]) / 2;
            y = perWidth * 1.5f - (paint.ascent() + paint.descent()) / 2;
            canvas.drawText(weeks[i], x, y, paint);
        }
    }

//画天
    private void drawDay(Canvas canvas) {
        float x, y;
        String string;
        for (Position position : list) {
            paint.setColor(textColor);
            if (selectPosition != null && position.time == selectPosition.time) {
                paint.setColor(textColorSelect);//选中那天换个颜色
            }
            if (position.week == 1 || position.week == 7) {
                paint.setColor(weekendTextColor);//周末变色
            }
            string = String.valueOf(position.time);
            if (position.time == today.getTimeInMillis()) {
                string = "今天";
            }
            x = perWidth * (position.x + 0.5f) - paint.measureText(string) / 2;
            y = perWidth * (position.y + 0.5f) - (paint.ascent() + paint.descent()) / 2;
            canvas.drawText(string, x, y, paint);
        }
    }

//选中的块画了个背景圆
    private void drawSelect(Canvas canvas) {
        if (selectPosition == null) {
            return;
        }
        int x = selectPosition.x;
        int y = selectPosition.y;
        RectF rect = new RectF((x + 0.3f) * perWidth, (y + 0.3f) * perWidth, (x + 0.7f) * perWidth, (y + 0.7f) * perWidth);
        paint.setColor(selectRectColor);
        canvas.drawRoundRect(rect, perWidth / 2, perWidth / 2, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startX = event.getX();
                startY = event.getY();//手势起始位置
                int x = (int) (startX / perWidth);
                int y = (int) (startY / perWidth);
                selectPosition = getValue(x, y);
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
                if (selectPosition != null) {
                    distance = Math.sqrt((event.getX() - startX) * (event.getX() - startX) + (event.getY() - startY) * (event.getY() - startY));//手指移动距离
                    if (distance < touchSlop) {
                        LogUtil.i(year + "-" + month + "-" + selectPosition.day);
                        if (onDayClickListener != null) {
                            onDayClickListener.onClick(year, month, selectPosition.day);//回调监听
                        }
                        invalidate();
                    }
                    distance = 0;
                }
                break;
        }
        return true;
    }

//触摸在了哪块
    private Position getValue(int x, int y) {
        for (Position position :
                list) {
            if (position.x == x && position.y == y) {
                return position;
            }
        }
        return null;
    }

    public void setOnDayClickListener(OnDayClickListener onDayClickListener) {
        this.onDayClickListener = onDayClickListener;
    }

    public interface OnDayClickListener {
        void onClick(int year, int month, int day);
    }

    private class Position {
        int x;
        int y;
        int day;
        int week;
        long time;

        public Position(int x, int y, int day, int week, long tiem) {
            this.x = x;
            this.y = y;
            this.day = day;
            this.week = week;
            this.time = time;
        }
    }

画完一个月后放在viewpager里就可以了

public OnDayClickListener onDayClickListener;
private int currentYear;
private int currentMonth;
    //日历的范围
private int minDateYear = 1900;
private int minDateMonth = 1;
private int maxDateYear = 2100;
private int maxDateMonth = 12;

public RCalendarView(Context context) {
    super(context);
    init();
}

public RCalendarView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

private void init() {
    Calendar now = Calendar.getInstance();
    currentYear = now.get(Calendar.YEAR);
    currentMonth = now.get(Calendar.MONTH) + 1;
    setAdapter(new CalendarAdapter());
    setCurrent(currentYear,currentMonth);//默认设置为当前月
}

//计算xxxx年 xx 月是在第几个位置并设置
public void setCurrent(int year,int month){
    int p = year * 12 + month - minDateYear * 12 - minDateMonth;
    setCurrentItem(p);
}
//设置年月范围
public void setRange(Calendar minDate, Calendar maxDate) {
    if (maxDate.getTimeInMillis() < minDate.getTimeInMillis()) {
        return;
    }
    this.minDateYear = minDate.get(Calendar.YEAR);
    this.minDateMonth = minDate.get(Calendar.MONTH) + 1;
    this.maxDateYear = maxDate.get(Calendar.YEAR);
    this.maxDateMonth = maxDate.get(Calendar.MONTH) + 1;
}

public void setOnDayClickListener(OnDayClickListener onDayClickListener) {
    this.onDayClickListener = onDayClickListener;
}
//某天的点击事件
public interface OnDayClickListener {
    void onClick(int year, int month, int day);
}

class CalendarAdapter extends PagerAdapter {

    private RItemCalendarView.OnDayClickListener onItemClickListener = new RItemCalendarView.OnDayClickListener() {
        @Override
        public void onClick(int year, int month, int day) {
            if (onDayClickListener != null) {
                onDayClickListener.onClick(year, month, day);
            }
        }
    };

    @Override
    public int getCount() {
        return maxDateYear * 12 + maxDateMonth - minDateYear * 12 - minDateMonth + 1;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        RItemCalendarView view = new RItemCalendarView(getContext());
        view.setData(getYear(position), getMonth(position));
        view.setOnDayClickListener(onItemClickListener);
        container.addView(view);
        return view;
    }
    //第position个位置是哪年
    private int getYear(int position) {
        int t = minDateYear * 12 + minDateMonth + position;
        if (t % 12 == 0) {
            return t / 12 - 1;
        }
        return t / 12;
    }
    //第position个位置是几月
    private int getMonth(int position) {
        int t = minDateMonth + position;
        if (t % 12 == 0) {
            return 12;
        }
        return t % 12;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值