最近在做一个自定义进度条,为了能实现任意多边形的进度条,以及是否包含中心点位置。等各种骚操作。我使用了不少的 Path.Op
的操作。只是显示的时候并没有什么毛病。但是调用进度更新的时候,界面会闪屏。
看一下效果先:
调用更新的代码如下:
final RoundProgressBar bar = findViewById(R.id.progress_bar);
bar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.i("click");
bar.setClickable(false);
ValueAnimator animator = ValueAnimator.ofInt(0, 100);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (Integer) animation.getAnimatedValue();
bar.setProgress((int) value);
LogUtils.e("value----" + value + " 0000000 ");
if (bar.getProgress() >= 100) {
animation.cancel();
bar.setClickable(true);
}
}
});
animator.setDuration(2000);
animator.start();
}
});
更新的代码看起来没什么毛病。但是界面就是像上面一下一直闪。
我怀疑是 onDraw
里面的逻辑太多导致的,然后尝试使用了双缓冲,就是先绘制到一个bitmap
上面,然后使用 canvas.drawBitmap
来绘制这个bitmap
到界面上。
不过并没有什么用,依然闪烁。
最终,我找到了黄猫。然后黄猫看了半天没发现什么毛病,最后告诉我,我的 path
在绘制之前没有 reset
,导致后续绘制的时候,path
会一直叠加。我感觉很有道理。然后绘制之前先重置一下,果然没毛病了。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
resetAllPathsBeforeDraw();
canvas.drawRect(0, 0, mWidth, mHeight, textPaint); // for debug
int centerX = (getPaddingLeft() + getWidth() - getPaddingRight()) / 2;
int centerY = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2;
int radius = Math.min(
getWidth() - getPaddingLeft() - getPaddingRight(),
getHeight() - getPaddingTop() - getPaddingBottom()
) / 2;
canvas.rotate(mStartAngle, centerX, centerY);
drawShape(canvas, centerX, centerY, radius);
drawProgress(canvas, centerX, centerY, radius);
drawSecondaryProgress(canvas, centerX, centerY, radius);
}
/**
* 闪烁的根源找到了, path 过度叠加导致。每次先 reset 即可!
*/
private void resetAllPathsBeforeDraw() {
if (outerShapePath != null) {
outerShapePath.reset();
}
if (innerShapePath != null) {
innerShapePath.reset();
}
if (progressPath != null) {
progressPath.reset();
}
if (secondaryProgressPath != null) {
secondaryProgressPath.reset();
}
}
看一下最终效果:
好了,各位老铁,今天关于闪烁的问题就说到这里了。
从这里也能看出,view
的闪烁往往都是操作太重导致的。比如这里,path
会一直叠加在更新到时候,导致进度条越更新,path
里面包含的内容越多。最终导致闪烁了。
完整代码其实比较简单,不过喜欢的老铁可以 双击666 查看。
然后,为了让这个进度条更6 ,我决定增加一个点击上面就让进度 +5%
,点击下面就让进度-5%
,向上滑动就慢慢加进度,向下滑动就慢慢减进度的骚操作。
核心逻辑:
case MotionEvent.ACTION_MOVE: {
float y = event.getY();
float x = event.getX();
float diffX = x - eventX;
float diffY = y - eventY;
// LogUtils.d("dx=%.1f,dy=%.1f,ts=%d", diffX, diffY, touchSlop);
if (/*Math.abs(diffX) < touchSlop && */Math.abs(diffY) >= touchSlop) {
// 默认滑太多,搞个 0.25 缓冲一下
float factor = (-1f) * diffY / mHeight * 0.25f; // 预期从中间滑到顶部,为 +50%
// 系统坐标决定下滑时 diffY 为正值,所以前面加 (-1f)
mProgress += factor * mMax;
// LogUtils.i("factor=%s,progress=%s", factor, mProgress);
mProgress = mProgress > mMax ? mMax : mProgress;
mProgress = mProgress < 0 ? 0 : mProgress;
setProgress(mProgress);
}
}
break;
case MotionEvent.ACTION_UP: {
// LogUtils.e("#ACTION_UP#ACTION_UPACTION_UP#ACTION_UP#ACTION_UP");
float y = event.getY();
float x = event.getX();
float diffX = x - eventX;
float diffY = y - eventY;
long elapsedRealtime = SystemClock.elapsedRealtime() - eventTime;
boolean noMove = Math.abs(diffX) < touchSlop && Math.abs(diffY) < touchSlop;
if (noMove && elapsedRealtime > longPressTimeout) {
// 超过此时间就认为是长按,认为是一次长按
performLongClick();
// 长按不进行 进度的累加
} else if (noMove && elapsedRealtime < tapTimeout) {
// 小于此时间,认为是一次点击
if (y < mHeight / 2) {
// 点击上边,让进度 + 5 %
mProgress += 0.05 * mMax;
} else {
// 点击下边,让进度 - 5 %
mProgress -= 0.05 * mMax;
}
mProgress = mProgress > mMax ? mMax : mProgress;
mProgress = mProgress < 0 ? 0 : mProgress;
setProgress(mProgress);
performClick();
}
}
break;
效果如下:
吐槽一下,录屏之后看不到手指触摸的效果了。明明已经打开了触摸效果显示。
然后,其实这个进度条可以设置任意多边形的,以及是否填满。比如三角形:
感觉还行的老铁,记得 双击666 。