您的位置:首页 > 其它

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

2016-03-22 23:27 489 查看
之前学过的东西隔了很长一段时间现在又忘了,原来已经不打算做码农了,但是好像没有什么选择了,先试试看自己合不合适做程序猿吧。今天来学一下,自定义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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: