android学习笔记之自定义View初步(一)

一、概述

        作为android程序员自定义View被称为初级工程师向中高级工程师的一个重要标志,自定义View都不会陌生,自定义View的3步骤,1、自定义属性,2、onMeasure,3、onDraw。 作为初学者一般不是写可复用的自定义View,所以对第一步,也就是自定义属性不会太多关注,而是关注后两个方法:onMeasure和onDraw。

二、简要概述

        view树的基本概念无需多加赘述,看看都理解,我真正想学习的是自定义view,不是基本概念。在View里绘制图形和现实中绘制图形。唯一不同的就是一个是用android自带api,一个是自己的手中的画笔,相同点是在画图时都需要先思考画多大的图形,图形太大没画完,画布不够用。图形太小就感觉太小了,看不清。这也就是android为何是先执行onMeasure后执行onDraw的原因吧。画画也好自定义view也好,第一步就是要想清楚画多大的图形打算显示在哪里,然后再拿笔作画。

三、自定义View的类型:

        基本上自定义View类型分3种,第一种叫组合View,第二种叫拓展已存在的View,第三种是完全自定义View。第一种组合现有点View,作为一个主体添加各种事件。第二种就是拓展现有的控件,如可可托拽的listView等,例如显示生活中的下图:

第三种完全自定义的控件才是我们初级者最向往的高度。无中生有,无懈可击,无法无天。我打算记录第三种,因为第三种知识多,虽然第二种知识也多,但是后面我也会记录我所学习的知识。

四、onMeasure:

    基本理解自定义view的流程,最难的是将其据为己有的复杂。首先看第一步onMeasure方法。万事看官网,先看官网定义:

Measure the view and its content to determine the measured width and the measured height. This method is invoked by measure(int, int) and should be overridden by subclasses to provide accurate and efficient measurement of their contents.

CONTRACT: When overriding this method, you must call setMeasuredDimension(int, int) to store the measured width and height of this view. Failure to do so will trigger an IllegalStateException, thrown by measure(int, int). Calling the superclass' onMeasure(int, int) is a valid use.

The base class implementation of measure defaults to the background size, unless a larger size is allowed by the MeasureSpec. Subclasses should override onMeasure(int, int) to provide better measurements of their content.

If this method is overridden, it is the subclass's responsibility to make sure the measured height and width are at least the view's minimum height and width (getSuggestedMinimumHeight() and getSuggestedMinimumWidth()).翻译:

     测量视图及其内容以确定测量宽度和测量高度。 此方法由measure(int,int)调用,并应由子类覆盖以提供其内容的准确和有效的度量。 CONTRACT:覆盖此方法时,必须调用setMeasuredDimension(int,int)来存储此视图的测量宽度和高度。 如果不这样做,将触发Measure(int,int)引发的IllegalStateException。 调用超类'onMeasure(int,int)是一种有效的用法。 

      Measure的基类实现默认为背景大小,除非MeasureSpec允许更大的大小。子类应该重写onMeasure(int,int)以提供更好的内容度量。如果重写此方法,则确保测量的高度和宽度至少为视图的最小高度和宽度(getSuggestedMinimumHeight()和getSuggestedMinimumWidth())是子类的责任。

总结一下:1、子类覆写onMeasure(int,int)可以测量View的宽高,2、子类覆写onMeasure(int,int)就需要调用setMeasuredDimension(int, int) 否则抛异常,3、子类不重写onMeasure(int,int)默认是其背景大小。但是如果子类应该重写此方法这必须要有最小高度和宽度。

        说了一些废话主要是说出它的作用和一些注意事项,接下来就是需要看比较重要的参数widthMeasureSpec和heightMeasure,两者除了一个获取宽一个获取高之外,基本都一样。通过MeasureSpce的getMode(int),getSize(int)和makeMeasureSpec(int,int)。一个获取模式,一个获取尺寸,一个根据模式和尺寸创建一个MeasureSpec。getMode(int)获取三个常量AT_MOST,EXACTLY,UNSPECIFIED。一般不考虑UNSPECIFIED,接下来就是EXACTLY和AT_MOST,

EXACTLY:设置layout_width和layout_height为具体值或者match_parent,会获取该模式

AT_MOST:设置layout_width和layout_height为wrap_content,会获取该模式

在有具体宽和高的值或者宽和高都为match_parent时我们可以不需要去复写onMeasure方法。但是如果布局文件layout_width和layout_height为具体值在onMeasure方法中调用setMeasureDimension方法中再此设值,它会保留setMeasureDimension的值。setMeasureDimension的值才是最终值。这个最简单。

at_most才是最具有难度的,layout_width和layout_height为wrap_content是这个模式。onMeasure一般需要测量的就是就是这个模式但是如果是拓展现有的控件到不一定,反正学自定义view先看看完全自定义的。layout_width和layout_height为wrap_content而不覆写onMeasure的话...


没错,充满整个屏幕,也就是match_parent的模式。对于测量我开始不是很理解,我不是还没画吗?咋测量啊?测量归测量,绘图归绘图是没有啥联系的。你测量的是View,绘制的是图形。举个简单例子在自定义view中绘制一段文本,首先显示静态的,也就是说文字个数是固定的,这样不论你啥样式测量View的长度和高度也就确定了。绘制的时候在onDraw方法绘制

对于文本的测量我也做一个小结:

对于text的高度,大多会有这张图:


和如下解释:

1.基准线是baseline 
2.ascent:字体在baseline上方被推荐的距离
3.descent:字体在是baseline下方被推荐的距离
4.top:ascent的最大值 
5.bottom:descent的最大值
baseline是基线,在Android中绘制文本都是从baseline处开始的,从baseline往上至至文本最高处的距离称之为ascent(上坡度),baseline至文本最低处的距离称之为descent(下坡度)。 
top和bottom是绘制文本时在最外层留出的一些内边距。 
baseline是基线,baseline以上是负值,baseline以下是正值,因此ascent和top都是负值,descent和bottom都是正值。

由于这个正负值得关系,那么从ascent到descent的距离是ascent到baseline的距离+descent到baseline的距离

这样就简单了要么设置两个值得绝对值,要么就是改下符号,descent-ascent就是text的高度;其实获取高度不需要文本多少,就算没有设置文本在onMeasure时高度也可以计算,因为和是否存在文本没有关系,只要设置textSize()才算是有文本高度,但是要测量宽度就需要有文本了。

但是测量text高度方法不是一种,宽度也不是一种,比如:

高度测量方法:

1、int textHeight= (int) (paint.descent()-paint.ascent());

2、  Paint.FontMetrics fontMetrics = paint.getFontMetrics();
       int textHeight= (int) (fontMetrics.descent-fontMetrics.ascent;

3、Rect boundound = new Rect();  

    paint.getTextBounds(text, 0, text.length(), bound);  

      int textHeight= mBounds.height();  

宽度度测量方法:

1、int textWidth = (int)paint.measureText(str)

2、

    Rect boundound = new Rect();  

    paint.getTextBounds(text, 0, text.length(), bound);  

      int textHeight= mBounds.width();  

由于这是对view进行测量,不是在测量文本,也不是测量其他内容,反正不是view的内容。所以测量基本上是这个样子:

测量宽度:width = getPaddingLeft()+contentWidth+ getPaddingRight().

测量高度:height = getPaddingTop()+contentHeight+getPaddingBottom().

最后的到底取多大的宽,取多大的高还是要看业务或者说想要的值。View是一个矩形区域,是矩形区域。

五、onDraw:

对于onDraw必须要先提到屏幕坐标系:如图:
此图一定要记住的 左上角就是原点,坐标为(0, 0),以原点往右坐标为正,往下坐标为正值 。这东西很容易遗忘,毕竟我我们的数学坐标系都在左下角,虽然X轴符合正常思维,但是Y轴可能就不是很好理解了。特别是动画,left,right,top,bottom值的运算等,view的宽度和高度:
public final int getWidth() {
        return mRight - mLeft;
    }
public final int getHeight() {
        return mBottom - mTop;
    }
宽度是right-left这是正常思维,但是高度是bottom-top是有这个区别。对后面view的操作获取点都有影响。

总结了这个基础后onDraw的主要部分其实就是对canvas的api的掌握了,作为初学者,一些复杂的绘制特别是动画的绘制比如贝塞尔曲线的绘制。我反正是从最简单的图形绘制,然后再进阶,再到实践的路子。对于canvas的api有很多建议没有都绘制一下。学习图形从点线面开始。但是之前既然已经绘制,与canvas同级别或者说比其级别高的paint了解一下。由于paint和canvas比较重要所以打算先记录这些,留着下一个继续学习。

五、总结:

       自定义View一般分三类,组合View,对现有View进行拓展,完全自定义View。onMeasure的三种模式以及最后调用setMeasuredDimension方法传递宽高。对于text的宽高进行测量方法以及手机屏幕的坐标系的了解。虽然简单但是很基础。

   




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值