Android自訂控制項1,android自訂控制項
概述
Android已經為我們提供了大量的View供我們使用,但是可能有時候這些組件不能滿足我們的需求,這時候就需要自訂控制項了。自訂控制項對於初學者總是感覺是一種複雜的技術。因為裡面涉及到的知識點會比較多。但是任何複雜的技術後面都是一點點簡單知識的積累。通過對自訂控制項的學習去可以更深入的掌握android的相關知識點,所以學習android自訂控制項是很有必要的。記得以前學習總是想著去先理解很多知識點,然後再來學著自訂控制項,但是每次寫自訂控制項的時候總是不知道從哪裡下手啊。後來在學習的過程中發現自己跟著去寫一些簡單的自訂控制項,然後在這個過程中遇到了沒有掌握的知識點再去學習。不僅自訂控制項的能力有所提高。其它的知識也有了很好的鞏固和認識。所以,今天寫的是怎麼去自訂一個控制項。而不是裡面涉及到的細化知識點。一個東西我們Crowdsourced Security Testing道怎麼用,再去問為什麼。
自訂控制項需要考慮的點
根據Android Developers官網的介紹,自訂控制項你需要以下的步驟。(根據你的需要,某些步驟可以省略)
1、建立View2、處理View的布局3、繪製View4、與使用者進行互動5、最佳化已定義的View
上面列出的五項就是android官方給出的自訂控制項的步驟。每個步驟裡面又包括了很多細小的知識點。我們可以記住這五個點,並且瞭解每個點裡包含的小知識點。再加上一些自訂控制項的練習。不斷的將這些知識熟練於心,相信我們每個人都能夠定義出優秀的自訂控制項。接下來我們開始對上面列出的5個要點進行細化解說
自訂控制項5個要點詳細說明1、建立View繼承View
對Android有一些瞭解的朋友都知道,android為我們提供的很多View都是繼承與View的。所以我們自訂的View當然也是繼承於View,當然如果你要自訂的View擁有某些android已經提供的控制項的功能,你可以直接繼承於已經提供的控制項。
我們在使用android提供的控制項的時候,我們在.xml檔案中編輯了一個控制項,在啟動並執行時候就能夠看到和獲得這個控制項。我們自訂的控制項當然也要支援配置和一些自訂屬性,所以下面的構造方法就必須有了。這個構造方法允許我們在.xml檔案中建立和編輯我們自訂控制項的執行個體。
上面說了那麼多其實就是下面一段代碼。
定義自訂屬性
大部分情況我們的自訂View需要有更多的靈活性,比如我們在xml中指定了顏色大小等屬性,在程式運行時候控制項就能展示出相應的顏色和大小。所以我們需要自訂屬性
class PieChart extends View {//繼承View public PieChart(Context context, AttributeSet attrs) { super(context, attrs); }//為了支援.xml中進行建立和編輯}
自訂屬性通常寫在在res/values/attrs.xml檔案中 下面是自訂屬性的標準寫法
<declare-styleable name="PieChart"> <attr name="showText" format="boolean" /> <attr name="labelPosition" format="enum"> <enum name="left" value="0"/> <enum name="right" value="1"/> </attr> </declare-styleable>
這段代碼聲明了兩個自訂屬性,showText和labelPosition,它們都是屬於styleable PieChart的,為了方便,一般styleable的name和我們自訂控制項的類名一樣。自訂控制項定義好了之後就是使用了。
使用程式碼範例
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews"> <com.example.customviews.charting.PieChart custom:showText="true" custom:labelPosition="left" /></LinearLayout>
使用自訂屬性的時候需要指定命名空間,固定寫法就是http://schemas.android.com/apk/res/你的包名。如果你是在android studio,也可以用http://schemas.android.com/apk/res/res-auto
擷取自訂屬性
在xml中設定了控制項自訂屬性,我們就需要拿到屬性做一些事情。否則定義自訂屬性就沒有意義了。
固定的擷取自訂屬性代碼如下
public PieChart(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.getTheme().obtainStyledAttributes( attrs, R.styleable.PieChart, 0, 0); try { mShowText = a.getBoolean(R.styleable.PieChart_showText, false); mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0); } finally { a.recycle(); }}
當我們在 xml中建立了一個view時,所有在xml中聲明的屬性都會被傳入到view的構造方法中的AttributeSet類型的參數當中。
通過調用Context的obtainStyledAttributes()方法返回一個TypedArray對象。然後直接用TypedArray對象擷取自訂屬性的值。
由於TypedArray對象是共用的資源,所以在擷取完值之後必須要調用recycle()方法來回收。
添加設定屬性事件
在xml中指定的自訂屬性只有在view被初始化的時候能夠擷取到,有時候我們可能在運行時做一些操作,這種情況就需要我們為自訂屬性設定getter和setter方法,以下代碼展示了自訂控制項暴露的set 和get方法
public boolean isShowText() { return mShowText;}public void setShowText(boolean showText) { mShowText = showText; invalidate(); requestLayout();}
重點看setShowText方法,在為mShowText賦值之後,調用了invalidate()和requestLayout()方法,我們自訂控制項的屬性發生改變之後,控制項的樣子也可能發生改變,在這種情況下就需要調用invalidate()方法讓系統去調用view的onDraw()重新繪製。同樣的,控制項屬性的改變可能導致控制項所佔的大小和形狀發生改變,所以我們需要調用requestLayout()來請求測量擷取一個新的布局位置。
2、處理View的布局.測量
一個View是在展示時總是有它的寬和高,測量View就是為了能夠讓自訂的控制項能夠根據各種不同的情況以合適的寬高去展示。提到測量就必須要提到onMeasure方法了。onMeasure方法是一個view確定它的寬高的地方。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { }
onMeasure方法裡有兩個重要的參數, widthMeasureSpec, heightMeasureSpec。在這裡你只需要記住它們包含了兩個資訊:mode和size
我們可以通過以下代碼拿到mode和size
int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);
那麼擷取到的mode和size又代表了什麼呢?
mode代表了我們當前控制項的父控制項告訴我們控制項,你應該按怎樣的方式來布局。
mode有三個可選值:EXACTLY, AT_MOST, UNSPECIFIED。它們的含義是:
EXACTLY:父控制項告訴我們子控制項了一個確定的大小,你就按這個大小來布局。比如我們指定了確定的dp值和macth_parent的情況。
AT_MOST:當前控制項不能超過一個固定的最大值,一般是wrap_content的情況。
UNSPECIFIED:當前控制項沒有限制,要多大就有多大,這種情況很少出現。
size其實就是父布局傳遞過來的一個大小,父布局希望當前布局的大小。
下面是一個重寫onMeasure的固定虛擬碼寫法:
if mode is EXACTLY{ 父布局已經告訴了我們當前布局應該是多大的寬高, 所以我們直接返回從measureSpec中擷取到的size }else{ 計算出希望的desiredSize if mode is AT_MOST 返回desireSize和specSize當中的最小值 else: 返回計算出的desireSize}
上面的代碼雖然基本都是固定的,但是需要寫的步驟還是有點多,如果你不想自己寫,你也可以用android為我們提供的工具方法:resolveSizeAndState,該方法需要傳入兩個參數:我們測量的大小和父布局希望的大小,它會返回根據各種情況返回正確的大小。這樣我們就可以不需要實現上面的模版,只需要計算出想要的大小然後調用resolveSizeAndState。之後在做自訂View的時候我會展示用這個方法來確定view的大小。
計算出height和width之後在onMeasure中別忘記調用setMeasuredDimension()方法。否則會出現運行時異常。
計算一些自訂控制項需要的值 onSizeChange()
onSizeChange() 方法在view第一次被指定了大小值、或者view的大小發生改變時會被調用。所以一般用來計算一些位置和與view的size有關的值。
3、繪製View(Draw)
一旦自訂控制項被建立並且測量代碼寫好之後,接下來你就可以實現onDraw()來繪製View了,onDraw方法包含了一個Canvas叫做畫布的參數,onDraw()簡單來說就兩點:
Canvas決定要去畫什麼
Paint決定怎麼畫
比如,Canvas提供了畫線方法,Paint就來決定線的顏色。Canvas提供了畫矩形,Paint又可以決定讓矩形是空心還是實心。
在onDraw方法中開始繪製之前,你應該讓畫筆Paint對象的資訊初始化完畢。這是因為View的重新繪製是比較頻繁的,這就可能多次調用onDraw,所以初始化的代碼不應該放在onDraw方法裡。
Canvas和Paint提供的很多方法在本文中就不一一列舉了。大家可以自己去查看api,之後的文章中我們也會用到,現在你只需要理解定義的大體步驟,然後再慢慢鍛煉加深理解。
4、與使用者進行互動
也許某些情況你的自訂控制項不僅僅只是展示一個漂亮的內容,還需要支援使用者點擊,拖動等等操作,這時候我們的自訂控制項就需要做使用者互動這一步驟了。
在android系統中最常見的事件就是觸摸事件了,它會調用view的onTouchEvent(android.view.MotionEvent).重寫這個方法去處理我們的事件邏輯
@Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
對與onTouchEvent方法相信大家都有一定瞭解,如果不瞭解的話,你就先記住這是處理Touch的地方。
現在的觸控有了更多的手勢,比如輕點,快速滑動等等,所以在支援特殊使用者互動的時候你需要用到android提供的GestureDetector.你只需要實現GestureDetector中相對應的介面,並且處理相應的回調方法。
除了手勢之外,如果有移動之類的情況我們還需要讓滑動的動畫顯示得比較平滑。動畫應該是平滑的開始和結束,而不是突然消失突然開始。在這種情況下,我們需要用到屬性動畫 property animation framework
由於與使用者進行互動中涉及到的知識舉例子會比較多,所以我在之後的自訂控制項文章中再講解。
5、最佳化你的自訂View
在上面的步驟結束之後,其實一個完善的自訂控制項已經出來了。接下來你要做的只是確保自訂控制項運行得流暢,官方的說法是:為了避免你的控制項看得來遲緩,確保動畫始終保持每秒60幀.
下面是官網給出的最佳化建議:
1、避免不必要的代碼
2、在onDraw()方法中不應該有會導致記憶體回收的代碼。
3、儘可能少讓onDraw()方法調用,大多數onDraw()方法調用都是手動調用了invalidate()的結果,所以如果不是必須,不要調用invalidate()方法。
總結
到這裡基本上自訂控制項的大致步驟和可能涉及到的知識點都說完了。看一張圖。
圖片基本描述了自訂控制項的大致流程,右邊是相對應的流程所涉及到的一些知識點。可以看到自訂控制項包括了很多android知識。所以學習android自訂控制項是很有必要的。當你能夠輕鬆的定義出一個完美的自訂控制項的時候,相信你已經是大牛了。
本篇文章只對自訂控制項的步驟進行了大體的介紹,如果你對自訂的流程還不清楚,請你記住上面所說的步驟,至少也要有個大致的印象。在之後的文章中,我在按照這個步驟去講解一些自訂控制項,用這裡列出的步驟去一步步實現我們想要的自訂控制項,歡迎關注我!
如果文章有錯誤的地方還請評論指正,看完了文章順便動手點個贊評論下唄!謝謝。