创建 View 的子类之后,类与框架之间就有两个主要的交互需要关注:测量和绘制。
测量:
在显示视图的层次结构之前,需要调用 onMeasure() 并向该方法传递两个约束,视图应根据这两个约束来管理应具备的大小,每个约束都为 MeasureSpec 的整数封装:
AT_MOST : 视图布局参数是 wrap_content。
EXACTLY : 视图布局参数是固定值;如:android:layout_width="100dp",或者指定为 match_parent。
UNSPECIFIED : 视图无约束时所需的大小,View 想多大就多大,通常在绘制自定义 View 时使用。
绘制:
对视图测量并放置在布局结构中之后,框架要为视图构造一个 Canvas 实例,调整其大小并放置在合适的位置。
效果图如下:
content_main.xml (AndroidStudio 1.5):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.crazy.customwidget.MainActivity"
tools:showIn="@layout/activity_main">
<com.crazy.customwidget.BullsView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
BullsView.java (MainActivity.java 无需修改):
package com.crazy.customwidget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.View;
public class BullsView extends View {
private Paint mPaint;
private Point mCenter;
private float mRadius;
public BullsView(Context context) {
this(context, null);
}
public BullsView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BullsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 创建画笔(支持锯齿)
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
// 创建圆心
mCenter = new Point();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width, height;
// 确定内容的理想大小,无约束
int cWidth = 100;
int mHeight = 100;
width = getHowToGetWH(widthMeasureSpec, cWidth);
height = getHowToGetWH(heightMeasureSpec, mHeight);
// 使用测量必须调用该方法
setMeasuredDimension(width, height);
}
/**
* 测量宽度和高度的方法
*/
private int getHowToGetWH(int measureSpec, int mSize) {
int specSize = MeasureSpec.getSize(measureSpec);
switch (MeasureSpec.getMode(measureSpec)){
case MeasureSpec.AT_MOST:
return Math.min(specSize, mSize);
case MeasureSpec.UNSPECIFIED:
return mSize;
case MeasureSpec.EXACTLY:
return specSize;
default:
return 0;
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// 如果有变化,则复位参数
if (w != oldw || h != oldh){
mCenter.x = w/2;
mCenter.y = h/2;
mRadius = Math.min(mCenter.x, mCenter.y);
}
}
@Override
protected void onDraw(Canvas canvas) {
// 绘制同心圆
mPaint.setColor(Color.RED);
canvas.drawCircle(mCenter.x, mCenter.y, mRadius, mPaint);
mPaint.setColor(Color.WHITE);
canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.8f, mPaint);
mPaint.setColor(Color.BLACK);
canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.6f, mPaint);
mPaint.setColor(Color.WHITE);
canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.4f, mPaint);
mPaint.setColor(Color.BLUE);
canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.1f, mPaint);
}
}