【笔记】ViewPager+自定义控件实现的日历控件CalenderView

来源:互联网 发布:数据分析师证 编辑:程序博客网 时间:2024/04/30 13:18

接入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控件。


源码Demo


原创粉丝点击