日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

深入理解Android中View

發布時間:2025/6/15 Android 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入理解Android中View 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

[隱藏]
  • 一、View是什么?
  • 二、View創建的一個概述:
  • 三、View的標志(Flag)系統
  • 四、MeasureSpec
  • 五、幾個重要方法簡介
    • 5.1 onFinishInflate()
    • 5.2 onMeasure(int, int)
    • 5.3 onLayout(boolean, int, int,int, int)
    • 5.4 onSizeChanged(int, int, int,int)
    • 5.5 onDraw(android.graphics.Canvas)

? 這回我們是深入到View內部,去研究View,去了解View的工作,拋棄其他因素,以便為以后能靈活的使用自定義空間打下一定的基礎。希望有志同道合的朋友一起來探討,深入Android內部,深入理解Android。

一、View是什么?

? ? ? ?View是什么了,每個人都有自己的理解。在Android的官方文檔中是這樣描述的:這個類表示了用戶界面的基本構建模塊。一個View占用了屏幕上的一個矩形區域并且負責界面繪制和事件處理。View是用來構建用戶界面組件(Button,Textfields等等)的基類。ViewGroup子類是各種布局的基類,它是個包含其他View(或其他ViewGroups)和定義這些View布局參數的容器。

? ? ? ?其實說白了,View就是一個矩形區域,我們可以在這個區域上定義自己的控件。

? ? ? ?注明:有對系統回調不太了解的回頭看看回調,這樣有助于對文章的理解。

二、View創建的一個概述:

? ? ? ?在API中對View的回調流程有以個詳細的描述,下面給出了原文翻譯:(翻譯有點倉促,大家多多包涵,有啥錯的地方麻煩告知下我,我好改過來)

? ? ? ?1.Creation ? ? ? ? ? :創建

? ? ? ? ?----Constructors(構造器) ? ?

? ? ? ?There is a form of the constructor that arecalled when the view is created from code and a form that is called when theview is inflated from a layout file. The second form should parse and apply anyattributes defined in the layout file.在構造器中有個一個表單當View從代碼中創建和從Layout File 文件中創建時。第二個表單應該解析和應用一些在Layout File中定義的屬性。

? ? ? ? ?---- onFinishInflate()

? ? ? ? Called after a view and all of itschildren has been inflated from XML.當View和他的所有子View從XML中解析完成后調用。

? ? ? ?2. Layout ? ? ? ? ? ?:布局

? ? ? ? ?----onMeasure(int, int)

? ? ? ? Called to determine the size requirementsfor this view and all of its children. ? 確定View和它所有的子View要求的尺寸時調用

? ? ? ? ?---- onLayout(boolean, int, int,int, int)

? ? ? ? Calledwhen this view should assign a size and position to all of its children當這個View為其所有的子View指派一個尺寸和位置時調用

? ? ? ? ?---- onSizeChanged(int, int, int,int)

? ? ? ? Calledwhen the size of this view has changed.當這個View的尺寸改變后調用

? ? ? ?3. Drawing ? ? ? ? :繪制

? ? ? ? ?---- onDraw(Canvas)

? ? ? ? Calledwhen the view should render its content.當View給定其內容時調用

? ? ? ?4.Event processing ? ?:事件流程

? ? ? ? ?----onKeyDown(int, KeyEvent)

? ? ? ? Calledwhen a new key event occurs.當一個新的鍵按下時

? ? ? ? ?---- onKeyUp(int, KeyEvent) ?

? ? ? ? Calledwhen a key up event occurs.當一個鍵彈起時

? ? ? ? ?----onTrackballEvent(MotionEvent)

? ? ? ? Calledwhen a trackball motion event occurs.當滾跡球事件發生時。

? ? ? ? ?----onTouchEvent(MotionEvent)

? ? ? ? Calledwhen a touch screen motion event occurs.當一個觸摸屏事件發生時。

? ? ? ?5. Focus ? ? ? ? ? ? ?:焦點

? ? ? ? ?---- onFocusChanged(boolean, int,Rect)

? ? ? ? onFocusChanged(boolean,int, Rect)當View得到和失去焦點時調用

? ? ? ? ---- onWindowFocusChanged(boolean)

? ? ? ? Called when the windowcontaining the view gains or loses focus.當Window包含的View得到或失去焦點時調用。

? ? ? ?根據View里面方法調用流程的概述,我們來重寫其中的幾個回調方法來直觀的了解下這個調用,具體代碼這里就不貼了,代碼見測試包:DEMO_View調用流程.rar,調用的log顯示:

? ? ? ?這樣大家就對View的調用有了個大概的認識,下面將針對View的標志系統、View的的布局參數系統等做一個簡單的描述。

三、View的標志(Flag)系統

? ? ? ?在一個系統中往往使用標志來指示系統中的某些參數,這里對View的標志系統做一些簡單的介紹,這樣大家可以借鑒下,以后也可以用這種表示方法。

? ? ? ?一般而言標志都是成對出現的也就是表示相反兩個屬性,對于這種屬性的表示方法我們使用一位的0和1就可以表示。如果有多個成對屬性,如果每對屬性都用一個int值來標志是不方便的。這種情況通常是用一個int的各個位來分別表示每個標志,在處理器中有一個標志位就是采用這種方式設計的。

我們先來看看位運算。位運算符包括: 與(&)、非(~)、或(|)、異或(^)

   ? ?&: ? 當兩邊操作數的位同時為1時,結果為1,否則為0。如1100&1010=1000   

? ? ? ?|: ? 當兩邊操作數的位有一邊為1時,結果為1,否則為0。如1100|1010=1110   

? ? ? ?~: ? 0變1,1變0   

? ? ? ?^: ? 兩邊的位不同時,結果為1,否則為0.如1100^1010=0110

? ? ? ?在View系統使用mViewFlags來表征這些屬性,其設置的主要方法如下

[java] view plaincopy
  • void?setFlag(int?mask,?int?falg)???
  • ????{???
  • ????????int?old?=?mViewFlags;①???
  • ????????mViewFlags?=?(mViewFlags?&?~mask)?|?(mask?&?falg);②???
  • ????int?changed?=?mViewFlags?^?old;//?獲取改變的位,方法是對改變的位置1③???
  • ????...?...???
  • ????}??
  • 其中mask指的是標志位所在的位,falg表示的標志位。下面舉個例子:

    [java] view plaincopy
  • public?static?final?int?VISIBLE?=?0x00000000;???
  • ????public?static?final?int?INVISIBLE?=?0x00000004;???
  • ????public?static?final?int?GONE?=?0x00000008;???
  • static?final?int?VISIBILITY_MASK?=?0x0000000C;??
  • ? ? ? ?其中VISIBLE和INVISIBLE和GONE就是標志位,VISIBILITY_MASK是標志位所在的位,也就有VISIBLE+INVISIBLE+GON=VISIBILITY_MASK。看不懂的把上面四個轉換為二進制就看出來了。

    ? ? ? ?為什么要使用VISIBILITY_MASK?會不會有些多余呢?我們來看View中的計算公式:

    [java] view plaincopy
  • mViewFlags?=?(mViewFlags?&?~mask)?|?(mask?&?falg);②??
  • ? ? ? ?其中mViewFlags & ~mask是用來將mViewFlags中表示該標志的位置零。mask & falg是用來獲得標志位。舉個例子:

    ? ? ? ?假設mViewFlags的二進制表示為110000;flag為INVISIBLE我們將上面的標志位轉換為二進制VISIBLE 0000、INVISIBLE 0100、GONE 1000、VISIBILITY_MASK 1100。

    mViewFlags & ~mask=110000 & 0011 = 110000(上面所用的標志位占用的是最后四位,我們通過這個運算來將這個標志位置零)。

    [java] view plaincopy
  • mask?&?falg?=?1100?&?0100?=0100(獲得標志)。??
  • ? ? ? ?110000 | 0100(通過或運算來計算出最后的標志)。

    ? ? ? ?一般而言:在多個同種類型的標志中,通常使用0來作為默認的標志。關于上面的標志系統的其他具體使用我們就不再深入,有興趣的可以自行深入,有啥好的想法在群里分享下。

    四、MeasureSpec

    ? ? ? ?在View系統中,指定寬和高,以及指定布局的屬性,是由MeasureSpec來封裝的。下面是各個模式的標志位表示。

    [java] view plaincopy
  • private?static?final?int?MODE_SHIFT?=?30;???
  • ????????private?static?final?int?MODE_MASK??=?0x3?<<?MODE_SHIFT;???
  • ????????/**??
  • ?????????*?Measure?specification?mode:?The?parent?has?not?imposed?any?constraint??
  • ?????????*?on?the?child.?It?can?be?whatever?size?it?wants.??
  • ?????????*/??
  • ????????public?static?final?int?UNSPECIFIED?=?0?<<?MODE_SHIFT;???
  • ????????/**??
  • ?????????*?Measure?specification?mode:?The?parent?has?determined?an?exact?size??
  • ?????????*?for?the?child.?The?child?is?going?to?be?given?those?bounds?regardless??
  • ?????????*?of?how?big?it?wants?to?be.??
  • ?????????*/??
  • ????????public?static?final?int?EXACTLY?????=?1?<<?MODE_SHIFT;???
  • ????????/**??
  • ?????????*?Measure?specification?mode:?The?child?can?be?as?large?as?it?wants?up??
  • ?????????*?to?the?specified?size.??
  • ?????????*/??
  • ????public?static?final?int?AT_MOST?????=?2?<<?MODE_SHIFT;??
  • ? ? ? ?在這個解析系統中是通過移位來存放更多的數據,現在每個數據標志位都向左移動了30位。這樣表示一個View大小是很方便的,我們來看下面的方法:

    [java] view plaincopy
  • public?static?int?makeMeasureSpec(int?size,?int?mode)?{???
  • ????????????return?size?+?mode;???
  • ????????}??
  • ? ? ? ?通過這個方法就可以制作一個含有兩個參數的int值,這個參數包含一個mode標志和一個寬或高的表示。

    ? ? ? ?我們通過如下方法來獲取到mode:

    [java] view plaincopy
  • public?static?int?getMode(int?measureSpec)?{???
  • ????????????return?(measureSpec?&?MODE_MASK);???
  • ????????}??
  • ? ? ? ?我們也可以用下面方法來獲取高或寬的數據表示:

    [java] view plaincopy
  • public?static?int?getSize(int?measureSpec)?{???
  • ????????????return?(measureSpec?&?~MODE_MASK);???
  • ????????}??
  • 五、幾個重要方法簡介

    ? ? ? ?正如第二節寫的那個調用流程一樣,這幾個重要的方法是系統回調是調用的,同樣對于這幾個方法也是自定義組件的重要的方法。

    ? ? ? ?在這節里我們主要是了解這些方法的用途,以期在自定義組件時可以對這些方法得心應手。

    5.1 onFinishInflate()

    ? ? ? ?這個是當系統解析XML完成,并且將子View全部添加完成之后調用這個方法,我們通常重寫這個方法,在這個方法中查找并獲得子View引用,當然前提是這個View中有子View所以一般都是繼承ViewGroup時用這個方法比較多,比如抽屜效果中:

    [java] view plaincopy
  • @Override??
  • protected?void?onFinishInflate()?{???
  • ????mHandle?=?findViewById(mHandleId);???
  • ????if?(mHandle?==?null)?{???
  • ????????throw?new?IllegalArgumentException("The?handle?attribute?is?must?refer?to?an"??
  • ????????????????+?"?existing?child.");???
  • ????}???
  • ????mHandle.setOnClickListener(new?DrawerToggler());???
  • ?????????
  • ????mContent?=?findViewById(mContentId);???
  • ????if?(mContent?==?null)?{???
  • ????????throw?new?IllegalArgumentException("The?content?attribute?is?must?refer?to?an"??
  • ????????????????+?"?existing?child.");???
  • ????}???
  • ????mContent.setVisibility(View.GONE);???
  • }??
  • ? ? ? 通過重寫這個方法來獲取手柄的View和要顯示內容的View。

    5.2 onMeasure(int, int)

    ? ? ? ?測量這個View的高和寬。通過調用這個方法來設置View的測量后的高和寬,其最終調用的方法是:

    [java] view plaincopy
  • protected?final?void?setMeasuredDimension(int?measuredWidth,?int?measuredHeight)?{???
  • ????????mMeasuredWidth?=?measuredWidth;???
  • ????????mMeasuredHeight?=?measuredHeight;???
  • ??????????
  • ????????mPrivateFlags?|=?MEASURED_DIMENSION_SET;???
  • ????}??
  • ? ? ? ?可見其最終是將高和寬保存在mMeasuredWidth、mMeasuredHeight這兩個參數中。

    ? ? ? ?其實調用onMeasure(int, int)的方法的不是系統,而是

    ? ? ? ? ? ?public final voidmeasure(int widthMeasureSpec, int heightMeasureSpec)

    ? ? ? ?這個才是系統回調的方法,然后通過這個方法調用onMeasure(int, int)方法,個人感覺這種設計就是把系統方法和用戶可以重寫的方法分離開,這樣避免一些不必要的錯誤。

    ? ? ? ?在這個方法中主要是用來初始化各個子View的布局參數,我們來看看抽屜中的實現:

    [java] view plaincopy
  • @Override??
  • protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)?{???
  • ????int?widthSpecMode?=?MeasureSpec.getMode(widthMeasureSpec);???
  • ????int?widthSpecSize?=??MeasureSpec.getSize(widthMeasureSpec);???
  • ?????????
  • ????int?heightSpecMode?=?MeasureSpec.getMode(heightMeasureSpec);???
  • ????int?heightSpecSize?=??MeasureSpec.getSize(heightMeasureSpec);???
  • ?????????
  • ????if?(widthSpecMode?==?MeasureSpec.UNSPECIFIED?||?heightSpecMode?==?MeasureSpec.UNSPECIFIED)?{???
  • ????????throw?new?RuntimeException("SlidingDrawer?cannot?have?UNSPECIFIED?dimensions");???
  • ????}???
  • ?????????
  • ????final?View?handle?=?mHandle;???
  • ????measureChild(handle,?widthMeasureSpec,?heightMeasureSpec);???
  • ?????????
  • ????if?(mVertical)?{???
  • ????????int?height?=?heightSpecSize?-?handle.getMeasuredHeight()?-?mTopOffset;???
  • ????????mContent.measure(MeasureSpec.makeMeasureSpec(widthSpecSize,?MeasureSpec.EXACTLY),???
  • ????????????????MeasureSpec.makeMeasureSpec(height,?MeasureSpec.EXACTLY));???
  • ????}?else?{???
  • ????????int?width?=?widthSpecSize?-?handle.getMeasuredWidth()?-?mTopOffset;???
  • ????????mContent.measure(MeasureSpec.makeMeasureSpec(width,?MeasureSpec.EXACTLY),???
  • ????????????????MeasureSpec.makeMeasureSpec(heightSpecSize,?MeasureSpec.EXACTLY));???
  • ????}???
  • ?????????
  • ????setMeasuredDimension(widthSpecSize,?heightSpecSize);???
  • }??
  • ? ? ? ?剛才我們已經獲取到mHandle和mContent的引用,因為onFinishInflate()方法調用在onMeasure(int, int)方法之前,所以這個不會出現nullPoint。我們可以看到在這個方法中主要就是為mHandle和mContent指定了布局參數。這里用到了MeasureSpec。

    5.3 onLayout(boolean, int, int,int, int)

    ? ? ? ?onLayout是用來指定各個子View的位置,這個方法和上面方法類似,也不是真正的系統回調函數,真正的回調函數是Layout。這個方法的使用主要在ViewGroup中。這里不再詳述。我們在ViewGroup講解時再去了解這個方法。

    5.4 onSizeChanged(int, int, int,int)

    ? ? ? ?這個是當View的大小改變時調用,這個也不再詳述,基本上用的也比較少。

    5.5 onDraw(android.graphics.Canvas)

    ? ? ? ?這個方法相信大家都不會陌生了,在我以前的博客里也有這個方法的使用。當然那個比較入門,比較膚淺,呵呵。這里我們深入進去,類似于onMeasure(int, int),其實這個方法是由draw(Canvas)方法調用的。在這個方法中有一個對這個方法的描述:

    [java] view plaincopy
  • /*??
  • ????????*?Draw?traversal?performs?several?drawing?steps?which?must?be?executed??
  • ????????*?in?the?appropriate?order:??
  • ????????*??
  • ????????*??????1.?Draw?the?background??
  • ????????*??????2.?If?necessary,?save?the?canvas'?layers?to?prepare?for?fading??
  • ????????*??????3.?Draw?view's?content??
  • ????????*??????4.?Draw?children??
  • ????????*??????5.?If?necessary,?draw?the?fading?edges?and?restore?layers??
  • ????????*??????6.?Draw?decorations?(scrollbars?for?instance)??
  • ????????*/??
  • ? ? ? ?我們可以看到:

    ? ? ? ? ? ? ? ?首先是繪制背景

    ? ? ? ? ? ? ? ?其次如果需要準備層之間的陰影

    ? ? ? ? ? ? ? ?然后繪制內容(這個內容就是調用我們的onDraw方法)

    ? ? ? ? ? ? ? ?再繪制children(dispatchDraw(canvas);)這個方法的調用主要實現在ViewGroup中,和繼承ViewGroup的組件中。

    ? ? ? ? ? ? ? ?如果需要繪制層之間的陰影。

    ? ? ? ? ? ? ? ?繪制裝飾,也就是scrollbars。

    ? ? ? ?dispatchDraw(canvas);這也是一個重要的方法,用于繪制子組件用的。下面是抽屜中的實現方法。也比較簡單,大家自行閱讀下也就了解了。

    [java] view plaincopy
  • @Override??
  • ??protected?void?dispatchDraw(Canvas?canvas)?{???
  • ??????final?long?drawingTime?=?getDrawingTime();???
  • ??????final?View?handle?=?mHandle;???
  • ??????final?boolean?isVertical?=?mVertical;???
  • ?????????
  • ??????drawChild(canvas,?handle,?drawingTime);???
  • ?????????
  • ??????if?(mTracking?||?mAnimating)?{???
  • ??????????final?Bitmap?cache?=?mContent.getDrawingCache();???
  • ??????????if?(cache?!=?null)?{???
  • ??????????????if?(isVertical)?{???
  • ??????????????????canvas.drawBitmap(cache,?0,?handle.getBottom(),?null);???
  • ??????????????}?else?{???
  • ??????????????????canvas.drawBitmap(cache,?handle.getRight(),?0,?null);???????????????????????
  • ??????????????}???
  • ??????????}?else?{???
  • ??????????????canvas.save();???
  • ??????????????canvas.translate(isVertical???0?:?handle.getLeft()?-?mTopOffset,???
  • ??????????????????????isVertical???handle.getTop()?-?mTopOffset?:?0);???
  • ??????????????drawChild(canvas,?mContent,?drawingTime);???
  • ??????????????canvas.restore();???
  • ??????????}???
  • ??????}?else?if?(mExpanded)?{???
  • ??????????drawChild(canvas,?mContent,?drawingTime);???
  • ??????}???
  • ??}??
  • ? ? ? ?好了,這個就是View里面的內容,關于事件監聽我們這里就不再詳細描述,自定義組件的話,在寫完深入ViewGroup中會有一個專門的專題,而ViewGroup中也會去深化View中一些東西。

    總結

    以上是生活随笔為你收集整理的深入理解Android中View的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。