美文网首页Android开发androidAndroid知识
一步一步构建自己的简单日历控件 MySimpleCalendar

一步一步构建自己的简单日历控件 MySimpleCalendar

作者: 涤生_Woo | 来源:发表于2018-01-15 21:26 被阅读719次

    日历控件大家应该不陌生,github 上面一搜一大堆,但是我们拿到 github 上面的一个日历控件,想动手改改功能改改需求,有时可能会觉得无从下手,(当然了,老司机就忽略我说的 —。—)那么,如果想知道一个日历控件是如何从无到有构建起来的,不妨各位看官快速浏览一下我的这篇文章。
    文章主要是带大家一步一步熟悉构建的流程,并没有什么特别酷炫狂拽的效果。

    先上一个效果图镇镇楼。


    MySimpleCalendar 控件

    一、数据准备

    1、实体类 MyCalendarBean

    /**
     * Created by deeson.woo
     */
    public class MyCalendarBean {
    
        private int year;
        private int month;//1-12
        private int day;//1-31
    
        public MyCalendarBean(int year, int month, int day) {
            this.year = year;
            this.month = month;
            this.day = day;
        }
    
        public int getYear() {
            return year;
        }
    
        public int getMonth() {
            return month;
        }
    
        public int getDay() {
            return day;
        }
    }
    

    2、构建日期实体

        /**
         * 构建具体一天的对象
         * @param year
         * @param month
         * @param day
         * @return
         */
        public MyCalendarBean generateCalendarBean(int year, int month, int day) {
            Calendar calendar = Calendar.getInstance();
            calendar.set(year, month - 1, day);
            year = calendar.get(Calendar.YEAR);
            month = calendar.get(Calendar.MONTH) + 1;
            day = calendar.get(Calendar.DATE);
    
            return new MyCalendarBean(year, month, day);
        }
    

    3、打印当前月份的所有日期

        /**
         * 获取当前月份的日期列表
         * @param year
         * @param month
         * @return
         */
        public List<MyCalendarBean> getDaysListOfMonth(int year, int month) {
    
            List<MyCalendarBean> list = new ArrayList<>();
    
            int daysOfMonth = getDaysOfCertainMonth(year, month);
    
            for (int i = 0; i < daysOfMonth; i++) {
                MyCalendarBean bean = generateCalendarBean(year, month, i + 1);
                list.add(bean);
            }
            return list;
        }
    
        /**
         * 获取具体月份的最大天数
         *
         * @param year
         * @param month
         * @return
         */
        public static int getDaysOfCertainMonth(int year, int month) {
            Calendar calendar = Calendar.getInstance();
            calendar.set(year, month - 1, 1);
            return calendar.getActualMaximum(Calendar.DATE);
        }
    
    //测试打印2018年2月份
    printDaysList(getDaysListOfMonth(2018, 2));
    

    2018年2月 = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28 }

    二、展示日期

    1、将月份的所有日期按照7列展示

    自定义ViewGroup(MonthCalendarView)

    /**
     * Created by deeson.woo
     * 月份视图
     */
    
    public class MonthCalendarView extends ViewGroup {
    
        private int column = 7;
    
        public MonthCalendarView(Context context) {
            super(context);
        }
    
        public MonthCalendarView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
    }
    
    (1)onMeasure()方法
    • 拿到控件的宽度,分成七份,赋给 item 的宽度和高度
    • 同时计算控件所需的高度
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            
            int parentWidth = MeasureSpec.getSize(MeasureSpec.makeMeasureSpec(widthMeasureSpec, MeasureSpec.EXACTLY));
    
            //将宽度平均分成七份,每个item的宽高都等于它
            int itemWidth = parentWidth / column;
            int itemHeight = itemWidth;
    
            int parentHeight = 0;
    
            for (int i = 0; i < getChildCount(); i++) {
                View childView = getChildAt(i);
                childView.measure(MeasureSpec.makeMeasureSpec(itemWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(itemHeight, MeasureSpec.EXACTLY));
                
                //计算控件所需的高度
                if (i % column == 0) {
                    parentHeight += childView.getMeasuredHeight();
                }
            }
    
            setMeasuredDimension(parentWidth, parentHeight);
        }
    
    (2)onLayout()方法
    • 按照七列布局的设计,计算出每个 item 的 left, top, right, bottom,精确地添加到控件里
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            for (int i = 0; i < getChildCount(); i++) {
                View itemView = getChildAt(i);
                int columnCount = i % column;
                int rowCount = i / column;
    
                int itemWidth = itemView.getMeasuredWidth();
                int itemHeight = itemView.getMeasuredHeight();
    
                left = columnCount * itemWidth;
                top = rowCount * itemHeight;
                right = left + itemWidth;
                bottom = top + itemHeight;
                itemView.layout(left, top, right, bottom);
            }
        }
    
    (3)填充月份日期数据
    • 暴露一个公共方法用于填充数据
    • 根据传进来的年、月,构建日期列表
    • 逐一构建 itemView,填充到控件里
    • 调用requestLayout()方法,重新绘制
        public void setMonth(int year, int month) {
            mList = calendarUtils.getDaysListOfMonth(year, month);
            addAllItem();
            requestLayout();
        }
    
        private void addAllItem() {
            for (int i = 0; i < mList.size(); i++) {
                MyCalendarBean bean = mList.get(i);
    
                View itemView = generateDateView(bean);
                addViewInLayout(itemView, i, itemView.getLayoutParams(), true);
            }
        }
    
        private View generateDateView(MyCalendarBean bean) {
            View itemView = LayoutInflater.from(getContext()).inflate(R.layout.item_date_view, null);
            if (bean.isCurrentMonth()) {
                TextView date = itemView.findViewById(R.id.date);
                date.setText(String.valueOf(bean.getDay()));
            }
            return itemView;
        }
    

    把 item_date_view.xml 布局也放出来

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <TextView
            android:id="@+id/date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:textSize="14sp"
            android:textColor="@color/text_black"
            />
    
    </RelativeLayout>
    

    2、在布局中使用

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="@color/background_white"
        >
    
        <com.example.deesonwoo.mysimplecalendar.calendar.MonthCalendarView
            android:id="@+id/month_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorAccent">
    
        </com.example.deesonwoo.mysimplecalendar.calendar.MonthCalendarView>
    
    </LinearLayout>
    
    

    3、在 MainActivity 中的使用

    package com.example.deesonwoo.mysimplecalendar;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    
    import com.example.deesonwoo.mysimplecalendar.calendar.MonthCalendarView;
    import com.example.deesonwoo.mysimplecalendar.calendar.MyCalendarBean;
    import com.example.deesonwoo.mysimplecalendar.calendar.MyCalendarUtils;
    
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
    
        MonthCalendarView monthView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            monthView = findViewById(R.id.month_view);
            initCalendar();
        }
    
        private void initCalendar() {
            //测试显示2018年3月
            monthView.setMonth(2018, 3);
        }
    }
    
    

    效果展示如下:

    2018年3月

    4、添加顶部周布局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="@color/background_white"
        >
    
    
        <LinearLayout
            android:id="@+id/week_layout"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:orientation="horizontal"
            android:background="@color/white"
            >
    
            <TextView
                android:id="@+id/week_00"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="周日"
                android:textColor="@color/text_black"
                android:textSize="12sp" />
    
            <TextView
                android:id="@+id/week_01"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="周一"
                android:textColor="@color/text_black"
                android:textSize="12sp" />
    
            <TextView
                android:id="@+id/week_02"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="周二"
                android:textColor="@color/text_black"
                android:textSize="12sp" />
    
            <TextView
                android:id="@+id/week_03"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="周三"
                android:textColor="@color/text_black"
                android:textSize="12sp" />
    
            <TextView
                android:id="@+id/week_04"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="周四"
                android:textColor="@color/text_black"
                android:textSize="12sp" />
    
            <TextView
                android:id="@+id/week_05"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="周五"
                android:textColor="@color/text_black"
                android:textSize="12sp" />
    
            <TextView
                android:id="@+id/week_06"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="周六"
                android:textColor="@color/text_black"
                android:textSize="12sp" />
        </LinearLayout>
    
        <com.example.deesonwoo.mysimplecalendar.calendar.MonthCalendarView
            android:id="@+id/month_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorAccent">
    
        </com.example.deesonwoo.mysimplecalendar.calendar.MonthCalendarView>
    
    </LinearLayout>
    
    

    效果如下:


    2018年3月

    5、优化日期与星期的对应关系

    相信你们已经发现,上面展示的效果中,日期与星期并没有进行一一对应的排布。接下来,一起我们优化一下。

    • 找到当前月份第一天对应的星期
    • 修改工具类方法 getDaysListOfMonth(), 将前面空缺的上一个月的日期填充到月份列表中
    • 将上个月的日期隐藏
    (1)在 MyCalendarUtils 工具类中添加下面“获取具体一天对应的星期”的方法
        /**
         * 获取具体一天对应的星期
         *
         * @param year
         * @param month
         * @param day
         * @return 1-7(周日-周六)
         */
        private int getWeekDayOnCertainDate(int year, int month, int day) {
            Calendar calendar = Calendar.getInstance();
            calendar.set(year, month - 1, day);
            return calendar.get(Calendar.DAY_OF_WEEK);
        }
    
    (2)修改 getDaysListOfMonth()方法,将前面空缺的上一个月的日期填充到月份列表中
        /**
         * 获取当前月份的日期列表
         *
         * @param year
         * @param month
         * @return
         */
        public List<MyCalendarBean> getDaysListOfMonth(int year, int month) {
    
            List<MyCalendarBean> list = new ArrayList<>();
    
            int daysOfMonth = getDaysOfCertainMonth(year, month);
    
            //找到当前月第一天的星期,计算出前面空缺的上个月的日期个数,填充到当月日期列表中
            int weekDayOfFirstDay = getWeekDayOnCertainDate(year, month, 1);
            int preMonthDays = weekDayOfFirstDay - 1;
    
            for (int i = preMonthDays; i > 0; i--) {
                MyCalendarBean preMonthBean = generateCalendarBean(year, month, 1 - i);
                list.add(preMonthBean);
            }
    
            for (int i = 0; i < daysOfMonth; i++) {
                MyCalendarBean monthBean = generateCalendarBean(year, month, i + 1);
                list.add(monthBean);
            }
            return list;
        }
    

    展示效果如下:

    2018年3月

    显然,上一个月的日期在这里是需要区别展示或者需要隐藏的,不然会给用户造成视觉上的困扰,这里,我直接做隐藏操作。

    (3)给日期实体类增加当前月标识,isCurrentMonth,并在构建数据的时候给标识赋值。

    实体类如下:

    /**
     * Created by deeson.woo
     */
    public class MyCalendarBean {
    
        private int year;
        private int month;//1-12
        private int day;//1-31
        private boolean isCurrentMonth = true;//是否为当前月份的日期
    
        public MyCalendarBean(int year, int month, int day) {
            this.year = year;
            this.month = month;
            this.day = day;
        }
    
        public int getYear() {
            return year;
        }
    
        public int getMonth() {
            return month;
        }
    
        public int getDay() {
            return day;
        }
    
        public boolean isCurrentMonth() {
            return isCurrentMonth;
        }
    
        public void setCurrentMonth(boolean currentMonth) {
            isCurrentMonth = currentMonth;
        }
    }
    

    给标识赋值,在 getDaysListOfMonth()中赋值:

        /**
         * 获取当前月份的日期列表
         *
         * @param year
         * @param month
         * @return
         */
        public List<MyCalendarBean> getDaysListOfMonth(int year, int month) {
    
            List<MyCalendarBean> list = new ArrayList<>();
    
            int daysOfMonth = getDaysOfCertainMonth(year, month);
    
            //找到当前月第一天的星期,计算出前面空缺的上个月的日期个数,填充到当月日期列表中
            int weekDayOfFirstDay = getWeekDayOnCertainDate(year, month, 1);
            int preMonthDays = weekDayOfFirstDay - 1;
    
            for (int i = preMonthDays; i > 0; i--) {
                MyCalendarBean preMonthBean = generateCalendarBean(year, month, 1 - i);
                preMonthBean.setCurrentMonth(false);
                list.add(preMonthBean);
            }
    
            for (int i = 0; i < daysOfMonth; i++) {
                MyCalendarBean monthBean = generateCalendarBean(year, month, i + 1);
                monthBean.setCurrentMonth(true);
                list.add(monthBean);
            }
            return list;
        }
    

    最后修改我们自定义月历类中 generateDateView()方法,不显示上个月的日期:

        private View generateDateView(MyCalendarBean bean) {
            View itemView = LayoutInflater.from(getContext()).inflate(R.layout.item_date_view, null);
            if(bean.isCurrentMonth()){
                TextView date = itemView.findViewById(R.id.date);
                date.setText(String.valueOf(bean.getDay()));
            }
            return itemView;
        }
    

    效果如下:


    2018年3月

    三、持续优化改进

    1、将静态日历改成动态可切换显示

    (1)添加头部布局,用于显示当前月份以及翻页
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="@color/background_blue"
            >
            <TextView
                android:id="@+id/title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:textSize="20sp"
                android:textColor="@color/white"
                />
    
            <ImageView
                android:id="@+id/pre_month"
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:layout_centerVertical="true"
                android:layout_alignParentLeft="true"
                android:src="@mipmap/btn_preview"
                />
            <ImageView
                android:id="@+id/next_month"
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:layout_centerVertical="true"
                android:layout_alignParentRight="true"
                android:src="@mipmap/btn_next"
                />
    
        </RelativeLayout>
    
    (2)修改自定义月历类
    • 增加当前显示的年月成员变量

    private int mYear, mMonth;

    • 修改构造方法和填充数据的方法
        public MonthCalendarView(Context context) {
            super(context);
            calendarUtils = new MyCalendarUtils(context);
        }
    
        public MonthCalendarView(Context context, AttributeSet attrs) {
            super(context, attrs);
            calendarUtils = new MyCalendarUtils(context);
        }
    
        public void setMonth(int year, int month) {
            this.mYear = year;
            this.mMonth = month;
            invalidateMonth();
        }
    
        private void invalidateMonth() {
            mList = calendarUtils.getDaysListOfMonth(mYear, mMonth);
            removeAllViews();
            addAllItem();
            requestLayout();
        }
    
    • 增加前翻页、后翻页方法
        /**
         * 展示上一个月
         */
        public void moveToPreMonth() {
            mMonth -= 1;
            invalidateMonth();
        }
    
        /**
         * 展示下一个月
         */
        public void moveToNextMonth() {
            mMonth += 1;
            invalidateMonth();
        }
    
    • 增加获取当前显示年月的方法
        public String getCurrentYearAndMonth() {
            return MyCalendarUtils.formatYearAndMonth(mYear, mMonth);
        }
    
    (3)增加工具类方法
        /**
         * 格式化标题展示
         * @param year
         * @param month
         * @return
         */
        public static String formatYearAndMonth(int year, int month) {
            Calendar calendar = Calendar.getInstance();
            calendar.set(year, month - 1, 1);
            year = calendar.get(Calendar.YEAR);
            month = calendar.get(Calendar.MONTH) + 1;
            return year + "年" + month + "月";
        }
    
        /**
         * 获取系统当前年月日
         *
         * @return
         */
        public static int[] getNowDayFromSystem() {
            Calendar cal = Calendar.getInstance();
            cal.setTime(new Date());
            return new int[]{cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DATE)};
        }
    
    (4)修改 MainActivity 类
    • 修改主题样式
    • 增加头部布局相关
    • 默认显示系统当前年月
    package com.example.deesonwoo.mysimplecalendar;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.view.Window;
    import android.view.WindowManager;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    import com.example.deesonwoo.mysimplecalendar.calendar.MonthCalendarView;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        MonthCalendarView monthView;
        ImageView btnPreMonth, btnNextMonth;
        TextView title;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
            setContentView(R.layout.activity_main);
            monthView = findViewById(R.id.month_view);
            initCalendar();
            initTitleView();
            updateTitle();
        }
    
        private void initCalendar() {
            int[] nowDay = MyCalendarUtils.getNowDayFromSystem();
            monthView.setMonth(nowDay[0], nowDay[1]);
        }
    
        private void initTitleView() {
            title = findViewById(R.id.title);
            btnPreMonth = findViewById(R.id.pre_month);
            btnNextMonth = findViewById(R.id.next_month);
            btnPreMonth.setOnClickListener(this);
            btnNextMonth.setOnClickListener(this);
        }
    
        /**
         * 刷新标题显示年月
         */
        private void updateTitle() {
            String yearAndMonth = monthView.getCurrentYearAndMonth();
            title.setText(yearAndMonth);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.pre_month:
                    monthView.moveToPreMonth();
                    updateTitle();
                    break;
                case R.id.next_month:
                    monthView.moveToNextMonth();
                    updateTitle();
                    break;
            }
        }
    }
    

    最后显示的效果如下动图:


    MyCalendar

    2、增加高亮显示系统当天日期

    • 增加工具类方法
        /**
         * 判断是否为系统当天
         * @param bean
         * @return
         */
        public static boolean isToday(MyCalendarBean bean) {
            int[] nowDay = getNowDayFromSystem();
            return bean.getYear() == nowDay[0] && bean.getMonth() == nowDay[1] && bean.getDay() == nowDay[2];
        }
    
    • 修改自定义月历类构建日期的方法
        private View generateDateView(MyCalendarBean bean) {
            View itemView = LayoutInflater.from(getContext()).inflate(R.layout.item_date_view, null);
            if (bean.isCurrentMonth()) {
                TextView date = itemView.findViewById(R.id.date);
                if (MyCalendarUtils.isToday(bean)) {
                    date.setBackgroundResource(R.drawable.item_today_bg);
                }
                date.setText(String.valueOf(bean.getDay()));
            }
            return itemView;
        }
    
    • 系统当天高亮显示的背景 item_today_bg.xml
    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval">
        <solid
            android:color="@color/theme_color" />
    
    </shape>
    

    效果如下:


    MyCalendar

    3、增加点击日期效果

    (1)修改自定义月历类
    • 修改构建日期方法
        private View generateDateView(MyCalendarBean bean) {
            View itemView = LayoutInflater.from(getContext()).inflate(R.layout.item_date_view, null);
            if (bean.isCurrentMonth()) {
                TextView date = itemView.findViewById(R.id.date);
                if (MyCalendarUtils.isToday(bean)) {
                    date.setBackgroundResource(R.drawable.item_today_bg);
                } else {
                    date.setBackgroundResource(R.drawable.item_pick_up);
                }
                date.setText(String.valueOf(bean.getDay()));
            }
            return itemView;
        }
    
    • item_pick_up.xml
    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item android:state_selected="true">
            <shape android:shape="oval">
                <stroke android:color="@color/theme_color" android:width="1dp"/>
            </shape>
        </item>
    
        <item android:drawable="@android:color/transparent" />
    </selector>
    
    • 增加在主界面点击的回调接口方法
        private OnDatePickUpListener onDatePickUpListener;
    
        public void setOnDatePickUpListener(OnDatePickUpListener onDatePickUpListener) {
            this.onDatePickUpListener = onDatePickUpListener;
        }
    
        public interface OnDatePickUpListener {
            void onDatePickUp(MyCalendarBean bean);
        }
    
    • 修改添加日期的方法,增加点击监听
        private void addAllItem() {
            for (int i = 0; i < mList.size(); i++) {
                final MyCalendarBean bean = mList.get(i);
    
                final View itemView = generateDateView(bean);
                addViewInLayout(itemView, i, itemView.getLayoutParams(), true);
                final int position = i;
                itemView.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
    
                        if (pickUpPosition == position) {
                            return;
                        }
    
                        if (pickUpPosition != -1) {
                            getChildAt(pickUpPosition).setSelected(false);
                        }
                        itemView.setSelected(true);
    
                        if (null != onDatePickUpListener) {
                            onDatePickUpListener.onOnDatePickUp(bean);
                        }
    
                        pickUpPosition = position;
                    }
                });
            }
        }
    
    (2)在MainActivity中调用
        private void initCalendar() {
            int[] nowDay = MyCalendarUtils.getNowDayFromSystem();
            monthView.setMonth(nowDay[0], nowDay[1]);
            monthView.setOnDatePickUpListener(new MonthCalendarView.OnDatePickUpListener() {
                @Override
                public void onDatePickUp(MyCalendarBean bean) {
                    Toast.makeText(MainActivity.this, bean.toString(), Toast.LENGTH_SHORT).show();
                }
            });
        }
    

    效果如下动图:


    MyCalendar

    四、整合自定义控件

    大家可能觉得我们的自定义控件到这里就完结了,但是young、simple、naive......(瞎bb)
    秉着高内聚低耦合的原则(再次瞎bb),我将刚刚出现的操作全部整合到一个控件SimpleCalendarView 中。
    直接上代码吧,也没几行,就不做什么解释了。

    1、SimpleCalendarView 类

    • 无非就是将标题视图、星期视图、月历视图逐一添加到自定义SimpleCalendarView 类中,再将相关接口补上,请看下面代码
    package com.example.deesonwoo.mysimplecalendar.calendar;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.RelativeLayout;
    import android.widget.TextView;
    
    import com.example.deesonwoo.mysimplecalendar.R;
    
    
    public class SimpleCalendarView extends LinearLayout implements View.OnClickListener, MonthCalendarView.OnDatePickUpListener {
    
        private MonthCalendarView monthCalendarView;// 月历
        private OnDatePickListener onDatePickListener;
    
        private TextView title;
    
        public SimpleCalendarView(Context context) {
            this(context, null);
        }
    
        public SimpleCalendarView(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            setOrientation(VERTICAL);
            setBackgroundColor(context.getResources().getColor(R.color.white));
    
            // 年月标题、翻页按钮
            LayoutParams titleParams = new LayoutParams(LayoutParams.MATCH_PARENT, MyCalendarUtils.dp2px(context, 50));
            RelativeLayout titleLayout = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.title_layout, null);
            title = titleLayout.findViewById(R.id.title);
            ImageView preMonth = titleLayout.findViewById(R.id.pre_month);
            ImageView nextMonth = titleLayout.findViewById(R.id.next_month);
            preMonth.setOnClickListener(this);
            nextMonth.setOnClickListener(this);
            addView(titleLayout, titleParams);
    
            //星期布局
            LayoutParams weekParams = new LayoutParams(LayoutParams.MATCH_PARENT, MyCalendarUtils.dp2px(context, 40));
            LinearLayout weekLayout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.week_layout, null);
            addView(weekLayout, weekParams);
    
            //月历视图
            LayoutParams monthParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
            monthCalendarView = new MonthCalendarView(context);
            initCalendarDate();
            monthCalendarView.setOnDatePickUpListener(this);
            addView(monthCalendarView, monthParams);
        }
    
        private void initCalendarDate() {
            int[] nowDay = MyCalendarUtils.getNowDayFromSystem();
            monthCalendarView.setMonth(nowDay[0], nowDay[1]);
            updateTitle();
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.pre_month:
                    if (null != monthCalendarView) {
                        monthCalendarView.moveToPreMonth();
                    }
                    updateTitle();
                    break;
                case R.id.next_month:
                    if (null != monthCalendarView) {
                        monthCalendarView.moveToNextMonth();
                    }
                    updateTitle();
                    break;
            }
        }
    
        private void updateTitle() {
            if (null != title && null != monthCalendarView) {
                title.setText(monthCalendarView.getCurrentYearAndMonth());
            }
        }
    
        @Override
        public void onDatePickUp(MyCalendarBean bean) {
            if (null != onDatePickListener) {
                onDatePickListener.onDatePick(bean);
            }
        }
    
        public void setOnDatePickListener(OnDatePickListener onDatePickListener) {
            this.onDatePickListener = onDatePickListener;
        }
    
        public interface OnDatePickListener {
            void onDatePick(MyCalendarBean bean);
        }
    }
    

    2、在布局中使用,非常简单

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/background_white"
        android:orientation="vertical">
    
        <com.example.deesonwoo.mysimplecalendar.calendar.SimpleCalendarView
            android:id="@+id/calendarView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
        </com.example.deesonwoo.mysimplecalendar.calendar.SimpleCalendarView>
    
    </LinearLayout>
    

    3、在MainActivity 中调用

    package com.example.deesonwoo.mysimplecalendar;
    
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.view.Window;
    import android.view.WindowManager;
    import android.widget.Toast;
    
    import com.example.deesonwoo.mysimplecalendar.calendar.MyCalendarBean;
    import com.example.deesonwoo.mysimplecalendar.calendar.SimpleCalendarView;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
            setContentView(R.layout.activity_main);
    
            SimpleCalendarView calendarView = findViewById(R.id.calendarView);
            calendarView.setOnDatePickListener(new SimpleCalendarView.OnDatePickListener() {
                @Override
                public void onDatePick(MyCalendarBean bean) {
                    Toast.makeText(MainActivity.this, bean.toString(), Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
    

    最后的效果就是文章开头的动态图。

    五、后续

    相关文章

      网友评论

      本文标题:一步一步构建自己的简单日历控件 MySimpleCalendar

      本文链接:https://www.haomeiwen.com/subject/usrgnxtx.html