如何自定义View

1. 首先

在values目录下建立attrs.xml文件,添加属性内容

·在布局文件中添加新的命名空间xmlns,然后可以使用命名空间给自定义的空间设置属性

 attrs.xml

<resources> 

    <declare-styleable name="MyView"

    <attr name="textColor" format="color"/> 

    <attr name="textSize" format="dimension"/> 

    declare-styleable> 

resources>

 

然后在布局文件中完成:

xmlns:my=http://schemas.android.com/apk/res/com.example.xhelloworld

 

<com.example.xhelloworld.MyView 

       android:layout_width="fill_parent" 

       android:layout_height="wrap_content"   

       my:textColor="#FFFFFFFF"   

       my:textSize="22dp" 

    /> 

 

最后在MyView.java中添加另一个构造方法,并添加代码来处理从xml中获得的属性

public MyView(Context context,AttributeSet attrs){
       super(context, attrs);
         mPaint = new Paint();  
        //TypedArray是一个用来存放由context.obtainStyledAttributes获得的属性的数组  
        //在使用完成后,一定要调用recycle方法  
        //属性的名称是styleable中的名称+“_”+属性名称  
        //TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView);
        int textColor = array.getColor(R.styleable.MyView_textColor, 0XFF00FF00); //提供默认值,放置未指定  
        float textSize = array.getDimension(R.styleable.MyView_textSize, 36);  
        mPaint.setColor(textColor);  
        mPaint.setTextSize(textSize);  
        array.recycle(); //一定要调用,否则这次的设定会对下次的使用造成影响  
      
    }

AttributeSet 里面存储了布局文件里面实际属性值。

TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView);

R.styleable.MyView是attrs.xml里面的名字

 

 

然后呢。重载onMeasure(),onLayout(),onDraw()三个函数构建了自定义View的外观形象。

再加上onTouchEvent()等重载视图的行为,可以构建任何我们需要的可感知到的自定义View。

 


我们知道,不管是自定义View还是系统提供的TextView这些,它们都必须放置在LinearLayout等一些ViewGroup中,因此理论上我们可以很好的理解onMeasure(),onLayout(),onDraw()这三个函数:1.View本身大小多少,这由onMeasure()决定;2.View在ViewGroup中的位置如何,这由onLayout()决定;3.绘制View,onDraw()定义了如何绘制这个View。

 

 

看一下onMeasure()

   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
}

 

widthMeasureSpec这个Int分为两部分,一部分表示了mode,一部分表示size
heightMeasureSpec也一样。
所有的View的onMeasure()的最后一行都会调用setMeasureDimension()函数的作用——这个函数调用中传进去的值是View最终的视图大小。也就是说onMeasure()中之前所作的所有工作都是为了最后这一句话服务的。
protected final void setMeasuredDimension (int measuredWidth, int measuredHeight) 
Added in API level 1

This method must be called by onMeasure(int, int) to store the measured width and measured height. Failing to do so will trigger an exception at measurement time.

 

我们知道在ViewGroup中,给View分配的空间大小并不是确定的,有可能随着具体的变化而变化,而这个变化的条件就是传到specMode中决定的,specMode一共有三种可能:

MeasureSpec.EXACTLY:父视图希望子视图的大小应该是specSize中指定的。

MeasureSpec.AT_MOST:子视图的大小最多是specSize中指定的值,也就是说不建议子视图的大小超过specSize中给定的值。

MeasureSpec.UNSPECIFIED:我们可以随意指定视图的大小。

由TextView中源码也可以知道这个值的设计意义是为了根据ViewGroup中具体能够提供的空间大小来指定子View的视图大小。

\

 

protected void onLayout (boolean changed, int left, int top, int right, int bottom) 
如果需要指定子元素布局,就需要这个方法。
Called from layout when this view should assign a size and position to each of its children. Derived classes with children should override this method and call layout on each of their children.

Parameters
changed  This is a new size or position for this view 
left  Left position, relative to parent 
top  Top position, relative to parent 
right  Right position, relative to parent 
bottom  Bottom position, relative to parent  

如果有孩

 

对于onDraw本身绘制

dispatchDraw是孩子绘制

 

posted @ 2015-08-17 00:13  stonehat  阅读(184)  评论(0编辑  收藏  举报