一、概述
从事了这个行业许久还是想想应该写点什么。不过第一次分享不知道该分享些什么比较好,恰好项目中用到了一个刻度尺来标明金额,所以就分享一下这个仅做纪念。
二、效果图
三、代码
话不多说了,直接上源码
这里需要先添加个样式
<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"
/>
好了,再看一眼代码
第二行 设置刻度尺的范围
第三行 设置刻度尺的渐变色
扫描二维码关注公众号,回复:
2682576 查看本文章
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);
}
}
好了,这就是全部的了,已经可以完整的运行了。第一次写博客,希望可以帮到大家,有问题也请指教