一、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>