自定義 View 迴圈滾動刻度控制元件

PandaQ發表於2017-02-14

LoopScaleView

先看效果圖:

自定義 View 迴圈滾動刻度控制元件
enter description here

LoopScaleView 是一個自定義的刻度尺風格的選值控制元件,從上面的動圖大家可以看到 LoopScaleView 的執行效果.可以設定螢幕內顯示的刻度數,也可以設定每一個刻度代表的值得大小。

LoopScaleView.class

Nested class

OnValueChangeListener 刻度取值監聽介面

Public methods

方法名 返回值型別 說明
getItemsCount() int 獲取總的刻度數
setCursorColor(int color) void 設定遊標顏色(遊標不採用圖片時)
setCursorWidth(int width) void 設定遊標寬度(同上)
setCursorMap(Bitmap map) void 設定圖片作為遊標
setScaleWidth(int scaleWidth) void 設定刻度寬度
setShowItemSize(int showItemSize) void 設定螢幕內可見的大刻度數
setScaleHeight(float scaleHeight) void 設定刻度的高度
setLineColor(int lineColor) void 設定底部直線的顏色
setScaleTextColor(int scaleTextColor) void 設定刻度標值的顏色
setScaleTextSize(int scaleTextSize) void 設定刻度標值的文字大小
setMaxValue(int maxValue) void 設定最大值
setOneItemValue(int oneItemValue) void 設定一個刻度表示的值的大小
setCurrentValue(int currValue) void 設定當前的值

分解剖析

  • onMeasure 方法中初始化一個刻度的畫素寬度,整個檢視的寬度
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
          viewHeight = MeasureSpec.getSize(heightMeasureSpec);
          //一個小刻度的寬度(十進位制,每5個小刻度為一個大刻度)
          scaleDistance = getMeasuredWidth() / (showItemSize * 5);
          //尺子長度總的個數*一個的寬度
          viewWidth = maxValue / oneItemValue * scaleDistance;
          maxX = getItemsCount() * scaleDistance;
          minX = -maxX;
      }複製程式碼
  • onDraw() 方法重寫繪製 ScaleView 的檢視
      @Override
      protected void onDraw(Canvas canvas) {
          canvas.clipRect(getPaddingStart(), getPaddingTop(), getWidth() - getPaddingRight(), viewHeight - getPaddingBottom());
          // 繪製底部線條
          drawLine(canvas);
          // 繪製遊標
          drawCursor(canvas);
          paint = new Paint(Paint.ANTI_ALIAS_FLAG);
          paint.setStrokeWidth(scaleWidth);
          // 繪製反向的一個刻度尺
          for (int i = 0; i < maxValue / oneItemValue; i++) {
              //drawScale 為繪製刻度線的方法
              drawScale(canvas, i, -1);
          }
          //繪製正向的一個刻度尺
          for (int i = 0; i < maxValue / oneItemValue; i++) {
                //drawScale 為繪製刻度線的方法
              drawScale(canvas, i, 1);
          }
      }複製程式碼
    可以看出上面的繪製過程,實際上是繪製出了兩個刻度尺。經過上面的步驟,靜止狀態下的 ScaleView 已經繪製完成,接下來就是要讓他動起來了
  • 手勢識別來處理滑動
    在 onTouchEvent() 方法中將觸控事件交給手勢識別 GestureDetector.SimpleOnGestureListener 來處理:

      /**
       * 滑動手勢處理
       */
      private GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {
    
          @Override
          public boolean onDown(MotionEvent e) {
              return true;
          }
          //滾動事件
          public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
              scrollView(distanceX);
              return true;
          }
          //快速滑動時間
          public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
              if (!mScroller.computeScrollOffset()) {
                  mScroller.fling((int) currLocation, 0, (int) (-velocityX / 1.5), 0, minX, maxX, 0, 0);
                  setNextMessage(0);
              }
              return true;
          }
    
          @Override
          public boolean onSingleTapUp(MotionEvent e) {
              return super.onSingleTapUp(e);
          }
      };複製程式碼

    上述程式碼主要注意兩個地方:onScroll() 和 onFling()。當正常左右滑動時,觸發 onScroll 方法呼叫 srcollView(float distance) 對整個檢視進行重繪。當快速慣性滑動時通過 Scroller 讓慣性滑動變得流暢,慣性滑動的狀態更新是通過 Handler 進行不斷的查詢 Scroller 的執行狀態得到的,當 Scroller 執行完慣性滑動的動畫到達目的地時,停止 Handler 的查詢任務,當 onFling 多次觸發時只會執行第一次的狀態。

  • 迴圈滾動的實現
    開始說到的繪製了正向反向兩個方向的刻度尺即是為了實現迴圈滾動而設定的,在 drawScale() 方法中有如下程式碼:
          if (currLocation + showItemSize / 2 * 5 * scaleDistance >= viewWidth) {
              currLocation = -showItemSize / 2 * 5 * scaleDistance;
              float speed = mScroller.getCurrVelocity();
              mScroller.fling((int) currLocation, 0, (int) speed, 0, minX, maxX, 0, 0);
              setNextMessage(0);
          } else if (currLocation - showItemSize / 2 * 5 * scaleDistance <= -viewWidth) {
              currLocation = showItemSize / 2 * 5 * scaleDistance;
              float speed = mScroller.getCurrVelocity();
              mScroller.fling((int) currLocation, 0, (int) speed, 0, minX, maxX, 0, 0);
              setNextMessage(0);
          }複製程式碼
    當 currLocation 加上可視檢視一半的距離大於刻度尺的寬度 viewWidth 或者 currLocation 減去可視檢視一半的距離小於 -viewWidth 時(即正向或者反向滑到最大/最小值時)通過為 currLocation 重新賦值將刻度值重置,來達到迴圈滾動的目的.如果到達臨界點時是在 Scroller 執行快速滑動的過程則重置之後需要再為 Scroller 重新設定初速度來達到流暢的滑動.
    基本思路就是上面所說的這樣了,詳細操作大家自己檢視 LoopScaleView 的原始碼。

    接入使用

    project's build.gradle (工程的 build.gradle)
    allprojects {
      repositories {
          jcenter()
          maven{
              url  "http://dl.bintray.com/huxinyu/maven"
          }
      }
    }複製程式碼
    module's build.gradle (模組的build.gradle)
    dependencies {
      compile 'com.pandaq:loopscale:1.0.1'
    }複製程式碼
  • xml 檔案中進行屬性配置,這些屬性也可以通過 Java 程式碼進行修改
      <com.pandaq.loopscaleview.LoopScaleView
          android:id="@+id/lsv_4"
          android:layout_width="match_parent"
          android:layout_height="50dp"
          android:layout_margin="8dp"
          android:background="@drawable/loopscaleview_bg"
          android:padding="8dp"
          app:cursorColor="@color/colorAccent"
          app:maxShowItem="4"
          app:maxValue="1000"
          app:oneItemValue="5"
          app:scaleTextColor="@color/colorPrimary"/>複製程式碼

    最後

    覺得本文對你有幫助
    簡書PandaQ404
    掘金PandaQ
    GithubPandaQAQ
    持續分享中,歡迎關注和 star。。。

相關文章