本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
前段时间用了京东金融的APP其中有一个选择金钱的效果感觉很炫就决定模仿下,下面是两张对比照。
这篇文章能够学到的知识点主要包含了:
- Canvas画布的使用
- 触摸事件的处理
- Scroller惯性滑动的处理
- 一些数学的加减运算
一些初始化画笔的操作就不再这里赘述了,下面直接讲自定义View几个方法安装顺序来。
1.onMeasure方法中主要是测量View的大小,这里我们给刻度尺在wrap_content的时候设置一个默认的高度。
int mode = MeasureSpec.getMode(heightMeasureSpec);
int size = MeasureSpec.getSize(heightMeasureSpec);
//默认宽高;
defaultValue = 150;
switch (mode) {
case MeasureSpec.AT_MOST:
//最大值模式
int value = Math.min(size, defaultValue);
return value;
case MeasureSpec.EXACTLY:
return size;
}
return size;
2.onDraw方法这是我们一个主要的方法在里面进行View的绘制,看效果图我们分为几个步骤:
- 画底部横线
- 画刻度
- 画值
画底部横线很简单就是画一条线:
canvas.drawLine(0, mStartY, getWidth(), mStartY, mLinePaint);
画刻度是10个格子为一个高一点的刻度和画当前值 代码如下:
for (int start = 0; start < mWidth; start++) {
int top = mStartY - 10;
//lastMoveX变成负的是因为向左画为负 实际意义向左画为增大
//lastMoveX+start是因为从屏幕左侧开始为0加lastMoveX为实际数值
if ((-lastMoveX + start) % (13 * 10) == 0) {
top = top - 20;
isDrawText = true;
} else {
isDrawText = false;
}
if ((-lastMoveX + start) % 13 == 0) {
if ((-lastMoveX + start) >= 0 && (-lastMoveX + start) <= 20000 * 13) {
canvas.drawLine(start, mStartY, start, top, mVerticalPaint);
}
}
if (isDrawText) {
if ((-lastMoveX + start) >= 0 && (-lastMoveX + start) <= 20000 * 13)
canvas.drawText((-lastMoveX + start) / 13 + "", start, top - 8, textPaint);
}
}
其实这段是项目中最重要的一部分,下面来解释下13代表每隔13个像素画一个刻度,20000代表刻度的最大值,其他解释之前要看下触摸事件怎么处理的。
case MotionEvent.ACTION_DOWN:
lastX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
moveX = (int) (event.getX() - lastX);
LogUtil.e("移动的moveX" + moveX);
if (lastCurrentMoveX == moveX) {
LogUtil.e("相等");
return true;
}
lastMoveX = lastMoveX + moveX;
if (moveX < 0) {
//向左滑动
isLeft = true;
if (-lastMoveX + mWidth / 2 > 20000 * 13) {
lastMoveX = lastMoveX - moveX;
return true;
}
} else {
//向右滑动
isLeft = false;
LogUtil.e("向右滑动lastMoveX" + lastMoveX);
if (lastMoveX > mWidth / 2) {
lastMoveX = lastMoveX - moveX;
return true;
}
}
lastCurrentMoveX = moveX;
lastX = (int) event.getX();
invalidate();
下面解释下:
- (-lastMoveX + start):这个代表什么意识比方说当前屏幕左侧刻度为10这是向左滑动了130个像素,看触摸事件知道lastMoveX为-130,这样-lastMoveX+stat=130+10*13=260像素所以屏幕左侧刻度应该为20这就正好符合。
其实到这里项目已经可以运行了,但是滑动一下就卡住了滑动一下就卡住了这时候体验就特别不好这时候就使用Scoller的fling函数实现View的甩动。
首先需要计算1秒内运动了多少个像素。
velocityTracker.computeCurrentVelocity(1000);
下面调用fling函数:
float xVelocity = velocityTracker.getXVelocity();
float yVelocity = velocityTracker.getYVelocity();
if (Math.abs(xVelocity) < 800) {
return true;
}
scroller.fling(130, mStartY, (int) (-Math.abs(xVelocity) + 0.5), (int) (Math.abs(yVelocity) + 0.5), 000, 10080, 0, 1920);
velocityTracker.recycle();
velocityTracker = null;
<800是在运行中发现如果不加这个判断有的时候稍微滑动一下View就跑了为了体验更好加入的,-Math.abs(xVelocity) + 0.5)是因为左右滑动的xVelocity值不一样为了方便处理改成统一的,方便在下面的方法中计算,可以打印下看下实际的值,这时候还要调用computeScroll 配合使用并在里面进行一些计算。
public void computeScroll() {
if (scroller.computeScrollOffset()) {
int currX = scroller.getCurrX();
LogUtil.e("currX" + currX);
if (isLeft) {
lastMoveX = lastMoveX - currX;
} else {
lastMoveX = lastMoveX + currX;
}
//向右滑动
if (lastMoveX > mWidth / 2) {
lastMoveX = lastMoveX - currX;
return;
}
//向左
if (-lastMoveX + mWidth / 2 > 20000 * 13) {
lastMoveX = lastMoveX - moveX;
return ;
}
invalidate();
}
}
到此整个项目就结束了,总结下主要是一些计算和Scoller的使用在看的时候不懂得地方可以打印日志看下比较下应该就明白了。
最后附上一些函数的解释.
“`
/**
* startY 滚动起始点Y坐标
velocityX 当滑动屏幕时X方向初速度,以每秒像素数计算
velocityY 当滑动屏幕时Y方向初速度,以每秒像素数计算
minX X方向的最小值,scroller不会滚过此点。
maxX X方向的最大值,scroller不会滚过此点。
minY Y方向的最小值,scroller不会滚过此点。
maxY Y方向的最大值,scroller不会滚过此点。
*/
scroller.fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY)
/**
* 计算那些已经发生触摸事件点的当前速率。这个函数只有在你需要得到速率消息的情况下才调用,因为使用它需要消耗很大的性能。
* units: 你使用的速率单位.1的意思是,以一毫秒运动了多少个像素的速率, 1000表示 一秒时间内运动了多少个像素。
*/
velocityTracker.computeCurrentVelocity(1000);