NumberProgressBar开源项目学习

简介:

1、概述

多看多学涨姿势, github真是个宝库

这个项目主要是实现数字进度条效果

github地址在https://github.com/daimajia/NumberProgressBar

感谢开源作者!

梳理主要知识点:

【1】熟悉自定义view的流程

【2】实现原理

【3】Android中的view坐标系使用

【4】onMeasure优雅的方法书写

【5】canvas中drawText方法注意点

【6】代码的可读性非常强


2、项目要点分析

【熟悉自定义view的流程】

自定义view需要多多看别写的精彩代码,不过流程基本都是一致的在我的自定义View入门中有详细介绍按照这个思路去分析自定义view即可

【本项目实现原理】

该项目比较基础,适合作为入门学习项目,作者主要将自定义控件分为3大区域

mReachedRectF——Text区域(可以选择没有)——mUnreachedRectF

(该控件支持没有text区域),主要是通过控制mReachedRectF和mUnreachedRectF的坐标来不断地刷新ui来实现移动效果,没有使用到动画

自定义view 步骤 之获取自定义属性

这里作者直接写到其中一个构造方法中

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public NumberProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {  
  2.       super(context, attrs, defStyleAttr);  
  3.   
  4.       default_reached_bar_height = dp2px(1.5f);  
  5.       default_unreached_bar_height = dp2px(1.0f);  
  6.       default_text_size = sp2px(10);  
  7.       default_progress_text_offset = dp2px(3.0f);  
  8.   
  9.       //load styled attributes.  
  10.       final TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NumberProgressBar,  
  11.               defStyleAttr, 0);  
  12.   
  13.       mReachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_reached_color, default_reached_color);  
  14.       mUnreachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_unreached_color, default_unreached_color);  
  15.       mTextColor = attributes.getColor(R.styleable.NumberProgressBar_progress_text_color, default_text_color);  
  16.       mTextSize = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_size, default_text_size);  
  17.   
  18.       mReachedBarHeight = attributes.getDimension(R.styleable.NumberProgressBar_progress_reached_bar_height, default_reached_bar_height);  
  19.       mUnreachedBarHeight = attributes.getDimension(R.styleable.NumberProgressBar_progress_unreached_bar_height, default_unreached_bar_height);  
  20.       mOffset = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_offset, default_progress_text_offset);  
  21.   
  22.       int textVisible = attributes.getInt(R.styleable.NumberProgressBar_progress_text_visibility, PROGRESS_TEXT_VISIBLE);  
  23.       if (textVisible != PROGRESS_TEXT_VISIBLE) {  
  24.           mIfDrawText = false;  
  25.       }  
  26.   
  27.       setProgress(attributes.getInt(R.styleable.NumberProgressBar_progress_current, 0));  
  28.       setMax(attributes.getInt(R.styleable.NumberProgressBar_progress_max, 100));  
  29.   
  30.       attributes.recycle();  
  31.       initializePainters();  
  32.   }  

这里作者开始初始化自定义的属性,通常我们可以单独使用一个函数放在每个构造器下面


然后就是onMeasure,这里比较优雅,

其中EXACTLY主要针对match_parent/具体参数

ATMOST主要针对wrap_content情况这里做了处理,有时候我们如果把布局文件的宽和高写成wrap_content,若此时父布局也为AT_MOST此时显示的就是父布局的PraentSize

因此我们支持设置wrap_content时候需要重写onMeasure方法,下面也是做了处理(AT_MOST)

鸿洋大神的自定义view博文对此也做了说明http://blog.csdn.net/lmj623565791/article/details/24252901

而UNSPECIFIED往往用于系统内部的测量通常只需要关注ATMOST和EXACTLY


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  3.        setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));  
  4.    }  
  5.   
  6.   
  7.    private int measure(int measureSpec, boolean isWidth) {  
  8.        int result;  
  9.        int mode = MeasureSpec.getMode(measureSpec);  
  10.        int size = MeasureSpec.getSize(measureSpec);  
  11.        int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();  
  12.        if (mode == MeasureSpec.EXACTLY) {  
  13.            result = size;  
  14.        } else {  
  15.            result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight();  
  16.            result += padding;  
  17.            if (mode == MeasureSpec.AT_MOST) {  
  18.                if (isWidth) {  
  19.                    result = Math.max(result, size);  
  20.                } else {  
  21.                    result = Math.min(result, size);  
  22.                }  
  23.            }  
  24.        }  
  25.        return result;  
  26.    }  

onDrawer方法的可读性更是6666

有时候我们也要注意写出“可以说话的代码”,注意函数的封装

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.   protected void onDraw(Canvas canvas) {  
  3.       if (mIfDrawText) {  
  4.           calculateDrawRectF();  
  5.       } else {  
  6.           calculateDrawRectFWithoutProgressText();  
  7.       }  
  8.   
  9.       if (mDrawReachedBar) {  
  10.           canvas.drawRect(mReachedRectF, mReachedBarPaint);  
  11.       }  
  12.   
  13.       if (mDrawUnreachedBar) {  
  14.           canvas.drawRect(mUnreachedRectF, mUnreachedBarPaint);  
  15.       }  
  16.   
  17.       if (mIfDrawText)  
  18.           canvas.drawText(mCurrentDrawText, mDrawTextStart, mDrawTextEnd, mTextPaint);  
  19.   }  

这段代码我们可以清晰的看出作者的逻辑 要是设置了数字显示执行 calculateDrawRectF ()否则执行 calculateDrawRectFWithoutProgressText ()

这俩个函数就是开始处理前文提到的mReachedRectF和mUnreachedRectF俩个矩形的位置变化,这里需要熟悉一下android中的坐标系和点击位置的获取

看一下有文字时候计算俩个区域的情况主要集中处理了开始阶段和最终阶段的文字未知的特殊情况

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private void calculateDrawRectF() {  
  2.   
  3.     mCurrentDrawText = String.format("%d", getProgress() * 100 / getMax());  
  4.     mCurrentDrawText = mPrefix + mCurrentDrawText + mSuffix;//转换成字符串  
  5.     mDrawTextWidth = mTextPaint.measureText(mCurrentDrawText);//测量出文字的长度  
  6.   
  7.     if (getProgress() == 0) {  
  8.         mDrawReachedBar = false;  
  9.         mDrawTextStart = getPaddingLeft();//起始位置(右)  
  10.     } else {  
  11.         mDrawReachedBar = true;  
  12.         mReachedRectF.left = getPaddingLeft();  
  13.         mReachedRectF.top = getHeight() / 2.0f - mReachedBarHeight / 2.0f;  
  14.         mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() - mOffset + getPaddingLeft();  
  15.         mReachedRectF.bottom = getHeight() / 2.0f + mReachedBarHeight / 2.0f;  
  16.         mDrawTextStart = (mReachedRectF.right + mOffset);//实际中右位置  
  17.     }  
  18.     //mTextPaint.descent() + mTextPaint.ascent() baseLine到字体最高+baseLine到字体最低=实际字体高度  
  19.     mDrawTextEnd = (int) ((getHeight() / 2.0f) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2.0f));  
  20.   
  21.     if ((mDrawTextStart + mDrawTextWidth) >= getWidth() - getPaddingRight()) {  
  22.         //文字终点位置重置文字起始位置和mReachedRectF矩形的right  
  23.         mDrawTextStart = getWidth() - getPaddingRight() - mDrawTextWidth;  
  24.         mReachedRectF.right = mDrawTextStart - mOffset;  
  25.     }  
  26.   
  27.     float unreachedBarStart = mDrawTextStart + mDrawTextWidth + mOffset;  
  28.     if (unreachedBarStart >= getWidth() - getPaddingRight()) {  
  29.         //没有到最终点  
  30.         mDrawUnreachedBar = false;  
  31.     } else {  
  32.         mDrawUnreachedBar = true;  
  33.         mUnreachedRectF.left = unreachedBarStart;  
  34.         mUnreachedRectF.top = getHeight() / 2.0f -mUnreachedBarHeight / 2.0f;  
  35.         mUnreachedRectF.right = getWidth() - getPaddingRight();  
  36.         mUnreachedRectF.bottom = getHeight() / 2.0f + mUnreachedBarHeight / 2.0f;  
  37.     }  
  38. }  

这里主要提下mTextPaint . descent()  +  mTextPaint . ascent()的这里参照的距离都是baseline,这样可以计算出整个字体的高度,具体可以参考

http://mikewang.blog.51cto.com/3826268/871765/ 详细讲解了androd的字体属性和测量


还有canvas.drawText方法中xy坐标其实是baseline的位置,这在校准字体位置的时候很有用!


转载:http://blog.csdn.net/xsf50717/article/details/50550895

目录
相关文章
|
2月前
|
数据可视化 Linux 数据库
来了!HelloGitHub 年度热门开源项目
本期为HelloGitHub 年度盘点,为了满足不同读者的需求,作者将内容分为 Top10 和 精选 两部分
好多好用的开源项目推荐
好多好用的开源项目推荐
|
10月前
|
消息中间件 缓存 NoSQL
2018-2021我的开源项目总结
2018-2021我的开源项目总结
59 0
|
存储 缓存 运维
07使用开源项目的正确姿势,血和泪的总结|学习笔记
快速学习07使用开源项目的正确姿势,血和泪的总结
125 0
|
消息中间件 存储 设计模式
聊聊如何学习开源项目
工作几年的程序员同学,有了一定的项目经验,对于编程也有了自己的理解,但他们偶尔也会感到困惑,不知道接下来该如何提升自己。 在笔者看来:"这个阶段的程序员最需要的是提升自身编程能力和视野高度,而学习开源项目是最有效的方法之一"。
聊聊如何学习开源项目
开源项目推荐:MiNiGUI和HybridOS
开源项目推荐:MiNiGUI和HybridOS
108 0
|
NoSQL Redis 数据安全/隐私保护
开源项目
renren-security 3.1.0 发布:拿来即用的权限管理系统 基于 Redis 实现的分布式队列 KMQueue lombok
896 0
|
存储
acffo的开源项目汇总
1、 XWaveView  , 流动波浪效果                            2、LogRecordHelper, logcat日志记录存储到文件 可以指定TAG , 将logcat日志写入文件,当log日志过多需要分析的时候使用。
1118 0