Android View繪製過程以及事件傳遞原理

一. 對於控件,Android中的測量方式android

在Android中,控件繪製的步驟是 measure,layout,drawide

//在onMeasure調用以前調用的測量方式
private void measureView(View child)
 {
    ViewGroup.LayoutParams p = child.getLayoutParams();
    if (p == null) {
        p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }
 
     //根據父佈局得到child寬度的建議大小
    int childWidthSpec = ViewGroup.getChildMeasureSpec(0,  mListPadding.left + mListPadding.right, p.width); 
    
    //高度不是固定的,可能比父佈局高,所以不該該調動ViewGroup.getChildMeasureSpec
    int lpHeight = p.height;
    int childHeightSpec;
    if (lpHeight > 0) {
        childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
                MeasureSpec.EXACTLY); //精確高度
    } else {
        childHeightSpec = MeasureSpec.makeMeasureSpec(0,
                MeasureSpec.UNSPECIFIED); //要多大有多大
    }
    child.measure(childWidthSpec, childHeightSpec);
}

通常來講,若是父佈局的寬度和高度,內外邊距位指定,那麼,上面的方法能夠簡化以下佈局

private void measureView(View child)
{
    child.measure(0, 0);
}

在android開發中,經常使用的測量方法還有 measureChildren(); measureChildpost

二.對於滑動事件

手指一動的最小距離獲取應該大於ViewConfiguration.getScaledTouchSlop ()纔開始滑動spa

注:ViewConfiguration註冊了各類滑動和觸摸的最小距離.net

三.寬度和高度的獲取

通常來講在onCreate中沒法獲得控件的高度,除非設定成已知數據,或者已對控件進行測量,但getWidth不必定是已知的,只能獲得getMeasureWidthcode

通常來講有以下幾種獲取方式orm

  @Override
    public void onWindowFocusChanged(boolean hasFocus) {
    	View iv1 = findViewById(R.id.iv1);
		View iv2=findViewById(R.id.iv2);
		String msg1="iv1' width:"+iv1.getWidth()+" height:"+iv1.getHeight()+"  measuredWidth:"+iv1.getMeasuredWidth()+"measuredHeight:"+iv1.getMeasuredHeight();
		String msg2="iv2' width:"+iv2.getWidth()+" height:"+iv2.getHeight()+"  measuredWidth:"+iv2.getMeasuredWidth()+"measuredHeight:"+iv2.getMeasuredHeight();
		i("onWindowFocusChanged() "+msg1);
		i("onWindowFocusChanged() "+msg2);
    	super.onWindowFocusChanged(hasFocus);
    }

或者以下,imageView徹底被繪製出展現server

imageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
			
			@Override
			public void onGlobalLayout() {
				view.postDelayed(new Runnable() {
					
					@Override
					public void run() {
						View iv1 = findViewById(R.id.iv1);
						View iv2=findViewById(R.id.iv2);
						String msg1="iv1' width:"+iv1.getWidth()+" height:"+iv1.getHeight()+"  measuredWidth:"+iv1.getMeasuredWidth()+"measuredHeight:"+iv1.getMeasuredHeight();
						String msg2="iv2' width:"+iv2.getWidth()+" height:"+iv2.getHeight()+"  measuredWidth:"+iv2.getMeasuredWidth()+"measuredHeight:"+iv2.getMeasuredHeight();
						i("onWindowFocusChanged() "+msg1);
						i("onWindowFocusChanged() "+msg2);
					}
				}, 300);
			}
		});

 

四.事件的分發傳遞

Android中對於事件的操做,主要集中於3個方法,dispatchTouchEvent,  onInteceptorTouchEvent(ViewGroup獨有),  onTouchEvent,其中,傳遞方向有2個blog

Activity->Window->ViewGroup->...->childView[--->分發攔截階段

Activity<-Window<-ViewGroup<-...<-childView[--->處理階段

 

分發攔截階段,dispatchTouchEvent會調用onInteceptorTouchEvent進行判斷,是否繼續傳遞,若是onInteceptorTouchEvent返回值爲ture,

表示直接中止分發,把事件交由onTouchEvent進行處理;不然想tragetview傳遞,childview調用dispatchTouchEvent進行相似判斷

 

處理階段,徹底由onTouchEvent進行處理,返回值爲ture,終止事件回傳

 

注意:調用requestDisallowInterceptTouchEvent(true)時,onInteceptorTouchEvent不會被調用,詳情請看源碼

 

五.View的繪製

DecorView爲頂級View,DecoreView分爲2部分,第一部分是Title,第二部分是Content,對應的ID分別爲R.Android.id.title和R.android.id.content

 

view 的繪製流程從 ViewRoot 的 performTraversals 開始,代碼流程是這樣的:
performMeasure -> measure -> onMeasure
performLayout -> layout -> onLayout
performDraw -> draw -> onDraw

 

View內部的繪製流程

view的繪製大體遵循以下流程:先繪製背景,再繪製本身(onDraw),接着繪製子元素(dispatchDraw),最後繪製一些裝飾等好比滾動條(onDrawScrollBars)

 

參考:http://blog.csdn.net/singwhatiwanna/article/details/42614953

相關文章
相關標籤/搜索