Android自定义控件之带下载进度的下载按钮DownloadProgressButton
2017-02-23 14:14
726 查看
前言
最近要用到一个带下载进度的按钮,各种搜索了一波,很抱歉(/TДT)/ ,实在没有发现自己想要的效果,没办法只能自己尝试实现了一个了。效果展示
支持圆角,支持是否显示边框
中间文字会根据下载进度有个变色的效果
下载完成后,“安装中“有一个 loading 的动画效果
继承自TextView,自带设置字体的功能
难点分析
1 . 怎样实现中间文字会根据下载进度有个变色的效果?可以通过 LinearGradient 实现字体渐变的效果,参考了LinearGradient与闪动文字效果,具体在代码中解释。
2 . 按钮的背景根据进度不断铺满?
以下图为例,根据当前进度画出一个和整个按钮相同大小的蓝色矩形( dst ),再画一个同样大小的白色矩形,只显示白色矩形中两个重叠的部分不就好了吗?对,我们联想到了两个图层重叠时的显示模式,就是这里的 SRC_ATOP 模式。
3 . “安装中“有一个 loading 的动画效果?
网上已经有很多成熟的 loading 动画效果,理解一下它们的源码,自己实现一下就可以了,我这里参考了AVLoadingIndicatorView 中的两个不错的动画效果,当然你也可以修改成你自己喜欢的效果。
实现
来到了贴代码时间,O(∩_∩)O,其实就是按照自定义控件的基本流程1 . 定义自定义属性
Property | Format | Default |
---|---|---|
progress_btn_radius | dimension | 0dp |
progress_btn_background_color | color | 3385FF |
progress_btn_background_second_color | color | E8E8E8 |
progress_btn_text_color | color | progress_btn_background_color |
progress_btn_text_cover_color | color | Color.WHITE |
progress_btn_border_width | dimension | 2dp |
progress_btn_ball_style | enum | STYLE_BALL_JUMP |
STYLE_BALL_JUMP
这张图可能更加直观吧:
2 . 绘制按钮背景(根据不同状态绘制不同的效果)
private void drawBackground(Canvas canvas) { mBackgroundBounds = new RectF(); //根据Border宽度得到Button的显示区域 mBackgroundBounds.left = showBorder ? mBorderWidth : 0; mBackgroundBounds.top = showBorder ? mBorderWidth : 0; mBackgroundBounds.right = getMeasuredWidth() - (showBorder ? mBorderWidth : 0); mBackgroundBounds.bottom = getMeasuredHeight() - (showBorder ? mBorderWidth : 0); if (showBorder) { mBackgroundPaint.setStyle(Paint.Style.STROKE); mBackgroundPaint.setColor(mBackgroundColor); mBackgroundPaint.setStrokeWidth(mBorderWidth); canvas.drawRoundRect(mBackgroundBounds, mButtonRadius, mButtonRadius, mBackgroundPaint); } mBackgroundPaint.setStyle(Paint.Style.FILL); //color switch (mState) { case STATE_NORMAL: mBackgroundPaint.setColor(mBackgroundColor); canvas.drawRoundRect(mBackgroundBounds, mButtonRadius, mButtonRadius, mBackgroundPaint); break; case STATE_PAUSE: case STATE_DOWNLOADING: //计算当前的进度 mProgressPercent = mProgress / (mMaxProgress + 0f); mBackgroundPaint.setColor(mBackgroundSecondColor); canvas.save(); //画出dst图层 canvas.drawRoundRect(mBackgroundBounds, mButtonRadius, mButtonRadius, mBackgroundPaint); //设置图层显示模式为 SRC_ATOP PorterDuffXfermode porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP); mBackgroundPaint.setColor(mBackgroundColor); mBackgroundPaint.setXfermode(porterDuffXfermode); //计算 src 矩形的右边界 float right = mBackgroundBounds.right * mProgressPercent; //在dst画出src矩形 canvas.drawRect(mBackgroundBounds.left, mBackgroundBounds.top, right, mBackgroundBounds.bottom, mBackgroundPaint); canvas.restore(); mBackgroundPaint.setXfermode(null); break; case STATE_FINISH: mBackgroundPaint.setColor(mBackgroundColor); canvas.drawRoundRect(mBackgroundBounds, mButtonRadius, mButtonRadius, mBackgroundPaint); break; } }
3 . 绘制文字
private void drawTextAbove(Canvas canvas) { //计算Baseline绘制的Y坐标 final float y = canvas.getHeight() / 2 - (mTextPaint.descent() / 2 + mTextPaint.ascent() / 2); if (mCurrentText == null) { mCurrentText = ""; } final float textWidth = mTextPaint.measureText(mCurrentText.toString()); mTextBottomBorder = y; mTextRightBorder = (getMeasuredWidth() + textWidth) / 2; //color switch (mState) { case STATE_NORMAL: mTextPaint.setShader(null); mTextPaint.setColor(mTextCoverColor); canvas.drawText(mCurrentText.toString(), (getMeasuredWidth() - textWidth) / 2, y, mTextPaint); break; case STATE_PAUSE: case STATE_DOWNLOADING: //进度条压过距离 float coverLength = getMeasuredWidth() * mProgressPercent; //开始渐变指示器 float indicator1 = getMeasuredWidth() / 2 - textWidth / 2; //结束渐变指示器 float indicator2 = getMeasuredWidth() / 2 + textWidth / 2; //文字变色部分的距离 float coverTextLength = textWidth / 2 - getMeasuredWidth() / 2 + coverLength; float textProgress = coverTextLength / textWidth; if (coverLength <= indicator1) { mTextPaint.setShader(null); mTextPaint.setColor(mTextColor); } else if (indicator1 < coverLength && coverLength <= indicator2) { //设置变色效果 mProgressTextGradient = new LinearGradient((getMeasuredWidth() - textWidth) / 2, 0, (getMeasuredWidth() + textWidth) / 2, 0, new int[]{mTextCoverColor, mTextColor}, new float[]{textProgress, textProgress + 0.001f}, Shader.TileMode.CLAMP); mTextPaint.setColor(mTextColor); mTextPaint.setShader(mProgressTextGradient); } else { mTextPaint.setShader(null); mTextPaint.setColor(mTextCoverColor); } canvas.drawText(mCurrentText.toString(), (getMeasuredWidth() - textWidth) / 2, y, mTextPaint); break; case STATE_FINISH: mTextPaint.setColor(mTextCoverColor); canvas.drawText(mCurrentText.toString(), (getMeasuredWidth() - textWidth) / 2, y, mTextPaint); drawLoadingBall(canvas); break; } }
这里使用了 LinearGradient 的第二个构造函数:
public LinearGradient(float x0, float y0, float x1, float y1,int colors[], float positions[], TileMode tile)
(x0,y0) 就是起始渐变点坐标,参数中 (x1,y1) 就是结束渐变点坐标
colors[] 用于指定渐变的颜色值数组,同样,颜色值必须使用 0xAARRGGBB 形式的16进制表示!表示透明度的AA一定不能少。
positions[]与渐变的颜色相对应,取值是0-1的float类型,表示在每一个颜色在整条渐变线中的百分比位置,之间的差值就是渐变区间。
4 . 两种动画效果
STYLE_BALL_PULSE
public ArrayList<ValueAnimator> createBallPulseAnimators() { ArrayList<ValueAnimator> animators = new ArrayList<>(); int[] delays = new int[]{120, 240, 360}; for (int i = 0; i < 3; i++) { final int index = i; ValueAnimator scaleAnim = ValueAnimator.ofFloat(1, 0.3f, 1); scaleAnim.setDuration(750); scaleAnim.setRepeatCount(-1); scaleAnim.setStartDelay(delays[i]); scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { scaleFloats[index] = (float) animation.getAnimatedValue(); postInvalidate(); } }); animators.add(scaleAnim); } return animators; }
STYLE_BALL_JUMP
public ArrayList<ValueAnimator> createBallJumpAnimators() { ArrayList<ValueAnimator> animators = new ArrayList<>(); int[] delays = new int[]{70, 140, 210}; for (int i = 0; i < 3; i++) { final int index = i; ValueAnimator scaleAnim = ValueAnimator.ofFloat(mTextBottomBorder, mTextBottomBorder - mBallRadius * 2, mTextBottomBorder); scaleAnim.setDuration(600); scaleAnim.setRepeatCount(-1); scaleAnim.setStartDelay(delays[i]); scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { translateYFloats[index] = (float) animation.getAnimatedValue(); postInvalidate(); } }); animators.add(scaleAnim); } return animators; }
总结
有什么不明白的可以下载完整源码学习一下,ps:自己也是站在巨人的肩膀上,才能够完整实现这个自定义控件,很感谢那些乐于分享和开源的大神,也希望可以帮助有需要的同学!如果您觉得对你有所帮助,轻轻点一下star,来鼓励一下我,谢谢!o(∩_∩)o
源码下载
参考
LinearGradient与闪动文字效果Paint之setXfermode(一)
安卓自定义View教程目录
Android 自定义view之FontMetric
相关文章推荐
- Android自定义控件之带下载进度的下载按钮DownloadProgressButton
- Android DownloadManager下载进度查询(系列3)
- Android DownloadManager下载进度查询(系列3)
- Android DownloadManager下载文件,实时显示下载进度
- Android DownloadManager 文件下载 带进度按钮 和通知栏同步
- Android 自定义控件一 带圆形进度的按钮 ControlButton2
- android自定义Dialog实现文件下载和下载进度
- Android关于后台下载前台通知更新进度条的问题
- UrlDownloadFile, 线程下载文件, 带进度条
- 使用Android内部的DownloadProvider下载文件,并获取cache权限
- Android自定义控件实现环形播放进度条
- android 下载 进度条
- Android开发之ListView详解(带按钮布局)【免费提供源码下载】
- Android文件下载进度条的实现
- android文件下载!download!
- android中使用AsyncTask做下载进度条
- Android开发 下载显示进度
- 使用Android内部的DownloadProvider下载文件,并获取cache权限
- 使用URLDownloadToFile下载文件,有进度
- Android 中下载文件到sdcard和进度条小结