中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

android如何自定義控件

發布時間:2021-12-03 10:44:14 來源:億速云 閱讀:188 作者:小新 欄目:移動開發

這篇文章將為大家詳細講解有關android如何自定義控件,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

自定義控件中除了繪制流程涉及到的測量布局繪制的api, 還有一些api使用的也比較頻繁,這里叔做了一下總結,也請同學們補充或糾正,讓叔也漲漲姿勢。

inflate

inflate方法常常用來解析一個xml布局文件,在自定義組合式控件中常常使用,使用的姿勢包括 :

View.inflate(context, resource, root)LayoutInflater.from(getContext()).inflate(resource, root);

而View.inflate其實還是調用的LayoutInflater去解析一個xml:

public static View inflate(Context context, int resource, ViewGroup root) {  LayoutInflater factory = LayoutInflater.from(context); return factory.inflate(resource, root);  }

所以這兩種姿勢沒啥區別,這里來討論一下inflate(resouce,  root)的返回值,參數resource是布局資源,root是傳入的一個根節點。如果root傳入一個null,inflate就會解析resource對應的xml,返回這個xml中的根節點,如果root傳入不為null,inflate會解析這個xml布局并且添加到根節點root下,然后返回根節點root。

另外還有帶三個參數的inflate方法:

inflate(int resource, ViewGroup root, boolean attachToRoot)

這里多了一個參數attachToRoot,如果root為null,則返回解析后的xml布局中的根節點;如果root不為null,attachToRoot為true,inflate會解析這個xml布局并且添加到根節點root下,然后返回根節點root;如果root不為null,attachToRoot為false,inflate會解析這個xml布局但不會添加到根節點root下,然后返回解析后的xml布局中的根節點,這時候root的作用只是為xml中的根節點提供布局參數的屬性,因為xml中的根節點不知道自己的父容器是誰,所以如果沒有人給它提供的話,它的布局參數就會失效。

onFinishInflate

onFinishInflate是當所有的孩子都解析完后的一個調用。比如我們自定義一個ViewGroup,想要去找到孩子做一些設置,這時候如果在自定義ViewGroup的構造函數去findViewById的話,會返回一個null,因為此時孩子還沒有解析好,也就是還沒有生出來。這時候我們可以去覆寫onFinishInflate,當孩子解析好后再去find。

requestLayout

關于requestLayout的介紹比較多,requestLayout()方法會觸發measure過程和layout過程,不會調用draw過程,也不會重新繪制任何View包括該調用者本身。

onSizeChange(int w, int h, int oldw, int oldh)

onSizeChange是控件的大小發生變化的時候的調用,它的調用軌跡是layout->setFrame->sizeChange->onSizeChange。當控件***次布局時肯定會被調用到,我們覆寫該方法可以獲取到控件的大小。所以這個方法通常被用來在里面初始化跟控件大小相關的成員變量。

invalidate

invalidate使用的非常頻繁,它會觸發View的重新繪制,也就是繪制流程的draw過程,但不會調用測量和布局過程。

postInvalidate

我們都知道Android的UI是單線程模型,只能在主線程更新UI,所以我們只能在主線程調用invalidate,如果想要在子線程更新ui,可以使用handler發送一個msg到主線程,然后在處理msg的時候去調用invalidate。另外,我們可以直接調用postInvalidate去在子線程更新UI,postInvalidate內部實現也是使用handler來發送msg到主線程然后調用invalidate。

setWillNotDraw

自定義ViewGroup通常是不會去繪制自己的,如果大家重寫ViewGroup里面的draw方法或者onDraw方法會發現它們根本就不會被調用到。但是如果給你的ViewGroup設置一個背景,就會發現draw方法和onDraw方法又都會走了。

我們知道ViewGroup本身是一個View,它的繪制是被其父容器發起的,具體的位置是在ViewGroup中的drawChild方法:

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {      return child.draw(canvas, this, drawingTime); }

注意這里的draw方法是帶三個參數的,與我們通常講的帶一個參數的draw方法不一樣。在View類中找到帶三個參數的draw方法,發現里面有這么一段代碼:

if (!hasDisplayList) {      // Fast path for layouts with no backgrounds       if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {              mPrivateFlags &= ~PFLAG_DIRTY_MASK;              dispatchDraw(canvas);     } else {              draw(canvas);     } }

從這里我們可以看出一點端倪,通常一個ViewGroup默認是會跳過繪制的,也即 (mPrivateFlags & PFLAG_SKIP_DRAW)  == PFLAG_SKIP_DRAW 會返回一個true,那么會直接走dispatchDraw方法去畫它自己的孩子去了,并不會調用帶一個參數的  draw(canvas) ,但是當這個ViewGroup有背景或者setWillNotDraw(false)時,就會走 draw(canvas)  方法。所以如果我們自定義一個ViewGroup并且想要實現它本身的繪制的話,就可以給它設置一個背景或者調用 setWillNotDraw(false)  。

onAttachedToWindow

onAttachedToWindow是當一個View綁定到window上時的調用,根據View類里面的對這個方法的注釋,onAttachedToWindow肯定會在onDraw方法之前調用。

在自定義控件里面,我們可以在onAttachedToWindow注冊一些廣播接收器,觀察者或者開啟一些任務,大家可以參考TextClock的里面的實現。

onDetachedFromWindow

onDetachedFromWindow對應于onAttachedToWindow,是當一個View從window上移除時的一個調用。如果在onAttachedWindow里面注冊了一些監聽,那么通常就要在onDetachedFromWindow里面反注冊。

ViewTreeObserver

ViewTreeObserver是視圖樹的觀察者,監聽一些視圖樹的全局變化,這些全局變化包括整個視圖樹的布局,開始繪制,觸摸模式的變化等。我們不能直接初始化ViewTreeObserver的對象,需要通過getViewTreeObserver()去獲取。

ViewTreeObserver.OnGlobalLayoutListener

當在一個視圖樹中全局布局發生改變或者視圖樹中的某個視圖的可視狀態發生改變的監聽器,一般的使用姿勢是:

getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {            @Override             public void onGlobalLayout() {                 getViewTreeObserver().removeGlobalOnLayoutListener(this);                //do something you like                 //for example, get view width or height height             }         });

ViewTreeObserver.OnPreDrawListener

當一個視圖樹將要繪制時的監聽器,一般的使用姿勢是:

getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {            @Override             public boolean onPreDraw() {                //do something before draw                 //for example, request a new layout                 return true;             }         });

關于“android如何自定義控件”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

祁阳县| 永德县| 扎囊县| 民乐县| 临沂市| 盘锦市| 齐河县| 晋宁县| 白城市| 甘孜县| 伊吾县| 肥乡县| 安平县| 萨迦县| 浠水县| 梅州市| 公主岭市| 五台县| 太白县| 酒泉市| 南岸区| 吉水县| 鄂伦春自治旗| 东乡县| 阿勒泰市| 古丈县| 葵青区| 嵊泗县| 屏山县| 二手房| 海宁市| 新安县| 大埔区| 青河县| 交口县| 集贤县| 密云县| 庆城县| 哈巴河县| 乐清市| 晋宁县|