Android自定義View

轉(zhuǎn)自:http://www.reibang.com/p/e9d8420b1b9c

自定義View是Android開發(fā)者必須了解的基礎(chǔ)

今天钙姊,我將手把手教你寫一個自定義View绍刮,并理清自定義View所有應(yīng)該的注意點

閱讀本文前,請先閱讀我寫的一系列自定義View文章

(1)自定義View基礎(chǔ) - 最易懂的自定義View原理系列

(2)自定義View Measure過程 - 最易懂的自定義View原理系列

(3)自定義View Layout過程 - 最易懂的自定義View原理系列

(4)自定義View Draw過程- 最易懂的自定義View原理系列

Android事件分發(fā)機制詳解:史上最全面轻姿、最易懂

1. 自定義View的分類

自定義View一共分為兩大類腹尖,具體如下圖:

分類

2. 具體介紹 & 使用場景

對于自定義View的類型介紹及使用場景如下圖:

具體介紹 & 使用場景

3. 使用注意點

在使用自定義View時有很多注意點(坑)驾茴,希望大家要非常留意:

使用注意點

3.1 支持特殊屬性

支持wrap_content

如果不在onMeasure()中對wrap_content作特殊處理,那么wrap_content屬性將失效

具體原因請看文章:為什么你的自定義View wrap_content不起作用倒槐?

支持padding & margin

如果不支持,那么padding和margin(ViewGroup情況)的屬性將失效

對于繼承View的控件附井,padding是在draw()中處理

對于繼承ViewGroup的控件导犹,padding和margin會直接影響measure和layout過程

3.2 多線程應(yīng)直接使用post方式

View的內(nèi)部本身提供了post系列的方法唱凯,完全可以替代Handler的作用,使用起來更加方便谎痢、直接磕昼。

3.3 避免內(nèi)存泄露

主要針對View中含有線程或動畫的情況:當View退出或不可見時,記得及時停止該View包含的線程和動畫节猿,否則會造成內(nèi)存泄露問題票从。

啟動或停止線程/ 動畫的方式:

啟動線程/ 動畫:使用view.onAttachedToWindow(),因為該方法調(diào)用的時機是當包含View的Activity啟動的時刻

停止線程/ 動畫:使用view.onDetachedFromWindow()滨嘱,因為該方法調(diào)用的時機是當包含View的Activity退出或當前View被remove的時刻

3.4 處理好滑動沖突

當View帶有滑動嵌套情況時峰鄙,必須要處理好滑動沖突,否則會嚴重影響View的顯示效果太雨。

4. 具體實例

接下來吟榴,我將用自定義View中最常用的繼承View來說明自定義View的具體應(yīng)用和需要注意的點

4.1 繼承VIew的介紹

Paste_Image.png

在下面的例子中,我將講解:

如何實現(xiàn)一個基本的自定義View(繼承VIew)

如何自身支持wrap_content & padding屬性

如何為自定義View提供自定義屬性(如顏色等等)

實例說明:畫一個實心圓

4.2 具體步驟

創(chuàng)建自定義View類(繼承View類)

布局文件添加自定義View組件

注意點設(shè)置(支持wrap_content & padding屬性自定義屬性等等)

下面我將逐個步驟進行說明:

步驟1:創(chuàng)建自定義View類(繼承View類)

CircleView.java

// 用于繪制自定義View的具體內(nèi)容// 具體繪制是在復(fù)寫的onDraw()內(nèi)實現(xiàn)publicclassCircleViewextendsView{// 設(shè)置畫筆變量Paint mPaint1;// 自定義View有四個構(gòu)造函數(shù)// 如果View是在Java代碼里面new的囊扳,則調(diào)用第一個構(gòu)造函數(shù)publicCircleView(Context context){super(context);// 在構(gòu)造函數(shù)里初始化畫筆的操作init();? ? }// 如果View是在.xml里聲明的吩翻,則調(diào)用第二個構(gòu)造函數(shù)// 自定義屬性是從AttributeSet參數(shù)傳進來的publicCircleView(Context context,AttributeSet attrs){super(context, attrs);? ? ? ? init();? ? }// 不會自動調(diào)用// 一般是在第二個構(gòu)造函數(shù)里主動調(diào)用// 如View有style屬性時publicCircleView(Context context,AttributeSet attrs,intdefStyleAttr ){super(context, attrs,defStyleAttr);? ? ? ? init();? ? }//API21之后才使用// 不會自動調(diào)用// 一般是在第二個構(gòu)造函數(shù)里主動調(diào)用// 如View有style屬性時publicCircleView(Context context, AttributeSet attrs,intdefStyleAttr,intdefStyleRes){super(context, attrs, defStyleAttr, defStyleRes);? ? }// 畫筆初始化privatevoidinit(){// 創(chuàng)建畫筆mPaint1 =newPaint ();// 設(shè)置畫筆顏色為藍色mPaint1.setColor(Color.BLUE);// 設(shè)置畫筆寬度為10pxmPaint1.setStrokeWidth(5f);//設(shè)置畫筆模式為填充mPaint1.setStyle(Paint.Style.FILL);? ? }// 復(fù)寫onDraw()進行繪制? @OverrideprotectedvoidonDraw(Canvas canvas){super.onDraw(canvas);// 獲取控件的高度和寬度intwidth = getWidth();intheight = getHeight();// 設(shè)置圓的半徑 = 寬,高最小值的2分之1intr = Math.min(width, height)/2;// 畫出圓(藍色)// 圓心 = 控件的中央,半徑 = 寬,高最小值的2分之1canvas.drawCircle(width/2,height/2,r,mPaint1);? ? }}

特別注意:

View的構(gòu)造函數(shù)一共有4個,具體使用請看:深入理解View的構(gòu)造函數(shù)

理解View的構(gòu)造函數(shù)

對于繪制內(nèi)容為何在復(fù)寫onDraw()里實現(xiàn)锥咸,具體請看我寫的文章:自定義View Draw過程- 最易懂的自定義View原理系列(4)

步驟2:在布局文件中添加自定義View類的組件

activity_main.xml

步驟3:在MainActivity類設(shè)置顯示

MainActivity.java

publicclassMainActivityextendsAppCompatActivity{@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);? ? ? ? setContentView(R.layout.activity_main);? ? }}

效果圖

好了狭瞎,至此,一個基本的自定義View已經(jīng)實現(xiàn)了搏予。接下來繼續(xù)看自定義View所有應(yīng)該注意的點:

如何手動支持wrap_content屬性

如何手動支持padding屬性

如何為自定義View提供自定義屬性(如顏色等等)

a. 手動支持wrap_content屬性

先來看wrap_content & match_parent屬性的區(qū)別

// 視圖的寬和高被設(shè)定成剛好適應(yīng)視圖內(nèi)容的最小尺寸android:layout_width="wrap_content"http:// 視圖的寬和高延伸至充滿整個父布局android:layout_width="match_parent"http:// 在Android API 8之前叫作"fill_parent"

如果不手動設(shè)置支持wrap_content屬性熊锭,那么wrap_content屬性是不會生效(顯示效果同match_parent)

具體原因 & 解決方案請看我寫的文章:為什么你的自定義View wrap_content不起作用?

b. 支持padding屬性

padding屬性:用于設(shè)置控件內(nèi)容相對控件邊緣的邊距雪侥;

區(qū)別與margin屬性(同樣稱為:邊距):控件邊緣相對父控件的邊距(父控件控制)碗殷,具體區(qū)別如下:

Paste_Image.png

如果不手動設(shè)置支持padding屬性,那么padding屬性在自定義View中是不會生效的速缨。

解決方案

繪制時考慮傳入的padding屬性值(四個方向)锌妻。

在自定義View類的復(fù)寫onDraw()進行設(shè)置

CircleView.java

// 僅看復(fù)寫的onDraw()@OverrideprotectedvoidonDraw(Canvas canvas){super.onDraw(canvas);// 獲取傳入的padding值finalintpaddingLeft = getPaddingLeft();finalintpaddingRight = getPaddingRight();finalintpaddingTop = getPaddingTop();finalintpaddingBottom = getPaddingBottom();// 獲取繪制內(nèi)容的高度和寬度(考慮了四個方向的padding值)intwidth = getWidth() - paddingLeft - paddingRight ;intheight = getHeight() - paddingTop - paddingBottom ;// 設(shè)置圓的半徑 = 寬,高最小值的2分之1intr = Math.min(width, height)/2;// 畫出圓(藍色)// 圓心 = 控件的中央,半徑 = 寬,高最小值的2分之1canvas.drawCircle(paddingLeft+width/2,paddingTop+height/2,r,mPaint1);? ? }

效果圖

c. 提供自定義屬性

系統(tǒng)自帶屬性,如

// 基本是以android開頭android:layout_width="match_parent"android:layout_height="match_parent"android:background="#000000"android:padding="30dp"

但有些時候需要一些系統(tǒng)所沒有的屬性鸟廓,稱為自定義屬性

使用步驟有如下:

在values目錄下創(chuàng)建自定義屬性的xml文件

在自定義View的構(gòu)造方法中解析自定義屬性的值

在布局文件中使用自定義屬性

下面我將對每個步驟進行具體介紹

步驟1:在values目錄下創(chuàng)建自定義屬性的xml文件

attrs_circle_view.xml

對于自定義屬性類型 & 格式如下:

<--1.reference:使用某一資源ID-->// 使用格式? // 1. Java代碼? private int ResID;? private Drawable ResDraw;? ResID = typedArray.getResourceId(R.styleable.SuperEditText_background, R.drawable.background); // 獲得資源ID? ResDraw = getResources().getDrawable(ResID); // 獲得Drawble對象? // 2. xml代碼<--2.color:顏色值-->// 格式使用<--3.boolean:布爾值-->// 格式使用<--4.dimension:尺寸值-->// 格式使用:<--5.float:浮點值-->// 格式使用<--6.integer:整型值-->// 格式使用<--7.string:字符串-->// 格式使用<--8.fraction:百分數(shù)-->// 格式使用<--9.enum:枚舉值-->// 格式使用<--10.flag:位或運算-->从祝、// 使用<--特別注意:屬性定義時可以指定多種類型值-->// 使用

步驟2:在自定義View的構(gòu)造方法中解析自定義屬性的值

此處是需要解析circle_color屬性的值

// 該構(gòu)造函數(shù)需要重寫publicCircleView(Context context, AttributeSet attrs){this(context, attrs,0);// 原來是:super(context,attrs);init();publicCircleView(Context context, AttributeSet attrs,intdefStyleAttr){super(context, attrs, defStyleAttr);// 加載自定義屬性集合CircleViewTypedArray a = context.obtainStyledAttributes(attrs,R.styleable.CircleView);// 解析集合中的屬性circle_color屬性// 該屬性的id為:R.styleable.CircleView_circle_color// 將解析的屬性傳入到畫圓的畫筆顏色變量當中(本質(zhì)上是自定義畫圓畫筆的顏色)// 第二個參數(shù)是默認設(shè)置顏色(即無指定circle_color情況下使用)mColor = a.getColor(R.styleable.CircleView_circle_color,Color.RED);// 解析后釋放資源a.recycle();? ? ? ? init();

步驟3:在布局文件中使用自定義屬性

activity_main.xml

xmlns:app="http://schemas.android.com/apk/res-auto"? ? xmlns:tools="http://schemas.android.com/tools"? ? android:layout_width="match_parent"? ? android:layout_height="match_parent"? ? tools:context="scut.carson_ho.diy_view.MainActivity"? ? >app:circle_color="#FF4081"? ? ? ? />

Paste_Image.png

至此,一個較為規(guī)范的自定義View已經(jīng)完成了引谜。

完整代碼下載

Carson_Ho的github:自定義View的具體應(yīng)用

5. 總結(jié)

本文對自定義View的具體應(yīng)用和注意點進行了全面分析

如果希望繼續(xù)了解自定義View的原理牍陌,請參考我寫的文章:

(1)自定義View基礎(chǔ) - 最易懂的自定義View原理系列

(2)自定義View Measure過程 - 最易懂的自定義View原理系列

(3)自定義View Layout過程 - 最易懂的自定義View原理系列

(4)自定義View Draw過程- 最易懂的自定義View原理系列

Android事件分發(fā)機制詳解:史上最全面、最易懂

Canvas類的最全面詳解 - 自定義View應(yīng)用系列

Path類的最全面詳解 - 自定義View應(yīng)用系列

作者:Carson_Ho

鏈接:http://www.reibang.com/p/e9d8420b1b9c

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末员咽,一起剝皮案震驚了整個濱河市毒涧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贝室,老刑警劉巖契讲,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仿吞,死亡現(xiàn)場離奇詭異,居然都是意外死亡捡偏,警方通過查閱死者的電腦和手機唤冈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門凡恍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晶衷,“玉大人,你說我怎么就攤上這事遣钳⊥埽” “怎么了傅物?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長琉预。 經(jīng)常有香客問我董饰,道長,這世上最難降的妖魔是什么圆米? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任卒暂,我火速辦了婚禮,結(jié)果婚禮上榨咐,老公的妹妹穿的比我還像新娘介却。我一直安慰自己谴供,他們只是感情好块茁,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著桂肌,像睡著了一般数焊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上崎场,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天佩耳,我揣著相機與錄音,去河邊找鬼谭跨。 笑死干厚,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的螃宙。 我是一名探鬼主播蛮瞄,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谆扎!你這毒婦竟也來了挂捅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤堂湖,失蹤者是張志新(化名)和其女友劉穎闲先,沒想到半個月后状土,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡伺糠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年蒙谓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片训桶。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡彼乌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渊迁,到底是詐尸還是另有隱情慰照,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布琉朽,位于F島的核電站毒租,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏箱叁。R本人自食惡果不足惜墅垮,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耕漱。 院中可真熱鬧算色,春花似錦、人聲如沸螟够。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妓笙。三九已至若河,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寞宫,已是汗流浹背萧福。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辈赋,地道東北人鲫忍。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像钥屈,于是被迫代替她去往敵國和親悟民。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

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

  • 【Android 自定義View】 [TOC] 自定義View基礎(chǔ) 接觸到一個類焕蹄,你不太了解他逾雄,如果貿(mào)然翻閱源碼只...
    Rtia閱讀 3,936評論 1 14
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,524評論 25 707
  • 自定義View的有好幾種分類,可以分成4種:1.特定的View的子類:Android的API已經(jīng)為我們提供了不少可...
    StChris閱讀 914評論 0 7
  • 通過前面的《View的事件體系》以及《View的工作原理》,對于自定義View已經(jīng)有了比較充分的了解鸦泳,接下來就可以...
    聽媽媽的話閱讀 290評論 0 1
  • 可能因芒果粉的緣故,對長沙格外有親近感钾麸。當水藍兒說她來自長沙時更振,距離一下就把我們拉近了。于是我們的交流就從芒果臺的...
    補拙莫如勤LV閱讀 262評論 2 3