自定义View的简单尝试——自定义日历视图

之前学过的东西隔了很长一段时间现在又忘了,原来已经不打算做码农了,但是好像没有什么选择了,先试试看自己合不合适做程序猿吧。今天来学一下,自定义View。我们看看今天要做的东西


这是我在某个地方看到的一张图,不知道在哪个地方了。我们先来分析一下,如果画出类似于这种效果。首先确定当前月的第一天是星期几,以及当前月的天数。然后用一个循环来画每一个日期,当然这要计算每个日期所在的位置。宽度是平均分很容易,高度的话看个人想要的了,我这里是总高度的1/6。判断是否和当前日期相等,如果相等则画出背景。接着便是用一个Map集合来存储每个日期所在的位置,以便用于监听是否点击了日期,然后做出相应的重绘。要监听事件,所以重写了onTounch方法。要注意的一点是绘制文字是基于文字的底部的,所以应该加上字体高度的一半。

  1. 自定义属性。分别是日期的背景颜色,日期表头的背景颜色,正常的日期字体颜色,当前日期的颜色,当前日期的背景颜色,选择的日期的背景颜色,日期的字体大小,日期表头的字体大小。
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
    
        <declare-styleable name="SunshineView">
            <attr name="android:background"/>
            <attr name="titleBackgroundColor" format="color"/>
            <attr name="normalDateColor" format="color"/>
            <attr name="currentDateColor" format="color"/>
            <attr name="currentDateBackgroundColor" format="color"/>
            <attr name="selectedDateBackgroundColor" format="color"/>
            <attr name="dateSize" format="dimension"/>
            <attr name="titleSize" format="dimension"/>
    
        </declare-styleable>
    
    
    </resources>

  2. 获取自定义的属性
         //默认的字体大小
            defaultSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 19, getResources().getDisplayMetrics());
            /**
             * 获取自定义属性
             */
            TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.SunshineView,defStyleAttr,0);
    
            int attrCount = typedArray.getIndexCount();
            for(int i=0; i<attrCount; i++) {
                int attr = typedArray.getIndex(i);
                switch (attr){
    
                    case R.styleable.SunshineView_android_background:
                        background = typedArray.getColor(attr,Color.parseColor("#ffec00"));
                        break;
    
                    case R.styleable.SunshineView_titleBackgroundColor:
                        titleBackgroundColor = typedArray.getColor(attr,0xff0000);
                        break;
    
                    case R.styleable.SunshineView_normalDateColor:
                        normalColor = typedArray.getColor(attr, 0x232323);
                        break;
    
                    case R.styleable.SunshineView_currentDateColor:
                        currentColor = typedArray.getColor(attr, 0xffffff);
                        break;
    
                    case R.styleable.SunshineView_currentDateBackgroundColor:
                        currentBgColor = typedArray.getColor(attr, 0xFFDA4336);
                        break;
    
                    case R.styleable.SunshineView_selectedDateBackgroundColor:
                        selectedBgColor = typedArray.getColor(attr, 0x7fd2d2d2);
                        break;
    
                    case R.styleable.SunshineView_dateSize:
                        dateSize = typedArray.getDimensionPixelSize(attr, defaultSize);
                        break;
    
                    case R.styleable.SunshineView_titleSize:
                        titleSize = typedArray.getDimensionPixelSize(attr,defaultSize);
                        break;
    
                }
            }
            //记得要调用,回收原来的属性
            typedArray.recycle();

  3. 初始化画笔
      private void initPaint(){
    
            //星期几标题字体大小
            titleSize = (int) 1.5*defaultSize;
    
            mTitlePaint = new TextPaint();
            mTitlePaint.setColor(Color.parseColor("#ffffff"));
            mTitlePaint.setTextSize(titleSize);
            mTitlePaint.setStyle(Paint.Style.STROKE);
            mTitlePaint.setTextAlign(Paint.Align.CENTER);
            mTitlePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
            mTitlePaint.setAntiAlias(true);
            mTitlePaint.getTextBounds("22", 0, 2, new Rect());
    
    
            //日期的字体大小
            mPaint = new TextPaint();
            mPaint.setColor(normalColor);
            mPaint.setTextSize(dateSize);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
            mPaint.setTextAlign(Paint.Align.CENTER);
            mPaint.setAntiAlias(true);
            mPaint.getTextBounds("22", 0, 2, new Rect());
        }

  4. 测量View的大小
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            /**
             * 设置宽度
             */
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    
            //match_parent
            if (widthMode == MeasureSpec.EXACTLY){
                width = widthSize;
            } else if(widthMode == MeasureSpec.AT_MOST){
                width = getResources().getDisplayMetrics().widthPixels - getPaddingLeft() - getPaddingRight();
            }
    
            /***
             * 设置高度
             */
    
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    
            //match_parent
            if(heightMode == MeasureSpec.EXACTLY){
                height = heightSize;
            }else if(heightMode == MeasureSpec.AT_MOST){
                height = getResources().getDisplayMetrics().heightPixels - getPaddingTop() - getPaddingBottom();
            }
    
            setMeasuredDimension(width, height);
        }
    

  5. 接着就是开始画了
     @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
        
            drawTitle(canvas);
            drawDate(canvas);
        }
    
        /**
         * 画表头
         * @param canvas
         */
        private void drawTitle(Canvas canvas){
            float titleHeight =  mTitlePaint.measureText("二");
            //画背景
            Paint bgPaint = new Paint();
            bgPaint.setColor(titleBackgroundColor);
            bgPaint.setStyle(Paint.Style.FILL);
            canvas.drawRect(getPaddingLeft(), getPaddingTop(), width, height / 7, bgPaint);
    
            //日期表头
            for(int i=0; i<7; i++){
                canvas.drawText(mTitles[i],(2*i+1)*width/14,height/14+titleHeight/2,mTitlePaint);
            }
        }
    
        /**
         * 画日期
         * @param canvas
         */
        private void drawDate(Canvas canvas){
    
            /**
             * 画背景
             */
            Paint paint = new Paint();
            paint.setColor(background);
            paint.setTextSize(titleSize);
            paint.setStyle(Paint.Style.STROKE);
            paint.setFlags(Paint.ANTI_ALIAS_FLAG);
            paint.setAntiAlias(true);
    
            canvas.drawRect(getPaddingLeft(),height /7,getPaddingRight(),height,paint);
    
            //当前的日期
            Calendar calendar = Calendar.getInstance();
            int mCurrentDay = calendar.get(Calendar.DAY_OF_MONTH);
            //当前天是星期几
            int currentDayIndex = calendar.get(Calendar.DAY_OF_WEEK)-1;
    
            //获取当月的第一天是星期几
            int firstDayOfMonthIndex = currentDayIndex - (mCurrentDay-1)%7;
            if(firstDayOfMonthIndex<=0){
                firstDayOfMonthIndex = firstDayOfMonthIndex + 7;
            }
    
            //标记所描绘的日期是星期几
            int dayWeekIndex = firstDayOfMonthIndex;
            //画到了第几行
            int rowIndex = 1;
    
            int dateHeight = (int) (mPaint.getFontMetrics().ascent+mPaint.getFontMetrics().descent);
            int paddingTop =  height/14+height /7;
    
            int monthDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
            for(int i=1; i<=monthDays; i++){
    
                if(dayWeekIndex>7){
                    dayWeekIndex=1;
                    rowIndex= rowIndex+1;
                }
    
    
                //先计算这天所要显示的位置
                int positionX = (2*dayWeekIndex-1)*width/14;
                int positionY = (2*rowIndex-1)*height/12 + dateHeight/2 + paddingTop;
    
                //判断日期是否是当前日期,是的话则画背景的圆,并且设置相应的日期颜色
                if(i!=mCurrentDay){
                    //选择其它日期时的背景颜色
                    if(i==Integer.parseInt(selectedDate)){
                        drawCircle(canvas, positionX, positionY+dateHeight/2, height / 13, selectedBgColor);
                        mPaint.setColor(Color.parseColor("#232323"));
                    }
                }else {
                    drawCircle(canvas, positionX, positionY+dateHeight/2, height / 13, currentBgColor);
                    mPaint.setColor(currentColor);
                }
                //画日期和重置字体颜色
                canvas.drawText(i+"",positionX,positionY,mPaint);
                mPaint.setColor(Color.parseColor("#232323"));
    
                //星期几递增1
                dayWeekIndex++;
    
                //将日期的信息添加进Map,用来监听点击事件的触发条件
                String dateKey = rowIndex+"_"+dayWeekIndex;
                datePositionMap.put(dateKey, i + "");
    
            }
        }
    
        /**
         * 画圆形的背景
         * @param canvas
         * @param positionX
         * @param positionY
         * @param radius
         * @param color
         */
        private void drawCircle(Canvas canvas,int positionX, int positionY, int radius,int color){
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(color);
            canvas.drawCircle(positionX, positionY, radius,mPaint);
        }

  6. 事件监听
        /**用来监听点击事件
         *
         * @param event
         * @return
         */
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int action = event.getAction();
            if(action==MotionEvent.ACTION_DOWN){
    
                selectedDate = selectedDate(event);
                if(selectedDate!=null){
                    invalidate();
                }else {
                    selectedDate="0";
                }
            }
            return true;
    
        }
    
        /**
         * 处理单击事件,重绘选中的日期的背景
         * @param event
         * @return
         */
        private String selectedDate(MotionEvent event){
    
            int paddingTop =  height/14+height /7;
            int column = (int)Math.ceil(event.getX()/(width/7))+1;
            int row = (int)Math.ceil((event.getY()-paddingTop)/(height/6));
    
            String key = row+"_"+column;
            String clickDay = datePositionMap.get(key);
    
            return clickDay;
        }

  7. 布局文件
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:sunshine="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <com.sharemebook.myapplication.SunshineView
            android:layout_width="match_parent"
            android:layout_height="282dp"
            android:background="#ffec00"
            sunshine:titleBackgroundColor="#ff0000"
            sunshine:dateSize="15sp"
            sunshine:normalDateColor="#232323"
            sunshine:currentDateColor="#ffffff"
            sunshine:selectedDateBackgroundColor="#7fd2d2d2"
            sunshine:currentDateBackgroundColor="#ff0000"/>
    </LinearLayout>

  8. 效果图

完整的代码

/**
 * Created by Sanisy on 2016/3/22.
 */
public class SunshineView extends View{

    private Paint mTitlePaint;
    private Paint mPaint;

    private int normalColor;
    private int currentColor;
    private int currentBgColor;
    private int selectedBgColor;
    private int dateSize;

    private int background;

    //星期几标签字体设定的大小和默认的字体大小
    private int titleBackgroundColor;
    private int titleSize;
    private int defaultSize;
    private String mTitles[] ={"一","二","三","四","五","六","日"};

    //日历整体的宽度和高度
    private int width;
    private int height;

    //被点击的日期
    private String selectedDate = "0";
    private Map<String,String> datePositionMap = new HashMap<>();

    public SunshineView(Context context) {
        this(context, null);
    }

    public SunshineView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SunshineView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //默认的字体大小
        defaultSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 19, getResources().getDisplayMetrics());
        /**
         * 获取自定义属性
         */
        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.SunshineView,defStyleAttr,0);

        int attrCount = typedArray.getIndexCount();
        for(int i=0; i<attrCount; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr){

                case R.styleable.SunshineView_android_background:
                    background = typedArray.getColor(attr,Color.parseColor("#ffec00"));
                    break;

                case R.styleable.SunshineView_titleBackgroundColor:
                    titleBackgroundColor = typedArray.getColor(attr,0xff0000);
                    break;

                case R.styleable.SunshineView_normalDateColor:
                    normalColor = typedArray.getColor(attr, 0x232323);
                    break;

                case R.styleable.SunshineView_currentDateColor:
                    currentColor = typedArray.getColor(attr, 0xffffff);
                    break;

                case R.styleable.SunshineView_currentDateBackgroundColor:
                    currentBgColor = typedArray.getColor(attr, 0xFFDA4336);
                    break;

                case R.styleable.SunshineView_selectedDateBackgroundColor:
                    selectedBgColor = typedArray.getColor(attr, 0x7fd2d2d2);
                    break;

                case R.styleable.SunshineView_dateSize:
                    dateSize = typedArray.getDimensionPixelSize(attr, defaultSize);
                    break;

                case R.styleable.SunshineView_titleSize:
                    titleSize = typedArray.getDimensionPixelSize(attr,defaultSize);
                    break;

            }
        }
        //记得要调用,回收原来的属性
        typedArray.recycle();

        initPaint();
    }


    private void initPaint(){

        //星期几标题字体大小
        titleSize = (int) 1.5*defaultSize;

        mTitlePaint = new TextPaint();
        mTitlePaint.setColor(Color.parseColor("#ffffff"));
        mTitlePaint.setTextSize(titleSize);
        mTitlePaint.setStyle(Paint.Style.STROKE);
        mTitlePaint.setTextAlign(Paint.Align.CENTER);
        mTitlePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mTitlePaint.setAntiAlias(true);
        mTitlePaint.getTextBounds("22", 0, 2, new Rect());


        //日期的字体大小
        mPaint = new TextPaint();
        mPaint.setColor(normalColor);
        mPaint.setTextSize(dateSize);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mPaint.setAntiAlias(true);
        mPaint.getTextBounds("22", 0, 2, new Rect());
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        /**
         * 设置宽度
         */
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        //match_parent
        if (widthMode == MeasureSpec.EXACTLY){
            width = widthSize;
        } else if(widthMode == MeasureSpec.AT_MOST){
            width = getResources().getDisplayMetrics().widthPixels - getPaddingLeft() - getPaddingRight();
        }

        /***
         * 设置高度
         */

        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        //match_parent
        if(heightMode == MeasureSpec.EXACTLY){
            height = heightSize;
        }else if(heightMode == MeasureSpec.AT_MOST){
            height = getResources().getDisplayMetrics().heightPixels - getPaddingTop() - getPaddingBottom();
        }

        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    
        drawTitle(canvas);
        drawDate(canvas);
    }

    /**
     * 画表头
     * @param canvas
     */
    private void drawTitle(Canvas canvas){
        float titleHeight =  mTitlePaint.measureText("二");
        //画背景
        Paint bgPaint = new Paint();
        bgPaint.setColor(titleBackgroundColor);
        bgPaint.setStyle(Paint.Style.FILL);
        canvas.drawRect(getPaddingLeft(), getPaddingTop(), width, height / 7, bgPaint);

        //日期表头
        for(int i=0; i<7; i++){
            canvas.drawText(mTitles[i],(2*i+1)*width/14,height/14+titleHeight/2,mTitlePaint);
        }
    }

    /**
     * 画日期
     * @param canvas
     */
    private void drawDate(Canvas canvas){

        /**
         * 画背景
         */
        Paint paint = new Paint();
        paint.setColor(background);
        paint.setTextSize(titleSize);
        paint.setStyle(Paint.Style.STROKE);
        paint.setFlags(Paint.ANTI_ALIAS_FLAG);
        paint.setAntiAlias(true);

        canvas.drawRect(getPaddingLeft(),height /7,getPaddingRight(),height,paint);

        //当前的日期
        Calendar calendar = Calendar.getInstance();
        int mCurrentDay = calendar.get(Calendar.DAY_OF_MONTH);
        //当前天是星期几
        int currentDayIndex = calendar.get(Calendar.DAY_OF_WEEK)-1;

        //获取当月的第一天是星期几
        int firstDayOfMonthIndex = currentDayIndex - (mCurrentDay-1)%7;
        if(firstDayOfMonthIndex<=0){
            firstDayOfMonthIndex = firstDayOfMonthIndex + 7;
        }

        //标记所描绘的日期是星期几
        int dayWeekIndex = firstDayOfMonthIndex;
        //画到了第几行
        int rowIndex = 1;

        int dateHeight = (int) (mPaint.getFontMetrics().ascent+mPaint.getFontMetrics().descent);
        int paddingTop =  height/14+height /7;

        int monthDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        for(int i=1; i<=monthDays; i++){

            if(dayWeekIndex>7){
                dayWeekIndex=1;
                rowIndex= rowIndex+1;
            }


            //先计算这天所要显示的位置
            int positionX = (2*dayWeekIndex-1)*width/14;
            int positionY = (2*rowIndex-1)*height/12 + dateHeight/2 + paddingTop;

            //判断日期是否是当前日期,是的话则画背景的圆,并且设置相应的日期颜色
            if(i!=mCurrentDay){
                //选择其它日期时的背景颜色
                if(i==Integer.parseInt(selectedDate)){
                    drawCircle(canvas, positionX, positionY+dateHeight/2, height / 13, selectedBgColor);
                    mPaint.setColor(Color.parseColor("#232323"));
                }
            }else {
                drawCircle(canvas, positionX, positionY+dateHeight/2, height / 13, currentBgColor);
                mPaint.setColor(currentColor);
            }
            //画日期和重置字体颜色
            canvas.drawText(i+"",positionX,positionY,mPaint);
            mPaint.setColor(Color.parseColor("#232323"));

            //星期几递增1
            dayWeekIndex++;

            //将日期的信息添加进Map,用来监听点击事件的触发条件
            String dateKey = rowIndex+"_"+dayWeekIndex;
            datePositionMap.put(dateKey, i + "");

        }
    }

    /**
     * 画圆形的背景
     * @param canvas
     * @param positionX
     * @param positionY
     * @param radius
     * @param color
     */
    private void drawCircle(Canvas canvas,int positionX, int positionY, int radius,int color){
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(color);
        canvas.drawCircle(positionX, positionY, radius,mPaint);
    }

    /**用来监听点击事件
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        if(action==MotionEvent.ACTION_DOWN){

            selectedDate = selectedDate(event);
            if(selectedDate!=null){
                invalidate();
            }else {
                selectedDate="0";
            }
        }
        return true;

    }

    /**
     * 处理单击事件,重绘选中的日期的背景
     * @param event
     * @return
     */
    private String selectedDate(MotionEvent event){

        int paddingTop =  height/14+height /7;
        int column = (int)Math.ceil(event.getX()/(width/7))+1;
        int row = (int)Math.ceil((event.getY()-paddingTop)/(height/6));

        String key = row+"_"+column;
        String clickDay = datePositionMap.get(key);

        return clickDay;
    }
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值