自定義View公式

本文基本不說原理央星,只說流程、公式惫东、套路與“安全措施”莉给。

Step 1

構(gòu)造函數(shù)只需要用到兩個,其余兩個百分之95的人與需求不會用得到廉沮。

public class CustomView extends View {
    public CustomView(Context context) {
        super(context);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
}

很簡單颓遏,代碼new用到第一個構(gòu)造函數(shù),xml則會用到第二個構(gòu)造函數(shù)滞时。

Step 2

獲取xml配置的屬性叁幢,以及使用自定義的屬性

public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomView, 0, 0);
        float attr_1 = ta.getDimension(R.styleable.CustomView_attr_1, 0f);
        ta.recycle();
}

在第二個構(gòu)造函數(shù)使用TypeArray獲取xml中設置的attrs
注意使用完typearray記得調(diào)用recycle()
再看看自定義屬性是怎么寫
values -> attrs

<declare-styleable name="CustomView">
    <attr name="attr_1" format="dimension" />
</declare-styleable>

注意了漂洋,這里和上面獲取屬性遥皂,名稱是拼接起來的。如上面代碼刽漂,nameCustomView演训,而attrnameattr_1,那么通過typearray獲取attr_1屬性的值是使用R.styleable.CustomView_attr_1贝咙。
接著看看xml布局怎么使用自定義屬性

布局怎么使用自定義屬性
布局怎么使用自定義屬性2

第一種寫法AS會提示你修改成第二種寫法样悟,故我們也用第二種寫法,除非你的自定義屬性名稱與其他的自定義屬性名稱有沖突就可以使用第一種來解決(指定自定義屬性的路徑別名)庭猩。

Step 3

測量

  1. 單一view的測量
    measure()為final方法窟她,子類不可復寫,最后會調(diào)用````onMeasure()蔼水。onMeasure()這里進行你的需求來measure->setMeasureDimension()儲存測量后的子view的寬高震糖。如果在使用自定義view時,用了wrap_content趴腋。那么在onMeasure中就要調(diào)用setMeasuredDimension吊说,來指定view的寬高。如果使用的matchl_parent或者一個具體的dp值优炬。那么直接使用super.onMeasure``即可颁井。
  2. viewgroup的測量
    measure()->onMeasure()->測量子view->setMeasureDimension()
    在這里我們需要在onMeasure()測量子view。測量子view的方法一般有四種
    2.1 getChildAt(int index).可以拿到index上的子view蠢护。
    通過getChildCount得到子view的數(shù)目雅宾,再循環(huán)遍歷出子view。
    接著葵硕,subView.measure(int wSpec, int hSpec); //使用子view自身的測量方法
    2.2 或者調(diào)用viewGroup的測量子view的方法:
    //某一個子view眉抬,多寬,多高, 內(nèi)部加上了viewGroup的padding值
    measureChild(subView, int wSpec, int hSpec);
    2.3 //所有子view 都是 多寬宣决,多高, 內(nèi)部調(diào)用了measureChild方法
    measureChildren(int wSpec, int hSpec);
    2.4 //某一個子view,多寬,多高, 內(nèi)部加上了viewGroup的padding值贤惯、margin值和傳入的寬高wUsed孵构、hUsed
    measureChildWithMargins(subView, intwSpec, int wUsed, int hSpec, int hUsed);

Step 4

布局
view 的布局兩個步驟layout->onLayout
layout:計算自身位置調(diào)用setFrame()
onLayout:看下面解析

  1. 自定義View
    1.1 在單一的View中烟很,只會用到layout雾袱,而onLayout是個空實現(xiàn)芹橡。
  2. 自定義ViewGroup,調(diào)用layout()計算自身的位置煎殷,調(diào)用onLayout()遍歷子View并調(diào)用子View layout()確定自身子View的位置豪直。
    那么自定義ViewGroup一般套路就是
// 偽代碼弓乙,會省略方法的一些參數(shù)
onLayout(){
// 循環(huán)所有子View
        for (int i=0; i<getChildCount(); i++) {
            View child = getChildAt(i);   

            // 計算當前子View的四個位置值
            // 計算的邏輯需要自己實現(xiàn)剑梳,也是自定義View的關(guān)鍵
            ...

            // 對計算后的位置值進行賦值
            int mLeft  = Left
            int mTop  = Top
            int mRight = Right
            int mBottom = Bottom

            // 調(diào)用子view的layout()并傳遞計算過的參數(shù)
            // 從而計算出子View的位置
            child.layout(mLeft, mTop, mRight, mBottom);
        }
}

然后到一個注意點了

  • getWidth() / getHeight() = View最終的寬 / 高
  • getMeasuredWidth() / getMeasuredHeight() = View的測量的寬 / 高
在哪里賦值 賦值方法 何處可用
getMeasuredWidth() onMeasure() setDimension() onMeasure()后
getWidth() layout() setFrame() 在layout()調(diào)用后

那何種情況下兩者會值不同
just one

@Override
public void layout( int l , int t, int r , int b){
   super.layout(l锨咙,t追逮,r+10,b+200)
}

Step 5

draw繪制
view的繪制流程為:
繪制過程如下:

  1. 繪制view背景
  2. 繪制view內(nèi)容
  3. 繪制子View
  4. 繪制裝飾(漸變框骂倘,滑動條等等)

代碼流程為draw() ->1 ->2 onDraw() ->3 dispatchDraw() ->4
其中自定義View沒有子view诅需,故不需要第三步(空實現(xiàn))荧库,一般只需要完成第二步即可,在onDraw繪制我們需要的內(nèi)容场刑。
其次自定義ViewGroup有子view牵现,故需要第三步邀桑,但一般我們也無需理會(系統(tǒng)已經(jīng)為我們實現(xiàn)了該方法)壁畸,故是在onDraw繪制我們需要的內(nèi)容瓤摧,但但但但是照弥,一般viewgroup也無需繪制什么東西

又到注意事項:
上面說到viewgroup也無需繪制什么東西,故viewgroup需要在onDraw()繪制東西的時候悔常,需要調(diào)用setWillNotDraw(boolean willNotDraw)机打,設置為false片迅。這樣viewgroup才會執(zhí)行onDraw(),否則不執(zhí)行驱闷。

Step 6

刷新
Android中實現(xiàn)view的更新有兩組方法空免,一組是invalidate,另一組是postInvalidate扼菠,其中前者是在UI線程自身中使用娇豫,而后者在非UI線程中使用畅厢。
那么套路就是handler+invalidatepostInvalidateonDraw根據(jù)條件+invalidate

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市咪辱,隨后出現(xiàn)的幾起案子椎组,更是在濱河造成了極大的恐慌寸癌,老刑警劉巖蒸苇,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件味咳,死亡現(xiàn)場離奇詭異槽驶,居然都是意外死亡鸳兽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛋济,“玉大人炮叶,你說我怎么就攤上這事镜悉。” “怎么了旧困?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長矩距。 經(jīng)常有香客問我拗盒,道長,這世上最難降的妖魔是什么锥债? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任陡蝇,我火速辦了婚禮,結(jié)果婚禮上哮肚,老公的妹妹穿的比我還像新娘登夫。我一直安慰自己,他們只是感情好允趟,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布悼嫉。 她就那樣靜靜地躺著,像睡著了一般拼窥。 火紅的嫁衣襯著肌膚如雪戏蔑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天总棵,我揣著相機與錄音,去河邊找鬼鞍爱。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瓷蛙!你這毒婦竟也來了冠桃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤迹蛤,失蹤者是張志新(化名)和其女友劉穎逆趣,沒想到半個月后身坐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涯鲁,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡警绩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年混狠,在試婚紗的時候發(fā)現(xiàn)自己被綠了予弧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坠七。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡惶桐,死狀恐怖救恨,靈堂內(nèi)的尸體忽然破棺而出肠槽,到底是詐尸還是另有隱情秸仙,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布桩盲,位于F島的核電站捞蛋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏迹鹅。R本人自食惡果不足惜阀蒂,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望垄琐。 院中可真熱鬧边酒,春花似錦、人聲如沸狸窘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翻擒。三九已至氓涣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間陋气,已是汗流浹背劳吠。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留巩趁,地道東北人痒玩。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像议慰,于是被迫代替她去往敵國和親蠢古。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容