滑动的刻度尺

一、概述

       从事了这个行业许久还是想想应该写点什么。不过第一次分享不知道该分享些什么比较好,恰好项目中用到了一个刻度尺来标明金额,所以就分享一下这个仅做纪念。

二、效果图


三、代码

      话不多说了,直接上源码

      这里需要先添加个样式

    <declare-styleable name="ScrollRuler">
        <attr format="dimension" name="rLineWidth"/>
        <attr format="color" name="rLineColor"/>
        <attr format="dimension" name="rPixel"/>
        <attr format="integer" name="rStep"/>
        <attr format="dimension" name="rTextSize"/>
        <attr format="color" name="rTextColor"/>
        <attr format="dimension" name="rLineHeight"/>
        <attr format="dimension" name="rLineToText"/>
        <attr format="integer" name="rMinVelocity"/>
        <attr format="integer" name="rAnimTime"/>
        <attr format="boolean" name="rIsTop"/>
        <attr format="integer" name="rBegin"/>
        <attr format="integer" name="rEnd"/>
    </declare-styleable>

展示一下布局文件

 <com.example.zhangxianpei.myapp.ScrollRuler
        android:id="@+id/sr"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        />

好了,再看一眼代码

第二行 设置刻度尺的范围

第三行 设置刻度尺的渐变色

scrollRuler = findViewById(R.id.sr);
scrollRuler.setRange(0,20000);
scrollRuler.setGradientColor(Color.BLUE,Color.YELLOW);
scrollRuler.computeScroll();

到此差不多已经完成了,最后贴上ScrollRuler的源码

package com.example.zhangxianpei.myapp;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.Scroller;

/**
 *
 */
public class ScrollRuler extends View implements ValueAnimator.AnimatorListener {
  private Paint lPaint; //线画笔
  private Paint tPaint; //字画笔
  private VelocityTracker velocityTracker;
  private Scroller scroller;
  private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
  private int mWidth;
  private int mHeight;
  private int startY;  //刻度开始的Y轴高度
  private int sumMoveX = 0;
  private float downX = 0;
  private int lastMoveX = 0;
  private int halfWidth;
  private int sumPixel;
  private boolean isAnim = false;
  private boolean isLeft;
  private boolean scrollFlag = false;    //在fling下判断防止死循环

  private float lineWidth;        //刻度线宽
  private long animTime;         //回弹基准时间
  private int lineColor;          //线的颜色
  private int pixel;              //基本刻度之间间隔像素
  private int step;               //一个基本刻度代表的大小
  private int lineHeight;         //刻度之间高度差值
  private int lineToText;         //文字与最高刻度之间的距离
  private int begin;              //起始值
  private int end;                //终止值
  private int minVelocity;        //惯性滑动的最小速度
  private boolean isTop;          //刻度是否在上边
  private LinearGradient linearGradient;//渐变色取值器

  private int dfColor = Color.rgb(0xbb, 0xbb, 0xbb);

  public ScrollRuler(Context context) {
    this(context, null);
  }

  public ScrollRuler(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public ScrollRuler(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs);
  }

  private void init(Context context, AttributeSet attrs) {

    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ScrollRuler);
    lineWidth = ta.getDimension(R.styleable.ScrollRuler_rLineWidth, 3f);
    lineColor = ta.getColor(R.styleable.ScrollRuler_rLineColor, dfColor);
    pixel = (int) ta.getDimension(R.styleable.ScrollRuler_rPixel, 15f);
    step = ta.getInt(R.styleable.ScrollRuler_rStep, 100);
    int textSize = (int) ta.getDimension(R.styleable.ScrollRuler_rTextSize, 30);
    int textColor = ta.getColor(R.styleable.ScrollRuler_rTextColor, dfColor);
    lineHeight = (int) ta.getDimension(R.styleable.ScrollRuler_rLineHeight, 20);
    lineToText = (int) ta.getDimension(R.styleable.ScrollRuler_rLineToText, 30);

    minVelocity = ta.getInt(R.styleable.ScrollRuler_rMinVelocity, 500);
    animTime = ta.getInt(R.styleable.ScrollRuler_rAnimTime, 2000);
    isTop = ta.getBoolean(R.styleable.ScrollRuler_rIsTop, false);
    scroller = new Scroller(context);
    setOverScrollMode(OVER_SCROLL_ALWAYS);

    lPaint = new Paint();
    lPaint.setAntiAlias(true);
    lPaint.setColor(lineColor);
    lPaint.setStrokeWidth(lineWidth);

    tPaint = new Paint();
    tPaint.setAntiAlias(true);
    tPaint.setTextAlign(Paint.Align.CENTER);
    tPaint.setColor(textColor);
    tPaint.setTextSize(textSize);
    tPaint.setStyle(Paint.Style.FILL);

    begin = ta.getInt(R.styleable.ScrollRuler_rBegin, 0);
    end = ta.getInt(R.styleable.ScrollRuler_rEnd, 1000);
    sumPixel = ((end - begin) / step) * pixel;

    linearGradient = new LinearGradient(Color.rgb(255, 52, 17),
        Color.rgb(255, 137, 90));

    ta.recycle();
  }

  @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    mWidth = MeasureSpec.getSize(widthMeasureSpec);
    mHeight = MeasureSpec.getSize(heightMeasureSpec);
    startY = 0;
    halfWidth = mWidth / 2;
  }

  @Override protected void onDraw(Canvas canvas) {
    drawScale(canvas);
    drawLineIndicate(canvas);
  }

  private int lastRulerValue = -1;

  private void drawScale(Canvas canvas) {
    lPaint.setStrokeWidth(lineWidth);
    if (onRulerValueChangeListener != null
        && sumMoveX <= halfWidth
        && -sumMoveX <= sumPixel - halfWidth) {
      int value = (-sumMoveX + halfWidth) / pixel * step + begin;
      if (lastRulerValue != value && value >= begin && value <= end) {
        lastRulerValue = value;
        onRulerValueChangeListener.value(value);
      }
    }
    for (int x = 0; x < mWidth; x++) {
      int y = startY + lineHeight;
      boolean isDrawText = false;
      if ((-sumMoveX + x) % (pixel * 5) == 0) {
        y += lineHeight;
      }
      if ((-sumMoveX + x) % (pixel * 10) == 0) {
        isDrawText = true;
      }
      int text = (-sumMoveX + x) / pixel * step + begin;
      if (text >= begin && text <= end && ((-sumMoveX + x) % pixel) == 0) {
        if (x < mWidth / 2 && linearGradient != null) {
          lPaint.setColor(linearGradient.getColor(x * 2f / mWidth));
        } else {
          lPaint.setColor(lineColor);
        }
        canvas.drawLine(x, isTop ? startY : mHeight, x, isTop ? y : mHeight - y, lPaint);
      }
      if (isDrawText) {
        if (text >= begin && text <= end) {
          canvas.drawText(String.valueOf(text), x,
              isTop ? y + lineToText : mHeight - y - lineToText, tPaint);
        }
      }
    }
  }

  private void drawLineIndicate(Canvas canvas) {
    //基线
    lPaint.setColor(lineColor);
    lPaint.setStrokeWidth(2);
    canvas.drawLine(0, isTop ? 0 : mHeight, mWidth, isTop ? 0 : mHeight, lPaint);
    ////线性指示器
    //lPaint.setColor(borderColor);
    //lPaint.setStrokeWidth(borderWidth);
    //canvas.drawLine(halfWidth, isTop ? 0 : mHeight, halfWidth,
    //    isTop ? mHeight - indicateHeight : indicateHeight, lPaint);
  }

  private int vCount = 0;

  @Override public boolean onTouchEvent(MotionEvent event) {
    if (velocityTracker == null) {
      velocityTracker = VelocityTracker.obtain();
      vCount = 0;
    }
    velocityTracker.addMovement(event);
    vCount++;

    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        downX = event.getX();
        break;
      case MotionEvent.ACTION_MOVE:
        int moveX = (int) (event.getX() - downX);
        if (lastMoveX == moveX) {
          return true;
        }
        sumMoveX += moveX;
        if (moveX < 0) {
          //向左滑动
          isLeft = true;
          if (-sumMoveX > sumPixel) {
            correct();
            return true;
          }
        } else {
          //向右滑动
          isLeft = false;
          if (sumMoveX >= mWidth) {
            correct();
            return true;
          }
        }
        lastMoveX = moveX;
        downX = event.getX();
        invalidate();
        break;
      case MotionEvent.ACTION_CANCEL:
      case MotionEvent.ACTION_UP:
        //越界直接返回
        if (sumMoveX > halfWidth || -sumMoveX + halfWidth > sumPixel) {
          correct();
          return true;
        }
        velocityTracker.computeCurrentVelocity(1000);
        float xVelocity = velocityTracker.getXVelocity();
        if (vCount < 4 || Math.abs(xVelocity) < minVelocity) {
          correct();
          return true;
        }
        int velocityX = -(int) Math.min(Math.abs(xVelocity), 3000);
        scroller.fling(pixel * 2, 0, velocityX, 0, 0, mWidth, 0, 0);
        recycleVelocityTracker();
        break;
    }
    return true;
  }

  @Override public void computeScroll() {
    boolean offset = scroller.computeScrollOffset();
    if (offset) {
      scrollFlag = true;
      int currX = Math.min(pixel * 5, scroller.getCurrX());
      if (sumMoveX >= mWidth) {
        scroller.abortAnimation();
        correct();
        return;
      }
      if (-sumMoveX >= sumPixel) {
        scroller.abortAnimation();
        correct();
        return;
      }
      if (isLeft) {
        sumMoveX -= currX;
      } else {
        sumMoveX += currX;
      }
      invalidate();
    }
    if (!offset && scrollFlag) {
      scrollFlag = false;
      correct();
    }
  }

  private void correct() {
    if (sumMoveX > halfWidth) {
      if (isAnim) return;
      ValueAnimator valueAnimator = ValueAnimator.ofInt(sumMoveX, halfWidth);
      if (sumMoveX - halfWidth < halfWidth) {
        valueAnimator.setDuration(animTime * (sumMoveX - halfWidth) / (halfWidth));
      } else {
        valueAnimator.setDuration(animTime);
      }
      valueAnimator.setInterpolator(mInterpolator);
      valueAnimator.addListener(this);
      valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override public void onAnimationUpdate(ValueAnimator animation) {
          sumMoveX = (int) animation.getAnimatedValue();
          invalidate();
        }
      });
      valueAnimator.start();
    } else if (-sumMoveX + halfWidth > sumPixel) {
      if (isAnim) return;
      ValueAnimator valueAnimator = ValueAnimator.ofInt(sumMoveX, halfWidth - sumPixel);

      if ((-sumMoveX + halfWidth) - sumPixel < halfWidth) {
        valueAnimator.setDuration(animTime * ((-sumMoveX + halfWidth) - sumPixel) / (halfWidth));
      } else {
        valueAnimator.setDuration(animTime);
      }
      valueAnimator.addListener(this);
      valueAnimator.setInterpolator(mInterpolator);
      valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override public void onAnimationUpdate(ValueAnimator animation) {
          sumMoveX = (int) animation.getAnimatedValue();
          invalidate();
        }
      });
      valueAnimator.start();
    } else {
      checkOutData();
      if (onRulerValueSelectedListener != null) {
        onRulerValueSelectedListener.value((-sumMoveX + halfWidth) / pixel * step + begin);
      }
    }
    recycleVelocityTracker();
  }

  private void recycleVelocityTracker() {
    if (velocityTracker != null) {
      velocityTracker.recycle();
      velocityTracker = null;
      vCount = 0;
    }
  }

  @Override public void onAnimationStart(Animator animation) {
    isAnim = true;
  }

  @Override public void onAnimationEnd(Animator animation) {
    isAnim = false;
    if (onRulerValueSelectedListener != null) {
      onRulerValueSelectedListener.value((-sumMoveX + halfWidth) / pixel * step + begin);
    }
  }

  @Override public void onAnimationCancel(Animator animation) {
    isAnim = false;
  }

  @Override public void onAnimationRepeat(Animator animation) {
    isAnim = true;
  }

  /**
   * 校验数据是否在刻度上,如果不在则对像素进行调整
   */
  private void checkOutData() {
    if (sumMoveX < halfWidth || -sumMoveX + halfWidth < sumPixel) {
      int initData = -sumMoveX + halfWidth;
      int checkAfterData = initData;
      int dValue = initData % pixel;
      if (dValue != 0) {
        if (dValue > (pixel / 2)) {
          checkAfterData = initData + (pixel - dValue);
        } else {
          checkAfterData = initData - dValue;
        }
      }
      sumMoveX = -(checkAfterData - halfWidth);
      postInvalidate();
    }
  }

  private void postSelectItem(final int selectItem, final Runnable runnable) {
    post(new Runnable() {
      @Override public void run() {
        sumMoveX = halfWidth - ((selectItem - begin) / step * pixel);
        if (onRulerValueSelectedListener != null) {
          onRulerValueSelectedListener.value(selectItem);
        }
        if (runnable != null) {
          runnable.run();
        }
        postInvalidate();
      }
    });
  }

  //-----------------------------方法-------------------------------

  /**
   * 设置左边渐变色
   */
  public void setGradientColor(int startColor, int endColor) {
    linearGradient = new LinearGradient(startColor, endColor);
    invalidate();
  }

  /**
   * 设置选中项
   */
  public void setSelectItem(int selectItem, Runnable runnable) {
    if (selectItem < begin) {
      selectItem = begin;
    }
    if (selectItem > end) {
      selectItem = end;
    }
    postSelectItem(selectItem, runnable);
  }

  public void setSelectItem(int selectItem) {
    setSelectItem(selectItem, null);
  }

  public int getSelectItem() {
    return (-sumMoveX + halfWidth) / pixel * step + begin;
  }

  /**
   * 设置范围
   */
  public void setRange(int begin, int end) {
    this.begin = begin;
    this.end = end;
    sumPixel = ((end - begin) / step) * pixel;
    invalidate();
  }

  private RulerValue onRulerValueChangeListener;
  private RulerValue onRulerValueSelectedListener;

  /**
   * 实时监听ruler值的变话
   *
   * @param onRulerValueChangeListener 监听器
   */
  public void setOnRulerValueChangeListener(RulerValue onRulerValueChangeListener) {
    this.onRulerValueChangeListener = onRulerValueChangeListener;
  }

  /**
   * 动画停止时的刻度监听
   */
  public void setOnRulerValueSelectedListener(RulerValue onRulerValueChangeListener) {
    this.onRulerValueSelectedListener = onRulerValueChangeListener;
  }

  public interface RulerValue {
    void value(int value);
  }
}

到这里还不够,还需要多加上一个工具类

package com.example.zhangxianpei.myapp;

import android.graphics.Color;

public class LinearGradient {
  private int mStartColor;
  private int mEndColor;

  public LinearGradient(int startColor, int endColor) {
    this.mStartColor = startColor;
    this.mEndColor = endColor;
  }

  public void setStartColor(int startColor) {
    this.mStartColor = startColor;
  }

  public void setEndColor(int endColor) {
    this.mEndColor = endColor;
  }

  //获取某一个百分比间的颜色,radio取值[0,1]
  public int getColor(float radio) {
    int redStart = Color.red(mStartColor);
    int blueStart = Color.blue(mStartColor);
    int greenStart = Color.green(mStartColor);
    int redEnd = Color.red(mEndColor);
    int blueEnd = Color.blue(mEndColor);
    int greenEnd = Color.green(mEndColor);

    int red = (int) (redStart + ((redEnd - redStart) * radio + 0.5));
    int greed = (int) (greenStart + ((greenEnd - greenStart) * radio + 0.5));
    int blue = (int) (blueStart + ((blueEnd - blueStart) * radio + 0.5));
    return Color.argb(255, red, greed, blue);
  }
}
好了,这就是全部的了,已经可以完整的运行了。第一次写博客,希望可以帮到大家,有问题也请指教

猜你喜欢

转载自blog.csdn.net/Painend/article/details/80256916
今日推荐