View(或 SurfaceView、TextureView)Context、Context, AttributeSet 兩個)onMeasure(int, int) — 計算自身期望尺寸(可選,若不需自適應可跳過)onDraw(Canvas) — **核心繪製邏輯**,建立 Paint、繪製圖形/文字/圖片onTouchEvent、onSizeChanged 等交互/生命週期方法public class CircleView extends View {
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public CircleView(Context context) { this(context, null); }
public CircleView(Context context, AttributeSet attrs) { this(context, attrs, 0); }
public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(dp2px(4));
paint.setColor(Color.parseColor(\"#FF5722\")); // 深橙
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int desired = dp2px(100); // 預期 100dp 正方形
int width = resolveSize(desired, widthMeasureSpec);
int height = resolveSize(desired, heightMeasureSpec);
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int cx = getWidth() / 2;
int cy = getHeight() / 2;
int radius = Math.min(cx, cy) - paint.getStrokeWidth() / 2;
canvas.drawCircle(cx, cy, radius, paint);
}
private int dp2px(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
}
}
<com.example.customview.CircleView
android:layout_width=\"wrap_content\"
android:layout_height=\"wrap_content\"
android:layout_gravity=\"center\" />
| 階段 | 重點 | 實作建議 |
|---|---|---|
| 入門 | Canvas 基礎繪製、Paint 設定、座標系 | 畫時鐘、進度環、波浪球 |
| 進階 | Path/Region 複雜形狀、Shader 漸變、Bitmap 圖片處理、矩陣變換 | 貝茲曲線簽名板、圖片濾鏡、摺疊動畫 |
| 高階 | 自定義屬性、屬性動畫、硬體加速、SurfaceView 雙緩衝、OpenGL ES 互操作 | 高性能圖表庫、遊戲引擎基礎渲染器 |
Paint、Path、RectF 等可複用物件。getPaddingLeft() 等計算實際繪製矩形。resolveSize() 或自行解析 MeasureSpec.getMode/Size。onTouchEvent 返回 false 導致父視圖攔截。**解決**:處理完返回 true。**自定義 View = 繼承 View + 構造函數 + onMeasure + onDraw**。先從簡單圖形入手,理解 Canvas 座標與 Paint 狀態,再逐步引入屬性動畫、觸摸交互、硬體加速。HenCoder 系列教程是公認最系統的中文進階資源。