提纲
ucrop中用到的自定义控件HorizontalProgressWheelView解析
HorizontalProgressWheelView
绘制如图所有的滚轮,我们需要的参数有:滚轮中线段的宽高,最中间的小线段的色值,以及间隔,红色刻度的高以及色值。
思路:
- 利用drawline方法,根据自定义属性提供的参数,绘制小线段
- 监听touch事件,获取滚动的距离,调整drawline的参数,重新绘制
- 对外提供事件监听
在该view中黑色小线段的高度是红色的一半,首先调用init获取绘制需要的参数:
private void init() {
mMiddleLineColor = ContextCompat.getColor(getContext(), R.color.ucrop_color_progress_wheel_line);
mProgressLineWidth = getContext().getResources().getDimensionPixelSize(R.dimen.ucrop_width_horizontal_wheel_progress_line);
mProgressLineHeight = getContext().getResources().getDimensionPixelSize(R.dimen.ucrop_height_horizontal_wheel_progress_line);
mProgressLineMargin = getContext().getResources().getDimensionPixelSize(R.dimen.ucrop_margin_horizontal_wheel_progress_line);
mProgressLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mProgressLinePaint.setStyle(Paint.Style.STROKE);
mProgressLinePaint.setStrokeWidth(mProgressLineWidth);
}
复制代码
接着关注ondraw方法: 首先获取绘制区域,还是通过canvas.getclipBound方法。 之后开始绘制,绘制分为两部分 一:绘制n条黑色线段
int linesCount = mCanvasClipBounds.width() / (mProgressLineWidth + mProgressLineMargin);
float deltaX = (mTotalScrollDistance) % (float) (mProgressLineMargin + mProgressLineWidth);
mProgressLinePaint.setColor(getResources().getColor(R.color.ucrop_color_progress_wheel_line));
for (int i = 0; i < linesCount; i++) {
if (i < (linesCount / 4)) {
mProgressLinePaint.setAlpha((int) (255 * (i / (float) (linesCount / 4))));
} else if (i > (linesCount * 3 / 4)) {
mProgressLinePaint.setAlpha((int) (255 * ((linesCount - i) / (float) (linesCount / 4))));
} else {
mProgressLinePaint.setAlpha(255);
}
canvas.drawLine(
-deltaX + mCanvasClipBounds.left + i * (mProgressLineWidth + mProgressLineMargin),
mCanvasClipBounds.centerY() - mProgressLineHeight / 4.0f,
-deltaX + mCanvasClipBounds.left + i * (mProgressLineWidth + mProgressLineMargin),
mCanvasClipBounds.centerY() + mProgressLineHeight / 4.0f, mProgressLinePaint);
}
复制代码
这里的算法非常简单:
1. 根据参数计算出可以绘制多少条小线段 2. 将小线段分为4部分,中间两部分设置alpha为255,两边递减 3. 计算出小线段的位置,使用drawline绘制 这里需要关注的是deltaX的值(滚动距离除以最小单元的长度取余)每次滚会修改滚动距离,并重新绘制。
二:绘制一条红色线段
mProgressLinePaint.setColor(mMiddleLineColor);
canvas.drawLine(mCanvasClipBounds.centerX(), mCanvasClipBounds.centerY() - mProgressLineHeight / 2.0f, mCanvasClipBounds.centerX(), mCanvasClipBounds.centerY() + mProgressLineHeight / 2.0f, mProgressLinePaint);
复制代码
接着我们关注onTouchEvent方法,在该方法中,我们提供对外的事件监听回调。
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastTouchedPosition = event.getX();
break;
case MotionEvent.ACTION_UP:
if (mScrollingListener != null) {
mScrollStarted = false;
mScrollingListener.onScrollEnd();
}
break;
case MotionEvent.ACTION_MOVE:
float distance = event.getX() - mLastTouchedPosition;
if (distance != 0) {
if (!mScrollStarted) {
mScrollStarted = true;
if (mScrollingListener != null) {
mScrollingListener.onScrollStart();
}
}
onScrollEvent(event, distance);
}
break;
}
return true;
}
复制代码
需要注意这里的回调时机 * onscrollstart时机:down之后第一次move * onscroll时机:每次滚动 * onscrollEnd时机:up事件 在onScrollEvent方法中:设置滚动的距离,调用postInvalidate()重新绘制滚轮。
private void onScrollEvent(MotionEvent event, float distance) {
mTotalScrollDistance -= distance;
postInvalidate();
mLastTouchedPosition = event.getX();
if (mScrollingListener != null) {
mScrollingListener.onScroll(-distance, mTotalScrollDistance);
}
}
复制代码
该view的监听:
public interface ScrollingListener {
void onScrollStart();
void onScroll(float delta, float totalDistance);
void onScrollEnd();
}
复制代码