首先来张截图:java
控件的外观可能不是很美观,不过功能基本都有了,能够本身设置选中的时间片断,暂时没有支持自定义样式。。。android
接下来介绍如何实现这个控件。git
首先要先知道android' view的绘制过程,首先的view先计算Measure,而后在进行Layout,最后Draw。那view的坐标系为左上角(0,0),github
图片网络的:canvas
那么在view上面画一些简单的图形的时候,也是根据这个坐标来绘制,若是你的画的图形超过了屏幕,就须要的移动view才能看见。由于view的自己是没有边界的,Canvas对象自己也是没有边界的。view的自己有两个函数scrollTo和scrollBy函数,能够用来移动view到自定义的位置。本身新建一个按钮,点击事件调用这两个函数的时候,能够看到按钮上的字移动,由于函数自己是view的移动,对于view的内容来讲就是,坐标改变了。那么也就是说要实现时间刻度的效果,就是在屏幕上绘制一条很长的时间刻度,而后根据手指的移动,调用scrollTo或者scrollBy来移动view,对于里面的内容就是看起来移动了。网络
对于直接调用scrollTo或者scrollBy来移动view显得有点生硬,没有平滑的效果,因此要引入Scroller类来辅助处理滑动,不过还有一个加速度的类能够实现移动带有弹性的效果,不过如今暂时没有用到。对于Scroller类能够上网查查资料,是一个辅助类,能够计算出view移动的距离,而且计算出移动距离的平滑的点。使用方法比较简单。ide
scroller = new Scroller(context);
介绍一下computeScroll,computeScroll()是View类的一个空函数,在view须要从新绘制的时候,会调用computeScroll函数,那么在computeScroll能够调用scrollTo或者scrollBy传入Scroller的坐标,再从新绘制view就能够实现view的滚动。函数
@Override public void computeScroll() { super.computeScroll(); if (scroller.computeScrollOffset()) { scrollTo(scroller.getCurrX(), scroller.getCurrY()); invalidate(); } }
view的从新绘制能够调用invalidate和postInvalidate 函数来通知系统从新绘制该view,前者要在ui线程里调用,后者能够在非ui线程里面调用。post
接下来要从新写触摸事件:ui
@Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (scroller != null && !scroller.isFinished()) { scroller.abortAnimation(); } lastX = x; return true; case MotionEvent.ACTION_MOVE: float dataX = lastX - x; int finalx = scroller.getFinalX(); //右边 if (dataX < 0) { if (finalx < -viewWidth / 2) { return super.onTouchEvent(event); } } if (dataX > 0) { if (finalx > timeScale * 21) { return super.onTouchEvent(event); } } /** 对于这里调用startScroll函数,就是从某个点移动一段距离,这里传入scroller.getFinalX(), scroller.getFinalY()的缘由是,获取的点都是手指移动的点 */ scroller.startScroll(scroller.getFinalX(), scroller.getFinalY(), (int) dataX, 0); lastX = x; postInvalidate(); return true; case MotionEvent.ACTION_UP: int finalx1 = scroller.getFinalX(); if (finalx1 < -viewWidth / 2) { scroller.setFinalX(-viewWidth / 2); } if (finalx1 > timeScale * 21) { scroller.setFinalX(timeScale * 21); } if (scrollListener != null) { int finalX = scroller.getFinalX(); //表示每个屏幕刻度的一半的总秒数,每个屏幕有6格 int sec = 3 * 3600; //滚动的秒数 int temsec = (int) Math.rint((double) finalX / (double) timeScale * 3600); sec += temsec; //获取的时分秒 int thour = sec / 3600; int tmin = (sec - thour * 3600) / 60; int tsec = sec - thour * 3600 - tmin * 60; scrollListener.onScrollFinish(thour, tmin, tsec); } postInvalidate(); break; } return super.onTouchEvent(event); }
也就是在移动的时候的调用
scroller.startScroll(scroller.getFinalX(), scroller.getFinalY(), (int) dataX, 0);
不停的生成新的点而后不停的从新画.
边界的处理,对于边界的处理,个人处理是判断坐标。
int finalx1 = scroller.getFinalX(); if (finalx1 < -viewWidth / 2) { scroller.setFinalX(-viewWidth / 2); } if (finalx1 > timeScale * 21) { scroller.setFinalX(timeScale * 21); }
控制坐标的左右最大值。这个能够本身定。
接下就是画图形,绘画就是在onDraw里面用Canvas来绘画。
画刻度:
public void drawLines(Canvas canvas) { //底部的线 canvas.drawLine(0, (float) (viewHeight * 0.9), totalTime, (float) (viewHeight * 0.9), linePaint); for (int i = 0; i <= totalTime; i++) { if (i % timeScale == 0) { canvas.drawLine(i, (float) (viewHeight * 0.7), i, (float) (viewHeight * 0.9), linePaint); //画刻度值 canvas.drawText( formatString(i / timeScale, 0, 0), i, (float) (viewHeight * 0.6), linePaint); } } }
画指针
public void drawMidLine(Canvas canvas) { //移动的距离整个view内容移动的距离 int finalX = scroller.getFinalX(); //表示每个屏幕刻度的一半的总秒数,每个屏幕有6格 int sec = 3 * 3600; //滚动的秒数 int temsec = (int) Math.rint((double) finalX / (double) timeScale * 3600); sec += temsec; //获取的时分秒 int thour = sec / 3600; int tmin = (sec - thour * 3600) / 60; int tsec = sec - thour * 3600 - tmin * 60; //滚动时的监听 if (scrollListener != null) { scrollListener.onScroll(thour, tmin, tsec); } //画指针 canvas.drawLine(timeScale * 3 + finalX, 0, timeScale * 3 + finalX, viewHeight, midPaint); //画数字 canvas.drawText(formatString(thour, tmin, tsec), timeScale * 3 + finalX, (float) (viewHeight * 0.3), textPaint); }
画背景
public void drawBg(Canvas canvas) { rect.set(-1, 0, timeScale * 24 + 1, viewHeight); canvas.drawRect(rect, bgPaint); }
画时间片断
public void drawTimeRect(Canvas canvas) { for (TimePart temp : data) { int seconds1 = temp.sHour * 3600 + temp.sMinute * 60 + temp.sSeconds; int seconds2 = temp.eHour * 3600 + temp.eMinute * 60 + temp.eSeconds; //若是是先除以3600小数点的数据会被舍去 位置就不许确了 int x1 = seconds1 * timeScale / 3600; int x2 = seconds2 * timeScale / 3600; rect.set(x1, 0, x2, (int) (viewHeight * 0.9)); canvas.drawRect(rect, timePaint); } }
定义一个时间片断的类:
//时间片断 用于标记选中的时间 public static class TimePart { //开始的时间 public int sHour, sMinute, sSeconds; //结束的时间 public int eHour, eMinute, eSeconds; public TimePart(int sHour, int sMinute, int sSeconds, int eHour, int eMinute, int eSeconds) { this.sHour = sHour; this.sMinute = sMinute; this.sSeconds = sSeconds; this.eHour = eHour; this.eMinute = eMinute; this.eSeconds = eSeconds; } }
其它的好像也没有什么介绍,若是有在补充。
项目地址:https://github.com/absolve/TimeScale