一、介绍
一个横向进度条
下载完成区域有一个滑块不断从左往右滑动(最开始就是被这个吸引的,就想着这个怎么实现的)
进度条中间显示当前进度,值得注意的是,进度条文本包含在下载区域中的部分显示为白色
点击暂停,进度条颜色改变,进度文本改变
二、分析
根据以上简单介绍,可以抓住要实现的重难点是上面的第2、3点。
1. 进度条文本包含在下载区域中的部分显示为白色怎么实现?
这个和歌词变色的效果是一样的,所以实现原理应该差不多。canvas有一个save的方法,然后设置成CLIP_SAVE_FLAG标志,这个标志的解释是restore the current clip when restore() is called.然后结合canvas的clip方法和restore方法就能实现。后文见代码④。
2. 下载完成区域有一个滑块不断从左往右滑动怎么实现?
首先想到的是画这样一个滑块(其实是一张图片),然后不断根据当前进度修改位置实现移动。需要注意的是这个滑块的移动特点: 滑块的右边界开始进入,最后左边界消失,而且只在下载完成这个区域内有显示(右边界超出下载完成右边界部分不显示)这让我想到两个图层重叠时的显示模式,再看看这幅图,那么这里就可以使用SRC_ATOP模式。
三、实现
1.自定义属性
<declare-styleable name="FlikerProgressBar"> <attr name="textSize" format="dimension|reference"/> <attr name="loadingColor" format="color|reference"/> <attr name="stopColor" format="color|reference"/> </declare-styleable>
private void initAttrs(AttributeSet attrs) { if (attrs != null) { TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.FlikerProgressBar); textSize = (int) ta.getDimension(R.styleable.FlikerProgressBar_textSize, dp2px(12)); loadingColor = ta.getColor(R.styleable.FlikerProgressBar_loadingColor, Color.parseColor("#40c4ff")); stopColor = ta.getColor(R.styleable.FlikerProgressBar_stopColor, Color.parseColor("#ff9800")); ta.recycle(); } }
2.重写onMeasure方法,当height设置为wrap_content时设置为默认高度
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); int height = 0; switch (heightSpecMode){ case MeasureSpec.AT_MOST: height = (int) dp2px(DEFAULT_HEIGHT_DP); break; case MeasureSpec.EXACTLY: case MeasureSpec.UNSPECIFIED: height = heightSpecSize; break; } setMeasuredDimension(widthSpecSize, height); }
3.重写onDraw方法
@Overrideprotected void onDraw(Canvas canvas) { super.onDraw(canvas); //1.边框 drawBorder(canvas); //2.进度 drawProgress(); canvas.drawBitmap(pgBitmap, 0, 0, null); //3.进度text drawProgressText(canvas); //4.变色处理 drawColorProgressText(canvas); }
①绘制边框
private void drawBorder(Canvas canvas) { bgPaint.setStyle(Paint.Style.STROKE); bgPaint.setColor(progressColor); bgPaint.setStrokeWidth(dp2px(1)); canvas.drawRect(0, 0, getWidth(), getHeight(), bgPaint); }
②绘制进度
private void drawProgress() { bgPaint.setStyle(Paint.Style.FILL); bgPaint.setStrokeWidth(0); bgPaint.setColor(progressColor); float right = (progress / MAX_PROGRESS) * getMeasuredWidth(); pgBitmap = Bitmap.createBitmap((int) Math.max(right, 1), getMeasuredHeight(), Bitmap.Config.ARGB_8888); pgCanvas = new Canvas(pgBitmap); pgCanvas.drawColor(progressColor); if(!isStop){ bgPaint.setXfermode(xfermode); pgCanvas.drawBitmap(flikerBitmap, flickerLeft, 0, bgPaint); bgPaint.setXfermode(null); } }
③绘制进度条显示文本
private void drawProgressText(Canvas canvas) { textPaint.setColor(progressColor); progressText = getProgressText(); textPaint.getTextBounds(progressText, 0, progressText.length(), textBouds); int tWidth = textBouds.width(); int tHeight = textBouds.height(); float xCoordinate = (getMeasuredWidth() - tWidth) / 2; float yCoordinate = (getMeasuredHeight() + tHeight) / 2; canvas.drawText(progressText, xCoordinate, yCoordinate, textPaint); }
④进度条文本变色处理
private void drawColorProgressText(Canvas canvas) { textPaint.setColor(Color.WHITE); int tWidth = textBouds.width(); int tHeight = textBouds.height(); float xCoordinate = (getMeasuredWidth() - tWidth) / 2; float yCoordinate = (getMeasuredHeight() + tHeight) / 2; float progressWidth = (progress / MAX_PROGRESS) * getMeasuredWidth(); if(progressWidth > xCoordinate){ canvas.save(Canvas.CLIP_SAVE_FLAG); float right = Math.min(progressWidth, xCoordinate + tWidth); canvas.clipRect(xCoordinate, 0, right, getMeasuredHeight()); canvas.drawText(progressText, xCoordinate, yCoordinate, textPaint); canvas.restore(); } }