自定义View讲解

一、View的绘制基本由measure()、layout()、draw()这个三个函数完成
1.onMeasure()

                     测量View的宽高 
                     如:onMeasure(),setMeasuredDimension(),onMeasure();

mesarue()过程 :

                 主要作用:为整个View树计算实际的大小,即设置实际的高(对应属性:mMeasuredHeigth)和宽(对应属性mMeasureWidth),每个View的空间的实际宽高都是由父试图和本身试视图决定的。
                 具体调用:
                  ViewRoot跟对象的属性mView(其类型一般位ViewGroup类型)调用measure()方法去计算View大小,回调View/ViewGroup对象的onMeasure()方法,该方法实现的功能如下: 
                  1.设置笨View视图的最终大小,该功能的实现通过调用setMeasuredDimension()方法去设置实际的高(对应属性:mMeasuredHeight)和宽(对应属性:mMeasureWidth)
                   2.如果该View对象是个ViewGroup类型,需要重写该onMeasure()方法。对其子视图进行遍历的measure()过程,对每个子时图的measure()过程,是通过调用父类ViewGroup.java类李的measureChildWithMargins()方法去实现,该方法内部只是简单的调用了View对象的measure()方法

2.onLayout()

                  计算当前View以及子View的位置
                  如:layout(),onLayout(),setFrame();

Layout布局过程:

            主要作用:为整个根据子视图的大小以及布局参数将View树放到合适的位置上。
              1.layout方法会设置该View视图位于父视图的坐标轴,即mLeft,mTop,mRight,mBottom  (调用setFrame()函数去实现)接下来回调onLayout()方法(如果该View是ViewGroup对象,需要实现该方法,对每个子视图进行布局)。
              2.如果该View是个ViewGroup类型,需要遍历每个子视图chiildView,调用该子视图的layout()方法区设置他的坐标值。

3.onDraw()

                    视图的绘制工作
                    如: draw(),onDraw();

Draw()绘制过程:

           首先由ViewGroup对象的performTraversals()方法调用draw()方法发起绘制View树,值得注意的是每次发起绘图时,并不会重新绘制每个View树的视图,而只会重新绘制那些” 需要重绘 “的视图,View类内部变量包含了一个标志位 DRAWN,当该视图需要重绘时,就会为该View添加该标志位。
            
            1.绘制该View的背景
            2.为显示渐变框做一些准备操作(但是在大多数的情况下,不需要改渐变框)
            3.调用onDraw()方法绘制视图本身,(每个View都需要重载该方法,ViewGroup不需要实现该方法)
            4.调用dispatchDraw()方法绘制子视图(如果View类型不为ViewGroup,即不包含子视图,不需要重载该方法)/
          但是ViewGroup类已经为我们重写了dispathchDraw()的功能实现,应用程序一般不需要重写该方法。但可以重载父类函数实现具体的功能。         

二、 View自身的坐标

通过如下方法可以获取View到其父控件的距离。

                     getTop();获取View到其父布局顶边的距离。
                     getLeft();获取View到其父布局左边的距离。
                getBottom();获取View到其父布局底边的距离。
                     getRight();获取View到其父布局右边的距离。

三、构造函数

 public class TestView extends View {

 // 在java代码里new的时候会用到
   public TestView(Context context) {
    super(context);
}


 // 在xml布局文件中使用时自动调用
    public TestView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
}


 // 不会自动调用,如果有默认style时,在第二个构造函数中调用
 public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}



 //只有在API版本>21时才会用到
 //不会自动调用,如果有默认style时,在第二个构造函数中调用
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
}
}

四、自定义属性
Android系统的控件以android开头的都是系统自带的属性。为了方便配置自定义View的属性,我们也可以自定义属性值。

Android自定义属性可分为以下几步:

                       1. 自定义一个View
                       2. 编写values/attrs.xml,在其中编写styleable和item等标签元素
                       3. 在布局文件中View使用自定义的属性(注意namespace)
                       4. 在View的构造方法中通过TypedArray获取

自定义属性的声明文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="test">
        <attr name="text" format="string" />
        <attr name="testAttr" format="integer" />
    </declare-styleable>
</resources>

自定义View类:

public class MyTextView extends View {
          private static final String TAG = MyTextView.class.getSimpleName();

//在View的构造方法中通过TypedArray获取
public MyTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.test);
    String text = ta.getString(R.styleable.test_testAttr);
    int textAttr = ta.getInteger(R.styleable.test_text, -1);
    Log.e(TAG, "text = " + text + " , textAttr = " + textAttr);
    ta.recycle();
   }
}

布局文件中使用:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools"
      xmlns:app="http://schemas.android.com/apk/res/com.example.test"
      android:layout_width="match_parent"
      android:layout_height="match_parent" >

  <com.example.test.MyTextView
       android:layout_width="100dp"
       android:layout_height="200dp"
       app:testAttr="520"
       app:text="helloworld" />
  
   </RelativeLayout>
上一篇:自定义viewGroup测量以及子view布局


下一篇:布局ViewGroup原理解析(三):RelativeLayout