Android進階——自定義View之View的繪製流程及實現onMeasure完全攻略
引言
Android實際專案開發中,自定義View不可或缺,而作為自定義View的一種重要實現方式——繼承View重繪尤其重要,前面很多文章基本總結了繼承View的基本流程:自定義屬性和繼承View重寫onDraw方法——>實現構造方法並完成相關初始化操作——>重寫onMeasure方法——>onSizeChanged()拿到view的寬高等資料——>重寫onLayout————>重寫onTouch實現互動——>定義互動回撥介面,但是由於當時的具體的業務需求並沒有詳解總結下關於onMeasure和onLayout方法,相信很多初學者都是處於知其然而不知其所以然,這篇文章就專門總結下。
一、View的系統架構
雖然前面已經總結過了,但是這在裡還是重申下,加深印象。總所周知,在Android中每一個控制元件都會再介面中佔據一塊矩形的區域,這和大多數系統的控制元件機制都差不多。Android中控制元件是通過構造樹的形式來管理的(所謂控制元件樹如下圖所示),主要分為View和ViewGroup兩大類,其中ViewGroup直接繼承自View,View作為系統所有可視元件的基類,而通過控制元件樹,上層控制元件負責下層子控制元件的測量與繪製(即先執行onMeasure——>onLayout——>onDraw的),並負責分發互動事件的即事件是先傳遞到ViewGroup的,再由ViewGroup決定是否傳遞給下層子View
二、View、ViewGroup的測量和繪製概述
Android中的GUI系統是客戶端和服務端配合的視窗系統,即後臺運行了一個繪製服務,每個應用程式都是該服務端的一個客戶端,當客戶端需要繪製時,首先請求服務端建立一個視窗,然後在視窗中進行具體的檢視內容繪製;對於每個客戶端而言,他們都感覺自己獨佔了螢幕,而對於服務端而言,它會給每一個客戶端視窗分配不同的層值,並根據使用者的互動情況動態改變視窗的層值,這就給使用者造成了所謂的前臺視窗和後臺視窗的概念。當然這是螢幕繪製的原理簡要描述,繪製離不開測量,無論是系統控制元件和自定義的View要想展示於介面之上都離不開測量工作。簡而言之,當Activity獲得焦點時,Activity將被通知要求繪製自己的佈局,從而Android framework接到Activity的訊息將會處理繪製過程,而Activity只需提供它的佈局的根節點
1、View的測量
由父級ViewGroup負責要求子級View進行測量和繪製。我們都知道每一個控制元件都會佔據一個矩形區域,但是Android系統在繪製前本身並不知道具體的大小和位置,所以它會先進行測量,主要是在View的onMeasure裡去實現(這也是我們在自定義View裡的構造方法裡,無論是呼叫getMeasureWidth抑或getWidth獲取寬度時得到的總是0的原因),而Android中還有一個功能類MeasureSpec(封裝了從父節點傳遞到子節點下的佈局資訊包括View的測量模式和大小)用於輔助測量View,當我們重寫了onMeasure方法之後,系統通過super.onMeasure方法去呼叫setMeasuredDimension(width, height)將測量的大小設定進去完成測量。
2、View的繪製
完成測量工作之後,View的根據ViewGroup傳人的測量值和模式,對自己寬高進行確定(onMeasure中完成),然後在onDraw中在Canvas上完成對自己的繪製。
3、ViewGroup的測量
ViewGroup需要管理子View,所有其中一項重要的職責就是負責子View的大小,當ViewGroup大小設定為wrap_content,ViewGroup會對子View進行層級遍歷,來決定自己的大小,而其他模式下則會取設定的值來為自己的大小。ViewGroup在測量時遍歷所有子View,從呼叫子View對應的onMeasure方法獲得子View的大小,完成測量之後再通過呼叫onLayout方法來決定子View的位置,同樣是通過遍歷呼叫子View的onLayout方法,最後在自己的onLayout中完成子View的位置佈局工作。
4、ViewGroup的繪製
ViewGroup通常不需要繪製,因為它本身沒有需要繪製的東西,所以不會觸發自己的onDraw方法,但如果指定了background屬性則會觸發自身的onDraw完成背景的繪製。但ViewGroup會通過dispatchDraw方法來繪製其子View,原理也是一樣通過遍歷子View呼叫其子View對應的onDraw方法來完成最終的繪製工作。
三、View.MeasureSpec和ViewGroup.LayoutParams
1、View.MeasureSpec
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
/**
* UNSPECIFIED 模式:
* 父View不對子View有任何限制,子View需要多大就多大
*/
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
/**
* EXACTYLY 模式:
* 父View已經測量出子Viwe所需要的精確大小,這時候View的最終大小
* 就是SpecSize所指定的值。對應於match_parent和精確數值這兩種模式
*/
public static final int EXACTLY = 1 << MODE_SHIFT;
/**
* AT_MOST 模式:
* 子View的最終大小是父View指定的SpecSize值,並且子View的大小不能大於這個值,
* 即對應wrap_content這種模式
*/
public static final int AT_MOST = 2 << MODE_SHIFT;
//將size和mode打包成一個32位的int型數值
//高2位表示SpecMode,測量模式,低30位表示SpecSize,某種測量模式下的規格大小
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
//將32位的MeasureSpec解包,返回SpecMode,測量模式
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
//將32位的MeasureSpec解包,返回SpecSize,某種測量模式下的規格大小
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
//...
}
View.MeasureSpec是View中的一個靜態內部類,封裝了從父級節點傳遞下來給子級節點的佈局需求資訊,每一個MeasureSpec體現的是子類的佈局的尺寸大小size(包括寬度或高度)和模式mode的需求,但是並不是子級的實際尺寸就必須是父級要求的,我們可以通過重寫onMeasure方法實現自己的規則,然後在子級中,而這裡的模式來源於父ViewGroup去解析子View對應的在佈局檔案中layout_width和layout_height值來決定採用什麼模式(至於怎麼解析,這是後話),其中主要有三種模式:UNSPECIFIED、EXACTLY、AT_MOST:
UNSPECIFIED:說明父級沒有對子級強加任何限制,子級可以是它想要的任何尺寸。用得比較少,表示子佈局被限制在一個最大值內,一般當childView設定其寬、高為wrap_content時,ViewGroup會將其設定為AT_MOST,換言之,表示子佈局想要多大就多大,一般出現在AadapterView的item的heightMode中、ScrollView的childView的heightMode中
EXACTLY:父級為子級決定了一個確切的尺寸,子級將會被強制賦予這些邊界限制,不管子級自己想要多大(View類onMeasure方法中只支援EXACTLY),換言之,表示設定了精確的值,一般當childView設定其寬、高為精確值、match_parent時,ViewGroup會將其設定為EXACTLY,即在佈局檔案中可以解析指定的具體尺寸和match_parent,不支援wrap_content。
AT_MOST:子級可以是自己指定的任意大小,但是有個上限。比如說當MeasureSpec.EXACTLY的父容器為子級決定了一個大小,子級大小隻能在這個父容器限制的範圍之內。即在佈局檔案中可以解析wrap_content,換言之,表示子佈局被限制在一個最大值內,一般當childView設定其寬、高為wrap_content時,ViewGroup會將其設定為AT_MOST。
方法 | 說明 |
---|---|
static int getMode(int measureSpec) | 獲取模式 |
static int getSize(int measureSpec) | 獲取尺寸 |
static int makeMeasureSpec(int size, int mode) | 根據指定的模式和尺寸建立對應的測量規則 |
2、ViewGroup.LayoutParams
ViewGroup.LayoutParams直接繼承於Object作為位置引數資訊的父類,是View用來告訴它的父容器它想要怎樣被放置的(包含高度、寬度、對齊方式、外邊距、內邊距等等),Android中的佈局資訊ViewGroup.LayoutParams家族來決定的,常見包括AbsListView.LayoutParams, AbsoluteLayout.LayoutParams, Gallery.LayoutParams, ViewGroup.MarginLayoutParams, ViewPager.LayoutParams, WindowManager.LayoutParams、ActionBar.LayoutParams, ActionMenuView.LayoutParams, AppBarLayout.LayoutParams, BaseCardView.LayoutParams, BoxInsetLayout.LayoutParams,CollapsingToolbarLayout.LayoutParams,CoordinatorLayout.LayoutParams,DrawerLayout.LayoutParams,FrameLayout.LayoutParams,GridLayout.LayoutParams, GridLayoutManager.LayoutParams, LinearLayout.LayoutParams, LinearLayoutCompat.LayoutParams,PercentFrameLayout.LayoutParams、RelativeLayout.LayoutParams等。不同的Layout提供了不同LayoutParams,它們共同承擔起整個Android 的佈局任務。
四、View中幾大重要的方法的意義和作用
繼承View/ViewGroup實現自定義View後,一般還需要複寫最基本的二、三個方法:onMeasure(),onSizeChanged()拿到view的寬高等資料、onLayout()、onDraw()。
onMeasure:用於本View寬高的測量,佈局複雜時可能觸發多次。ViewGroup的onMeasure則負責處理它children的測量工作。由於View預設的onMeasure()僅僅支援EXACTLY模式,也就是說如果不重寫onMeasure()方法的話則無法在正確解析佈局檔案裡的wrap_content,因為onMeasure()是Android提供給我們告訴系統自己定義的View的實際大小(是否是僅僅依賴於父級要求的,也就是說自主定義View大小的)的機會,同時也是提供了我們自定義的解析規則的方法(如果你願意,你可以完全實現match_parent和wrap_content和具體值一樣的效果),最終呼叫setMeasuredDimension(int ,int)完成最終的測量(因為onMeasure方法沒有返回值,所以測量的結果應該通過setMeasuredDimension方法告知系統)。
onSizeChanged():可拿到view的寬高等資料資訊
onLayout:常複寫於viewGroup的自定義子類。它有負責對它內部所有children進行處理,告知childrenView的位置,以正確擺放。ViewGroup中onLayout是抽象方法必須複寫,這是children位置能正確擺放的保證。依靠mLeft,mTop,mRight,mBottom這四個值,以坐上為原點,這四個值分別為對應邊到原點的距離。最後和onMeasure一樣,記得呼叫child.layout()方法。
onDraw:UI最終呈現的過程,使用者使用Paint(What to draw)、Canvas(How to
draw)兩個類完成自定義畫面。繪製的時候需要考慮下padding,與margin不同,padding是屬於本View的屬性,不同於margin(不需要自定義時做處理系統就能很好的使用margin),所以要在測量繪圖時考慮它:- 測量時:desireSize=實際所需size+相應方向的padding。
- 繪圖時:考慮padding,做相應的位移。
五、onMeasure方法詳解及實現
onMeasure方法是測量View及其內容的,決定measured width和measured height的,這個方法由 measure(int, int)方法喚起,子類可以重寫onMeasure來提供更加準確和有效的測量。(以前有一個約定:在重寫onMeasure方法的時候,必須呼叫 setMeasuredDimension(int,int)來儲存這個View經過測量得到的measured width and height。否則,將會由measure(int, int)方法丟擲一個IllegalStateException。)View類onMeasure方法中只支援EXACTLY,如果不重寫onMeasure的話就只支援EXACTLY模式。
1、onMeasure方法簽名
/**
*這兩個引數都是按趙View.MeasureSpec類來進行編碼的
*@param :widthMeasureSpec 父級提出的水平寬度要求
*@param :heightMeasureSpec 父級提出的垂直高度要求
*/
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
2、實現onMeasure方法的步驟
從父級傳遞過來的View.MeasureSpec物件裡獲取測量模式和尺寸
然後根據不同的模式,給出不同的測量值,(即實際值)寬高都採用一樣的機制,一般mode為EXACTLY時,直接使用父類傳遞過來的測量值specValue;mode為UNSPECIFIED時,直接指定為預設的大小(這個值需要我們自己定義);當mode為AT_MOST時也指定為預設的大小,但還需要我們拿指定的預設大小和測量值specValue比較取最小值。
呼叫父類測量方法setMeasuredDimension(測量寬度值,測量高度值)
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measure(widthMeasureSpec);
measure(heightMeasureSpec);
Log.e("onMeasure", "realWidth: " + realWidth + "realHeiht: " + realHeiht + "widthMeasureSpec" + widthMeasureSpec + "heightMeasureSpec" + heightMeasureSpec);
setMeasuredDimension(realWidth, realHeiht);
}
private void measure(int measureValue) {
int defalueSize = 200;
int mode = View.MeasureSpec.getMode(measureValue);
int specValue = View.MeasureSpec.getSize(measureValue);
Log.e("onMeasure", "mode: " + mode + "specValue: " + specValue);
switch (mode) {
//指定一個預設值
case MeasureSpec.UNSPECIFIED:
realWidth = defalueSize;
realHeiht = defalueSize;
break;
//取測量值
case MeasureSpec.EXACTLY:
realHeiht = specValue;
realWidth = specValue;
break;
//取測量值和預設值中的最小值
case MeasureSpec.AT_MOST:
realWidth = Math.min(defalueSize, specValue);
realHeiht = Math.min(defalueSize, specValue);
break;
default:
break;
}
}
3、模仿谷歌官方寫法實現onMeasure
這裡主要就是模仿View.resolveSizeAndState(int size, int measureSpec, int childMeasuredState),childMeasuredState其中 View.getMeasuredState()是由返回的,最終佈局將結合childMeasuredState通過View.combineMeasuredStates()完成最終的測量結果,作用應該是自定義viewGroup時才使用用於記錄children測量狀態的,一般自定義View傳0即可,特殊情況下可以傳遞1。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//third param. usually 0. http://stackoverflow.com/questions/13650903/whats-the-utility-of-the-third-argument-of-view-resolvesizeandstate
int w = resolveSizeAndState2(getDesireW(), widthMeasureSpec, 0);
int h = resolveSizeAndState2(300, heightMeasureSpec, 0);
setMeasuredDimension(MeasureSpec.getSize(w), MeasureSpec.getSize(h));
}
private int getDesireW(){
return 300;
}
/**
*
* @param size How big the view wants to be.即傳入你希望View的大小
* @param measureSpec Constraints imposed by the parent. 父級約束大小
* @param childMeasuredState 一般傳遞0即可,特殊情況還可以傳入1
* @return
*/
private int resolveSizeAndState2(int size, int measureSpec, int childMeasuredState) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
//當specMode為AT_MOST,並且父控制元件指定的尺寸specSize小於View自己想要的尺寸時,
//我們就會用掩碼MEASURED_STATE_TOO_SMALL向量算結果加入尺寸太小的標記
//這樣其父ViewGroup就可以通過該標記其給子View的尺寸太小了,
//然後可能分配更大一點的尺寸給子View
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;//按味或
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result | (childMeasuredState&MEASURED_STATE_MASK);
}
五、一個簡單的例子
/**
* Auther: Crazy.Mo
* DateTime: 2017/5/3 15:52
* Summary:
*/
public class MeasuredView extends View {
private Context context;
private int realWidth, realHeiht;
public MeasuredView(Context context) {
this(context, null);
}
public MeasuredView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MeasuredView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measure(widthMeasureSpec);
measure(heightMeasureSpec);
Log.e("onMeasure", "realWidth: " + realWidth + "realHeiht: " + realHeiht + "widthMeasureSpec" + widthMeasureSpec + "heightMeasureSpec" + heightMeasureSpec);
setMeasuredDimension(realWidth, realHeiht);
}
private void measure(int measureValue) {
int defalueSize = 200;
int mode = View.MeasureSpec.getMode(measureValue);
int specValue = View.MeasureSpec.getSize(measureValue);
Log.e("onMeasure", "mode: " + mode + "specValue: " + specValue);
switch (mode) {
//指定一個預設值
case MeasureSpec.UNSPECIFIED:
Log.e("onMeasure", "mode: " + mode + "UNSPECIFIED " );
realWidth = defalueSize;
realHeiht = defalueSize;
break;
//取測量值
case MeasureSpec.EXACTLY:
Log.e("onMeasure", "mode: " + mode + "EXACTLY " );
realHeiht = specValue;
realWidth = specValue;
break;
//取測量值和預設值中的最小值
case MeasureSpec.AT_MOST:
Log.e("onMeasure", "mode: " + mode + "AT_MOST " );
realWidth = Math.min(defalueSize, specValue);
realHeiht = Math.min(defalueSize, specValue);
break;
default:
break;
}
}
}
此時在佈局中使用的話,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:orientation="vertical"
android:background="#0f8">
<!-- <com.ce.sesamecredit.ClockView
android:layout_width="match_parent"
android:layout_height="match_parent" />-->
<com.ce.sesamecredit.MeasuredView
android:background="@color/colorAccent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
2、執行結果分析:
未重寫onMeasure方法時,預設的onMeasure僅可以解析match_parent和指定的具體數值
重寫onMeasure方法時,可以解析match_parent、指定的具體數值和wrap_content
相關推薦
Android進階——自定義View之View的繪製流程及實現onMeasure完全攻略
引言 Android實際專案開發中,自定義View不可或缺,而作為自定義View的一種重要實現方式——繼承View重繪尤其重要,前面很多文章基本總結了繼承View的基本流程:自定義屬性和繼承View重寫onDraw方法——>實現構造方法並完成相關初始化操
Android進階——自定義View之擴充套件系統控制元件的另一種思路實現漸變文字動畫的TextView
引言 前面幾篇文章 繼承或組合系統現有控制元件實現新控制元件,擴充套件新功能都是在對應的構造方法中去擴充套件的,但千萬不要把思路侷限於只能在構造方法中去擴充套件,這篇就簡單地分享另一種思路,通過重寫對應的週期方法實現擴充套件。 一、View中幾種重
Android進階——自定義View之繼承TextView巧用DrawableLeft實現自己的CheckableTextView
引言 Android自帶的許多控制元件已經十分強大,甚至很多功能都已經有現成的控制元件去使用了,不過介面效果是肯定會打折扣的,幸好android控制元件自身的擴充套件性十分優秀,很多時候我們只需要簡單繼承下現有控制元件擴充套件些許功能就能得到一個全新的控制元件
Android進階——自定義View之組合系統控制元件實現水珠形狀的ItemView
引言 相信大家在專案開發的過程中一定會有不少需要在上方顯示一張圖片,而在其下方顯示提示標題的效果,作為一個介面的功能按鈕或者單純作為一個列表的item項,尤其是當這個item還需要顯示一些動畫效果時候,此時更應該當成一個整體,否則動畫效果就會需要額外的調整,否
Android 進階自定義 ViewGroup 自定義佈局
前言 在我們的實際應用中, 經常需要用到自定義控制元件,比如自定義圓形頭像,自定義計步器等等。但有時我們不僅需要自定義控制元件,舉個例子,FloatingActionButton 大家都很常用,所以大家也很經常會有一種需求,點選某個 FloatingActionButton 彈出更多 FloatingActi
HenCoder Android 開發進階: 自定義 View 1-1 繪製基礎
自定義繪製概述 二話不說,我反手就是一個視訊:(視訊掛了,先直接點到優酷去看吧:優酷連結) 首先總結一下視訊中的關鍵點: 自定義繪製的方式是重寫繪製方法,其中最常用的是 onDraw() 繪製的關鍵是 Canvas 的使用 Canvas 的繪製類方法: drawXX
Android開發進階——自定義View的使用及其原理探索
在Android開發中,系統提供給我們的UI控制元件是有限的,當我們需要使用一些特殊的控制元件的時候,只靠系統提供的控制元件,可能無法達到我們想要的效果,這時,就需要我們自定義一些控制元件,來完成我們想要的效果了。下面,我就來講講自定義控制元件的那些事。 首先,我來講講Android的控制元件架構。
Android輸入框自動提示進階--自定義佈局
發現Android的有兩種方法AutoCompleteTextView和MultiAutoCompleteTextView提示出來的提示框只是純文字而且是單條資料,要是想實現加一個圖片或者是每一條資料展示兩個資料呢,這就需要重寫介面卡設定佈局了 重寫介面卡: packag
Android進階:十二、最簡單的方式實現自定義陰影效果
clas new round war port scale dimens tro hdr 網話說UI設計有三寶 :透明,陰影,加圓角。很多UI在做設計的時候都喜歡做卡片形式,然後添加陰影。卡片UI確實挺好看,但是對Android開發者來說,顯示陰影卻並不那麽手到擒來,因為A
Android進階——Java註解實戰之APT構建模組化的第一步
前言 APT的學習要花點時間去掌握和實踐的,短時間內只能掌握知識點,更多的是在實戰中去實踐。其實,APT就是一種工具而已,只要用多了,自然就會熟練了,不過要想實踐之前,還是必須把基礎知識學好才能實戰進入開發。文章會從基礎用例講解知識點,然後再通過實戰進行實踐 APT簡介 AP
Android註解:自定義註解之原始碼註解
首先如果你對註解沒有太多瞭解,建議先看一下我之前的兩篇部落格 Android註解:Java註解 Android註解:Android 常用註解 這兩篇部落格都詳細介紹了關於Android註解的一些基礎知識。這是Android自定義註解的第一篇,原始碼註解。A
C++進階--自定義new handler
tput code cer urn 卸載 可用 幫助 malloc dog //############################################################################ // 自定義new handler /
【Android 進階】ORM 框架之 greenDAO學習筆記
前言 當初學習Hibernate的時候就非常驚歎這種ORM思想,後來才知道原來Android中也有這種基於ORM思想的開源框架greenDAO。 greenDAO簡介: 簡單的講,greenDAO 是一個將物件對映到 SQLite 資料庫
Android進階——Fragment詳解之操作原理(三)
引言 前一篇文章總結了Fragment 的基本概念和基本的用法,相信大家也能夠掌握一些知識了,但是對於一些操作可能還是不知其所以然,說實話曾經很長一段時間為也是暈乎乎的,後來才慢慢重視去學習瞭解,才略知一二,遂分享之。 一、管理Fragement所涉及到
android遙控器新增自定義的鍵值+用遙控器按鍵實現軟鍵盤字元鍵的切換(amlogice平臺) --- (一)
最近的專案客戶有個需求,遙控器上要增加兩個按鍵,分別是系統軟鍵盤的DEL鍵和字元切換鍵(也就是左下角那個按鍵)。任務相對來說比較簡單,所以安排給我這個新手做,廢話不多說,直接上正題,有不對的地方,歡迎指正。 幹活前,先整理下按下遙控器的一個按鍵後,在系統中的邏
【進階】RecyclerView原始碼解析(一)——繪製流程
引言 自從Google出了RecyclerView後,基本上列表的場景已經完全替代了原來的ListView和GridView,現在不僅僅是列表,多樣式(俗稱蓋樓),複雜頁面等,只要我們願意,RecyclerView幾乎可以代替實現80%的佈局,Git
我的Android進階之旅------>Android自定義View來實現解析lrc歌詞並同步滾動、上下拖動、縮放歌詞的功能
前言 最近有個專案有關於播放音樂時候,關於歌詞有以下幾個功能: 1、實現歌詞同步滾動的功能,即歌曲播放到哪句歌詞,就高亮地顯示出正在播放的這個歌詞; 2、實現上下拖動歌詞時候,可以拖動播放器的進度。即可以不停地上下拖動歌詞,
Android進階之自定義View實戰(一)仿iOS UISwitch控制元件實現
一.引言 個人覺得,自定義View一直是Android開發最變換莫測、最難掌握、最具吸引力的地方。因為它涉及到的知識點比較多,想在實際應用中駕輕就熟,由淺入深,你需要掌握以下知識點: 1. View的繪製機制以及Canvas、Paint、Rect等的常用方
Android進階之自定義View(1)實現可換行的TextView
今天來一起學習一下最簡單的自定義view,自己動手寫一個MyTextView,當然不會像系統的TextView那麼複雜,只是實現一下TextView的簡單功能,包括分行顯示及自定義屬性的處理,主要目的是介紹自定義view的實現的基本思路和需要掌握的一些基礎知
我的Android進階之旅------>Android自定義View實現帶數字的進度條(NumberProgressBar)
今天在Github上面看到一個來自於 daimajia所寫的關於Android自定義View實現帶數字的進度條(NumberProgressBar)的精彩案例,在這裡分享給大家一起來學習學習!同時感謝daimajia的開源奉獻! 第一步、效果展