安卓的日历_公认不卡的安卓手机

安卓的日历_公认不卡的安卓手机一款安卓日历,包含周日历、月日历以及滑动切换视图

大家好,又见面了,我是你们的朋友全栈君。

最近写了一款日历,包含周日历、月日历以及滑动切换视图,先上效果图:

效果图

代码已上传到github:https://github.com/yannecer/NCalendar
项目主要用到了自定义View,ViewPager,RecyclerView和NestedScrollingParent。

本篇文章主要说一下月日历数据、月视图绘制以及点击日期的实现。

数据

数据部分,网上能找到比较完整的工具类,主要是根据本月和上月的天数以及本月第一天是周几来计算。

首先计算上月日期: 由本月的第一天是周几和上个月的天数,得出上月的日期的显示

int temp = lastMonthDays - firstDayOfWeek + 1;//上个月的天数减去本月第一天周几再加上1

再计算本月日期:本月内的数据根据该月的天数跑循环。

再计算下月计算上月日期的显示: 下月的天数显示可以看本月最后一天是周几,根据距离一周最后一天的间隔天数,从1开始直接加上就可以了。

这里要分情况了,有的月份跨5个周,有的月份能跨6个周。计算上没有区别,但是显示的时候会有区别,为了简单,统一成6周,共42个元素,一月多余的用下月日期补充。日期计算肯定使用joda-time了,天数、月份、年份计算都非常简单,有一点,这个库每周是周一开始的,周日历要注意一下。

一月的数据:

List<Integer> date = new ArrayList();
 int j = 1;
 for (int i = 0; i < 42; i++) {
     if (i < firstDayOfWeek) { // 前一个月
         int temp = lastMonthDays - firstDayOfWeek + 1;
         date .add(temp + i);
      } else if (i < days + firstDayOfWeek) { // 本月
        int temp = i - firstDayOfWeek + 1;
          date .add(temp)
       } else { // 下一个yue 
          date .add(j);
         j++;
       }        
  }

这里简化了操作,项目中我把每个数据都转化成了joda-time中的DateTime对象,方便后面操作。
数据有了,接着就是绘制这些数据。

MonthView

MonthView继承于View,重写onDraw(canva)方法。
首先在构造方法中根据颜色和字体大小初始化画笔:

  mSorlarPaint = getPaint(mSolarTextColor, mSolarTextSize);

    .....
  private Paint getPaint(int paintColor, float paintSize) {
        Paint paint = new Paint();
        paint.setColor(paintColor);
        paint.setTextSize(paintSize);
        paint.setAntiAlias(true);
        paint.setTextAlign(Paint.Align.CENTER);
        return paint;
    }

接着就是在onDraw(canva)方法中绘制。
我们先考虑一下我们都需要做哪些事情。需要绘制公历、农历、小圆点、选中的圆环包括后面的点击操作,这些元素确定位置都需要一个矩形(Rect),那么就可以先在这个View里面绘制42个矩形。四个点确定一个矩形,可以在纸上画一下大致的图案,大致画个一两行矩形,应该就找到规律了,感觉有点像以前上学时做的找规律的数学题。

6行7列的一个矩形阵

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mWidth = getWidth();//view的宽度
        mHeight = getHeight();//view的高度

        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < 7; j++) {
                Rect rect = new Rect(j * mWidth / 7, i * mHeight / 6, j * mWidth / 7 + mWidth / 7, i * mHeight / 6 + mHeight / 6);
                canvas.drawRect(rect,mSorlarPaint);
            }
        }
    }

有了这42个矩形,我们做后面的事情就简单了。

绘制文字

绘制文字 canvas.drawText()会发现,可能会出现文字不在矩形的中心,解决办法参看这篇博客,Android Canvas drawText实现中文垂直居中

Paint.FontMetricsInt fontMetrics = mSorlarPaint.getFontMetricsInt();
int baseline = (rect.bottom + rect.top - fontMetrics.bottom - fontMetrics.top) / 2;
canvas.drawText(dateTime.getDayOfMonth() + "", rect.centerX(), baseline, mSorlarPaint);

我们需要在绘制的循环里面要判断这些内容:

1、是不是本月的数据(用颜色区分本月和其他月的数据)
2、是不是今天
3、有没有选中的日期
4、显示不显示农历

其中今天和选中的日期用圆环表示,就需要在当天和选中的日期的矩形中绘制圆环。
已今天为例:

 //是今天,且是当月的今天才绘制今天的标识  
 if (Utils.isToday(dateTime) && Utils.isEqualsMonth(dateTime, mInitialDateTime)) {
     mSorlarPaint.setColor(mSelectCircleColor);//画笔设置选中的颜色
     int radius = Math.min(Math.min(rect.width() / 2, rect.height() / 2), mSelectCircleRadius);//圆环半径取矩形宽、高和设置半径的最小值
     canvas.drawCircle(rect.centerX(), rect.centerY(), radius, mSorlarPaint);
     mSorlarPaint.setColor(Color.WHITE);//当天的文本设置成白色
     canvas.drawText(dateTime.getDayOfMonth() + "", rect.centerX(), baseline, mSorlarPaint);
  }

完整的onDraw(Canvas canvas)里面的代码

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mWidth = getWidth();
        mHeight = getHeight();
        mRectList.clear();
        //67列
        for (int i = 0; i < 6; i++) { 
   
            for (int j = 0; j < 7; j++) { 
   
                Rect rect = new Rect(j * mWidth / 7, i * mHeight / 6, j * mWidth / 7 + mWidth / 7, i * mHeight / 6 + mHeight / 6);
                mRectList.add(rect);
                DateTime dateTime = monthDateTimeList.get(i * 7 + j);
                Paint.FontMetricsInt fontMetrics = mSorlarPaint.getFontMetricsInt();
                int baseline = (rect.bottom + rect.top - fontMetrics.bottom - fontMetrics.top) / 2;
                //判断是不是当月,当月和上下月的颜色不同
                if (Utils.isEqualsMonth(dateTime, mInitialDateTime)) {
                    //当天和选中的日期不绘制农历
                    if (Utils.isToday(dateTime)) {
                        mSorlarPaint.setColor(mSelectCircleColor);
                        int radius = Math.min(Math.min(rect.width() / 2, rect.height() / 2), mSelectCircleRadius);
                        canvas.drawCircle(rect.centerX(), rect.centerY(), radius, mSorlarPaint);
                        mSorlarPaint.setColor(Color.WHITE);
                        canvas.drawText(dateTime.getDayOfMonth() + "", rect.centerX(), baseline, mSorlarPaint);
                        //选中日期不为null绘制空心圆
                    } else if (mSelectDateTime != null && dateTime.toLocalDate().equals(mSelectDateTime.toLocalDate())) {
                        mSorlarPaint.setColor(mSelectCircleColor);
                        int radius = Math.min(Math.min(rect.width() / 2, rect.height() / 2), mSelectCircleRadius);
                        canvas.drawCircle(rect.centerX(), rect.centerY(), radius, mSorlarPaint);
                        mSorlarPaint.setColor(mHollowCircleColor);
                        canvas.drawCircle(rect.centerX(), rect.centerY(), radius - mHollowCircleStroke, mSorlarPaint);
                        mSorlarPaint.setColor(mSolarTextColor);
                        canvas.drawText(dateTime.getDayOfMonth() + "", rect.centerX(), baseline, mSorlarPaint);
                    } else {
                        mSorlarPaint.setColor(mSolarTextColor);
                        canvas.drawText(dateTime.getDayOfMonth() + "", rect.centerX(), baseline, mSorlarPaint);
                        drawLunar(canvas, rect, mLunarTextColor, i, j);
                    }
                } else {
                    mSorlarPaint.setColor(mHintColor);
                    canvas.drawText(dateTime.getDayOfMonth() + "", rect.centerX(), baseline, mSorlarPaint);
                    drawLunar(canvas, rect, mHintColor, i, j);
                }
                    //绘制提示的小圆点
                    if (mPointList.contains(dateTime.toLocalDate().toString())) {
                        mSorlarPaint.setColor(mPointColor);
                        canvas.drawCircle(rect.centerX(), rect.bottom-mPointSize, mPointSize, mSorlarPaint);
                    }
            }
        }
    }

......
    //绘制农历
    private void drawLunar(Canvas canvas, Rect rect, int color, int i, int j) {
        if (isShowLunar) {
            mLunarPaint.setColor(color);
            String lunar = lunarList.get(i * 7 + j);
            canvas.drawText(lunar, rect.centerX(), rect.bottom - Utils.dp2px(getContext(), 5), mLunarPaint);
        }
    }

里面的一些工具类可参见github上的项目:https://github.com/yannecer/NCalendar

点击事件

点击操作使用了GestureDetector,这个类里面已经定义好了单级,双击,长按等操作,只需要我们重写相应的方法就可以,不用我们在去定义一个点击操作了。
重写MonthViewonTouchEvent(MotionEvent event)方法,交给GestureDetector处理

 @Override
 public boolean onTouchEvent(MotionEvent event) {
    return mGestureDetector.onTouchEvent(event);
 }

触摸事件交给GestureDetector,当发生单击时,循环刚才绘制文本时的矩形,根据用户点击的XY坐标值判断是在哪个矩形内,我们就知道用户点击的是哪个日期了。

    private GestureDetector mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            for (int i = 0; i < mRectList.size(); i++) {
                Rect rect = mRectList.get(i);
                //判断
                if (rect.contains((int) e.getX(), (int) e.getY())) {
                    DateTime selectDateTime = monthDateTimeList.get(i);
                    //点击的是上个月
                    if (Utils.isLastMonth(selectDateTime, mInitialDateTime)) {
                        onClickMonthViewListener.onClickLastMonth(selectDateTime);
                    } else if (Utils.isNextMonth(selectDateTime, mInitialDateTime)) {
                        //点击的是下个月
                        onClickMonthViewListener.onClickNextMonth(selectDateTime);
                    } else {
                         //点击的是本月
                        onClickMonthViewListener.onClickCurrentMonth(selectDateTime);
                    }
                    break;
                }
            }
            return true;
        }
    });

里面写了一些回调,方便在ViewPager中跳转到相应的月份。剩下的操作放到了ViewPager中完成,如果不是本月就跳转再设置选中的日期,如果是本月,就直接设置选中的日期:

    @Override
    public void onClickCurrentMonth(DateTime dateTime) {
        doClickEvent(dateTime, getCurrentItem());
    }

    @Override
    public void onClickLastMonth(DateTime dateTime) {
        int currentItem = getCurrentItem() - 1;
        doClickEvent(dateTime, currentItem);
    }

    @Override
    public void onClickNextMonth(DateTime dateTime) {
        int currentItem = getCurrentItem() + 1;
        doClickEvent(dateTime, currentItem);
    }

    ........
    //处理点击
    private void doClickEvent(DateTime dateTime, int currentItem) {
        MonthCalendar.this.setCurrentItem(currentItem);
        MonthView monthView = (MonthView) calendarAdapter.getCalendarViews().get(currentItem);
        monthView.setSelectDateTime(dateTime);
        if (onClickMonthCalendarListener != null) {
            onClickMonthCalendarListener.onClickMonthCalendar(dateTime);
        }

    }

doClickEvent(DateTime dateTime, int currentItem)方法中,得到当前的MonthView ,设置选中日期monthView.setSelectDateTime(dateTime);
而在setSelectDateTime(DateTime dateTime)中就是赋值和重绘页面:

    public void setSelectDateTime(DateTime dateTime) {
        this.mSelectDateTime = dateTime;
        invalidate();
    }

这样在onDraw(Canvas canvas)mSelectDateTime!=null,就会绘制选中的圆环了。

MonthView没有重写onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,是因为这个MonthView是放在继承自ViewPagerMonthCalendar中使用的,只需在布局文件中设置MonthCalendarlayout_widthlayout_height即可:

 <com.necer.ncalendar.calendar.MonthCalendar
        android:id="@+id/monthCalendar"
        android:layout_width="match_parent"
        android:layout_height="240dp"
        android:background="@color/white"
        app:selectCircleColor= "@android:color/holo_red_light"
        app:pointcolor="#00c8aa"
        app:pointSize="1dp"
        app:solarTextSize= "15sp"/>
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/125033.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 阅读书源最新2020在线导入_书源篇五「建议收藏」

    阅读书源最新2020在线导入_书源篇五「建议收藏」书源篇五爱阅书香语昨晚半夜发的书源,竟然把另一位同学的书源仓库给忘了,我的失误啊,已经自罚酒一杯,喝完再干三杯。新的书源仓库分别是『Liquor030』及『Mxy』同学,写法很厉害,虽然我设计了这一套规则,但怎么用,用得好,全靠各位同学自己创建的。被各位同学不断鞭策,才能不断的完善。让我们一起努力吧。书源及工作原理书源:一个网站的规则描述文件,可能包括有多个来源;…

  • 用python画爱心代码_python打印心形图案

    用python画爱心代码_python打印心形图案1、工具python3.0及以上版本;pycharm或其他开发环境2、思路首先,把你想说的话想好,我们用split()函数按空格切割成一个一个词其次,了解心形函数,百度一下哈,这个很多的,比如下面这个:再次,打印第一个词,两个for循环。一行一行打印,在函数内部的我们打印词,在函数外面的打印空格即可…

  • mysql怎么批量导入数据_oracle如何批量导入大量数据

    mysql怎么批量导入数据_oracle如何批量导入大量数据1、确定需要导入数据的表名称以及字段,然后在新建的Excel表中,按照表字段正确排序;(注:(Excel文件的名称最好和数据库的名称一致,sheet表的名字最好和表名称一致,方便需要导入多张表数据时一一对应))2、在Excel表中,正确填写需要导入的数据,一行数据对应着数据库表中的一行记录;(注:各个字段的格式要求需要和数据库中的限制一样,避免出现差错)3、收集好需要导入的数据后,点击保存。(注:…

  • SQL可视化工具_可视化工具tableau

    SQL可视化工具_可视化工具tableauSQLite数据库的特性特点:1.轻量级2.独立性,没有依赖,无需安装3.隔离性全部在一个文件夹系统4.跨平台支持众多操作系统5.多语言接口支持众多编程语言6.安全性事物,通过独占性和共享

  • java如何实现换行_网页换行代码

    java如何实现换行_网页换行代码在taxtarea中输入的文本。如果含有回车或空格。在界面上显示的时候则不哪么正常。回车消失了,空格变短了。如何解决这个问题呢。有2种方法。1.使用标签w3c对pre元素是这样定义的:pre元素可定义预格式化的文本。被包围在pre元素中的文本通常会保留空格和换行符。而文本也会呈现为等宽字体。更详细的内容请参考http://www.w3school.com.cn/tags/tag_pre.a…

  • pycharm提示代码功能问题「建议收藏」

    pycharm提示代码功能问题「建议收藏」在使用pycharm写python代码时,对于tensorflow模块,不能显示代码提示内容。在project中设置解释器地址后, 要等待下面列出的package包全部扫描完,显示完当前版本和最新版本完,再点OK按钮。在代码区输入相关类的点后,要稍微等一会,才会出现代码提示的信息。…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号