Android自定义View深度剖析:从入门到精通

Android自定义View深度剖析:从入门到精通

一、基础知识

1.1 View的生命周期

public class LifecycleView extends View {
    public LifecycleView(Context context) {
        super(context);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        // View被添加到Window时调用
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 测量View大小
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        // 确定View位置
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 绘制View
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        // View从Window中移除时调用
    }
}

1.2 MeasureSpec详解

public class MeasureSpecUtil {
    public static String getModeName(int mode) {
        switch (mode) {
            case MeasureSpec.EXACTLY:
                return "EXACTLY";
            case MeasureSpec.AT_MOST:
                return "AT_MOST";
            case MeasureSpec.UNSPECIFIED:
                return "UNSPECIFIED";
            default:
                return "UNKNOWN";
        }
    }

    public static void printMeasureSpec(int measureSpec) {
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        Log.d("MeasureSpec", "mode: " + getModeName(mode) + ", size: " + size);
    }
}

二、基础绘制

2.1 画笔设置

public class PaintExample extends View {
    private Paint mPaint;

    public PaintExample(Context context) {
        super(context);
        initPaint();
    }

    private void initPaint() {
        mPaint = new Paint();
        // 抗锯齿
        mPaint.setAntiAlias(true);
        // 防抖动
        mPaint.setDither(true);
        // 设置颜色
        mPaint.setColor(Color.RED);
        // 设置样式:填充/描边/填充且描边
        mPaint.setStyle(Paint.Style.FILL);
        // 设置线宽
        mPaint.setStrokeWidth(5);
        // 设置线帽
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        // 设置字体大小
        mPaint.setTextSize(50);
    }
}

2.2 基本图形绘制

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    
    // 1. 绘制直线
    canvas.drawLine(0, 0, 100, 100, mPaint);
    
    // 2. 绘制矩形
    RectF rect = new RectF(100, 100, 200, 200);
    canvas.drawRect(rect, mPaint);
    
    // 3. 绘制圆形
    canvas.drawCircle(300, 300, 50, mPaint);
    
    // 4. 绘制圆角矩形
    RectF roundRect = new RectF(400, 400, 500, 500);
    canvas.drawRoundRect(roundRect, 20, 20, mPaint);
    
    // 5. 绘制路径
    Path path = new Path();
    path.moveTo(0, 0);
    path.lineTo(100, 100);
    path.quadTo(150, 0, 200, 100);
    canvas.drawPath(path, mPaint);
    
    // 6. 绘制文字
    canvas.drawText("Hello Custom View", 100, 600, mPaint);
}

三、高级绘制

3.1 Canvas变换

@Override
protected void onDraw(Canvas canvas) {
    // 1. 平移
    canvas.translate(100, 100);
    
    // 2. 旋转
    canvas.rotate(45);
    
    // 3. 缩放
    canvas.scale(0.5f, 0.5f);
    
    // 4. 错切
    canvas.skew(0.5f, 0.5f);
    
    // 保存和恢复画布状态
    canvas.save();
    // 执行变换操作
    canvas.restore();
    
    // 保存图层
    int layer = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
    // 绘制操作
    canvas.restoreToCount(layer);
}

3.2 绘制顺序控制

@Override
protected void onDraw(Canvas canvas) {
    // 1. 使用clipRect裁剪画布
    canvas.clipRect(0, 0, 200, 200);
    
    // 2. 使用clipPath裁剪复杂区域
    Path clipPath = new Path();
    clipPath.addCircle(100, 100, 50, Path.Direction.CW);
    canvas.clipPath(clipPath);
    
    // 3. 设置图层混合模式
    Paint paint = new Paint();
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
}

四、触摸事件处理

4.1 基本触摸事件

public class TouchView extends View {
    private float lastX;
    private float lastY;
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
                break;
                
            case MotionEvent.ACTION_MOVE:
                float deltaX = x - lastX;
                float deltaY = y - lastY;
                // 处理移动
                offsetLeftAndRight((int)deltaX);
                offsetTopAndBottom((int)deltaY);
                lastX = x;
                lastY = y;
                break;
                
            case MotionEvent.ACTION_UP:
                // 处理抬起
                break;
        }
        return true;
    }
}

4.2 手势检测

public class GestureView extends View {
    private GestureDetector mGestureDetector;
    
    public GestureView(Context context) {
        super(context);
        initGestureDetector();
    }
    
    private void initGestureDetector() {
        mGestureDetector = new GestureDetector(getContext(), 
            new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onDoubleTap(MotionEvent e) {
                    // 处理双击
                    return true;
                }
                
                @Override
                public boolean onScroll(MotionEvent e1, MotionEvent e2, 
                                      float distanceX, float distanceY) {
                    // 处理滑动
                    return true;
                }
                
                @Override
                public boolean onFling(MotionEvent e1, MotionEvent e2, 
                                     float velocityX, float velocityY) {
                    // 处理快速滑动
                    return true;
                }
            });
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return mGestureDetector.onTouchEvent(event);
    }
}

五、性能优化

5.1 绘制优化

public class OptimizedView extends View {
    private Paint mPaint;
    private Path mPath;
    private Region mRegion;
    
    // 1. 避免在onDraw中创建对象
    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPath = new Path();
        mRegion = new Region();
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        // 2. 判断是否需要绘制
        if (!isVisible()) {
            return;
        }
        
        // 3. 使用硬件加速
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            setLayerType(LAYER_TYPE_HARDWARE, null);
        }
        
        // 4. 减少不必要的绘制
        canvas.clipRect(getScrollX(), getScrollY(), 
                       getScrollX() + getWidth(), getScrollY() + getHeight());
    }
}

5.2 测量优化

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // 1. 获取宽高的测量模式和大小
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    
    // 2. 根据不同模式计算实际大小
    int width;
    int height;
    
    if (widthMode == MeasureSpec.EXACTLY) {
        width = widthSize;
    } else {
        width = calculateDesiredWidth();
        if (widthMode == MeasureSpec.AT_MOST) {
            width = Math.min(width, widthSize);
        }
    }
    
    if (heightMode == MeasureSpec.EXACTLY) {
        height = heightSize;
    } else {
        height = calculateDesiredHeight();
        if (heightMode == MeasureSpec.AT_MOST) {
            height = Math.min(height, heightSize);
        }
    }
    
    // 3. 保存测量结果
    setMeasuredDimension(width, height);
}

六、实战案例

6.1 自定义进度条

public class CircleProgressBar extends View {
    private Paint mPaint;
    private RectF mRectF;
    private float mProgress;
    private int mColor;
    
    public CircleProgressBar(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(10);
        mRectF = new RectF();
        mColor = Color.BLUE;
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mRectF.set(10, 10, w - 10, h - 10);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        // 绘制背景圆环
        mPaint.setColor(Color.GRAY);
        canvas.drawArc(mRectF, 0, 360, false, mPaint);
        
        // 绘制进度
        mPaint.setColor(mColor);
        canvas.drawArc(mRectF, -90, mProgress * 360, false, mPaint);
    }
    
    public void setProgress(float progress) {
        mProgress = progress;
        invalidate();
    }
}

6.2 自定义折线图

public class LineChartView extends View {
    private List<Point> mPoints;
    private Paint mPaint;
    private Path mPath;
    
    private void drawLine(Canvas canvas) {
        if (mPoints.size() < 2) {
            return;
        }
        
        mPath.reset();
        Point firstPoint = mPoints.get(0);
        mPath.moveTo(firstPoint.x, firstPoint.y);
        
        for (int i = 1; i < mPoints.size(); i++) {
            Point point = mPoints.get(i);
            mPath.lineTo(point.x, point.y);
        }
        
        canvas.drawPath(mPath, mPaint);
    }
    
    private void drawPoints(Canvas canvas) {
        for (Point point : mPoints) {
            canvas.drawCircle(point.x, point.y, 5, mPaint);
        }
    }
}

七、注意事项

  1. 控制绘制范围
@Override
protected void onDraw(Canvas canvas) {
    // 只绘制可见区域
    Rect bounds = canvas.getClipBounds();
    if (!bounds.intersects(left, top, right, bottom)) {
        return;
    }
}
  1. 处理配置变化
@Override
protected Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();
    SavedState ss = new SavedState(superState);
    // 保存状态
    return ss;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    SavedState ss = (SavedState) state;
    super.onRestoreInstanceState(ss.getSuperState());
    // 恢复状态
}

总结

自定义View的开发需要注意:

  1. 理解View的生命周期和测量规则
  2. 掌握Canvas的绘制方法
  3. 正确处理触摸事件
  4. 注意性能优化
  5. 做好状态保存

通过合理的设计和优化,可以创建出性能优秀、体验良好的自定义View。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

跳房子的前端

你的打赏能让我更有力地创造

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值