Android 4.0日历(calendar)源码分析之日视图
日视图
相比其他的视图,日视图会显得很“简单”,说简单是因为在日视图下,除了actionbar之外没有任何其他可见的控件,唯一的控件是ViewSwitcher,但是是不可见的。ViewSwitcher里面装着两个显示日信息的自定义View—DayView。这两个View交替着切换,这使得我们能在日视图中一天一天的横向滚动。
ViewSwitcher不说了,这里要说的是这个DayView。
DayView是一个完全自定义的View,直接继承至View类。一般我们都是继承一些现成的控件,比如TextView,但是这里直接是将需要的东西一点一点的画出来,所以要弄懂,可能需要对android canvas绘图方面有比较多的了解。DayView的重点和难点有如下几个:
如何画出每天需要的信息,比如在恰当的位置画出每个小时的数字和横线,以及当天存在的事件。
如何实现在手指水平左右拖动时,能够看到天与天之间的过度效果;如何在垂直滚动时,响应用户的意图,并保证基本不和水平滚动冲突。
第一个问题纯粹是canvas方面的问题,很琐碎,但是逻辑不是那么强。
第二个问题涉及到触摸设备中的手势处理,要做到日历这么人性化的效果,还是比较难的。
DayView中的手势处理结合了比较原始的onTouchEvent()方法和GestureDetector(封装好了常用的几种手势)。onTouchEvent首先捕获触摸事件,将这个事件,传递给GestureDetector,然后交给GestureDetector分析。根据GestureDetector中的数据,我们得到一些有用的数值(比如在scroll滚动事件发生的时候,我们不断获取滚动点的水平偏移量,然后响应的View也移动相同的偏移量,从而实现水平滑动),然后根据这些数据来重绘View-即调用view的onDraw函数。
给出一小段代码让你找到感觉,不必细究每句话的含义,只需明白大致的处理方式,在ondraw开始几行有如下代码:
float yTranslate = -mViewStartY + DAY_HEADER_HEIGHT + mAlldayHeight;
// offset canvas by the current drag and header position
canvas.translate(-mViewStartX, yTranslate);
// clip to everything below the allDay area
Rect dest = mDestRect;
dest.top = (int) (mFirstCell - yTranslate);
dest.bottom = (int) (mViewHeight - yTranslate);
dest.left = 0;
dest.right = mViewWidth;
canvas.save();
canvas.clipRect(dest);
// Draw the movable part of the view
doDraw(canvas);
其中canvas.translate(-mViewStartX, yTranslate)可以实现view的平移。
手势处理我们先进入捕获触摸事件的onTouchEvent函数:
onScroll()
onScroll函数不多,我就直接将整个函数贴出来了:
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (DEBUG) Log.e(TAG, "GestureDetector.onScroll");
if (mTouchStartedInAlldayArea) {
if (Math.abs(distanceX) < Math.abs(distanceY)) {
return false;
}
// don't scroll vertically if this started in the allday area
distanceY = 0;
}
Log.i("scroll", "DayView::onScroll:distanceX="+distanceX+"and distanceY="+distanceY);
DayView.this.doScroll(e1, e2, distanceX, distanceY);
return true;
}
onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)有四个参数,这里其实只用了后面两个distanceX,distanceY,字面意思上分别指水平和垂直滚动的距离。
根据我的log信息来看,当我手指向左的时候distanceX为正,手指向右的时候distanceX为负。手指向上的时候distanceY为正,手指向下的时候distanceY为负。