1 簡介
當Android系統內置的View無法實現我們的需求,需要針對業務定製我們想要的View。自定義view分爲兩種:自定義view和定義viewgroup。
自定義View需重寫三個函數:onMeasure()、onLayout()、onDraw()。
- onMeasure
onMeasure(int widthMeasureSpec, int heightMeasureSpec)是View讓其父節點知道它想要多大的尺寸。方法的參數widthMeasureSpec和heightMeasureSpec代表 測量模式和寬高的大小。它本身是一個32位的數據,前兩位代表模式,後30位代表尺寸大小。如何獲取。參考附件代碼:
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
onMeasure包含了3種測量模式:UNSPECIFIED,EXACTLY,AT_MOST。
1. EXACTLY:父view已經強制設置了子view的大小,一般是MATCH_PARENT和固定值
2. UNSPECIFIED:父view對子view沒有任何限制,子view可以是任何大小
3. AT_MOST:子view限制在一個最大範圍內,一般是WARP_CONTENT-包裹內容
- onLayout ()
onLayout 讓Android知道View在其父控件中的位置,即距父控件四邊的距離left、right、top、bottom。佈局是繪圖的基礎,只有完成了佈局,才能對View進行繪圖。 - onDraw()
onDraw()繪圖的前提是已經對View進行了量算和佈局,View通過調用draw()方法進行繪圖,繪圖的目的就是讓View在UI界面上呈現出來。
2 onMeasure
首頁自定義一個viewgroup 一個button
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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"
android:orientation="vertical"
tools:context=".MainActivity">
<com.example.test.SelftLayout
android:background="@color/colorPrimary"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.test.MyButton
android:id="@+id/result"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@color/colorAccent"
android:text="Hello World!"
/>
</com.example.test.SelftLayout>
</LinearLayout>
案例—通過改變自定義layout的寬高模式,獲取到的數據不同
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int mode = MeasureSpec.getMode(widthMeasureSpec);
final int size = MeasureSpec.getSize(widthMeasureSpec);
switch (mode) {
case MeasureSpec.AT_MOST:
Log.d(TAG, "onMeasure: AT_MOST=" );
break;
case MeasureSpec.EXACTLY:
Log.d(TAG, "onMeasure: EXACTLY=" );
break;
case MeasureSpec.UNSPECIFIED:
Log.d(TAG, "onMeasure: UNSPECIFIED=" );
break;
}
int width = SystemUtil.dp2px(getContext(),200);
setMeasuredDimension(width,width);
Log.d(TAG, "onMeasure: AT_MOST=" +size+"---width="+getWidth()+"---height="+getHeight());
}
3.onLayout
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
final int childCount = getChildCount();
int left = 0,top = 0,right = 0,bottom = 0;
for (int x=0;x<childCount;x++){
final View childAt = getChildAt(x);
final int measuredWidth = childAt.getMeasuredWidth();
final int measuredHeight = childAt.getMeasuredHeight();
left = (getWidth()-measuredWidth)/2;
top = (getHeight()-measuredHeight)/2;
right = getWidth()-left;
bottom = top+measuredHeight;
Log.d(TAG, "onLayout: "+left+"--"+top+"--");
childAt.layout(left,top,right,bottom);
}
}
4 onDraw
繪製的意思,繪製的內容,形狀。
4.1 Canvas 畫布
Canvas我們可以稱之爲畫布,能夠在上面繪製各種東西,是安卓平臺2D圖形繪製的基礎,非常強大。
4.2 常用api
操作類型 | 相關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比較難以理解和使用,故封裝了一些常用的方法。 |
4.3 使用
4.3.1 繪製點
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//創建畫筆
paint = new Paint();
// 畫筆爲填充模式
paint.setStyle(Paint.Style.FILL);
//畫筆顏色
paint.setColor(Color.RED);
// 畫筆寬度
paint.setStrokeWidth(11f);
canvas.drawPoint(300,300, paint);
}
4.3.2 繪製線
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//創建畫筆
paint = new Paint();
// 畫筆爲填充模式 STROKE //描邊 FILL //填充 FILL_AND_STROKE //描邊加填充
paint.setStyle(Paint.Style.FILL);
//畫筆顏色
paint.setColor(Color.RED);
// 畫筆寬度
paint.setStrokeWidth(11f);
canvas.drawLine(300,300,500,600,paint);
}
4.3.3 繪製矩形
確定一個矩形最少需要四個數據,就是對角線的兩個點的座標值,這裏一般採用左上角和右下角的兩個點的座標。
// 第一種
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);
4.3.4 繪製圓
canvas.drawCircle(500,500,400,mPaint); // 繪製一個圓心座標在(500,500),半徑爲400 的圓。
4.3.5 繪製圓角矩形
// 第一種
RectF rectF = new RectF(100,100,800,400);
canvas.drawRoundRect(rectF,30,30,mPaint);
// 第二種
canvas.drawRoundRect(100,100,800,400,30,30,mPaint);