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

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

      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的位置,这在校准字体位置的时候非常实用!









  • 相关阅读:
    HTTP学习笔记(1)ULR语法
    wsdl地址如何在远程服务器上查看源码?
    java线程详解(三)
    java线程详解(二)
    java线程详解(一)
    java中this用法总结
    Linux运行python程序
    如何获取到Java对象的地址
    IDEA 远程调试
    linux环境中mysql默认端口3306无法连接问题排查
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/7298621.html
Copyright © 2020-2023  润新知