首先附上github地址:github.com/rgf456/Anim…
上一篇文章中讲过的内容有两点须要复习一下:javascript
咱们将上面两点得到的信息所有保存在一个数组对象中,这个数组对象就是咱们用来绘制的基准。java
view的绘制都是在onDraw方法中,在ondraw方法中会传递一个Canvas参数,这个就是咱们须要绘制的画布。在这里咱们须要绘制出全部的天的状态。git
在绘制的过程当中,咱们须要根据天天的标记(State)来分状况绘制,因此此处要作一个便利循环来去除出天天的内容:github
private void drawDays(Canvas canvas, CellDay[] cellDays) {
for (CellDay c : cellDays) {
switch (c.getDayState()) {
case LASTMONTH:
if (c.isSelected()) {
circlePaint.setColor(Color.TRANSPARENT);
} else {
circlePaint.setColor(Color.TRANSPARENT);
textPaint.setColor(Color.GRAY);
}
break;
case CURRENTMONTH:
if (c.isSelected()) {
circlePaint.setColor(Color.YELLOW);
textPaint.setColor(Color.BLACK);
} else {
circlePaint.setColor(Color.RED);
textPaint.setColor(Color.RED);
canvas.drawText("班",
c.getPointX() + textPaint.measureText(tempDate) / 2,
c.getPointY() - textPaint.getTextSize() / 2,
textPaint);
}
break;
case NEXTMONTH:
if (c.isSelected()) {
circlePaint.setColor(Color.TRANSPARENT);
} else {
circlePaint.setColor(Color.TRANSPARENT);
textPaint.setColor(Color.GRAY);
}
break;
case CURRENTDAY:
circlePaint.setColor(Color.YELLOW);
textPaint.setColor(Color.BLACK);
break;
case WEEKEND:
circlePaint.setColor(Color.CYAN);
textPaint.setColor(Color.CYAN);
canvas.drawText("休",
c.getPointX() + textPaint.measureText(tempDate) / 2,
c.getPointY() - textPaint.getTextSize() / 2,
textPaint);
break;
case SPECIALDAY:
circlePaint.setColor(Color.GREEN);
textPaint.setColor(Color.GREEN);
canvas.drawText("假",
c.getPointX() + textPaint.measureText(tempDate) / 2,
c.getPointY() - textPaint.getTextSize() / 2,
textPaint);
}
canvas.drawCircle(tempPositionX, tempPositionY, radius - 10, selectPaint);
// canvas.drawText(tempDate,
// tempPositionX - textPaint.measureText(tempDate) / 2,
// tempPositionY + textPaint.getTextSize() / 2,
// selectTextPaint);
canvas.drawText(c.getDate(),
c.getPointX() - textPaint.measureText(c.getDate()) / 2,
c.getPointY() + textPaint.getTextSize() / 2,
textPaint);
// canvas.drawCircle(c.getPointX(), c.getPointY(), radius - 10, circlePaint);
//这个地方改成rectf能够向下兼容
canvas.drawArc(c.getPointX() - radius + 10,
c.getPointY() - radius + 10,
c.getPointX() + radius - 10,
c.getPointY() + radius - 10,
0, 270, false, circlePaint);
c.setSelected(false);
oldPositionX = newPositionX;
oldPositionY = newPositionY;
}
}复制代码
对因而否选中的状态咱们采用的是特殊的标记,因此没有在siwtch中直接使用,而是经过if来细分每一个switch的状态。在LASTMONTH、NEXTMONTH中咱们对画笔作了透明处理,这样作的目的是防止在选中的状态下选中状态是透明的,那么也就不会产生视觉高亮效果。(后面会讲如何处理上个月和下个月不被选中)。canvas
case SPECIALDAY:
circlePaint.setColor(Color.GREEN);
textPaint.setColor(Color.GREEN);
canvas.drawText("假",
c.getPointX() + textPaint.measureText(tempDate) / 2,
c.getPointY() - textPaint.getTextSize() / 2,
textPaint);复制代码
在SPECIALDAY中咱们直接绘制了特殊日期显示的文字。固然此处能够传任意参数进来做为绘制文字,在后期的改进中我会将这个做为接口供使用者调用。数组
canvas.drawText(c.getDate(),
c.getPointX() - textPaint.measureText(c.getDate()) / 2,
c.getPointY() + textPaint.getTextSize() / 2,
textPaint);
// canvas.drawCircle(c.getPointX(), c.getPointY(), radius - 10, circlePaint);
//这个地方改成rectf能够向下兼容
canvas.drawArc(c.getPointX() - radius + 10,
c.getPointY() - radius + 10,
c.getPointX() + radius - 10,
c.getPointY() + radius - 10,
0, 270, false, circlePaint);复制代码
这段代码是用来绘制日期的数字和一个半圆的效果。app
c.setSelected(false);复制代码
这段代码的做用是每次选中后清除选中状态,防止下次同时选中两个或者多个。此处能够延伸,做为网上较流行的选中多日期的状态表示。ide
至此,咱们的界面基本完成了。看一下效果图:post
本月的绘制完美完成。动画
此处咱们须要重写public boolean onTouchEvent(MotionEvent event)
方法。
此处咱们假定自定义view的事件不会被viewgroup拦截,则触摸事件的顺序依次为:
MotionEvent.ACTION_DOWN->MotionEvent.ACTION_MOVE->MotionEvent.ACTION_UP
我发现我特别懒,因此我只计算了DOWN和UP事件之间移动的距离
case MotionEvent.ACTION_DOWN:
touchRawX = event.getX();
touchRawY = event.getY();
break;复制代码
记录原始按下的点的坐标位置。
case MotionEvent.ACTION_UP:
Log.d(TAG, "MotionEvent:ACTION_UP");
Log.d(TAG, "rawY= " + touchRawY + ",touchY= " + event.getY());
float touchX = event.getX();
float touchY = event.getY();
if (touchRawY - touchY < -200) {
//下滑事件
Log.d(TAG, "下滑事件");
setMaxView();
setCellDay();
this.clearCanvas = false;
invalidate();
cutGrid();
init();
this.layout(0, 0, this.viewWidth, this.viewHeight / 2);
setCellDay();
invalidate();
}
if (touchRawY - touchY > 200) {
//上划事件
Log.d(TAG, "上划事件");
setMinView();
this.clearCanvas = true;
invalidate();
}
if (Math.abs(touchRawX - touchX) < 100 && Math.abs(touchY - touchRawY) < 100) {
//点击事件
Log.d(TAG, "点击事件");
int touchRow = (int) (touchX / cellWidth);
int touchLine = (int) (touchY / cellHeight);
final int touchId = touchLine * ROW_COUNT + touchRow;
if (canClickNextOrPreMonth) {
setClickEvent(touchId);
} else {
if (touchId > firstDayOfWeek - 2 && touchId < monthDaySum + firstDayOfWeek - 1) {
setClickEvent(touchId);
tempCellDay = cellDays[touchId];
tempState = cellDays[touchId].getDayState();
cellDays[touchId].setSelected(true);
newPositionX = cellDays[touchId].getPointX();
newPositionY = cellDays[touchId].getPointY();
setselectAnimator(touchId);
}
}
}
break;复制代码
在up事件中,首先根据触摸的位置换算为touchId,也就是在数组对象cellDays中的索引。而后作了三种状态判断,上滑下滑和点击事件,其中上滑和下滑事件能够先无论,由于到最后大家都不会用到。讲一下点击事件中的内容,首先判断是否是设置了上月或者下月是否能够点击,canClickNextOrPreMonth为真,则能够点击,为假则不能够点击。为假的时候,咱们只须要再设置一个条件,即点击的坐标点没有落在上月或下月的索引内。
先看如何设置点击事件:
private void setClickEvent(int touchId) {
CustomDate customDate = cellDays[touchId].getCustomDate();
if (clickCellListener != null) {
clickCellListener.onClickCell(customDate);
}
}复制代码
在这个方法中传进了一个int值,这个值就是cellday数组的索引值。
只要实现接口便可得到这个传出的customDate。
最后看setSelectAnimator方法:
private void setselectAnimator(final int touchId) {
selectAnimatorX.removeAllUpdateListeners();
selectAnimatorX.setFloatValues(oldPositionX, newPositionX);
selectAnimatorX.setDuration(300);
selectAnimatorX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
tempPositionX = (float) animation.getAnimatedValue();
}
});
selectAnimatorY.removeAllUpdateListeners();
selectAnimatorY.setFloatValues(oldPositionY, newPositionY);
selectAnimatorY.setDuration(300);
selectAnimatorY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
tempPositionY = (float) animation.getAnimatedValue();
postInvalidate();
}
});
animatorSet = new AnimatorSet();
animatorSet.playTogether(selectAnimatorX, selectAnimatorY);
animatorSet.start();
}复制代码
这个方法看我文章的应该都很熟悉了,作一个valueanimator来做为世间引擎不断引发XY坐标的变化,产生一个动画移动的效果。而后调用inalidate做为标记,刷新界面。
此时看一下效果图:
至此,咱们日历的点击、选中状态改变、特殊日期、周末等高亮显示都已经完成。
我是利用了viewpager来实现滑动,我假定你们都对viewpager的使用很擅长。此处只看一下代码便可:
private class CalendarAdapter extends PagerAdapter {
private MyCalendar calendar;
private LayoutParams lp;
public CalendarAdapter(MyCalendar calendar) {
this.calendar = calendar;
}
public CalendarAdapter(MyCalendar calendar, LayoutParams lp) {
this.calendar = calendar;
this.lp = lp;
}
@Override
public int getCount() {
return 1000;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (myCalendars.size() > position) {
MyCalendar myCalendar = myCalendars.get(position);
if (myCalendar != null) {
return myCalendar;
}
}
calendar = new MyCalendar(context);
calendar.setClickCellListener(MyViewPager.this);
customeDate = new CustomDate(position / 12 + 2000, position % 12, 1);
calendar.setDate(customeDate);
calendar.setLayoutParams(lp);
calendar.setWeekendHighLight(weekendHightLight);
calendar.setSpecialDay(new int[]{10, 20});
calendar.setCanClickNextOrPreMonth(false);
while (myCalendars.size() <= position) {
myCalendars.add(null);
}
myCalendars.set(position, calendar);
ViewParent vp = calendar.getParent();
if (vp != null) {
ViewGroup parent = (ViewGroup) vp;
parent.removeView(calendar);
}
container.addView(calendar);
return calendar;
}
}复制代码
这里是前一个取巧的方式,我设定的是1000个月,从2000年开始计算。而后根据position来计算是哪一个月,customeDate = new CustomDate(position / 12 + 2000, position % 12, 1);
就是这段代码,而后将calendar的接口全都设置了一下。
再来看一下如何使用:
viewPager.setCurrentItem((calendar.get(Calendar.YEAR) - 2000) * 12 + calendar.get(Calendar.MONTH));复制代码
这个设置当前日期我并未封装到calendar中,正在不断改进。
再为viewpager添加一个ViewPager.OnPageChangeListener监听器。
public void onPageSelected(int position) {
StringBuilder builder = new StringBuilder();
builder.append(position / 12 + 2000);
builder.append("年");
builder.append(position % 12 + 1);
builder.append("月");
date.setText(builder.toString());
}复制代码
在这里设置显示的头的日期。
好了,全部的工做完成了。
讲的不是很好,思路基本都是这样实现的,有问题能够直接联系我。