精华内容
下载资源
问答
  • 作者| 黄海彬地址|https://juejin.im/post/5a6743836fb9a01caa20aefc详解此框架采用组合方式,各个模块互相独立,可自由采用各种提供的控件组合,完全自定义自己需要UI,周视图和月视图可通过简单自定义任意自由...

    a6a912c5d4b9892022bd644d8ae4504c.png

    秦子帅明确目标,每天进步一点点.....e5402ff35a27135dc77d04e676fe9c8a.png

    作者 |  黄海彬

    地址 | https://juejin.im/post/5a6743836fb9a01caa20aefc

    详解

    此框架采用组合的方式,各个模块互相独立,可自由采用各种提供的控件组合,完全自定义自己需要的UI,周视图和月视图可通过简单自定义任意自由绘制,不怕美工提需求!下面教程将介绍如何实现3个API,自定义Canvas绘制日历

    CalendarView的优势:

    1、热插拔设计,根据不同的UI需求完全自定义UI,简单几步即可实现,自定义事件日历标记、颜色、农历等

    2、完全Canvas绘制,性能和速度都很不错,相比大多数基于GridView或RecyclerView实现的占用内存更低,启动速度更快

    3、支持收缩、展开、快速年月份选择等

    4、简洁易懂的源码,易学习。

    Gradle

    compile 'com.haibin:calendarview:3.2.9'

    <dependency><groupId>com.haibingroupId><artifactId>calendarviewartifactId><version>3.2.9version><type>pomtype>dependency>

    混淆proguard-rules

    -keepclasseswithmembers class * {public (android.content.Context);
    }

    国际惯例,先上一个自定义效果图,结尾还有几个其它效果图,可自己自定义

    09eb84b490ae302563b68e0a38552af0.png

    4b149a14e7eb30ceccd70365d465ef38.png

    各个类功能介绍

    CalendarLayout

    这是个辅助类,负责CalendarView的收缩控制功能,如果不需要收缩功能,无需使用它,一般使用教程如下

    <com.haibin.calendarview.CalendarLayout
            android:id="@+id/calendarLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:background="#fff"
            app:only_week_view="false"
            app:default_status="shrink"
            app:calendar_content_view_id="@+id/recyclerView">
            <com.haibin.calendarview.CalendarView
                android:id="@+id/calendarView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="#fff"
                app:current_month_text_color="#333333"
                app:current_month_lunar_text_color="#CFCFCF"
                app:min_year="2004"
                app:max_year="2020"
                app:other_month_text_color="#e1e1e1"
                app:other_month_lunar_text_color="#e1e1e1"
                app:scheme_text="假"
                app:week_start_with="mon"
                app:scheme_text_color="#333"
                app:scheme_theme_color="#128c4b"
                app:selected_lunar_text_color="#CFCFCF"
                app:month_view="com.haibin.calendarviewproject.meizu.MeiZuMonthView"
                app:week_view="com.haibin.calendarviewproject.meizu.MeizuWeekView"
                app:selected_text_color="#333"
                app:selected_theme_color="#108cd4"
                app:week_background="#fff"
                app:week_text_color="#111"
                app:year_view_day_text_color="#333333"
                app:year_view_day_text_size="9sp"
                app:year_view_month_text_color="#ff0000"
                app:year_view_month_text_size="20sp"
                app:month_view_show_mode="mode_only_current"
                app:year_view_scheme_color="#f17706"/>
            <com.haibin.calendarviewproject.group.GroupRecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/content_background" />com.haibin.calendarview.CalendarLayout>

    CalendarLayout api

    public void expand(); //展开public void shrink(); //收缩public boolean isExpand();//是否已经展开

    <attr name="calendar_show_mode"><enum name="both_month_week_view" value="0" /><enum name="only_week_view" value="1" /><enum name="only_month_view" value="2" />attr><attr name="default_status"><enum name="expand" value="0" /> <enum name="shrink" value="1" />attr><attr name="calendar_content_view_id" format="integer" />

    CalendarView

    真正的日历类,可以自行通过attr配置

    <com.haibin.calendarview.CalendarView
                android:id="@+id/calendarView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="#fff"
                app:current_month_text_color="#333333"
                app:current_month_lunar_text_color="#CFCFCF"
                app:min_year="2004"
                app:max_year="2020"
                app:other_month_text_color="#e1e1e1"
                app:other_month_lunar_text_color="#e1e1e1"
                app:scheme_text="假"
                app:scheme_text_color="#333"
                app:scheme_theme_color="#128c4b"
                app:selected_lunar_text_color="#CFCFCF"
                app:month_view="com.haibin.calendarviewproject.meizu.MeiZuMonthView"
                app:week_view="com.haibin.calendarviewproject.meizu.MeizuWeekView"
                app:selected_text_color="#333"
                app:selected_theme_color="#108cd4"
                app:week_background="#fff"
                app:week_text_color="#111"
                app:year_view_day_text_color="#333333"
                app:year_view_day_text_size="9sp"
                app:year_view_month_text_color="#ff0000"
                app:year_view_month_text_size="20sp"
                app:year_view_scheme_color="#f17706"/>

    完整attr

    <declare-styleable name="CalendarView"><attr name="week_background" format="color" /><attr name="week_line_background" format="color" /><attr name="week_text_color" format="color" /><attr name="week_bar_view" format="string" /><attr name="month_view" format="string" /><attr name="week_view" format="string" /><attr name="scheme_text" format="string" /><attr name="day_text_size" format="dimension" /><attr name="lunar_text_size" format="dimension" /><attr name="calendar_height" format="dimension" /><attr name="scheme_text_color" format="color" /><attr name="scheme_month_text_color" format="color" /><attr name="scheme_lunar_text_color" format="color" /><attr name="scheme_theme_color" format="color" /><attr name="selected_theme_color" format="color" /><attr name="selected_text_color" format="color" /><attr name="selected_lunar_text_color" format="color" /><attr name="current_day_text_color" format="color" /><attr name="current_day_lunar_text_color" format="color" /><attr name="current_month_text_color" format="color" /><attr name="other_month_text_color" format="color" /><attr name="current_month_lunar_text_color" format="color" /><attr name="other_month_lunar_text_color" format="color" /><attr name="year_view_month_text_size" format="dimension" /><attr name="year_view_day_text_size" format="dimension" /><attr name="year_view_month_text_color" format="color" /><attr name="year_view_day_text_color" format="color" /><attr name="year_view_scheme_color" format="color" /><attr name="year_view_background" format="color" /><attr name="min_year" format="integer" /><attr name="max_year" format="integer" /><attr name="min_year_month" format="integer" /><attr name="max_year_month" format="integer" /><attr name="month_view_show_mode"><enum name="mode_all" value="0" /> <enum name="mode_only_current" value="1" /> <enum name="mode_fix" value="2" /> attr><attr name="week_start_with"><enum name="sun" value="1" /><enum name="mon" value="2" /><enum name="sat" value="7" />attr>declare-styleable>

    CalendarView api

    public int getCurDay(); //今天public int getCurMonth(); //当前的月份public int getCurYear(); //今年public void showSelectLayout(final int year); //快速弹出年份选择月份public void closeSelectLayout(final int position); //关闭选择年份并跳转日期/**
    * 设置日期范围
    *
    * @param minYear 最小年份
    * @param minYearMonth 最小年份对应月份
    * @param maxYear 最大月份
    * @param maxYearMonth 最大月份对应月份
    */
    public void setRange(int minYear, int minYearMonth,int maxYear, int maxYearMonth) public void setOnYearChangeListener(OnYearChangeListener listener);//年份切换事件public void setOnDateSelectedListener(OnDateSelectedListener listener);//日期选择事件public void setSchemeDate(List mSchemeDate);//标记日期public void update();//动态更新public Calendar getSelectedCalendar(); //获取选择的日期public void scrollToPre();//滚动到上一个月public void scrollToNext();//滚动到下一个月public void scrollToCalendar(int year, int month, int day);//滚动到指定日期/**
      * 设置背景色
      *
      * @param monthLayoutBackground 月份卡片的背景色
      * @param weekBackground 星期栏背景色
      * @param lineBg 线的颜色
     */
    public void setBackground(int monthLayoutBackground, int weekBackground, int lineBg)/**
      * 设置文本颜色
      *
      * @param curMonthTextColor 当前月份字体颜色
      * @param otherMonthColor 其它月份字体颜色
      * @param lunarTextColor 农历字体颜色
     */public void setTextColor(int curMonthTextColor,int otherMonthColor,int lunarTextColor)/**
      * 设置选择的效果
      *
      * @param style 选中的style CalendarCardView.STYLE_FILL or CalendarCardView.STYLE_STROKE
      * @param selectedThemeColor 选中的标记颜色
      * @param selectedTextColor 选中的字体颜色
     */public void setSelectedColor(int style, int selectedThemeColor, int selectedTextColor)/**
      * 设置标记的色
      *
      * @param style 标记的style CalendarCardView.STYLE_FILL or CalendarCardView.STYLE_STROKE
      * @param schemeColor 标记背景色
      * @param schemeTextColor 标记字体颜色
     */public void setSchemeColor(int style, int schemeColor, int schemeTextColor)/**
     * 设置背景色
     *
     * @param yearViewBackground 年份卡片的背景色
     * @param weekBackground 星期栏背景色
     * @param lineBg 线的颜色
    */public void setBackground(int yearViewBackground, int weekBackground, int lineBg)

    这个控件的特别之处就是它的UI是可以交给客户端自由绘制的,因此可以自由发挥想象力,绘制你需要的日历效果UI

    接下来介绍如何完全自定义日历,自定义日历需要同时自定义月视图和周视图,代码几乎一样,需要实现三个回调函数即可,如下:

    onDrawSelectedonDrawSchemeonDrawText

    /**
     * 首先继承月视图,假如我们想实现高仿魅族的日历
     */
    public class MeiZuMonthView extends MonthView {    /**
         * 自定义魅族标记的文本画笔
         */
        private Paint mTextPaint = new Paint();    /**
         * 自定义魅族标记的圆形背景
         */
        private Paint mSchemeBasicPaint = new Paint();    private float mRadio;    private int mPadding;    private float mSchemeBaseLine;    public MeiZuMonthView(Context context) {        super(context);        mTextPaint.setTextSize(dipToPx(context, 8));        mTextPaint.setColor(0xffffffff);        mTextPaint.setAntiAlias(true);        mTextPaint.setFakeBoldText(true);        mSchemeBasicPaint.setAntiAlias(true);        mSchemeBasicPaint.setStyle(Paint.Style.FILL);        mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);        mSchemeBasicPaint.setFakeBoldText(true);        mRadio = dipToPx(getContext(), 7);        mPadding = dipToPx(getContext(), 4);        Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics();        mSchemeBaseLine = mRadio - metrics.descent + (metrics.bottom - metrics.top) / 2 + dipToPx(getContext(), 1);    }    /**
         * 绘制选中的日子
         * @param canvas canvas
         * @param calendar 日历日历calendar
         * @param x 日历Card x起点坐标
         * @param y 日历Card y起点坐标
         * @param hasScheme hasScheme 非标记的日期
         * @return 返回true 则会继续绘制onDrawScheme,因为这里背景色不是是互斥的,所以返回true,返回false,则点击scheme标记的日子,则不继续绘制onDrawScheme,自行选择即可
         */
        @Override    protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {        mSelectedPaint.setStyle(Paint.Style.FILL);        mSelectedPaint.setColor(0x80cfcfcf);        canvas.drawRect(x + mPadding, y + mPadding, x + mItemWidth - mPadding, y + mItemHeight - mPadding, mSelectedPaint);        return true;    }    /**
         * 绘制标记的事件日子
         * @param canvas canvas
         * @param calendar 日历calendar
         * @param x 日历Card x起点坐标
         * @param y 日历Card y起点坐标
         */
        @Override    protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {        mSchemeBasicPaint.setColor(calendar.getSchemeColor());        canvas.drawCircle(x + mItemWidth - mPadding - mRadio / 2, y + mPadding + mRadio, mRadio, mSchemeBasicPaint);        canvas.drawText(calendar.getScheme(), x + mItemWidth - mPadding - mRadio, y + mPadding + mSchemeBaseLine, mTextPaint);    }    /**
         * 绘制文本
         * @param canvas canvas
         * @param calendar 日历calendar
         * @param x 日历Card x起点坐标
         * @param y 日历Card y起点坐标
         * @param hasScheme 是否是标记的日期
         * @param isSelected 是否选中
         */
        @Override    protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {        int cx = x + mItemWidth / 2;        int top = y - mItemHeight / 6;        if (isSelected) {//优先绘制选择的            canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,                    mSelectTextPaint);            canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mSelectedLunarTextPaint);        } else if (hasScheme) {//否则绘制具有标记的            canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,                    calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);            canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mCurMonthLunarTextPaint);        } else {//最好绘制普通文本            canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,                    calendar.isCurrentDay() ? mCurDayTextPaint :                            calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);            canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10,                    calendar.isCurrentDay() ? mCurDayLunarTextPaint :                            calendar.isCurrentMonth() ? mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);        }    }    /**
         * dp转px
         *
         * @param context context
         * @param dpValue dp
         * @return px
         */
        private static int dipToPx(Context context, float dpValue) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (dpValue * scale + 0.5f);    }}

    实现自定义周视图,周视图除了三个回调函数少了一个y参数,其它一样,因为周视图只有一行,所以可直接copy MonthView的代码,令y=0即可,如下

    public class MeizuWeekView extends WeekView {    private Paint mTextPaint = new Paint();    private Paint mSchemeBasicPaint = new Paint();    private float mRadio;    private int mPadding;    private float mSchemeBaseLine;    public MeizuWeekView(Context context) {        super(context);        mTextPaint.setTextSize(dipToPx(context, 8));        mTextPaint.setColor(0xffffffff);        mTextPaint.setAntiAlias(true);        mTextPaint.setFakeBoldText(true);        mSchemeBasicPaint.setAntiAlias(true);        mSchemeBasicPaint.setStyle(Paint.Style.FILL);        mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);        mSchemeBasicPaint.setColor(0xffed5353);        mSchemeBasicPaint.setFakeBoldText(true);        mRadio = dipToPx(getContext(), 7);        mPadding = dipToPx(getContext(), 4);        Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics();        mSchemeBaseLine = mRadio - metrics.descent + (metrics.bottom - metrics.top) / 2 + dipToPx(getContext(), 1);    }    /**
         *
         * @param canvas canvas
         * @param calendar 日历日历calendar
         * @param x 日历Card x起点坐标
         * @param hasScheme hasScheme 非标记的日期
         * @return true 则绘制onDrawScheme,因为这里背景色不是是互斥的
         */
        @Override    protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, boolean hasScheme) {        mSelectedPaint.setStyle(Paint.Style.FILL);        mSelectedPaint.setColor(0x80cfcfcf);        canvas.drawRect(x + mPadding, mPadding, x + mItemWidth - mPadding, mItemHeight - mPadding, mSelectedPaint);        return true;    }    @Override    protected void onDrawScheme(Canvas canvas, Calendar calendar, int x) {        mSchemeBasicPaint.setColor(calendar.getSchemeColor());        canvas.drawCircle(x + mItemWidth - mPadding - mRadio / 2, mPadding + mRadio, mRadio, mSchemeBasicPaint);        canvas.drawText(calendar.getScheme(), x + mItemWidth - mPadding - mRadio, mPadding + mSchemeBaseLine, mTextPaint);    }    @Override    protected void onDrawText(Canvas canvas, Calendar calendar, int x, boolean hasScheme, boolean isSelected) {        int cx = x + mItemWidth / 2;        int top = -mItemHeight / 6;        if (isSelected) {            canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,                    mSelectTextPaint);            canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10, mSelectedLunarTextPaint);        } else if (hasScheme) {            canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,                    calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);            canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10, mCurMonthLunarTextPaint);        } else {            canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top,                    calendar.isCurrentDay() ? mCurDayTextPaint :                            calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint);            canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + mItemHeight / 10,                    calendar.isCurrentDay() ? mCurDayLunarTextPaint :                            calendar.isCurrentMonth() ? mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);        }    }    /**
         * dp转px
         *
         * @param context context
         * @param dpValue dp
         * @return px
         */
        private static int dipToPx(Context context, float dpValue) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (dpValue * scale + 0.5f);    }}

    最后通过CalendarView两个attr配置class路径即可,是不是很像自定义Behavior?

    app:month_view="com.haibin.calendarviewproject.meizu.MeiZuMonthView"app:week_view="com.haibin.calendarviewproject.meizu.MeizuWeekView"

    最后各种自定义UI效果自由欣赏,源码都在demo

    52559aa45c945a852d6539cf482a2f4e.png

    53b5da52740e19435596e23f514f6032.png

    f8e1c20699679f475aa6e2d0f5f5ecff.png

    daeb3ca35585d77de6065b32125d4a50.png

    baaa6b29a176d88c6d0bdeddb1da8f4a.png

    09eb84b490ae302563b68e0a38552af0.png

    里面作者实现了几种类型的风格,可以参考实现

    项目开源地址,一点贡献

    https://github.com/huanghaibin-dev/CalendarView

    ---END---

    78550a327859f6307f2143aca1d6c518.png

     创作不易,点个“在看29dc9b1ad8221c73e942e372bb5adcbe.png
    展开全文
  • 1.思路:日历内容我用了一个GridView装,实现界面效果如下:1.1分析:1.1.1. 界面内容从上到下依次为:显示当前页面展示年月;跳转到上一个月或者下一个月按钮;周期标题头;数据内容(即当前展示月份所有...

    1.思路:

    日历内容我用了一个GridView装,实现的界面效果如下:

    588129e90e55252fb38beeea96f9f149.png

    1.1分析:

    1.1.1. 界面内容从上到下依次为:显示当前页面展示的年月;跳转到上一个月或者下一个月的按钮;周期标题头;数据内容(即当前展示的月份所有日期,以及上一个月和下一个月的几个日期)。


    1.1.2. 数据内容部分我使用了一个GridView来实现,其余部分均为Button和TextView,因此最重要部分就是实现数据部分。


    1.1.3.  数据内容部分分为三大块:1.上一个月的最后几天,2.本月的所有日期,3.下一个月的前几天。


    1.1.4.  获得这三大块的数据,然后渲染在GridView上。


    1.1.5.  将获得数据然后渲染在页面上写成一个方法,将当前年月的日期作为参数传进去,实现点击上一个月或下一个月按跳转日期页面。

    2.步骤详情:

    2.1.步骤一:

    创建布局文件。以下是布局文件的代码:

    <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:gravity="center_horizontal"    tools:context=".calendar.Calendar2Activity">        <TextView        android:id="@+id/calendar_date"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginTop="10dp"        android:text="2020-10-1"/>        <LinearLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:orientation="horizontal">        <Button            android:id="@+id/calendar_last"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="上一个月"/>        <Button            android:id="@+id/calendar_next"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginLeft="10dp"            android:text="下一个月"/>    LinearLayout>        <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal">        <TextView            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="wrap_content"            android:gravity="center"            android:text="周一"            android:background="@drawable/bg_white_black_border_yuanjiao"/>        <TextView            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="wrap_content"            android:gravity="center"            android:text="周二"            android:background="@drawable/bg_white_black_border_yuanjiao"/>        <TextView            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="wrap_content"            android:gravity="center"            android:text="周三"            android:background="@drawable/bg_white_black_border_yuanjiao"/>        <TextView            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="wrap_content"            android:gravity="center"            android:text="周四"            android:background="@drawable/bg_white_black_border_yuanjiao"/>        <TextView            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="wrap_content"            android:gravity="center"            android:text="周五"            android:background="@drawable/bg_white_black_border_yuanjiao"/>        <TextView            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="wrap_content"            android:gravity="center"            android:text="周六"            android:background="@drawable/bg_white_black_border_yuanjiao"/>        <TextView            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="wrap_content"            android:gravity="center"            android:text="周日"            android:background="@drawable/bg_white_black_border_yuanjiao"/>    LinearLayout>        <GridView        android:id="@+id/calendar_gridview"        android:layout_width="match_parent"        android:layout_height="0dp"        android:layout_weight="1"        android:numColumns="7"/>LinearLayout>

    预览界面如下:

    84e4ea399957b2ded2527e44a7c33868.png

    2.2.步骤二:

    获得数据并将其渲染到GridView上。
    数据分为下图标出的三部分:

    397e70af0e63c315a0810a26d7fefc7c.png

    蓝色部分为当前显示年月的一个月的所有日期,因此,我们需要获取当前显示的月份有多少天。代码如下:

    Calendar calendar = Calendar.getInstance();        calendar.set(Calendar.YEAR,year);        calendar.set(Calendar.MONTH,month-1);        calendar.set(Calendar.DATE,1);//把日期设置为当月第一天        calendar.roll(Calendar.DATE,-1);//日期回滚一天,也就最后一天        int date = calendar.get(Calendar.DATE);

    红色部分为上一个月的最后几天,因此,我们需要得到上一个月的最后一天是几号,也就是上一个月的总天数(因为每个月的第一天都是1号,所以每个月的最后一天的号数就等于这个月的总天数),因此我将上面的求某个月份的天数的代码写成了一个方法,如下:

        //获取输入月份的总天数    private int getMonthNum(int year,int month){        Calendar calendar = Calendar.getInstance();        calendar.set(Calendar.YEAR,year);        calendar.set(Calendar.MONTH,month-1);        calendar.set(Calendar.DATE,1);//把日期设置为当月第一天        calendar.roll(Calendar.DATE,-1);//日期回滚一天,也就最后一天        return calendar.get(Calendar.DATE);    }

    先将红色和蓝色部分的数据放好,最后剩余几个格子就从1开始依次递增放入。
    注意:当前月份1号是星期天,且这个月有30天或31天的天数则总共GridView有6行,即总共有42个格子,(例如2020年11月)。


    在这里你可以直接将所有的月份的格子都规定成42个,也可以在当前月份的最后一天超出35个格子(即5行)的时候换成42个格子。在这里我就让它在超出35个格子的时候再换成42个格子。


    这里我们要抓住零界点,也就是上一个月的最后一天到本月第一天和本月最后一天到下一个月的第一天。


    由于我写的周期是从周一到周日,因此要先获取本月的第一天是周几,就能获得本月第一天的下标位置。获得当天是周几的代码如下(同样我将它写成了一个方法,方便多次使用),上一个月的总天数用变量lastdates表示,本月总天数用变量dates表示:

    private int getWeek(String date){        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");        Calendar calendar = Calendar.getInstance();        try {            calendar.setTime(format.parse(date));        } catch (ParseException e) {            e.printStackTrace();        }        //1:周日,7:周六        return calendar.get(Calendar.DAY_OF_WEEK);    }

    getWeek()方法返回的的周期数为:1,2,3,4,5,6,7,分别对应:周日,周一,周二,周三,周四,周五,周六;


    但是我们需要让它的对应关系为:1,2,...,7,分别对应:周一,周二,...,周日。因此我们需要对获得到的周期数进行处理,代码如下:

    //获取当前月份1号是周几        int week = getweek(today);        if (week == 1) {            week = 7;//周日        } else week = week - 1;

    接下来就是重点了:获得三个部分的数据集合,直接放代码:

            int num = week+dates-1;//当下标从1开始,num为输入月份的最后一个值的下标        if (num > 35){            gridview_size = 42;        }else gridview_size = 35;        int j = 1, a = 1; //当前月份的各个日期以及下一个月的日期        int last = lastdates - (week - 2);//第一个单元格的值        for (int i = 1; i <= gridview_size; i++) {            if (i < week) {//上月剩余                dataList.add(last);                last++;            } else if (i <= num) {                dataList.add(j);//本月                j++;            } else {                dataList.add(a);//下月                a++;            }        }

    dataList为放三个部分的数据的整数型集合,即:
    List dataList = new ArrayList<>();
    我将下标从1开始
    int num = week+dates-1,num为本月最后一天的下标,等于周期数(第一天的下标)+本月总天数-1
    int last = lastdates - (week - 2);last为第一个格子的值,lastdates为上一个月的最后一天的号数,用最后一天的号数减去最后一天所在格子的前面的总格子数(最后一天的格子不算在内),就等于第一个格子内的值,比如数据:__,23,24,25,26;26数字前面有4个数,所以:26-4=22,所得的22就是横线处的值。

    2.3.步骤三:

    将获得的dataList渲染到GridView上。

    3.所有代码(代码分为两部分:布局文件代码和Java代码):

    1.布局文件的代码如上述的布局代码。
    2.Java代码部分如下:

    public class CalendarActivity extends AppCompatActivity implements View.OnClickListener {    private Button calendar_last;    private Button calendar_next;    private GridView calendar_gridview;    private List dataList = new ArrayList<>();    private String today;    private TextView calendar_date;    private int week,dates;    private MyAdapter adapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_calendar);        initView();        //获取当前月份        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM");        Date date = new Date(System.currentTimeMillis());        today = simpleDateFormat.format(date);        getData(today);    }    private void getData(String today) {        calendar_date.setText(today);        today = today + "-01";        String years = today.split("-")[0];        String months = today.split("-")[1];        Log.i("日历", months);        //获取当前月份日期数        dates = getmoth(Integer.parseInt(years), Integer.parseInt(months));        //获取当前月份1号是周几        week = getweek(today);        if (week == 1) {            week = 7;//周日        } else week = week - 1;        //获取上一个月份的天数        int lastdates;        if (Integer.parseInt(months) == 1) {            lastdates = getmoth(Integer.parseInt(years) - 1, 12);        } else {            lastdates = getmoth(Integer.parseInt(years), Integer.parseInt(months) - 1);        }        //获取当前展示的日历界面的数据        int num = week+dates-1;//当下标从1开始,num为输入月份的最后一个值的下标        int gridview_size;        if (num > 35){            gridview_size = 42;        }else gridview_size = 35;        int j = 1, a = 1; //当前月份的各个日期以及下一个月的日期        int last = lastdates - (week - 2);//第一个单元格的值        for (int i = 1; i <= gridview_size; i++) {            if (i < week) {//上月剩余                dataList.add(last);                last++;            } else if (i <= num) {                dataList.add(j);//本月                j++;            } else {                dataList.add(a);//下月                a++;            }        }        //渲染数据        if (adapter == null){            adapter = new MyAdapter(CalendarActivity.this, dataList);            calendar_gridview.setAdapter(adapter);        }else adapter.notifyDataSetChanged();    }    private int getmoth(int year, int month) {        Calendar calendar = Calendar.getInstance();        calendar.set(Calendar.YEAR, year);        calendar.set(Calendar.MONTH, month - 1);        calendar.set(Calendar.DATE, 1);        calendar.roll(Calendar.DATE, -1);        int date = calendar.get(Calendar.DATE);        return date;    }    private int getweek(String date) {        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");        Calendar calendar = Calendar.getInstance();        try {            calendar.setTime(simpleDateFormat.parse(date));        } catch (ParseException e) {            e.printStackTrace();        }        //1:周日,7:周六        return calendar.get(Calendar.DAY_OF_WEEK);    }    private void initView() {        calendar_last = (Button) findViewById(R.id.calendar_last);        calendar_next = (Button) findViewById(R.id.calendar_next);        calendar_gridview = (GridView) findViewById(R.id.calendar_gridview);        calendar_last.setOnClickListener(this);        calendar_next.setOnClickListener(this);        calendar_date = (TextView) findViewById(R.id.calendar_date);        calendar_date.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.calendar_last:                //上一个月按钮的点击事件                dataList.clear();                int years = Integer.parseInt(today.split("-")[0]);                int months = Integer.parseInt(today.split("-")[1]);                if (months == 1) {                    months = 12;                    years = years - 1;                    getData(years + "-" + months);                    today = years + "-" + months;                } else {                    months = months - 1;                    getData(years + "-" + months);                    today = years + "-" + months;                }                break;            case R.id.calendar_next:                //下一个月按钮的点击事件                dataList.clear();                int years1 = Integer.parseInt(today.split("-")[0]);                int months1 = Integer.parseInt(today.split("-")[1]);                if (months1 == 12) {                    months1 = 1;                    years1 = years1 + 1;                    getData(years1 + "-" + months1);                    today = years1 + "-" + months1;                } else {                    months1 = months1 + 1;                    getData(years1 + "-" + months1);                    today = years1 + "-" + months1;                }                break;        }    }    class MyAdapter extends BaseAdapter {        private Context context;        private List list;        public MyAdapter(Context context, List list) {            this.context = context;            this.list = list;        }        @Override        public int getCount() {            return list.size();        }        @Override        public Object getItem(int position) {            return list.get(position);        }        @Override        public long getItemId(int position) {            return position;        }        @Override        public View getView(int position, View convertView, ViewGroup parent) {            if (convertView == null) {                convertView = LayoutInflater.from(context).inflate(R.layout.calendar_gridview_item, null, false);            }            final TextView textView = (TextView) convertView.findViewById(R.id.calendar_gridview_item_tv);            textView.setText(list.get(position) + "");            //设置本月的日期高亮显示,非本月的日期灰色显示            if (position >= week-1 && position < week+dates-1){//判断是当月的日期                textView.setTextColor(Color.BLACK);//黑色            }else textView.setTextColor(Color.GRAY);//灰色            return convertView;        }    }}

    到这里就结束啦.

    370c9298ff99fc551efbe25e82ca81c8.png

    a3f76b341ad64b8a9421cb88c7f17dee.png

    展开全文
  • 来源:wenzhihao123https://www.jianshu.com/p/a2f102c728ce前言最近项目需要日历效果,考虑用第三方反而不太适合设计需求,修改复杂,与其这样不入自己重新写一个干净的控件。虽不是什么牛逼控件,但是也需要我们...

    点击上方蓝字关注 ??

    69226441eb38cbd174e924db79f3f016.png

    来源: wenzhihao123

    https://www.jianshu.com/p/a2f102c728ce

    前言

    最近项目需要日历效果,考虑用第三方的反而不太适合设计需求,修改复杂,与其这样不入自己重新写一个干净的控件。虽不是什么牛逼控件,但是也需要我们能按照设计自己写出来。在此记录一下实现思路。

    效果图:

    a80a7dae768abb209ff14b42b708908b.png

    731bead7f680aefa38f92343a3de3f1d.png

    详解

    实现思路

    • 头部是一个自定义组合控件;

    • 显示一周的日期部分用GridView 更加方便更新;

    • 切换月的部分是一个自定义PopupWindow;

    • GridView选中效果;

    • GridView根据手势GestureDetector监听左右滑动;

    • 核心其实还是Calendar类,根据这个类我们可以获取制定日期一周的日期集合、可以获取制定日期一月的日期集合等等;

    • 根据阳历日期获取阴历日期

    使用:

    // xml布局引用 android:id="@+id/week"
    android:layout_width="match_parent"
    android:background="@color/color_ffffff"
    android:layout_height="wrap_content">// 代码中,自定义回调监听选中的日期
    dataView = (DataView) findViewById(R.id.week);
    dataView.setOnSelectListener(new DataView.OnSelectListener() {
    @Overridepublic void onSelected(DateEntity date) {
    info.setText("日期:"+ date.date+"\n"+"周几:"+ date.weekName+"\n"+"今日:"+ date.isToday+"\n"+"时间戳:"+ date.million+"\n");
    Log.e("wenzhiao--------------",date.toString());
    }
    });//需要传递此种格式的日期,不传默认是获取今日的日期
    dataView.getData("2017-04-19");

    实现整体逻辑

    回调的日期信息封装成一个实体类DateEntity:

    public class DateEntity {public long million ; //时间戳public String weekName ; //周几public int weekNum ; //一周中第几天,非中式public String date ; //日期public boolean isToday ; //是否今天public String day ; //天public String luna ; //阴历@Overridepublic String toString() {return "DateEntity{" +"million=" + million +", weekName='" + weekName + '\'' +", weekNum=" + weekNum +", date='" + date + '\'' +", isToday=" + isToday +", day='" + day + '\'' +", luna='" + luna + '\'' +'}';
    }
    }

    封装的日期获取的工具类:

    package com.wzh.calendar.utils;
    import com.wzh.calendar.bean.DateEntity;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Calendar;
    import java.util.Date;public class DataUtils {public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");public static int selectPosition =-1;public static int getSelectPosition() {return selectPosition;
    }/**
    *
    * 获取当前日期一周的日期
    * @param date
    * @return
    */
    public static ArrayListgetWeek(String date){
    ArrayList result = new ArrayList<>();
    Calendar cal =Calendar.getInstance();try {
    cal.setTime(dateFormat.parse(date));
    } catch (ParseException e) {// TODO Auto-generated catch block
    e.printStackTrace();
    }
    cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); //获取本周一的日期for (int i = 0; i < 7; i++) {
    DateEntity entity = new DateEntity();
    entity.date = getValue(cal.get(cal.YEAR))+"-"+getValue(cal.get(cal.MONTH)+1)+"-"+getValue(cal.get(cal.DATE));
    entity.million = cal.getTimeInMillis() ;
    entity.day = getValue(cal.get(cal.DATE));
    entity.weekNum = cal.get(Calendar.DAY_OF_WEEK);
    entity.weekName = getWeekName(entity.weekNum);
    entity.isToday = isToday(entity.date);
    cal.add(Calendar.DATE, 1);
    result.add(entity);
    }return result ;
    }/**
    * 获取当前日期一月的日期
    * @param date
    * @return
    */
    public static ArrayListgetMonth(String date){
    ArrayList result = new ArrayList<>();
    Calendar cal =Calendar.getInstance();try {
    cal.setTime( new SimpleDateFormat("yyyy-MM").parse(date));
    } catch (ParseException e) {// TODO Auto-generated catch block
    e.printStackTrace();
    }int max = cal.getActualMaximum(Calendar.DAY_OF_MONTH);for (int i = 1; i <=max; i++) {
    DateEntity entity = new DateEntity();
    entity.date = getValue(cal.get(cal.YEAR))+"-"+getValue(cal.get(cal.MONTH)+1)+"-"+getValue(cal.get(cal.DATE));
    entity.million = cal.getTimeInMillis() ;
    entity.weekNum = cal.get(Calendar.DAY_OF_WEEK);
    entity.day = getValue(cal.get(cal.DATE));
    entity.weekName = getWeekName(entity.weekNum);
    entity.isToday = isToday(entity.date);
    entity.luna = getLuna(entity.date);
    cal.add(Calendar.DATE, 1);
    result.add(entity);
    }//为了用空的值填补第一个之前的日期//先获取在本周内是周几int weekNum = result.get(0).weekNum -1 ;for (int j = 0 ;j DateEntity entity = new DateEntity();
    result.add(0,entity);
    }for (int i = 0; i if (date.equals(result.get(i).date)){
    selectPosition = i ;
    }
    }return result ;
    }/**
    * 根据美式周末到周一 返回
    * @param weekNum
    * @return
    */
    private static String getWeekName(int weekNum) {
    String name = "" ;switch (weekNum) {case 1:
    name = "星期日";break;case 2:
    name = "星期一";break;case 3:
    name = "星期二";break;case 4:
    name = "星期三";break;case 5:
    name = "星期四";break;case 6:
    name = "星期五";break;case 7:
    name = "星期六";break;default:break;
    }return name;
    }/**
    * 是否是今天
    * @param sdate
    * @return
    */
    public static boolean isToday(String sdate){
    boolean b = false;
    Date time = null ;try {
    time = dateFormat.parse(sdate);
    } catch (ParseException e) {// TODO Auto-generated catch block
    e.printStackTrace();
    }
    Date today = new Date();if(time != null){
    String nowDate = dateFormater.get().format(today);
    String timeDate = dateFormater.get().format(time);if(nowDate.equals(timeDate)){
    b = true;
    }
    }return b;
    }/**
    * 个位数补0操作
    * @param num
    * @return
    */
    public static String getValue(int num){return String.valueOf(num>9?num:("0"+num));
    }private final static ThreadLocal dateFormater = new ThreadLocal() {
    @Overrideprotected SimpleDateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd");
    }
    };/**
    * 获取系统当前日期
    */
    public static String getCurrDate(String format) {
    SimpleDateFormat formatter = new SimpleDateFormat(format);
    Date curDate = new Date(System.currentTimeMillis());//获取当前时间
    String str = formatter.format(curDate);return str;
    }/**
    * 格式化日期
    */
    public static String formatDate(String date ,String format) {
    SimpleDateFormat formatter = new SimpleDateFormat(format);
    Date curDate = null;//获取当前时间try {
    curDate = formatter.parse(date);
    } catch (ParseException e) {
    e.printStackTrace();
    }
    String str = formatter.format(curDate);return str;
    }/**
    * 切换周的时候用
    * 获取前/后 几天的一个日期
    * @param currentData
    * @param dayNum
    * @return
    */
    public static String getSomeDays(String currentData,int dayNum){
    Calendar c = Calendar.getInstance();//过去七天try {
    c.setTime(DataUtils.dateFormat.parse(currentData));
    } catch (ParseException e) {
    e.printStackTrace();
    }
    c.add(Calendar.DATE, dayNum);
    Date d = c.getTime();
    String day = DataUtils.dateFormat.format(d);return day ;
    }/**
    * 获取前/后 几个月的一个日期 切换月的时候用
    * @param currentData
    * @param monthNum
    * @return
    */
    public static String getSomeMonthDay(String currentData,int monthNum){
    Calendar c = Calendar.getInstance();try {
    c.setTime(new SimpleDateFormat("yyyy-MM").parse(currentData));
    } catch (ParseException e) {
    e.printStackTrace();
    }
    c.set(Calendar.MONTH, c.get(Calendar.MONTH) +monthNum);
    Date day = c.getTime();return new SimpleDateFormat("yyyy-MM-dd").format(day);
    }/**
    * 获取阴历
    * @param date
    * @return
    */
    public static String getLuna(String date){
    Calendar today = Calendar.getInstance();try {
    today.setTime(Lunar.chineseDateFormat.parse(date));
    } catch (ParseException e) {
    e.printStackTrace();
    }return new Lunar(today).toString() ;
    }
    }

    这里有个地方需要注意一下,因为我们一个月第一天是周几不确定,显示GridView的时候第一天的position也不确定,但是我们可以根据前面少了几天再添加上空对象即可:

    //为了用空的值填补第一个之前的日期//先获取在本周内是周几int weekNum = result.get(0).weekNum -1 ;for (int j = 0 ;j DateEntity entity = new DateEntity();
    result.add(0,entity);
    }

    还有一个获取阴历日期的工具类,比较复杂,所以直接从网上找了一个,这里就不贴了。

    剩下的就是去写布局、自定义PopupWindow了,这些应该是没什么难度吧。关于GridView选中,原理就是在Adapter里面设置一个选中方法:

    private int selectedPosition = -1;// 选中的位置public void setSelectedPosition(int position) {
    selectedPosition = position;
    notifyDataSetChanged();
    }
    ...
    在Adapter的getView(int position, View convertView, ViewGroup parent)
    方法去判断 position是否和selectedPosition 是否相等,相等就表示选中了,可以修改背景、字体颜色等等
    ...
    当然在用到Adapter的地方也要调用setSelectedPosition方法
    具体怎么使用可以参考里面的代码。

    关于GrdiView左右滑动的判断(关键代码片段):

    private GestureDetector gestureDetector;//初始化
    gestureDetector = new GestureDetector(context,onGestureListener);/**
    * 手势监听是否是左右滑动,这里认为滑动距离超过100就算左右滑动
    */
    private GestureDetector.OnGestureListener onGestureListener =new GestureDetector.SimpleOnGestureListener() {@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {float x = e2.getX() - e1.getX();float y = e2.getY() - e1.getY();if (x > 100) {
    doResult(RIGHT);
    } else if (x < -100) {
    doResult(LEFT);
    }return true;
    }
    };public void doResult(int action) {switch (action) {case RIGHT:
    date = DataUtils.getSomeMonthDay(date,-1);
    adapter.setData(DataUtils.getMonth(date));
    adapter.setDateString(date);
    adapter.setSelectedPosition(DataUtils.getSelectPosition());
    currentDateTv.setText("当前月份:"+DataUtils.formatDate(date,"yyyy-MM"));
    Log.e("wenzihao","go right");break;case LEFT:
    date = DataUtils.getSomeMonthDay(date,+1);
    adapter.setData(DataUtils.getMonth(date));
    adapter.setDateString(date);
    adapter.setSelectedPosition(DataUtils.getSelectPosition());
    currentDateTv.setText("当前月份:"+DataUtils.formatDate(date,"yyyy-MM"));
    Log.e("wenzihao","go left");break;
    }
    }
    ...设置手势给gridview
    gridView.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View view, MotionEvent event) {return gestureDetector.onTouchEvent(event);
    }
    });

    最后就是点击PopupWindow的时候自定义回调方法把选中日期带过去即可。

    好了,其他的代码也不贴了,关键点就那么点,没啥太大难度,感觉主要还是考验大家的基本功吧。

    这么一个自定义日历控件就写好了,是不是很简单感觉,希望能够对大家有启发和帮助,可以灵活自定义出设计产品需要的各种控件。

    最后附上项目地址:

    https://github.com/wenzhihao123/Android-CalendarView-master

    —————END—————

    9db397a52cf8858eb7496d2188c1b091.png     

       创作不易,点个“在看1c9d2e6313c22668184c6e55ea8b18eb.png

    展开全文
  • 很多JS日历控件拿来都不能使用,都是参数的问题估计是找不到控件的原因,求高手指点,如果没有,那么要怎么样才能使用JS获取模版列控件的ClientID, 通过 var temp = document.getElementById("<%=GridView....
  • CalendarView使用详细文档 日历控件定制是移动开发平台上比较常见而且...性能差,加载速度慢,原因是各种基于GridView或RecyclerView等ViewGroup实现的日历控件数太多,假设一个月视图界面有42个item,每个it...

    github地址:https://github.com/huanghaibin-dev/CalendarView

    CalendarView使用详细文档

    日历控件定制是移动开发平台上比较常见的而且比较难的需求,一般会遇到以下问题:

    • 性能差,加载速度慢,原因是各种基于GridView或RecyclerView等ViewGroup实现的日历,控件数太多,假设一个月视图界面有42个item,每个item里面分别就有2个子TextView:天数、农历数和本身3个控件,这样一个月视图就有42 * 3+1(RecyclerView or GridView),清楚ViewPager特性的开发者就会明白,一般ViewPager持有3个item,那么一个日历控件持有的View控件数的数量将达到 1(ViewPager)+ 3(RecyclerView or GridView) + 3 * 42 * 3 = 382,如果用1个View来代替RecyclerView等,用Canvas来代替各种TextView,那View的数量瞬间将下降360+,内存和性能优势将相当明显了
    • 难定制 一般日历框架发布的同时也将UI风格确定下来了,假如人人都使用这个日历框架,那么将会千篇一律,难以突出自己的风格,要么就得改源码,成本太大,不太实际
    • 功能性不足 例如无法自定义周起始、无法更改选择模式、动态设置UI等等
    • 无法满足产品经理提出的变态需求 今天产品经历说我们要这样的实现、明天跟你说这里得改、后天说我们得限制一些日期...

    但现在有了全新的 CalendarView 控件,它解锁了各种姿势,而且你可以任意定制,直到你满足为止...

    插拔式设计

    插拔式设计:好比插座一样,插上灯泡就会亮,插上风扇就会转,看用户需求什么而不是看插座有什么,只要是电器即可。此框架使用插拔式,既可以在编译时指定年月日视图,如:app:month_view="xxx.xxx.MonthView.class",也可在运行时动态更换年月日视图,如:CalendarView.setMonthViewClass(MonthView.Class),从而达到UI即插即用的效果,相当于框架不提供UI实现,让UI都由客户端实现,不至于日历UI都千篇一律,只需遵守插拔式接口即可随意定制,自由化程度非常高。

    CalendarView 的特性

    • 基于Canvas绘制,极速性能
    • 热插拔思想,任意定制周视图、月视图,即插即用!
    • 支持单选、多选、范围选择、国内手机日历默认自动选择等选择模式
    • 支持静态、动态设置周起始,一行代码搞定
    • 支持静态、动态设置日历项高度、日历填充模式
    • 支持设置任意日期范围、任意拦截日期
    • 支持多点触控、手指平滑切换过渡,拒绝界面抖动
    • 类NestedScrolling特性,嵌套滚动
    • 既然这么多支持,那一定支持英语、繁体、简体,任意定制实现

    注意: 框架本身只是实现各自逻辑,不实现UI,UI如同一张白纸,任凭客户端自行通过Canvas绘制实现,如果不熟悉Canvas的,请自行了解各自Canvas.drawXXX方法,UI都靠Canvas实现,坐标都已经计算好了,因此怎么隐藏农历,怎么换某些日期的字,这些都不属于框架范畴,只要你想换,都能随便换。

    再次注意: app Demo只是Demo,只是示例如何使用,与框架本身没有关联,不属于框架一部分

    接下来请看CalendarView操作,前方高能

    • 你这样继承自己的月视图和周视图,只需要依次实现绘制选中:onDrawSelected、绘制事务:onDrawScheme、绘制文本:onDrawText 这三个回调即可,参数和坐标都已经在回调函数上实现好,周视图也是一样的逻辑,只是不需要y参数
    /**
     * 定制高仿魅族日历界面,按你的想象力绘制出各种各样的界面
     *
     */
    public class MeiZuMonthView extends MonthView {
    
        /**
         * 绘制选中的日子
         *
         * @param canvas    canvas
         * @param calendar  日历日历calendar
         * @param x         日历Card x起点坐标
         * @param y         日历Card y起点坐标
         * @param hasScheme hasScheme 非标记的日期
         * @return 返回true 则绘制onDrawScheme,因为这里背景色不是是互斥的,所以返回true
         */
        @Override
        protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
            //这里绘制选中的日子样式,看需求需不需要继续调用onDrawScheme
            return true;
        }
    
        /**
         * 绘制标记的事件日子
         *
         * @param canvas   canvas
         * @param calendar 日历calendar
         * @param x        日历Card x起点坐标
         * @param y        日历Card y起点坐标
         */
        @Override
        protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
           //这里绘制标记的日期样式,想怎么操作就怎么操作
        }
    
        /**
         * 绘制文本
         *
         * @param canvas     canvas
         * @param calendar   日历calendar
         * @param x          日历Card x起点坐标
         * @param y          日历Card y起点坐标
         * @param hasScheme  是否是标记的日期
         * @param isSelected 是否选中
         */
        @Override
        protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
            //这里绘制文本,不要再问我怎么隐藏农历了,不要再问我怎么把某个日期换成特殊字符串了,要怎么显示你就在这里怎么画,你不画就不显示,是看你想怎么显示日历的,而不是看框架
        }
    }
    • 当你实现好之后,直接在xml界面上添加特性,编译后可以即时预览效果:
    <attr name="month_view" format="string" /><!--自定义月视图路径-->
    <attr name="week_view" format="string" /> <!--自定义周视图路径-->
    
    app:month_view="com.haibin.calendarviewproject.MeiZuMonthView"
    app:week_view="com.haibin.calendarviewproject.MeiZuWeekView"
    
    • 视图有多种模式可供选择,几乎涵盖了各种需求,看各自的需求自行继承
    如果继承这2个,MonthView、WeekView,即select_mode="default_mode",这是默认的手机自带的日历模式,会自动选择月的第一天,不支持拦截器,
    也可以设置select_mode="single_mode",即单选模式,支持拦截器
    
    如果继承这2个,RangeMonthView、RangeWeekView,即select_mode="range_mode",这是范围选择模式,支持拦截器
    
    如果继承这2个,MultiMonthView、MultiWeekView,即select_mode="multi_mode",这是多选模式,支持拦截器
    
    • 如果静态模式无法满足你的需求,你可能需要动态变换定制的视图界面,你可以使用热插拔特性,即插即用,不爽就换:
    mCalendarView.setWeekView(MeiZuWeekView.class);
    
    mCalendarView.setMonthView(MeiZuMonthView.class);
    
    • 如果你需要可收缩的日历,你可以在 CalendarView 父布局添加 CalendarLayout,当然你不需要周视图也可以不用,例如原生日历,使用如下:
    <com.haibin.calendarview.CalendarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            app:default_status="shrink"
            app:calendar_show_mode="only_week_view"
            app:calendar_content_view_id="@+id/recyclerView">
    
            <com.haibin.calendarview.CalendarView
                 android:id="@+id/calendarView"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:background="#fff"
                 app:month_view="com.haibin.calendarviewproject.simple.SimpleMonthView"
                 app:week_view="com.haibin.calendarviewproject.simple.SimpleWeekView"
                 app:week_bar_view="com.haibin.calendarviewproject.EnglishWeekBar"
                 app:calendar_height="50dp"
                 app:current_month_text_color="#333333"
                 app:current_month_lunar_text_color="#CFCFCF"
                 app:min_year="2004"
                 app:other_month_text_color="#e1e1e1"
                 app:scheme_text="假"
                 app:scheme_text_color="#333"
                 app:scheme_theme_color="#333"
                 app:selected_text_color="#fff"
                 app:selected_theme_color="#333"
                 app:week_start_with="mon"
                 app:week_background="#fff"
                 app:month_view_show_mode="mode_only_current"
                 app:week_text_color="#111" />
    
            <android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#ffffff" />
        </com.haibin.calendarview.CalendarLayout>
    • 使用可收缩的日历你可以使用监听器,监听视图变换
    public void setOnViewChangeListener(OnViewChangeListener listener);
    
    • 当然 CalendarLayout 有很多特性可提供周月视图无缝切换,而且,平滑手势不抖动!使用 CalendarLayout,你需要指定 calendar_content_view_id,用他来平移收缩月视图,更多特性如下:
    <!-- 日历显示模式 -->
    <attr name="calendar_show_mode">
          <enum name="both_month_week_view" value="0" /><!-- 默认都有 -->
          <enum name="only_week_view" value="1" /><!-- 仅周视图 -->
          <enum name="only_month_view" value="2" /><!-- 仅月视图 -->
    </attr>
    
    <attr name="default_status">
          <enum name="expand" value="0" /> <!--默认展开-->
          <enum name="shrink" value="1" /><!--默认搜索-->
    </attr>
    
    <attr name="calendar_content_view_id" format="integer" /><!--内容布局id,用于提供月视图平移过渡-->
    
    • CalendarView 可以设置全屏,只需设置 app:calendar_match_parent="true"即可,全屏CalendarView是不需要周视图的,不必嵌套CalendarLayout

    • CalendarView 也提供了高效便利的年视图,可以快速切换年份、月份,十分便利

    • 但年视图也不一定就适合你的胃口,如果你希望像弹出 DatePickerView,通过它来跳转日期,你可以使用以下的API来让日历与其它控件联动
    
    CalendarView.scrollToCalendar();
    
    CalendarView.scrollToNext();
    
    CalendarView.scrollToPre();
    
    CalendarView.scrollToXXX();
    
    
    • 你也许需要像魅族日历一样,可以静态、动态更换周起始
    
    app:week_start_with="mon、sun、sat"
    
    CalendarView.setWeekStarWithSun();
    
    CalendarView.setWeekStarWithMon();
    
    CalendarView.setWeekStarWithSat();
    
    
    • 假如你是做酒店、旅游等应用场景的APP的,那么需要可选范围的日历,你可以这样继承,和普通视图实现完全一样
    public class CustomRangeMonthView extends RangeMonthView{
    
    }
    
    public class CustomRangeWeekView extends RangeWeekView{
    
    }
    
    • 然后你需要设置选择模式为范围模式:select_mode="range_mode"

    • 酒店式日历场景当然是不能从昨天开始订房的,也不能无限期订房,所以你需要静态或动态设置日历范围、精确到具体某一天!!!

    
    <attr name="min_year" format="integer" />
    <attr name="max_year" format="integer" />
    <attr name="min_year_month" format="integer" />
    <attr name="max_year_month" format="integer" />
    <attr name="min_year_day" format="integer" />
    <attr name="max_year_day" format="integer" />
    
    CalendarView.setRange(int minYear, int minYearMonth, int minYearDay,
             int maxYear, int maxYearMonth, int maxYearDay)
    
    
    • 当然还有更特殊的日子也是不能选择的,例如:某月某号起这N天时间内因为超强台风来袭,酒店需停止营业N天,这段期间不可订房,这时日期拦截器就排上用场了
    //设置日期拦截事件
    mCalendarView.setOnCalendarInterceptListener(new CalendarView.OnCalendarInterceptListener() {
         @Override
         public boolean onCalendarIntercept(Calendar calendar) {
             //这里写拦截条件,返回true代表拦截,尽量以最高效的代码执行
             return calendar.isWeekend();
         }
    
         @Override
         public void onCalendarInterceptClick(Calendar calendar, boolean isClick) {
             //todo 点击拦截的日期回调
         }
    });
    • 添加日期拦截器和范围设置后,你可以在周月视图按需求获得他们的结果
    boolean isInRange = isInRange(calendar);//日期是否在范围内,超出范围的可以置灰
    
    boolean isEnable = !onCalendarIntercept(calendar);//日期是否可用,没有被拦截,被拦截的可以置灰
    

    • 假如你是做清单类、任务类APP的,可能会有这样的需求:标记某天事务的进度,这也很简单,因为:日历界面长什么样,你自己说了算!!!

    • 也许你只需要像原生日历那样就够了,但原生日历那奇怪且十分不友好的style,受到theme的影响,各种头疼,使用此控件,你只需要简简单单定制月视图就够了,CalendarView 能非常简单就高仿各种日历UI

    • CalendarView 提供了 setSchemeDate(Map<String, Calendar> mSchemeDates) 这个十分高效的API用来动态标记事务,即时你的数据量达到数千、数万、数十万,都不会对UI渲染造成影响

    • 日历类 Calendar 提供了许多十分有用的API

    boolean isWeekend();//判断是不是周末,可以用不同的画笔绘制周末的样式
    
    int getWeek();//获取星期
    
    String getSolarTerm();//获取24节气,可以用不同颜色标记不同节日
    
    String getGregorianFestival();//获取公历节日,自由判断,把节日换上喜欢的颜色
    
    String getTraditionFestival();//获取传统节日
    
    boolean isLeapYear();//是否是闰年
    
    int getLeapMonth();//获取闰月
    
    boolean isSameMonth(Calendar calendar);//是否相同月
    
    int compareTo(Calendar calendar);//比较日期大小 -1 0 1
    
    long getTimeInMillis();//获取时间戳
    
    int differ(Calendar calendar);//日期运算,相差多少天

    CalendarView 的全部xml特性如下:

    <declare-styleable name="CalendarView">
    
            <attr name="calendar_padding" format="dimension" /><!--日历内部左右padding-->
    
            <attr name="month_view" format="color" /> <!--自定义类日历月视图路径-->
            <attr name="week_view" format="string" /> <!--自定义类周视图路径-->
            <attr name="week_bar_height" format="dimension" /> <!--星期栏的高度-->
            <attr name="week_bar_view" format="color" /> <!--自定义类周栏路径,通过自定义则 week_text_color week_background xml设置无效,当仍可java api设置-->
            <attr name="week_line_margin" format="dimension" /><!--线条margin-->
    
            <attr name="week_line_background" format="color" /><!--线条颜色-->
            <attr name="week_background" format="color" /> <!--星期栏的背景-->
            <attr name="week_text_color" format="color" /> <!--星期栏文本颜色-->
            <attr name="week_text_size" format="dimension" /><!--星期栏文本大小-->
    
            <attr name="current_day_text_color" format="color" /> <!--今天的文本颜色-->
            <attr name="current_day_lunar_text_color" format="color" /><!--今天的农历文本颜色-->
    
            <attr name="calendar_height" format="string" /> <!--日历每项的高度,56dp-->
            <attr name="day_text_size" format="string" /> <!--天数文本大小-->
            <attr name="lunar_text_size" format="string" /> <!--农历文本大小-->
    
            <attr name="scheme_text" format="string" /> <!--标记文本-->
            <attr name="scheme_text_color" format="color" /> <!--标记文本颜色-->
            <attr name="scheme_month_text_color" format="color" /> <!--标记天数文本颜色-->
            <attr name="scheme_lunar_text_color" format="color" /> <!--标记农历文本颜色-->
    
            <attr name="scheme_theme_color" format="color" /> <!--标记的颜色-->
    
            <attr name="selected_theme_color" format="color" /> <!--选中颜色-->
            <attr name="selected_text_color" format="color" /> <!--选中文本颜色-->
            <attr name="selected_lunar_text_color" format="color" /> <!--选中农历文本颜色-->
    
            <attr name="current_month_text_color" format="color" /> <!--当前月份的字体颜色-->
            <attr name="other_month_text_color" format="color" /> <!--其它月份的字体颜色-->
    
            <attr name="current_month_lunar_text_color" format="color" /> <!--当前月份农历节假日颜色-->
            <attr name="other_month_lunar_text_color" format="color" /> <!--其它月份农历节假日颜色-->
    
            <!-- 年视图相关 -->
            <attr name="year_view_month_text_size" format="dimension" /> <!-- 年视图月份字体大小 -->
            <attr name="year_view_day_text_size" format="dimension" /> <!-- 年视图月份日期字体大小 -->
            <attr name="year_view_month_text_color" format="color" /> <!-- 年视图月份字体颜色 -->
            <attr name="year_view_day_text_color" format="color" /> <!-- 年视图日期字体颜色 -->
            <attr name="year_view_scheme_color" format="color" /> <!-- 年视图标记颜色 -->
    
            <attr name="min_year" format="integer" />  <!--最小年份1900-->
            <attr name="max_year" format="integer" />  <!--最大年份2099-->
            <attr name="min_year_month" format="integer" /> <!--最小年份对应月份-->
            <attr name="max_year_month" format="integer" /> <!--最大年份对应月份-->
    
            <!--月视图是否可滚动-->
            <attr name="month_view_scrollable" format="boolean" />
            <!--周视图是否可滚动-->
            <attr name="week_view_scrollable" format="boolean" />
            <!--年视图是否可滚动-->
            <attr name="year_view_scrollable" format="boolean" />
            
            <!--配置你喜欢的月视图显示模式模式-->
            <attr name="month_view_show_mode">
                 <enum name="mode_all" value="0" /> <!--全部显示-->
                 <enum name="mode_only_current" value="1" /> <!--仅显示当前月份-->
                 <enum name="mode_fix" value="2" /> <!--自适应显示,不会多出一行,但是会自动填充-->
            </attr>
    
            <!-- 自定义周起始 -->
            <attr name="week_start_with">
                 <enum name="sun" value="1" />
                 <enum name="mon" value="2" />
                 <enum name="sat" value="7" />
            </attr>
    
            <!-- 自定义选择模式 -->
            <attr name="select_mode">
                  <enum name="default_mode" value="0" />
                  <enum name="single_mode" value="1" />
                  <enum name="range_mode" value="2" />
                  <enum name="multi_mode" value="3" />
            </attr>
    
            <!-- when select_mode = multi_mode -->
            <attr name="max_multi_select_size" format="integer" />
    
            <!-- 当 select_mode=range_mode -->
            <attr name="min_select_range" format="integer" />
            <attr name="max_select_range" format="integer" />
    </declare-styleable>
    

    写在最后,其它各种场景姿势就不多说了,看自己需求去实现。再次注意:Demo只是Demo,只是示例如何使用,与框架本身没有关联,不属于框架一部分

    框架本身是为了解决各种各样的场景而设计的,UI本身是靠自己绘制的,非常简单,如果还不懂的请优先看Demo,你可以自由发挥想象力定制最喜欢的日历,只有你想不到,Demo基本给出了各种场景的实现思路。觉得可以的请给个star或者留下你宝贵的意见。

    展开全文
  • 最近写了一个简易自定义日历控件使用...本日历控件使用gridview编写,可扩展性好,适合需要简单用到日历的项目。效果如下图:  代码如下: import android.content.Context; import android.support.v4
  • ASP.NET 控件的使用

    2009-04-02 16:02:54
    11.3 GridView控件的事件 365 11.3.1 突出显示GridView行 366 11.3.2 显示列汇总 368 11.3.3 显示嵌套的主从表单 369 11.4 扩展GridView控件 371 11.4.1 创建长文本字段 371 11.4.2 创建删除按钮字段 374 11.4.3 ...
  • 之前一直想写一个android签到控件,参照了...选择使用GridView作为日期显示,但是需要注意一点就是需要给GridView设置一个最大尺寸,这个我们可以通过继承GridView,重载里面方法来进行实现。 代码如下:...
  • 自定义日历控件-CalendarView

    千次阅读 2015-01-23 15:39:15
    我们在开发中会遇到使用日历控件,下面就介绍一个自定义日历控件。 思路: 1.自定义类CalendarView继承LinearLayout,使用布局文件,显示布局。 2.使用ViewFlipper,里面添加GridView,当月日期。 3.使用手势...
  • 开源日历控件DatePicker源码解析

    千次阅读 2016-05-06 20:54:31
    比如通过GridView扣扣你敢、自定义View实现日历控件,这些都是我们解决问题的手段,我也实现过一个自定义日历控件(Android自定义控件之日历控件55993)),由于我只是粗糙的进行实现,并没有进行过多的在控件的可扩展...
  • 这篇文章是看慕课网视频笔记,包括图片素材均来自于慕课网,但实际代码是自己手敲.附上视频路径,若有侵权,请留言.Android...在activity_main.xml文件拖一个GridView控件,去掉父容器内边距.<?xml version="1.0" encodi
  • asp.net 的服务器端控件提供了多种样式的设计,如果对每个控件都单独设置,是比较...但对于gridview或者日历控件等,css 文件无法灵活的控制,这就需要微软专门为服务器端控件提供的主题和皮肤。主题和皮肤的使用方...
  • 最近提出申请,主接口是一个历,网上有很多第三方的日历控件,有使用ImageView实现,有使用GridView实现,它优点是控制灵活性. 而我选择使用本机CalendarView,关于这个控件,详细能够參考:...
  • ASP.NET.4揭秘

    2012-06-08 16:20:52
    11.3 gridview控件的事件417 11.3.1 突出显示gridview行417 11.3.2 显示列汇总419 11.3.3 显示嵌套的主从表单421 11.4 扩展gridview控件424 11.4.1 创建长文本字段424 11.4.2 创建删除按钮字段428 11.4.3 创建验证...
  • 实例159 PlaceHolder容器控件的使用 262 实例160 设置Web窗体的默认焦点控件 263 实例161 设置Web窗体的默认按钮 264 实例162 应用ViewState保存数据信息 265 实例163 默默无闻——隐藏域(HiddenField控件) 266 实例...
  • 实例159 PlaceHolder容器控件的使用 262 实例160 设置Web窗体的默认焦点控件 263 实例161 设置Web窗体的默认按钮 264 实例162 应用ViewState保存数据信息 265 实例163 默默无闻——隐藏域(HiddenField控件) 266 实例...
  • 实例159 PlaceHolder容器控件的使用 262 实例160 设置Web窗体的默认焦点控件 263 实例161 设置Web窗体的默认按钮 264 实例162 应用ViewState保存数据信息 265 实例163 默默无闻——隐藏域(HiddenField控件) 266 实例...
  • 5.12 Repeater控件的使用 实例113 Repeater控件数据绑定 实例114 Repeater控件分页 实例115 编辑Repeater控件标识商品信息 实例116 Repeater控件实现主细表 第6章 验证控件 6.1 通信类验证 实例117 验证E-mail地址...
  • 5.12 Repeater控件的使用 实例113 Repeater控件数据绑定 实例114 Repeater控件分页 实例115 编辑Repeater控件标识商品信息 实例116 Repeater控件实现主细表 第6章 验证控件 6.1 通信类验证 实例117 验证E-mail地址...
  • 5.12 Repeater控件的使用 实例113 Repeater控件数据绑定 实例114 Repeater控件分页 实例115 编辑Repeater控件标识商品信息 实例116 Repeater控件实现主细表 第6章 验证控件 6.1 通信类验证 实例117 验证E-mail地址...
  • 实例159 PlaceHolder容器控件的使用 262 实例160 设置Web窗体的默认焦点控件 263 实例161 设置Web窗体的默认按钮 264 实例162 应用ViewState保存数据信息 265 实例163 默默无闻——隐藏域(HiddenField 控件) 266...
  • asp.net的服务器端控件提供了多种样式的设计,如果对每个控件都单独设置,是...但对于gridview或者日历控件等,css文件无法灵活的控制,这就需要微软专门为服务器端控件提供的主题和皮肤。  主题和皮肤的使用方法:
  • ASP.NET主题和皮肤

    2011-11-05 18:35:17
    asp.net的服务器端控件提供了多种样式的设计,如果对每个控件都单独设置,是...但对于gridview或者日历控件等,css文件无法灵活的控制,这就需要微软专门为服务器端控件提供的主题和皮肤。  主题和皮肤的使用方法:
  • 6-02.aspx RegularExpressionValidator控件的使用。 6-03.aspx 使用CompareValidator控件比较两个控件的值。 6-04.aspx CompareValidator控件比较一个控件的值与固定值。 6-05.aspx CompareValidator控件执行...
  • 4.8 GridView控件的应用 108 实例064 数据绑定并实现分页功能 108 实例065 选中、编辑、取消、删除数据项 109 实例066 GridView实现正反双向排序功能 111 实例067 GridView控件中Drop DownList控件的绑定 ...
  • 4.8 GridView控件的应用 108 实例064 数据绑定并实现分页功能 108 实例065 选中、编辑、取消、删除数据项 109 实例066 GridView实现正反双向排序功能 111 实例067 GridView控件中Drop DownList控件的绑定 ...
  • 4.8 GridView控件的应用 108 实例064 数据绑定并实现分页功能 108 实例065 选中、编辑、取消、删除数据项 109 实例066 GridView实现正反双向排序功能 111 实例067 GridView控件中Drop DownList控件的绑定 ...

空空如也

空空如也

1 2 3 4 5 6
收藏数 102
精华内容 40
关键字:

日历gridview控件的使用