Android控件架構(gòu)
View的測量與繪制
ViewGroup的測量與繪制
自定義控件的三種方式
事件的攔截機(jī)制
3.1 Android控件架構(gòu)
每個(gè)控件在界面中占據(jù)一塊矩形的區(qū)域,控件大致可以分為兩類:ViewGroup控件與View控件;ViewGroup作為父控件可以包含多個(gè)View控件,負(fù)責(zé)下層控件的測量與繪制,并傳遞交互事件
Android界面的架構(gòu)圖
每個(gè)Activity都包含一個(gè)Window對(duì)象,Window對(duì)象由PhoneWindow實(shí)現(xiàn);PhoneWindow將一個(gè)DecorView設(shè)置為整個(gè)應(yīng)用窗口的根View;DecorView包含了TitleView和ContentView
3.2 View的測量
Android提供了一個(gè)MeasureSpec類,通過它來測量View。MeasureSpec是一個(gè)32位的int值,高2位是測量的模式,低30位是測量的大小;測量的模式分為三種:
1.EXACTLY:精確值模式(默認(rèn)模式)將控件的“l(fā)ayout_height”,"layout_width"屬性指定為具體值的時(shí)候就是EXACTLY模式
2.AT_MOST:最大值模式當(dāng)寬度高度指定為“wrap_content”時(shí),控件大小一般隨控件的子空間或內(nèi)容的變化而變化,此時(shí)控件的尺寸不能超過父控件允許的最大尺寸
3.UNSPECIFIED:不指定其大小測量模式 總結(jié):View的onMeasure()方法只支持EXACTLY模式,所以如果要讓自定義View支持"wrap_content"屬性就要重寫onMeasure()方法來指定wrap_content時(shí)的大小。
源碼分析:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
進(jìn)入onMeasure();方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
我們可以看到源代碼是通過setMeasuredDimension來測量大小的
代碼示例
package com.example.view;
import android.content.Context;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by 小新 on 2016/6/4.
*/
public class Myview extends View {
public Myview(Context context) {
super(context);
}
public Myview(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Myview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasure(widthMeasureSpec),heightMeasure(heightMeasureSpec));
}
private int heightMeasure(int heightMeasureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(heightMeasureSpec);
int specSize = MeasureSpec.getSize(heightMeasureSpec);
if(specMode==MeasureSpec.EXACTLY){
result=specSize;
}else{
result=200;
if(specMode==MeasureSpec.AT_MOST){
result=Math.min(result,specSize);
}
}
return result;
}
private int widthMeasure(int widthMeasureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if(specMode==MeasureSpec.EXACTLY){
result=specSize;
}else{
result=200;
if(specMode==MeasureSpec.AT_MOST){
result=Math.min(result,specSize);
}
}
return result;
}
}
這樣當(dāng)我們自定義寬高為"wrap_content的時(shí)候"默認(rèn)的大小是200px;不設(shè)置onMeasure()方法的時(shí)候我們使用“wrap_content”默認(rèn)的大小是充滿父控件