自定義一個可滑動時間刻度尺

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

相關文章
相關標籤/搜索