1. 程式人生 > >Android中View繪製流程以及invalidate()等相關方法分析

Android中View繪製流程以及invalidate()等相關方法分析

            前言: 本文是我讀《Android核心剖析》第13章----View工作原理總結而成的,在此膜拜下作者 。同時真摯地向渴望瞭解

Android 框架層網友,推薦這本書,希望你們能夠在Android開發裡學到更多的知識 。 

            整個View樹的繪圖流程是在ViewRoot.java類的performTraversals()函式展開的,該函式做的執行過程可簡單概況為

 根之前狀態,判斷是否需要重新計算檢視大小(measure)、是否重新需要安置檢視的位置(layout)、以及是否需要重繪

 (draw),其框架過程如下:

 步驟其實為host.layout() 

           

      接下來溫習一下整個View樹的結構,對每個具體View物件的操作,其實就是個遞迴的實現。

                  

           關於這個 DecorView 根檢視的說明,可以參考我的這篇部落格:

  流程一:      mesarue()過程

        主要作用:為整個View樹計算實際的大小,即設定實際的高(對應屬性:mMeasuredHeight)和寬(對應屬性:

  mMeasureWidth),每個View的控制元件的實際寬高都是由父檢視和本身檢視決定的。

     具體的呼叫鏈如下

          ViewRoot根物件地屬性mView(其型別一般為ViewGroup型別)呼叫measure()方法去計算View樹的大小,回撥

View/ViewGroup物件的onMeasure()方法,該方法實現的功能如下:    

         1、設定本View檢視的最終大小,該功能的實現通過呼叫setMeasuredDimension()方法去設定實際的高(對應屬性:  

                mMeasuredHeight)和寬(對應屬性:mMeasureWidth)   ;

         2 、如果該View物件是個ViewGroup型別,需要重寫該onMeasure()方法,對其子檢視進行遍歷的measure()過程。

               2.1  對每個子檢視的measure()過程,是通過呼叫父類ViewGroup.java類裡的measureChildWithMargins()方法去

          實現,該方法內部只是簡單地呼叫了View物件的measure()方法。(由於measureChildWithMargins()方法只是一個過渡

          層更簡單的做法是直接呼叫View物件的measure()方法)。

     整個measure呼叫流程就是個樹形的遞迴過程

     measure函式原型為 View.java 該函式不能被過載

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        //....
	
        //回撥onMeasure()方法  
        onMeasure(widthMeasureSpec, heightMeasureSpec);
       
        //more
    }

   為了大家更好的理解,採用“二B程式設計師”的方式利用虛擬碼描述該measure流程

   //回撥View視圖裡的onMeasure過程
   private void onMeasure(int height , int width){
	   //設定該view的實際寬(mMeasuredWidth)高(mMeasuredHeight)
	   //1、該方法必須在onMeasure呼叫,否者報異常。
	   setMeasuredDimension(h , l) ;
	   
	   //2、如果該View是ViewGroup型別,則對它的每個子View進行measure()過程
	   int childCount = getChildCount() ;
	   
	   for(int i=0 ;i<childCount ;i++){
		   //2.1、獲得每個子View物件引用
		   View child = getChildAt(i) ;
		   
		   //整個measure()過程就是個遞迴過程
		   //該方法只是一個過濾器,最後會呼叫measure()過程 ;或者 measureChild(child , h, i)方法都
		   measureChildWithMargins(child , h, i) ; 
		   
		   //其實,對於我們自己寫的應用來說,最好的辦法是去掉框架裡的該方法,直接呼叫view.measure(),如下:
		   //child.measure(h, l)
	   }
   }
   
   //該方法具體實現在ViewGroup.java裡 。
   protected  void measureChildWithMargins(View v, int height , int width){
	   v.measure(h,l)   
   }

流程二、 layout佈局過程:

     主要作用 :為將整個根據子檢視的大小以及佈局引數將View樹放到合適的位置上。

     具體的呼叫鏈如下:

       host.layout()開始View樹的佈局,繼而回調給View/ViewGroup類中的layout()方法。具體流程如下

        1 、layout方法會設定該View檢視位於父檢視的座標軸,即mLeft,mTop,mLeft,mBottom(呼叫setFrame()函式去實現)

  接下來回調onLayout()方法(如果該View是ViewGroup物件,需要實現該方法,對每個子檢視進行佈局) ;

       2、如果該View是個ViewGroup型別,需要遍歷每個子檢視chiildView,呼叫該子檢視的layout()方法去設定它的座標值。

          layout函式原型為 ,位於View.java

   /* final 識別符號 , 不能被過載 , 引數為每個檢視位於父檢視的座標軸
    * @param l Left position, relative to parent
    * @param t Top position, relative to parent
    * @param r Right position, relative to parent
    * @param b Bottom position, relative to parent
    */
   public final void layout(int l, int t, int r, int b) {
       boolean changed = setFrame(l, t, r, b); //設定每個檢視位於父檢視的座標軸
       if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
           if (ViewDebug.TRACE_HIERARCHY) {
               ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
           }

           onLayout(changed, l, t, r, b);//回撥onLayout函式 ,設定每個子檢視的佈局
           mPrivateFlags &= ~LAYOUT_REQUIRED;
       }
       mPrivateFlags &= ~FORCE_LAYOUT;
   }


    同樣地, 將上面layout呼叫流程,用虛擬碼描述如下: 
   // layout()過程  ViewRoot.java
   // 發起layout()的"發號者"在ViewRoot.java裡的performTraversals()方法, mView.layout()
   
   private void  performTraversals(){
	   
       //...
   	
       View mView  ;
   	   mView.layout(left,top,right,bottom) ;
   	
       //....
   }
   
   //回撥View視圖裡的onLayout過程 ,該方法只由ViewGroup型別實現
   private void onLayout(int left , int top , right , bottom){
	 
	   //如果該View不是ViewGroup型別
	   //呼叫setFrame()方法設定該控制元件的在父檢視上的座標軸
	   
	   setFrame(l ,t , r ,b) ;
	   
	   //--------------------------
	   
	   //如果該View是ViewGroup型別,則對它的每個子View進行layout()過程
	   int childCount = getChildCount() ;
	   
	   for(int i=0 ;i<childCount ;i++){
		   //2.1、獲得每個子View物件引用
		   View child = getChildAt(i) ;
		   //整個layout()過程就是個遞迴過程
		   child.layout(l, t, r, b) ;
	   }
   }


   流程三、 draw()繪圖過程

     由ViewRoot物件的performTraversals()方法呼叫draw()方法發起繪製該View樹,值得注意的是每次發起繪圖時,並不

  會重新繪製每個View樹的檢視,而只會重新繪製那些“需要重繪”的檢視,View類內部變數包含了一個標誌位DRAWN,當該

檢視需要重繪時,就會為該View新增該標誌位。

   呼叫流程 :

     mView.draw()開始繪製,draw()方法實現的功能如下:

          1 、繪製該View的背景

          2 、為顯示漸變框做一些準備操作(見5,大多數情況下,不需要改漸變框)          

          3、呼叫onDraw()方法繪製檢視本身   (每個View都需要過載該方法,ViewGroup不需要實現該方法)

          4、呼叫dispatchDraw ()方法繪製子檢視(如果該View型別不為ViewGroup,即不包含子檢視,不需要過載該方法)

值得說明的是,ViewGroup類已經為我們重寫了dispatchDraw ()的功能實現,應用程式一般不需要重寫該方法,但可以過載父類

  函式實現具體的功能。

            4.1 dispatchDraw()方法內部會遍歷每個子檢視,呼叫drawChild()去重新回撥每個子檢視的draw()方法(注意,這個 

地方“需要重繪”的檢視才會呼叫draw()方法)。值得說明的是,ViewGroup類已經為我們重寫了dispatchDraw()的功能

實現,應用程式一般不需要重寫該方法,但可以過載父類函式實現具體的功能。

     5、繪製滾動條

  於是,整個呼叫鏈就這樣遞迴下去了。

     同樣地,使用虛擬碼描述如下:
   // draw()過程     ViewRoot.java
   // 發起draw()的"發號者"在ViewRoot.java裡的performTraversals()方法, 該方法會繼續呼叫draw()方法開始繪圖
   private void  draw(){
	   
       //...
	   View mView  ;
       mView.draw(canvas) ;  
   	
       //....
   }
   
   //回撥View視圖裡的onLayout過程 ,該方法只由ViewGroup型別實現
   private void draw(Canvas canvas){
	   //該方法會做如下事情
	   //1 、繪製該View的背景
	   //2、為繪製漸變框做一些準備操作
	   //3、呼叫onDraw()方法繪製檢視本身
	   //4、呼叫dispatchDraw()方法繪製每個子檢視,dispatchDraw()已經在Android框架中實現了,在ViewGroup方法中。
	        // 應用程式程式一般不需要重寫該方法,但可以捕獲該方法的發生,做一些特別的事情。
	   //5、繪製漸變框	
   }
   
   //ViewGroup.java中的dispatchDraw()方法,應用程式一般不需要重寫該方法
   @Override
   protected void dispatchDraw(Canvas canvas) {
	   // 
	   //其實現方法類似如下:
	   int childCount = getChildCount() ;
	   
	   for(int i=0 ;i<childCount ;i++){
		   View child = getChildAt(i) ;
		   //呼叫drawChild完成
		   drawChild(child,canvas) ;
	   }	   
   }
   //ViewGroup.java中的dispatchDraw()方法,應用程式一般不需要重寫該方法
   protected void drawChild(View child,Canvas canvas) {
	   // ....
	   //簡單的回撥View物件的draw()方法,遞迴就這麼產生了。
	   child.draw(canvas) ;
	   
	   //.........
   }

   關於繪製背景圖片詳細的過程,請參考我的另外的部落格:

    強調一點的就是,在這三個流程中,Google已經幫我們把draw()過程框架已經寫好了,自定義的ViewGroup只需要實現

 measure()過程和layout()過程即可 。

     這三種情況,最終會直接或間接呼叫到三個函式,分別為invalidate(),requsetLaytout()以及requestFocus() ,接著

這三個函式最終會呼叫到ViewRoot中的schedulTraversale()方法,該函式然後發起一個非同步訊息,訊息處理中呼叫

performTraverser()方法對整個View進行遍歷。

    invalidate()方法 :

   說明:請求重繪View樹,即draw()過程,假如檢視發生大小沒有變化就不會呼叫layout()過程,並且只繪製那些“需要重繪的”

檢視,即誰(View的話,只繪製該View ;ViewGroup,則繪製整個ViewGroup)請求invalidate()方法,就繪製該檢視。

     一般引起invalidate()操作的函式如下:

            1、直接呼叫invalidate()方法,請求重新draw(),但只會繪製呼叫者本身。

            2、setSelection()方法 :請求重新draw(),但只會繪製呼叫者本身。

            3、setVisibility()方法 : 當View可視狀態在INVISIBLE轉換VISIBLE時,會間接呼叫invalidate()方法,

                     繼而繪製該View。

            4 、setEnabled()方法 : 請求重新draw(),但不會重新繪製任何檢視包括該呼叫者本身。

    requestLayout()方法 :會導致呼叫measure()過程 和 layout()過程 。

           說明:只是對View樹重新佈局layout過程包括measure()和layout()過程,不會呼叫draw()過程,但不會重新繪製

任何檢視包括該呼叫者本身。

    一般引起invalidate()操作的函式如下:

         1、setVisibility()方法:

             當View的可視狀態在INVISIBLE/ VISIBLE 轉換為GONE狀態時,會間接呼叫requestLayout() 和invalidate方法。

    同時,由於整個個View樹大小發生了變化,會請求measure()過程以及draw()過程,同樣地,只繪製需要“重新繪製”的檢視。

    requestFocus()函式說明:

          說明:請求View樹的draw()過程,但只繪製“需要重繪”的檢視。

    下面寫個簡單的小Demo吧,主要目的是給大家演示繪圖的過程以及每個流程裡該做的一些功能。截圖如下:

                                                

 1、    MyViewGroup.java  自定義ViewGroup型別

	/**
	 * @author http://http://blog.csdn.net/qinjuning
	 */
	//自定義ViewGroup 物件
	public class MyViewGroup  extends ViewGroup{


		private static String TAG = "MyViewGroup" ;
		private Context mContext ;
		
		public MyViewGroup(Context context) {
			super(context);
			mContext = context ;
			init() ;
		}

		//xml定義的屬性,需要該建構函式
	    public MyViewGroup(Context context , AttributeSet attrs){
	    	super(context,attrs) ;
	    	mContext = context ;
	    	init() ;
	    }
		
	    //為MyViewGroup新增三個子View
	    private void init(){
	    	//呼叫ViewGroup父類addView()方法新增子View
	    	
	    	//child 物件一 : Button
	    	Button btn= new Button(mContext) ;
	    	btn.setText("I am Button") ;
	    	this.addView(btn) ;
	    	
	    	//child 物件二 : ImageView 
	    	ImageView img = new ImageView(mContext) ;
	    	img.setBackgroundResource(R.drawable.icon) ;
	    	this.addView(img) ;
	    	
	    	//child 物件三 : TextView
	    	TextView txt = new TextView(mContext) ;
	    	txt.setText("Only Text") ;
	    	this.addView(txt) ; 
	    	
	    	//child 物件四 : 自定義View
	    	MyView myView = new MyView(mContext) ;
	    	this.addView(myView) ; 
	    }
	    
	    @Override
	    //對每個子View進行measure():設定每子View的大小,即實際寬和高
	    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
	    	//通過init()方法,我們為該ViewGroup物件添加了三個檢視 , Button、 ImageView、TextView
	    	int childCount = getChildCount() ;
	    	Log.i(TAG, "the size of this ViewGroup is ----> " + childCount) ;
	    	    	    	
	    	Log.i(TAG, "**** onMeasure start *****") ;
	    	
	    	//獲取該ViewGroup的實際長和寬  涉及到MeasureSpec類的使用
	    	int specSize_Widht = MeasureSpec.getSize(widthMeasureSpec) ;
	    	int specSize_Heigth = MeasureSpec.getSize(heightMeasureSpec) ;
	    	
	    	Log.i(TAG, "**** specSize_Widht " + specSize_Widht+ " * specSize_Heigth   *****" + specSize_Heigth) ;
	    	
	    	//設定本ViewGroup的寬高
	    	setMeasuredDimension(specSize_Widht , specSize_Heigth) ;
	    	
	    	
	    	
	    	
	    	for(int i=0 ;i<childCount ; i++){
	    		View child = getChildAt(i) ;   //獲得每個物件的引用
	    		child.measure(50, 50) ;   //簡單的設定每個子View物件的寬高為 50px , 50px  
	    		//或者可以呼叫ViewGroup父類方法measureChild()或者measureChildWithMargins()方法
	    	    //this.measureChild(child, widthMeasureSpec, heightMeasureSpec) ;
	    	}
	    	
	    }
	    
		@Override
		//對每個子View檢視進行佈局
		protected void onLayout(boolean changed, int l, int t, int r, int b) {
			// TODO Auto-generated method stub
	    	//通過init()方法,我們為該ViewGroup物件添加了三個檢視 , Button、 ImageView、TextView
	    	int childCount = getChildCount() ;
	    	
	    	int startLeft = 0 ;//設定每個子View的起始橫座標 
	    	int startTop = 10 ; //每個子View距離父檢視的位置 , 簡單設定為10px吧 。 可以理解為 android:margin=10px ;
	    	
	    	Log.i(TAG, "**** onLayout start ****") ;
	    	for(int i=0 ;i<childCount ; i++){
	    		View child = getChildAt(i) ;   //獲得每個物件的引用
	    		child.layout(startLeft, startTop, startLeft+child.getMeasuredWidth(), startTop+child.getMeasuredHeight()) ;
	    		startLeft =startLeft+child.getMeasuredWidth() + 10;  //校準startLeft值,View之間的間距設為10px ;
	    		Log.i(TAG, "**** onLayout startLeft ****" +startLeft) ;
	    	}   	   	
		}
		//繪圖過程Android已經為我們封裝好了 ,這兒只為了觀察方法呼叫程
		protected void dispatchDraw(Canvas canvas){
			Log.i(TAG, "**** dispatchDraw start ****") ;
			
			super.dispatchDraw(canvas) ;
		}
	    
		protected boolean drawChild(Canvas canvas , View child, long drawingTime){
			Log.i(TAG, "**** drawChild start ****") ;
			
			return super.drawChild(canvas, child, drawingTime) ;
		}
	}

          2、MyView.java 自定義View型別,重寫onDraw()方法 ,

//自定義View物件
	public class MyView extends View{

		private Paint paint  = new Paint() ;
		
		public MyView(Context context) {
			super(context);
			// TODO Auto-generated constructor stub
		}
		public MyView(Context context , AttributeSet attrs){
			super(context,attrs);
		}
		
		protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
			//設定該View大小為 80 80
			setMeasuredDimension(50 , 50) ;
		}
		
		
		
		//存在canvas物件,即存在預設的顯示區域
		@Override
		public void onDraw(Canvas canvas) {
			// TODO Auto-generated method stub
			super.onDraw(canvas);
			
			Log.i("MyViewGroup", "MyView is onDraw ") ;
			//加粗
			paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
			paint.setColor(Color.RED);
			canvas.drawColor(Color.BLUE) ;
			canvas.drawRect(0, 0, 30, 30, paint);
			canvas.drawText("MyView", 10, 40, paint);
		}
	}


          主Activity只是顯示了該xml檔案,在此也不羅嗦了。 大家可以檢視該ViewGroup的Log仔細分析下View的繪製流程以及

相關方法的使用。第一次啟動後捕獲的Log如下,網上找了些資料,第一次View樹繪製過程會走幾遍,具體原因可能是某些

View 發生了改變,請求重新繪製,但這根本不影響我們的介面顯示效果 。

        總的來說: 整個繪製過程還是十分十分複雜地,每個具體方法的實現都是我輩難以立即的,感到悲劇啊。對Android提

 供的一些ViewGroup物件,比如LinearLayout、RelativeLayout佈局物件的實現也很有壓力。 本文重在介紹整個View樹的繪製

流程,希望大家在此基礎上,多接觸原始碼進行更深入地擴充套件。

//==========================================================

// 本次更新於 2012-05-20 晚

//==========================================================

 Al Last,關於UI繪製的這塊,我部落格裡零零散散的敘說了一些知識,建議大家都能夠去看看:

   1、  詳解measure過程以及如何設定View寬高的,建議看我的另外兩篇部落格:

2、詳解DecorView以及Activity視窗對應佈局地說明

3、詳解View繪製過程中如何繪製背景圖片:

 希望各位能暫停你的腳步,踏踏實實學習。Best regards for U ~~ 。

//==========================================================

// 本次更新於 2012-10-29 晚

//==========================================================


       

相關推薦

AndroidView繪製流程以及invalidate()相關方法分析

            前言: 本文是我讀《Android核心剖析》第13章----View工作原理總結而成的,在此膜拜下作者 。同時真摯地向渴望瞭解Android 框架層的網友,推薦這本書,希望你們能夠在Android開發裡學到更多的知識 。             整個V

android-進階(3)-自定義view(2)-AndroidView繪製流程以及相關方法分析

最近正在學自定義view,這篇文章主要講view的繪製流程和一些相關的方法,淺顯易懂,寫的非常好,忍不住就轉載了。             前言: 本文是我讀《Android核心剖析》第13章----View工作原理總結而成的,在此膜拜下作者 。

Android View 繪製流程invalidate 和postInvalidate 分析--從原始碼角度

整個View樹的繪製流程是在ViewRootImpl.java類的performTraversals()函式展開的,該函式做的執行過程可簡單概況為  根據之前設置的狀態,判斷是否需要重新計算檢視大小(measure)、是否重新需要佈局檢視的位置(layout

Androidview繪製過程

1 背景 看見沒有,如上圖中id為content的內容就是整個View樹的結構,所以對每個具體View物件的操作,其實就是個遞迴的實現。 前面《Android觸控式螢幕事件派發機制詳解與原始碼分析一(View篇)》文章的3-1小節說過And

Android開發——View繪製流程

網上講解View的繪製流程有很多優秀的文章。主要分為三個步驟:分別是measure、layout和draw。measure根據父佈局的尺寸以及自己想要的尺寸得到最終自己的尺寸,layout用於確定子View的位置,draw負責繪製自己。View分為View和Vi

AndroidView和ViewGroup事件分發攔截機制完美分析

出自:http://www.cnblogs.com/linjzong/p/4191891.html Touch事件分發中只有兩個主角:ViewGroup和View。Activity的Touch事件事實上是呼叫它內部的ViewGroup的Touch事件,可以直接當成Vie

Android系統原始碼分析--View繪製流程之-setContentView

上一篇分析了四大元件之ContentProvider,這也是四大元件最後一個。因此,從這篇開始我們分析新的篇章--View繪製流程,View繪製流程在Android開發中佔有非常重要的位置,只要有檢視的顯示,都離不開View的繪製,所以瞭解View繪製原理對於應用開發以及系統的學習至關重要。由於View

AndroidView自定義XML屬性詳解以及R.attr與R.styleable的區別

為View新增自定義XML屬性 Android中的各種Widget都提供了很多XML屬性,我們可以利用這些XML屬性在layout檔案中為Widget的屬性賦值。 如下所示: <TextView android:layout_wi

Android應用層View繪製流程之DecorView與ViewRootImpl

概述 一直對Android中View的整個繪製流程不是很瞭解,View是怎麼新增到Activity當中去的?當View中的內容發生改變的時候是怎樣執行介面的重新整理的?因此,今天準備從原始碼的角度來對View的整個繪製流程來進行分析,原始碼基於API25。由於

Android GUI之View繪製流程

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) { mLayoutRequested =

Android View繪製流程

或許你已經看到過很多部落格總結的View的繪製流程的.我這篇部落格是跟著原始碼去看,對自己學到的知識加以印證.水平有限,僅供參考,如有錯誤,歡迎指正 我在之前的部落格就已經說明了Activity的啟動到顯示的相關流程,現在我們來看下View具體的工作流程. 上次

AndroidView繪製過程 onMeasure方法簡述 附有自定義View例子

/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use thi

Android-UI控制元件的繪製流程以及自定義控制元件的具體操作

  View檢視安卓應用中非常重要的組成部分,它不僅是構成應用介面的基本單元,還是與使用者互動的最直接物件。檢視View作為介面的基本元素,是由View System進行管理的。   在Android中,檢視控制元件主要被分成兩大類,一類是單一控制元件View,另外一類

RGB簡述以及Android設定透明、半透明效果

一、RGB   簡單說說RGB,RGB由Red、Green、Blue三種成分色組成,每種顏色由2位16進位制數表示。   如:紅色 FF0000 表示紅全有(最滿),綠沒有,藍沒有       綠色

Android View繪製流程原始碼淺析

Android中View的繪製是一個面試的必答題,網上他人的博文也很多,本文旨在分析出大致流程。 廢話不說,read the fucking source code! 先從ActivityThread主執行緒啟動Activity說起,當Activity初始化 Window和

Android View 繪製流程(Draw) 完全解析

前言 前幾篇文章,筆者分別講述了DecorView,measure,layout流程等,接下來將詳細分析三大工作流程的最後一個流程——繪製流程。測量流程決定了View的大小,佈局流程決定了View的位置,那麼繪製流程將決定View的樣子,一個View該顯示什麼

解決 Android View 的 setPivotX 和 setPivotY 不生效的問題以及設定縮放中心的方法

背景是這樣的:有一個需求要對下方的關注按鈕實現如下動畫,動畫的最後要根據滑動位置對關注按鈕進行縮放,縮放結束時整體大小為控制元件原始大小的90%,最終效果圖如下所示(模擬器是 4.2 的系統,最上面的沉浸式有點問題,忽略之): 如圖,關注按鈕向上滑

Android應用層View繪製流程與原始碼分析

1 背景 看見沒有,如上圖中id為content的內容就是整個View樹的結構,所以對每個具體View物件的操作,其實就是個遞迴的實現。 前面《Android觸控式螢幕事件派發機制詳解與原始碼分析一(View篇)》文章的3-1小節說過And

Android View繪製流程看這篇就夠了

前言        自定義View、多執行緒、網路,被認為是Android開發者必須牢固掌握的最基礎的三大基本功。Android View的繪製流程原理又是學好自定義View的理論基礎,所以掌握好View的繪製原理是Android開發進階中無法繞過的一道坎。而關乎到原理

android View, Window, Activity, WindowManager,ViewRoot幾者之間的關系

line 消息傳遞 post att 顯示 增加 調用 eas window對象 (1)View:最主要的UI組件,表示屏幕上的一個矩形區域。 (2)Window: 表示一個窗體,不一定有屏幕那麽大,能夠非常大也能夠非常小;