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. 算法之路(三)----查找斐波纳契数列中第 N 个数

    算法题目 查找斐波纳契数列中第 N 个数. 所谓的斐波纳契数列是指: * 前2个数是 0 和 1 . * 第 i 个数是第 i-1 个数和第i-2 个数的和. 斐波纳契数列的前10个数字是: 0, 1 ...

  2. HOG OpenCV 代码片段

    直接上代码: #include <opencv2/opencv.hpp> using namespace cv; #include <cmath> using namespac ...

  3. Writing Sentences [1]

    1) try 'there will be' instead of 'then' In homogeneity MRF, if and , there will be even if . 2) in ...

  4. 合成/聚合复用原则(CARP)

    组合/聚合复用原则(Composite/Aggregate Reuse Principle或CARP),就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分,新对象通过向这些对象的委派达到 ...

  5. UNIX环境高级编程——实现uid to name

    setpwent()用来将getpwent()的读写地址指回文件开头,即从头读取密码文件中的账号数据. strcut passwd * getpwent(void); getpwent()用来从密码文 ...

  6. EBS财务模块表结构

     gl_code_combinations:科目组合 字段名 含义 备注 code_combination_id 主键,科目编码ID,自动编号 segment1 分行代码 setgment2 是受 ...

  7. 创建银行分行的API

    DECLARE p_api_version NUMBER := 1.0; p_init_msg_list VARCHAR2(1) := 'F'; v_bank_id NUMBER := 530705; ...

  8. 03_MyBatis基本查询,mapper文件的定义,测试代码的编写,resultMap配置返回值,sql片段配置,select标签标签中的内容介绍,配置使用二级缓存,使用别名的数据类型,条件查询ma

     1 PersonTestMapper.xml中的内容如下: <?xmlversion="1.0"encoding="UTF-8"?> < ...

  9. 06 intent flag三种属性

    flag属性可以看做和写在清单文件中的启动模式一样 但效果有一定差别 1,FLAG_ACTIVITY_SINGLE_TOP:启动模式里的SingleTop一致  如果X启动模式设置为FLAG_ACTI ...

  10. 从Storm和Spark 学习流式实时分布式计算的设计

    0. 背景 最近我在做流式实时分布式计算系统的架构设计,而正好又要参加CSDN博文大赛的决赛.本来想就写Spark源码分析的文章吧.但是又想毕竟是决赛,要拿出一些自己的干货出来,仅仅是源码分析貌似分量 ...