寫自定義view該有的的流程和思路

因?yàn)楣ぷ髟颍雽懸黄远xview的初級心得筛璧。
一卧波、一般而言寫自定義view有大體6個(gè)步驟(以下順序不分先后):

  1. 繼承View的某個(gè)子類,包括ViewGroup的子類(畢竟ViewGroup也是View的子類嘛╮(╯_╰)╭) 2. 重寫繼承的父類View的一些特定函數(shù)及常用的三個(gè):(測量measure),(放置layout)恃锉,(繪制draw)3.為自定義View類增加屬性(主要是在那三個(gè)重寫的構(gòu)造方法里)4.繪制控件(代碼形式導(dǎo)入布局)5.響應(yīng)用戶事件(單擊搀菩、輸入文字、觸摸破托、滑動等等~~)6.定義回調(diào)函數(shù)(相當(dāng)于反饋信息嘛)
    二肪跋、針對繼承對象的不同自定義View分為繼承View 與ViewGroup兩種的情況,我上面2里的所說的常用三個(gè)使用上有所區(qū)別土砂。
    測量measure:
    View:
    普通View的onMeasure邏輯大同小異州既,基本都是測量自身內(nèi)容和背景,然后根據(jù)父View傳遞過來的MeasureSpec進(jìn)行最終的大小判定萝映,例如TextView會根據(jù)文字的長度吴叶,文字的大小,文字行高序臂,文字的行寬蚌卤,顯示方式,背景圖片奥秆,以及父View傳遞過來的模式和大小最終確定自身的大小逊彭。具體的View寬高測量是調(diào)用了 setMeasuredDimension() 方法:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } 

onMeasure通過父View傳遞過來的大小和模式,以及自身的背景圖片的大小得出自身最終的大小构订,通過setMeasuredDimension()方法設(shè)置給mMeasuredWidth和mMeasuredHeight侮叮。 ViewGroup:
ViewGroup本身沒有實(shí)現(xiàn)onMeasure(但是!有setMeasuredDimension()方法)鲫咽,但是他的子類(比如:四大布局控件)都有各自的實(shí)現(xiàn)签赃,通常他們都是通過measureChildWithMargins()這種測量內(nèi)部子view的方法來遍歷內(nèi)部,測量子View分尸。當(dāng)所有的子View都測量完畢后锦聊,才根據(jù)父View傳遞過來的模式和大小來最終決定自身的大小。
** 注意事項(xiàng):如果子View被GONE的將不參與測量箩绍。**
ViewGroup一般都在測量完所有子View后才會調(diào)用setMeasuredDimension()設(shè)置自身大小孔庭。
經(jīng)過measure 完成后,我們就可以通過getMeasuredWidth/Height 獲取View 的寬高材蛛。 放置layout:
View:
普通View中的onLayout()這個(gè)函數(shù)為空函數(shù)圆到。所以不用理會,想想也是的吧卑吭,如果你繼承的是view芽淡,你還有擺放你里面的內(nèi)容嗎?如果里面有東西需要你的擺放豆赏,那么挣菲,這個(gè)view不就是父view了富稻!這個(gè)不就該是繼承的是ViewGroup。好的白胀,往下看椭赋。
ViewGroup:
對于ViewGroup而言,循環(huán)遍歷所有子View是主要的思想;蚋堋D恼!因此如果我們繼承ViewGroup 我們需要遍歷執(zhí)行所有的child.layout()向抢。
Layout方法中接受四個(gè)參數(shù)认境,是由父View提供,指定了子View在父View中的左笋额、上元暴、右篷扩、下的位置兄猩。父View在指定子View的位置時(shí)通常會根據(jù)子View在measure中測量的大小來決定。注意事項(xiàng):子View的位置通常還受到父View的orientation鉴未,gravity枢冤,padding,子View的margin等等屬性的影響哦铜秆,我相信寫過在xml寫過布局的各位大大肯定是了解的吧淹真。
ViewGroup中的onLayout()方法:

@Override protected abstract void onLayout(boolean changed, int l, int t, int r, int b); 

抽象就表示了繼承ViewGroup的子類布局控件,都要去重寫连茧。而這個(gè)重寫也就導(dǎo)致了核蘸,不同的布局方式。怎么重寫呢啸驯?舉個(gè)例子:我這里將第一個(gè)子控件通過layout()放置到左上角0,0 寬高是測量值客扎。

 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) {
View childView = getChildAt(0); childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight()); } 

繪制draw draw()的過程就是繪制View到屏幕上的過程,draw()的執(zhí)行遵循如下步驟:

  1. 繪制背景
    2.保存畫布的圖層來準(zhǔn)備色變
  2. 繪制內(nèi)容
    4.繪制children
    5.畫出褪色的邊緣和恢復(fù)層
  3. 繪制裝飾 比如scollbar
    2和5 可以跳過的罚斗。
    View:
    view中onDraw()是個(gè)空函數(shù)徙鱼,也就是說需要每個(gè)視圖根據(jù)想要展示的內(nèi)容來自行繪制,View是不會幫我們繪制內(nèi)容部分的针姿,因此需要每個(gè)視圖根據(jù)想要展示的內(nèi)容來自行繪制袱吆。如果你去觀察TextView、ImageView等類的源碼距淫,你會發(fā)現(xiàn)它們都有重寫onDraw()這個(gè)方法绞绒,并且在里面執(zhí)行了相當(dāng)不少的繪制邏輯: 在TextView中在該方法中繪制文字、光標(biāo)和CompoundDrawable;ImageView中相對簡單榕暇,只是繪制了圖片蓬衡。繪制的方式主要是借助Canvas這個(gè)類饲趋,它會作為參數(shù)傳入到onDraw()方法中,供給每個(gè)視圖使用撤蟆。Canvas這個(gè)類的用法非常豐富奕塑,基本可以把它當(dāng)成一塊畫布,在上面繪制任意的東西家肯,那么我們就來嘗試一下吧龄砰。

View 的繪制主要通過dispatchDraw(),先根據(jù)自身的padding剪裁畫布,所有的子View都將在畫布剪裁后的區(qū)域繪制讨衣。遍歷所有子View换棚,調(diào)用子View的computeScroll對子View的滾動值進(jìn)行計(jì)算。根據(jù)滾動值和子View在父View中的坐標(biāo)進(jìn)行畫布原點(diǎn)坐標(biāo)的移動反镇,根據(jù)子在父View中的坐標(biāo)計(jì)算出子View的視圖大小固蚤,然后對畫布進(jìn)行剪裁,請看下面的示意圖歹茶。
ViewGroup:
對于ViewGroup則不需要實(shí)現(xiàn)該函數(shù)夕玩,因?yàn)樽鳛槿萜魇恰皼]有內(nèi)容“的(但必須ViewGroup要有實(shí)現(xiàn)dispatchDraw()函數(shù),告訴子view去繪制自己)惊豺。注意事項(xiàng):dispatchDraw的邏輯其實(shí)比較復(fù)雜燎孟,但ViewGroup已經(jīng)處理好了,我們不必要重載該方法對子View進(jìn)行繪制事件的派遣分發(fā)尸昧。
三揩页、其他一些可以用來重寫的方法:
onTouchEvent定義觸屏事件來響應(yīng)用戶操作。 onKeyDown 當(dāng)按下某個(gè)鍵盤時(shí)
onKeyUp 當(dāng)松開某個(gè)鍵盤時(shí)
onTrackballEvent 當(dāng)發(fā)生軌跡球事件時(shí)
onSizeChange() 當(dāng)該組件的大小被改變時(shí)
onFinishInflate() 回調(diào)方法烹俗,當(dāng)應(yīng)用從XML加載該組件并用它構(gòu)建界面之后調(diào)用的方法
onWindowFocusChanged(boolean) 當(dāng)該組件得到爆侣、失去焦點(diǎn)時(shí)
onAttachedToWindow() 當(dāng)把該組件放入到某個(gè)窗口時(shí)
onDetachedFromWindow() 當(dāng)把該組件從某個(gè)窗口上分離時(shí)觸發(fā)的方法
onWindowVisibilityChanged(int): 當(dāng)包含該組件的窗口的可見性發(fā)生改變時(shí)觸發(fā)的方法

四、View的繪制流程
繪制流程函數(shù)調(diào)用關(guān)系如下圖(取來用之):

這里寫圖片描述

五:requestLayout() 幢妄、invalidate()兔仰、postInvalidate()
requestLayout(): 當(dāng)view確定自身已經(jīng)不再適合現(xiàn)有的區(qū)域時(shí),該view本身調(diào)用requestLayout()方法來要求parent view(父類的視圖)重新調(diào)用他的measure和layout來重新設(shè)置自己位置磁浇。特別是當(dāng)view的layoutparameter發(fā)生改變斋陪,并且它的值還沒能應(yīng)用到view上時(shí),這時(shí)候適合調(diào)用這個(gè)方法置吓。注意无虚,并不會不執(zhí)行ondraw。

invalidate()衍锚、postInvalidate(): 調(diào)用invalidate()友题、postInvalidate()會 界面刷新,執(zhí)行 draw 過程戴质。區(qū)別就是Invalidate不能直接在線程中調(diào)用度宦,因?yàn)樗沁`背了單線程模型:Android UI操作并不是線程安全的踢匣,并且這些操作必須在UI線程中調(diào)用。 鑒于此戈抄,如果要使用invalidate的刷新离唬,那我們就得配合handler的使用,使異步非ui線程轉(zhuǎn)到ui線程中調(diào)用划鸽,如果要在非ui線程中直接使用就調(diào)用postInvalidate方法即可输莺,這樣就省去使用handler的煩惱。

六裸诽、自定義控件的三種方式
1嫂用、 繼承已有的控件當(dāng)要實(shí)現(xiàn)的控件和已有的控件在很多方面比較類似, 通過對已有控件的擴(kuò)展來滿足要求。即:繼承TextView丈冬、Button這樣已有的View(包括項(xiàng)目里已有的自定義View)嘱函。
2、 繼承一個(gè)布局文件一般用于自定義組合控件埂蕊,在構(gòu)造函數(shù)中通過inflater和addView()方法加載自定義控件的布局文件形成圖形界面(不需要onDraw方法)往弓,就好像是把a(bǔ)ctivity的xml變成用自定義view的xml來表示绳泉。
3供璧、繼承view通過onDraw方法來繪制出組件界面。即繼承View,得到和TextView、Button這樣等級的View 匀们。

七、自定義屬性的兩種方法
1准给、在布局文件中直接加入屬性泄朴,在構(gòu)造函數(shù)中去獲得。
布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <rcjs.com.customview.ZYView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
       Text="rcjs"
        />
</RelativeLayout>

獲取屬性值:

  public ZYView(Context context, AttributeSet attrs) {
        super(context, attrs);
        int textId = attrs.getAttributeResourceValue(null, "Text", 0);
        String text = context.getResources().getText(textId).toString();
    }

2露氮、在res/values/ 下建立一個(gè)attrs.xml 來聲明自定義view的屬性祖灰。
可以定義的屬性有:

<declare-styleable name="名稱">//參考某一資源ID (name可以隨便命名)
<attr name="background" format="reference"/>
//顏色值
<attr name="textColor" format="color"/>
//布爾值
<attr name="focusable" format="boolean"/>
//尺寸值
<attr name="layout_width" format="dimension"/>
//浮點(diǎn)值
<attr name="fromAlpha" format="float"/>
//整型值
<attr name="frameDuration" format="integer"/>
//字符串
<attr name="text" format="string"/>
//百分?jǐn)?shù)
<attr name="pivotX" format="fraction"/>
//枚舉值
<attr name="orientation">
    <enum name="horizontal" value="0"/>
    <enum name="vertical" value="1"/>
</attr>
//位或運(yùn)算
<attr name="windowSoftInputMode">
    <flag name="stateUnspecified" value="0"/>
    <flag name="stateUnchanged" value="1"/>
</attr>
//多類型
<attr name="background" format="reference|color"/>
</declare-styleable> 

attrs.xml進(jìn)行屬性聲明

declare-styleable的name 就是自定義的名稱用于布局文件里去
attr的name是屬性名稱

<?xml version="1.0" encoding="utf-8"?>
<resources>
        <declare-styleable name="zyView">
            <attr name="Text" format="string"/>
            <attr name="textColor" format="color"/>
        </declare-styleable>
</resources>

添加到布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:zyView="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <rcjs.com.customview.ZYView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        zyView:Text="rcjs"
        />

</RelativeLayout>

注意事項(xiàng):
**命名空間: **
xmlns:前綴=”http://schemas.android.com/apk/res/包名(或res-auto)”,
前綴:+使用屬性畔规。

在構(gòu)造函數(shù)中獲取屬性值局扶,注意!Hā三妈!我想有一些人應(yīng)該會很郁悶 ,復(fù)制粘貼了自定義view.class后莫绣,發(fā)現(xiàn)自定義view的構(gòu)造方法里面獲得資源文件里的屬性時(shí)****畴蒲,看到R.styleable.XXX這個(gè),然后點(diǎn)擊時(shí)****找不到具體寫的地方对室。其實(shí)這個(gè)就在res -> values ->attrs里模燥。所以要記得去copy哦咖祭。

public class ZYView extends View { 
public ZYView(Context context) { 
super(context); 
} 
public ZYView(Context context, @Nullable AttributeSet attrs) { 
super(context, attrs);
//獲取資源文件里面的屬性,由于這里只有一個(gè)屬性值蔫骂,不用遍歷數(shù)組么翰,直接通過R文件拿出color值 
//把屬性放在資源文件里,方便設(shè)置和復(fù)用 
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.zyView); String text = a.getString(R.styleable.zyView_Text); 
int textColor = a.getColor(R.styleable.zyView_textColor, Color.WHITE); a.recycle(); 
}
}

八辽旋、結(jié)尾
這只是讓大家知道自定義view的制作需要什么和要哪些步驟硬鞍。像我這種完全一竅不通的,然后一下子去接觸自定義view的戴已,是會很糊涂的固该,所以,在此糖儡,小僧稍微筆記一波伐坏,助人助己。而具體的對自定義view的學(xué)習(xí)握联,請待續(xù)桦沉。。金闽。
當(dāng)然纯露,現(xiàn)在已有大佬們寫了很多博客。請參考這篇總的去學(xué)習(xí):
http://www.reibang.com/p/6aea80e1fa22

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末代芜,一起剝皮案震驚了整個(gè)濱河市埠褪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挤庇,老刑警劉巖钞速,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異嫡秕,居然都是意外死亡渴语,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門昆咽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驾凶,“玉大人,你說我怎么就攤上這事掷酗〉魑ィ” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵汇在,是天一觀的道長翰萨。 經(jīng)常有香客問我,道長糕殉,這世上最難降的妖魔是什么亩鬼? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任殖告,我火速辦了婚禮,結(jié)果婚禮上雳锋,老公的妹妹穿的比我還像新娘黄绩。我一直安慰自己,他們只是感情好玷过,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布爽丹。 她就那樣靜靜地躺著,像睡著了一般辛蚊。 火紅的嫁衣襯著肌膚如雪粤蝎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天袋马,我揣著相機(jī)與錄音初澎,去河邊找鬼。 笑死虑凛,一個(gè)胖子當(dāng)著我的面吹牛碑宴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播桑谍,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼延柠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了锣披?” 一聲冷哼從身側(cè)響起贞间,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盈罐,沒想到半個(gè)月后榜跌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盅粪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悄蕾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片票顾。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖帆调,靈堂內(nèi)的尸體忽然破棺而出奠骄,到底是詐尸還是另有隱情,我是刑警寧澤番刊,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布含鳞,位于F島的核電站,受9級特大地震影響芹务,放射性物質(zhì)發(fā)生泄漏蝉绷。R本人自食惡果不足惜鸭廷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望熔吗。 院中可真熱鬧辆床,春花似錦、人聲如沸桅狠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽中跌。三九已至咨堤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間漩符,已是汗流浹背一喘。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留陨仅,地道東北人津滞。 一個(gè)月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像灼伤,于是被迫代替她去往敵國和親触徐。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

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