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 步骤 之获取自定义属性

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

  public NumberProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        default_reached_bar_height = dp2px(1.5f);
        default_unreached_bar_height = dp2px(1.0f);
        default_text_size = sp2px(10);
        default_progress_text_offset = dp2px(3.0f);

        //load styled attributes.
        final TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NumberProgressBar,
                defStyleAttr, 0);

        mReachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_reached_color, default_reached_color);
        mUnreachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_unreached_color, default_unreached_color);
        mTextColor = attributes.getColor(R.styleable.NumberProgressBar_progress_text_color, default_text_color);
        mTextSize = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_size, default_text_size);

        mReachedBarHeight = attributes.getDimension(R.styleable.NumberProgressBar_progress_reached_bar_height, default_reached_bar_height);
        mUnreachedBarHeight = attributes.getDimension(R.styleable.NumberProgressBar_progress_unreached_bar_height, default_unreached_bar_height);
        mOffset = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_offset, default_progress_text_offset);

        int textVisible = attributes.getInt(R.styleable.NumberProgressBar_progress_text_visibility, PROGRESS_TEXT_VISIBLE);
        if (textVisible != PROGRESS_TEXT_VISIBLE) {
            mIfDrawText = false;
        }

        setProgress(attributes.getInt(R.styleable.NumberProgressBar_progress_current, 0));
        setMax(attributes.getInt(R.styleable.NumberProgressBar_progress_max, 100));

        attributes.recycle();
        initializePainters();
    }

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

然后就是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

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));
    }

    private int measure(int measureSpec, boolean isWidth) {
        int result;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight();
            result += padding;
            if (mode == MeasureSpec.AT_MOST) {
                if (isWidth) {
                    result = Math.max(result, size);
                } else {
                    result = Math.min(result, size);
                }
            }
        }
        return result;
    }

onDrawer方法的可读性更是6666

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

  @Override
    protected void onDraw(Canvas canvas) {
        if (mIfDrawText) {
            calculateDrawRectF();
        } else {
            calculateDrawRectFWithoutProgressText();
        }

        if (mDrawReachedBar) {
            canvas.drawRect(mReachedRectF, mReachedBarPaint);
        }

        if (mDrawUnreachedBar) {
            canvas.drawRect(mUnreachedRectF, mUnreachedBarPaint);
        }

        if (mIfDrawText)
            canvas.drawText(mCurrentDrawText, mDrawTextStart, mDrawTextEnd, mTextPaint);
    }

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

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

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

    private void calculateDrawRectF() {

        mCurrentDrawText = String.format("%d", getProgress() * 100 / getMax());
        mCurrentDrawText = mPrefix + mCurrentDrawText + mSuffix;//转换成字符串
        mDrawTextWidth = mTextPaint.measureText(mCurrentDrawText);//测量出文字的长度

        if (getProgress() == 0) {
            mDrawReachedBar = false;
            mDrawTextStart = getPaddingLeft();//起始位置(右)
        } else {
            mDrawReachedBar = true;
            mReachedRectF.left = getPaddingLeft();
            mReachedRectF.top = getHeight() / 2.0f - mReachedBarHeight / 2.0f;
            mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() - mOffset + getPaddingLeft();
            mReachedRectF.bottom = getHeight() / 2.0f + mReachedBarHeight / 2.0f;
            mDrawTextStart = (mReachedRectF.right + mOffset);//实际中右位置
        }
        //mTextPaint.descent() + mTextPaint.ascent() baseLine到字体最高+baseLine到字体最低=实际字体高度
        mDrawTextEnd = (int) ((getHeight() / 2.0f) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2.0f));

        if ((mDrawTextStart + mDrawTextWidth) >= getWidth() - getPaddingRight()) {
            //文字终点位置重置文字起始位置和mReachedRectF矩形的right
            mDrawTextStart = getWidth() - getPaddingRight() - mDrawTextWidth;
            mReachedRectF.right = mDrawTextStart - mOffset;
        }

        float unreachedBarStart = mDrawTextStart + mDrawTextWidth + mOffset;
        if (unreachedBarStart >= getWidth() - getPaddingRight()) {
            //没有到最终点
            mDrawUnreachedBar = false;
        } else {
            mDrawUnreachedBar = true;
            mUnreachedRectF.left = unreachedBarStart;
            mUnreachedRectF.top = getHeight() / 2.0f -mUnreachedBarHeight / 2.0f;
            mUnreachedRectF.right = getWidth() - getPaddingRight();
            mUnreachedRectF.bottom = getHeight() / 2.0f + mUnreachedBarHeight / 2.0f;
        }
    }

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

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

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

NumberProgressBar开源项目学习的更多相关文章

  1. 转:从开源项目学习 C 语言基本的编码规则

    从开源项目学习 C 语言基本的编码规则 每个项目都有自己的风格指南:一组有关怎样为那个项目编码约定.一些经理选择基本的编码规则,另一些经理则更偏好非常高级的规则,对许多项目而言则没有特定的编码规则,项 ...

  2. Halo 开源项目学习(七):缓存机制

    基本介绍 我们知道,频繁操作数据库会降低服务器的系统性能,因此通常需要将频繁访问.更新的数据存入到缓存.Halo 项目也引入了缓存机制,且设置了多种实现方式,如自定义缓存.Redis.LevelDB ...

  3. android开源项目学习

    FBReaderJ FBReaderJ用于Android平台的电子书阅读器,它支持多种电子书籍格式包括:oeb.ePub和fb2.此外还支持直接读取zip.tar和gzip等压缩文档. 项目地址:ht ...

  4. php开源项目学习二次开发的计划

      开源项目: cms 国内 dedecms cmstop 国外 joomla, drupal 电商 国内 ecshop 国外 Magento 论坛 discuz 博客 wordpress   学习时 ...

  5. github开源项目学习-front-end-collect

    About 项目地址 项目预览demo(githubio加载较慢) 开源项目fork自:https://github.com/foru17/front-end-collect 此文章是对此开源项目使用 ...

  6. 如何高效地在github上找开源项目学习?

    1.高级条件组合(精确搜索) in:readme 微服务 stars:>1000 in:readme spring security stars:>3000 in:name python  ...

  7. ASP.NET MVC 开源项目学习之ProDinner (二)

    下面我们来看第二层:Data   这一层相对来说是对Core层的具体实现了. 从命名可以看出来,这和数据库相关. 1.Db.cs CodeFirst模式的本地数据库类,继承了DbContext. pr ...

  8. [Android 开源项目学习]Android的UITableView(1)

         最近由于项目加急,手里有好多看了差不多的开源项目,其中好多是大家经常用到的.图片的缓存BitmapFun(Android的文档中),AfinalMap,下拉刷新PullToRefresh等等 ...

  9. caffe_ocr开源项目学习笔记

    本机配置cuda8.0使用的cudnn是下面要说的重点,vs2015,win10,1080Ti 下载了开源项目:https://github.com/senlinuc/caffe_ocr 编译的时候报 ...

随机推荐

  1. OpenCV + Python 人脸检测

    必备知识 Haar-like opencv api 读取图片 灰度转换 画图 显示图像 获取人脸识别训练数据 探测人脸 处理人脸探测的结果 实例 图片素材 人脸检测代码 人脸检测结果 总结 下午的时候 ...

  2. Apache shiro集群实现 (三)shiro身份认证(Shiro Authentication)

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  3. Linux之dmesg命令

    功能说明:显示内核缓冲区系统控制信息的工具 ,比如系统在启动时的信息会写到/var/log/中.语 法:dmesg [-cn][-s <缓冲区大小>] 补充说明:kernel会将开机信息存 ...

  4. 带你深入理解STL之迭代器和Traits技法

    在开始讲迭代器之前,先列举几个例子,由浅入深的来理解一下为什么要设计迭代器. //对于int类的求和函数 int sum(int *a , int n) { int sum = 0 ; for (in ...

  5. UNIX环境高级编程——环境变量表读取/添加/修改/删除

    #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char* pVal ...

  6. Xcode7.2中如何添加一个Empty Application模板

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) Xcode 6.0正式版之后已经没有所谓的Empty Appl ...

  7. 解决Scrollview 嵌套recyclerview不能显示,高度不正常的问题

    我们先看一个效果,问题说的就是中间的Grid效果在Scrollview 嵌套recyclerview显示问题,在Android Api 24是好的,不过在5,1,1版本(api 22)缺出现了问题 最 ...

  8. 【Netty源码学习】入门示例

    Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序.        也就是说,Netty ...

  9. android开发之this.finish()的使用

    在一个Activity用完之后应该将之finish掉,但是,之前在学校里自己摸索着开发时并没有太注意这个问题,因为activity无论是否finish掉对功能的影响貌似都不是那么明显(这是读书时候的观 ...

  10. 饮一盏Bug留香,唱一曲项目飞扬

    沿途的风景    牵挂的项目    两情迢迢 学生档案管理项目在2月的末尾从稍带寒意的季节里完成了第一次迭代,验收的结果不尽善尽美,演示的功能也惨不忍睹,各种"关爱"的点评充斥耳旁 ...