首先來張截圖: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