我的第一个自定义View

直接上代码,做个学习笔记好了。

/**
 * Created by Jimbray  .
 * on 2016/7/12
 * Email: jimbray16@gmail.com
 * Description: 这是我第一个真正意义上的自定义View
 */
public class MyFirstProgressbar extends View {

    //max progress 虽然注释写了,但是还是不解释
    private int mMaxProgress = 100;

    // current progress
    private int mCurrentProgress;

    //progress bar 背景颜色
    private int mBackgroundColor;

    //progress bar 进度条颜色
    private int mProgressColor;

    //progress bar 进度条高度
    private float mProgressBarheight;

    //progress bar 背景高度
    private float mBackgroundHeight;

    //设置默认值
    //默认progress bar 背景颜色
    private final int default_background_color = Color.rgb(204, 204, 204);
    //默认progress bar 进度条颜色
    private final int default_progressbar_color = Color.rgb(66, 145, 241);
    //默认 pregress bar 背景高度
    private float default_background_height;
    //默认 porgress 进度条高度
    private float default_progressbar_height;
    //默认progress bar 背景宽度(最小宽度)
    private final float default_background_width = dp2px(45.f);

    //画笔
    private Paint mBackgroundPaint, mProgressbarPaint;

    //progress 需要draw 的矩形区域
    private RectF mBackgroundRectF = new RectF(0, 0, 0, 0);
    private RectF mProgressbarRectF = new RectF(0, 0, 0, 0);


    public MyFirstProgressbar(Context context) {
        super(context);
        init(context, null, 0);
    }

    public MyFirstProgressbar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

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

    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        default_background_height = dp2px(1.0f);
        default_progressbar_height = dp2px(1.5f);
        //progress bar 背景高度 比 progress bar 进度条高度 矮啊

        //都是用默认值,没有把这些数值抛出去做属性
        mBackgroundHeight = default_background_height;
        mProgressBarheight = default_progressbar_height;

        mBackgroundColor = default_background_color;
        mProgressColor = default_progressbar_color;

        initPainters();
    }

    /**
     * 初始化 画笔 Paint
     */
    private void initPainters() {
        mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBackgroundPaint.setColor(mBackgroundColor);

        mProgressbarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mProgressbarPaint.setColor(mProgressColor);
    }

    /**
     * 重载 函数
     * @return view 的推荐最小宽度
     * 修改为 progress bar 背景的默认高度
     */
    @Override
    protected int getSuggestedMinimumWidth() {
        return (int) default_background_width;
    }

    /**
     * 重载 函数
     * @return view 的推荐最小高度
     * 修改为 progress bar 的默认高度 (娶个最高的吧,不然高的那个头被截掉了就不好了)
     */
    @Override
    protected int getSuggestedMinimumHeight() {
        return Math.max((int) mBackgroundHeight, (int) mProgressBarheight);
    }

    /**
     * 这个函数的 解析 Google 一大把,就不解释了
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     * 现在这里是个套路活,跟着老司机走就行了
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));
    }

    /**
     * 测量 view 的 宽高信息
     * @param measureSpec
     * @param isWidth
     * @return
     */
    private int measure(int measureSpec, boolean isWidth) {
        int result;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        //不要忘记 padding ,万一有人设置了呢
        int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();
        if (mode == MeasureSpec.EXACTLY) { //如果 width 或者 height 都设置了 精确值:比如 45dp啊什么的
            result = size; //就按主人的来吧,诶,森么是主人
        } else {
            //其他情况,既然你都不知道要什么,就按我的套路来吧
            result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight();
            result += padding; //padding 这个小伙子还是很调皮的
            if (mode == MeasureSpec.AT_MOST) {
                if (isWidth) {
                    result = Math.max(result, size);
                } else {
                    result = Math.min(result, size);
                }
            }
        }
        return result;
    }

    /** 要开车了,快上车!
     * @param canvas
     */
    @Override
    protected synchronized void onDraw(Canvas canvas) {
        calRectF();

        canvas.drawRect(mBackgroundRectF, mBackgroundPaint);

        canvas.drawRect(mProgressbarRectF, mProgressbarPaint);
    }

    /**
     * 精华就在这里了。
     * 一开始我也看不懂
     * 现在我也看不懂
     * 可是效果出来了
     * 就这么滴吧
     * 多看几次
     * 说不定就懂了呢
     */
    private void calRectF() {
        mProgressbarRectF.left = getPaddingLeft();
        mProgressbarRectF.top = getHeight() / 2.0f - mProgressBarheight / 2.0f;
        mProgressbarRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() + getPaddingLeft();
        mProgressbarRectF.bottom = getHeight() / 2.0f + mProgressBarheight / 2.0f ;

        mBackgroundRectF.left = mProgressbarRectF.right;
        mBackgroundRectF.top = getHeight() / 2.0f - mBackgroundHeight / 2.0f;
        mBackgroundRectF.right = getWidth() - getPaddingRight();
        mBackgroundRectF.bottom = getHeight() / 2.0f + mBackgroundHeight / 2.0f;

    }

    /** 这个我也不想说
     * 如果三个月之后我看不懂这个代码
     * 说明
     * 我应该是失忆了
     * @return
     */
    public int getProgress() {
        return mCurrentProgress;
    }

    /**
     * 同上
     * @return
     */
    public int getMax() {
        return mMaxProgress;
    }

    /**
     * 同上
     * @param progress
     */
    public void setProgress(int progress) {
        mCurrentProgress = progress;
        invalidate();
    }

    /**
     * 让我的 progress 动起来!
     * ValueAnimator 是个神器
     * Google一下
     * @param progress
     */
    public void anitProgress(int progress) {
        ValueAnimator animator = ValueAnimator.ofInt(0, progress);
        animator.setDuration(1500);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurrentProgress = (int) animation.getAnimatedValue();
                postInvalidate();
            }
        });

        animator.start();
    }

    public float dp2px(float dp) {
        final float scale = getResources().getDisplayMetrics().density;
        return dp * scale + 0.5f;
    }

这就是我第一个实质意义上的自定义View了,以前一般都是扩展TextView啊,组合LinearLayout
之类的。
虽然这个View也是看着别人的代码写的。
不过总算是自己动手敲出来了。
敲开心啊。
照抄学习的 project 代码家NumberProgressBar
已发布到

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,051评论 25 707
  • 国内自定义View的文章汗牛充栋,但是,即便是你全部看完也未必掌握这一知识(实际上,我也看了很多,但是一旦涉及自定...
    SnowDragonYY阅读 1,621评论 3 36
  • 国内自定义View的文章汗牛充栋,但是,即使你全部看完它们也未必能掌握这一知识点(实际上,我就几乎看完了所有的国内...
    工程师milter阅读 37,661评论 143 672
  • 地处安丘西南大约一百里的石埠子镇(原庵上镇,为什么要提庵上呢?后续会有庵上镇的故事的)有座本地很有名的文化名山——...
    天涯行者阅读 2,987评论 0 3
  • 手和嘴不协调,手紧紧握住小馒头,嘴也用劲张的,手抓着小馒头已经送到嘴边,正常应该是松手嘴就吃上了,可是实际上宝宝手...
    宸宸的楠楠妈妈阅读 560评论 0 0