自定义View的简单尝试——自定义日历视图
2016-03-22 23:27
489 查看
之前学过的东西隔了很长一段时间现在又忘了,原来已经不打算做码农了,但是好像没有什么选择了,先试试看自己合不合适做程序猿吧。今天来学一下,自定义View。我们看看今天要做的东西
这是我在某个地方看到的一张图,不知道在哪个地方了。我们先来分析一下,如果画出类似于这种效果。首先确定当前月的第一天是星期几,以及当前月的天数。然后用一个循环来画每一个日期,当然这要计算每个日期所在的位置。宽度是平均分很容易,高度的话看个人想要的了,我这里是总高度的1/6。判断是否和当前日期相等,如果相等则画出背景。接着便是用一个Map集合来存储每个日期所在的位置,以便用于监听是否点击了日期,然后做出相应的重绘。要监听事件,所以重写了onTounch方法。要注意的一点是绘制文字是基于文字的底部的,所以应该加上字体高度的一半。
自定义属性。分别是日期的背景颜色,日期表头的背景颜色,正常的日期字体颜色,当前日期的颜色,当前日期的背景颜色,选择的日期的背景颜色,日期的字体大小,日期表头的字体大小。
获取自定义的属性
初始化画笔
测量View的大小
接着就是开始画了
事件监听
布局文件
效果图
完整的代码
这是我在某个地方看到的一张图,不知道在哪个地方了。我们先来分析一下,如果画出类似于这种效果。首先确定当前月的第一天是星期几,以及当前月的天数。然后用一个循环来画每一个日期,当然这要计算每个日期所在的位置。宽度是平均分很容易,高度的话看个人想要的了,我这里是总高度的1/6。判断是否和当前日期相等,如果相等则画出背景。接着便是用一个Map集合来存储每个日期所在的位置,以便用于监听是否点击了日期,然后做出相应的重绘。要监听事件,所以重写了onTounch方法。要注意的一点是绘制文字是基于文字的底部的,所以应该加上字体高度的一半。
自定义属性。分别是日期的背景颜色,日期表头的背景颜色,正常的日期字体颜色,当前日期的颜色,当前日期的背景颜色,选择的日期的背景颜色,日期的字体大小,日期表头的字体大小。
<?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>
获取自定义的属性
//默认的字体大小 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();
初始化画笔
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()); }
测量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); }
接着就是开始画了
@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; }
布局文件
<?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>
效果图
完整的代码
/** * 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; } }
相关文章推荐
- task4:结对项目-词频统计
- PSP记录个人项目耗时情况
- bzoj 3171: [Tjoi2013]循环格
- WinServer-授权规则
- 基础补完系列 - C++ Primer Plus 第三章
- CentOS、RHEL、SUSE平台的rpm包的简单制作
- Intent_filter匹配规则
- 简单的警告对话框
- Oracle数据库学习(三)--基础查询及关联查询
- 正则表达式工具
- WinServer-IIS-身份验证\SSL设置
- 关于Android开发的40条优化建议
- Web支付
- numpy库常用函数记录(不断更新)
- python学习(二):函数
- 广域网详解
- [ 总结 ] Linux下两种常用的双网卡绑定
- 代码复审
- WinServer-IIS-MIME类型
- ASP.NET-属性与过滤器