创建自定义视图

如果你需要完全自定义的视图,则需要子类 View(所有 Android 视图的超类)并提供自定义大小(onMeasure(...))和绘图(onDraw(...))方法:

  1. 创建自定义视图框架: 对于每个自定义视图,这基本相同。在这里,我们为自定义视图创建骨架,可以绘制一个名为 SmileyView 的笑脸:

    public class SmileyView extends View {
        private Paint mCirclePaint;
        private Paint mEyeAndMouthPaint;
    
        private float mCenterX;
        private float mCenterY;
        private float mRadius;
        private RectF mArcBounds = new RectF();
    
        public SmileyView(Context context) {
            this(context, null, 0);
        }
    
        public SmileyView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public SmileyView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initPaints();
        }
    
        private void initPaints() {/* ... */}
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {/* ... */}
    
        @Override
        protected void onDraw(Canvas canvas) {/* ... */}
    }
    
  2. 初始化你的颜料: Paint 对象是虚拟画布的画笔,用于定义几何对象的渲染方式(例如颜色,填充和描边样式等)。在这里,我们创建了两个 Paints,一个用于圆圈的黄色填充绘图和一个用于眼睛和嘴巴的黑色笔触绘图:

    private void initPaints() {
        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCirclePaint.setStyle(Paint.Style.FILL);
        mCirclePaint.setColor(Color.YELLOW);
        mEyeAndMouthPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mEyeAndMouthPaint.setStyle(Paint.Style.STROKE);
        mEyeAndMouthPaint.setStrokeWidth(16 * getResources().getDisplayMetrics().density);
        mEyeAndMouthPaint.setStrokeCap(Paint.Cap.ROUND);
        mEyeAndMouthPaint.setColor(Color.BLACK);
    }
    
  3. 实现你自己的 onMeasure(...) 方法: 这是必需的,以便父布局(例如 FrameLayout)可以正确对齐你的自定义视图。它提供了一组 measureSpecs,你可以使用它来确定视图的高度和宽度。在这里,我们通过确保高度和宽度相同来创建一个正方形:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int w = MeasureSpec.getSize(widthMeasureSpec);
        int h = MeasureSpec.getSize(heightMeasureSpec);
    
        int size = Math.min(w, h);
        setMeasuredDimension(size, size);
    }
    

    请注意,onMeasure(...) 必须至少包含一次对 setMeasuredDimension(..) 的调用,否则你的自定义视图将与 IllegalStateException 一起崩溃。

  4. 实现你自己的 onSizeChanged(...) 方法: 这允许你捕获自定义视图的当前高度和宽度,以正确调整渲染代码。这里我们只计算我们的中心和半径:

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mCenterX = w / 2f;
        mCenterY = h / 2f;
        mRadius = Math.min(w, h) / 2f;
    }
    
  5. 实现自己的 onDraw(...) 方法: 这是实现视图的实际渲染的地方。它提供了一个可以绘制的 Canvas 对象( 有关所有可用的绘图方法,请参阅官方 Canvas 文档)。

    @Override
    protected void onDraw(Canvas canvas) {
        // draw face
        canvas.drawCircle(mCenterX, mCenterY, mRadius, mCirclePaint);
        // draw eyes
        float eyeRadius = mRadius / 5f;
        float eyeOffsetX = mRadius / 3f;
        float eyeOffsetY = mRadius / 3f;
        canvas.drawCircle(mCenterX - eyeOffsetX, mCenterY - eyeOffsetY, eyeRadius, mEyeAndMouthPaint);
        canvas.drawCircle(mCenterX + eyeOffsetX, mCenterY - eyeOffsetY, eyeRadius, mEyeAndMouthPaint);
        // draw mouth
        float mouthInset = mRadius /3f;
        mArcBounds.set(mouthInset, mouthInset, mRadius * 2 - mouthInset, mRadius * 2 - mouthInset);
        canvas.drawArc(mArcBounds, 45f, 90f, false, mEyeAndMouthPaint);
    }
    
  6. 将自定义视图添加到布局: 自定义视图现在可以包含在你拥有的任何布局文件中。在这里,我们将其包装在 FrameLayout 中:

    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.example.app.SmileyView
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
    

请注意,建议在视图代码完成后构建项目。如果不构建它,你将无法在 Android Studio 的预览屏幕上看到该视图。

将所有内容放在一起后,在启动包含上述布局的活动后,你将看到以下屏幕:

https://i.stack.imgur.com/0yhjv.jpg