先看效果
网上开源的进度条很多,效果好,优化好,可是为了学习,还是自己写一个试试。
实习太忙,匆匆忙忙贴上代码,只为抛砖引玉。
在这里把代码分享出来,特别简单,入门自定义View的可以看看,如果有什么不好的请大家指正。
/**
* auther : LiuJiakuo
* e-mail :
* erp :
* date : 2018/8/29 18:49
* desc :
*/
public class SeekBar extends View {
private Context context;
private Paint nonePaint;//播放进度
private Paint progressPaint;//播放进度
private Paint bufferPaint;//缓冲进度
private Path path;
private Paint bubblepaint;//气泡
private int bufferColor;//缓冲颜色
private int progressColor;//播放进度颜色
private int noneColor;//未加载颜色
private int barWidth;//进度条粗
private Bitmap loadBitmap;
private Matrix matrix;
private Rect rect;//进度条可以点击的地方
private int playProgress;
private int bufferProgress;
private float r;//气泡半径
private float h;//三角高度
private float lineR;//圆点
private int loadImgDegrees = 0;
private boolean isSeek = false;//是否在加载
private ProgressListener listener;
public SeekBar(Context context) {
super(context);
init(context);
}
public SeekBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SeekBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public void setListener(ProgressListener listener) {
this.listener = listener;
}
/**
* 设置播放进度
*
* @param playProgress 0-100
*/
public void setPlayProgress(int playProgress) {
if (playProgress < 0 || playProgress > 100 || this.playProgress == playProgress || isSeek)
return;
this.playProgress = playProgress;
invalidate();
}
/**
* 设置缓存进度
*
* @param bufferProgress 0-100
*/
public void setBufferProgress(int bufferProgress) {
if (bufferProgress < 0 || bufferProgress > 100 || this.bufferProgress == bufferProgress || isSeek)
return;
this.bufferProgress = bufferProgress;
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = widthSize;
int height = heightSize;
if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
width = getWidth();
height = (int) (lineR * 2 + r + h + 4 + 1);
} else if (widthMode == MeasureSpec.AT_MOST) {
width = getWidth();
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
width = widthSize;
height = (int) (lineR * 2 + r + h + 4 + 1);
}
setMeasuredDimension(width, height);
}
private void init(Context context) {
this.context = context;
defaultSize();
path = new Path();
bufferPaint = new Paint();
progressPaint = new Paint();
nonePaint = new Paint();
bubblepaint = new Paint();
bufferPaint.setStyle(Paint.Style.FILL);
bufferPaint.setStrokeCap(Paint.Cap.ROUND);
progressPaint.setStyle(Paint.Style.FILL);
progressPaint.setStrokeCap(Paint.Cap.ROUND);
nonePaint.setStyle(Paint.Style.FILL);
nonePaint.setStrokeCap(Paint.Cap.ROUND);
bubblepaint.setStyle(Paint.Style.FILL);
bufferPaint.setAntiAlias(true);
progressPaint.setAntiAlias(true);
nonePaint.setAntiAlias(true);
bubblepaint.setAntiAlias(true);
bufferPaint.setStrokeWidth(barWidth);
progressPaint.setStrokeWidth(barWidth);
nonePaint.setStrokeWidth(barWidth);
bufferPaint.setColor(bufferColor);
progressPaint.setColor(progressColor);
nonePaint.setColor(noneColor);
bubblepaint.setColor(Color.parseColor("#3A7EEC"));
}
public void setBufferColor(@ColorInt int bufferColor) {
this.bufferColor = bufferColor;
}
public void setProgressColor(@ColorInt int progressColor) {
this.progressColor = progressColor;
}
public void setNoneColor(@ColorInt int noneColor) {
this.noneColor = noneColor;
}
private void defaultSize() {
barWidth = 3;
r = 14;
h = 20;
lineR = 8;
noneColor = Color.parseColor("#999999");
bufferColor = Color.parseColor("#CCCCCC");
progressColor = Color.parseColor("#00EE00");
matrix = new Matrix();
loadBitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_load);
matrix.setRotate(loadImgDegrees, loadBitmap.getWidth() >> 1, loadBitmap.getHeight() >> 1);
post(new Runnable() {
@Override
public void run() {
rect = new Rect(0, (int) (r + h), getMeasuredWidth(), (int) (r + h + lineR * 2 + 4));
}
});
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d("TAGTAG", "onDraw: " + bufferProgress);
Log.d("TAGTAG", "onDraw: " + playProgress);
int width = getMeasuredWidth();
float startY = r + h + 4 + lineR;
canvas.drawLine(0, startY, width, startY, nonePaint);
canvas.drawLine(0, startY, width * bufferProgress / 100, startY, bufferPaint);
canvas.drawLine(0, startY, width * playProgress / 100, startY, progressPaint);
bubblepaint.setColor(Color.parseColor("#993A7EEC"));
canvas.drawCircle(width * playProgress / 100, startY, lineR, bubblepaint);
drawBubble(canvas);
}
private void drawBubble(Canvas canvas) {
bubblepaint.setColor(Color.parseColor("#CC3A7EEC"));
int width = getMeasuredWidth();
float startY = r + h;//-3比线高
float pro = width * playProgress / 100;
float sqrt = r / ((float) Math.sqrt(2));
RectF rect = new RectF((pro - r), (startY - h - r), (pro + r), (startY - h + r));
path.reset();
path.moveTo(pro, startY);
path.lineTo(pro + sqrt, sqrt + r);
path.addArc(rect, 45, -270);
path.lineTo(pro - sqrt, sqrt + r);
path.lineTo(pro, startY);
canvas.drawPath(path, bubblepaint);
bubblepaint.setTextSize(14);
bubblepaint.setColor(Color.WHITE);
Paint.FontMetricsInt fontMetricsInt = bubblepaint.getFontMetricsInt();
bubblepaint.setTextAlign(Paint.Align.CENTER);
int baseline = (int) ((rect.bottom + rect.top - fontMetricsInt.bottom - fontMetricsInt.top) / 2);//文字居中,基线算法
if (bufferProgress != 0 && bufferProgress <= playProgress && !isSeek) {
//todo 内存抖动风险
Bitmap bitmap = Bitmap.createBitmap(loadBitmap, 0, 0, loadBitmap.getWidth(), loadBitmap.getHeight(), matrix, true);
canvas.drawBitmap(bitmap, null, rect, nonePaint);
loadImgDegrees += 20;
matrix.setRotate(loadImgDegrees, loadBitmap.getWidth() >> 1, loadBitmap.getHeight() >> 1);
postInvalidateDelayed(160, (int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom);
} else {
canvas.drawText(playProgress + "%", rect.centerX(), baseline, bubblepaint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (isPointInRect(new PointF(event.getX(), event.getY()), rect)) {
int x = (int) (event.getX() * 100 / getMeasuredWidth());
seekTo(x);
if (listener != null && !isSeek) {
listener.onPause();
}
isSeek = true;
}
break;
case MotionEvent.ACTION_UP:
if (isSeek) {
isSeek = false;
if (listener != null)
listener.onResume();
}
break;
case MotionEvent.ACTION_MOVE:
if (isSeek) {
int x = (int) (event.getX() * 100 / getMeasuredWidth());
seekTo(x);
}
break;
}
return true;
}
/**
* @param x 0~100
*/
private void seekTo(int x) {
playProgress = x;
invalidate();
if (listener != null)
listener.seekTo(x);
}
public interface ProgressListener {
//0-100
void seekTo(int seek);
void onPause();
void onResume();
}
/**
* 判断这个点有没有在矩形内
*
* @param pointF
* @param targetRect
* @return
*/
private boolean isPointInRect(PointF pointF, Rect targetRect) {
if (pointF.x < targetRect.left) {
return false;
}
if (pointF.x > targetRect.right) {
return false;
}
if (pointF.y < targetRect.top) {
return false;
}
if (pointF.y > targetRect.bottom) {
return false;
}
return true;
}
}