1.構造函數css
構造函數是View的入口,能夠用於初始化一些的內容,和獲取自定義屬性。java
View的構造函數有四種重載分別以下:android
public void SloopView(Context context) {} public void SloopView(Context context, AttributeSet attrs) {} public void SloopView(Context context, AttributeSet attrs, int defStyleAttr) {} public void SloopView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {}
有三個參數的構造函數中第三個參數是默認的Style,這裏的默認的Style是指它在當前Application或Activity所用的Theme中的默認Style,且只有在明確調用的時候纔會生效,以系統中的ImageButton爲例說明:canvas
public ImageButton(Context context, AttributeSet attrs) { //調用了三個參數的構造函數,明確指定第三個參數 this(context, attrs, com.android.internal.R.attr.imageButtonStyle); } public ImageButton(Context context, AttributeSet attrs, int defStyleAttr) { //此處調了四個參數的構造函數,無視便可 this(context, attrs, defStyleAttr, 0); }
排除了兩個以後,只剩下一個參數和兩個參數的構造函數,他們的詳情以下:ide
//通常在直接New一個View的時候調用。 public void SloopView(Context context) {} //通常在layout文件中使用的時候會調用,關於它的全部屬性(包括自定義屬性)都會包含在attrs中傳遞進來。 public void SloopView(Context context, AttributeSet attrs) {}
如下方法調用的是一個參數的構造函數:函數
//在Avtivity中 SloopView view new SloopView(this);
如下方法調用的是兩個參數的構造函數:oop
//在layout文件中 - 格式爲: 包名.View名 <com.sloop.study.SloopView android:layout_width"wrap_content" android:layout_height"wrap_content"/>
2.測量View大小(onMeasure)佈局
View的大小不只由自身所決定,同時也會受到父控件的影響,爲了咱們的控件能更好的適應各類狀況,通常會本身進行測量。this
測量View大小使用的是onMeasure函數,咱們能夠從onMeasure的兩個參數中取出寬高的相關數據:spa
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthsize = MeasureSpec.getSize(widthMeasureSpec); //取出寬度的確切數值 int widthmode = MeasureSpec.getMode(widthMeasureSpec); //取出寬度的測量模式 int heightsize = MeasureSpec.getSize(heightMeasureSpec); //取出高度的確切數值 int heightmode = MeasureSpec.getMode(heightMeasureSpec); //取出高度的測量模式 }
從上面能夠看出 onMeasure 函數中有 widthMeasureSpec 和 heightMeasureSpec 這兩個 int 類型的參數, 毫無疑問他們是和寬高相關的, 但它們其實不是寬和高, 而是由寬、高和各自方向上對應的測量模式來合成的一個值:
測量模式一共有三種, 被定義在 Android 中的 View 類的一個內部類View.MeasureSpec中:
模式 | 二進制數值 | 描述 |
---|---|---|
UNSPECIFIED | 00 | 默認值,父控件沒有給子view任何限制,子View能夠設置爲任意大小。 |
EXACTLY | 01 | 表示父控件已經確切的指定了子View的大小。 |
AT_MOST | 10 | 表示子View具體大小沒有尺寸限制,可是存在上限,上限通常爲父View大小。 |
用 MeasureSpec 的 getSize是獲取數值, getMode是獲取模式。
若是對View的寬高進行修改了,不要調用 super.onMeasure( widthMeasureSpec, heightMeasureSpec); 要調用 setMeasuredDimension( widthsize, heightsize)這個函數。
這個函數在視圖大小發生改變時調用。
由於View的大小不只由View自己控制,並且受父控件的影響,因此咱們在肯定View大小的時候最好使用系統提供的onSizeChanged回調函數。
onSizeChanged以下:
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); }
四個參數,分別爲 寬度,高度,上一次寬度,上一次高度。
只需關注 寬度(w), 高度(h) 便可,這兩個參數就是View最終的大小。
肯定佈局的函數是onLayout,它用於肯定子View的位置,在自定義ViewGroup中會用到,他調用的是子View的layout函數。
在自定義ViewGroup中,onLayout通常是循環取出子View,而後通過計算得出各個子View位置的座標值,而後用如下函數設置子View位置。
child.layout(l, t, r, b);
5.繪製內容(onDraw)
onDraw是實際繪製的部分,也就是咱們真正關心的部分,使用的是Canvas繪圖。
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); }
自定義完View以後,通常會對外暴露一些接口,用於控制View的狀態等,或者監聽View的變化.
操做類型 |
相關API |
備註 |
---|---|---|
繪製顏色 |
drawColor, drawRGB, drawARGB |
使用單一顏色填充整個畫布 |
繪製基本形狀 |
drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc |
依次爲 點、線、矩形、圓角矩形、橢圓、圓、圓弧 |
繪製圖片 |
drawBitmap, drawPicture |
繪製位圖和圖片 |
繪製文本 |
drawText, drawPosText, drawTextOnPath |
依次爲 繪製文字、繪製文字時指定每一個文字位置、根據路徑繪製文字 |
繪製路徑 |
drawPath |
繪製路徑,繪製貝塞爾曲線時也須要用到該函數 |
頂點操做 |
drawVertices, drawBitmapMesh |
經過對頂點操做可使圖像形變,drawVertices直接對畫布做用、 drawBitmapMesh只對繪製的Bitmap做用 |
畫布剪裁 |
clipPath, clipRect |
設置畫布的顯示區域 |
畫布快照 |
save, restore, saveLayerXxx, restoreToCount, getSaveCount |
依次爲 保存當前狀態、 回滾到上一次保存的狀態、 保存圖層狀態、 回滾到指定狀態、 獲取保存次數 |
畫布變換 |
translate, scale, rotate, skew |
依次爲 位移、縮放、 旋轉、錯切 |
Matrix(矩陣) |
getMatrix, setMatrix, concat |
實際上畫布的位移,縮放等操做的都是圖像矩陣Matrix, 只不過Matrix比較難以理解和使用,故封裝了一些經常使用的方法。 |
private Paint mPaint = new Paint();初始化畫筆
private void initPaint() {
mPaint.setColor(Color.BLACK);//設置畫筆顏色
mPaint.setStyle(Paint.Style.FILL);//設置畫筆模式爲填充
mPaint.setStrokeWidth(10f); //設置畫筆寬度爲10px
}
public SloopView(Context context, AttributeSet attrs) {
super(context, attrs); initPaint();
}
繪製點: canvas.drawPoint(200, 200, mPaint);
繪製線: canvas.drawLine(300,300,500,600,mPaint);
繪製矩形: canvas.drawRect(100,100,800,400,mPaint);
Rect rect = new Rect(100,100,800,400); canvas.drawRect(rect,mPaint);
RectF rectF = new RectF(100,100,800,400); canvas.drawRect(rectF,mPaint);
繪製圓: canvas.drawCircle(500,500,400,mPaint);
繪製圓弧: public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint){}
一、在 xml中建立了一個view時,全部在xml中聲明的屬性都會被傳入到view的構造方法中的AttributeSet類型的參數當中。 經過調用Context的obtainStyledAttributes()方法返回一個TypedArray對象。而後直接用TypedArray對象獲取自定義屬性的值。TypedArray對象是共享的資源,因此在獲取完值以後必需要調用recycle()方法來回收。
二、自定義控件的屬性發生改變以後,控件的樣子也可能發生改變,在這種狀況下就須要調用invalidate()方法讓系統去調用view的onDraw()從新繪製。
三、在onDraw方法中開始繪製以前,應該讓畫筆Paint對象的信息初始化完畢。這是由於View的從新繪製是比較頻繁的,這就可能屢次調用onDraw,因此初始化的代碼不該該放在onDraw方法裏。