首页 > 代码库 > BullsEyeView自定义view

BullsEyeView自定义view

package com.osblog.androidrecipes;  
  
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.util.Log;  
import android.view.View;  
  
public class BullsEyeView extends View {  
  
    private final static String TAG = BullsEyeView.class.getSimpleName();  
  
    private Paint mPaint;  
  
    private Point mCenter;  
    private float mRadius;  
  
    public BullsEyeView(Context context) {  
        this(context, null);  
    }  
  
    public BullsEyeView(Context context, AttributeSet attrs) {  
        this(context, attrs, 0);  
    }  
  
  
    public BullsEyeView(Context context, AttributeSet attrs,  
                        int defStyle) {  
        super(context, attrs, defStyle);  
  
        init();  
    }  
  
    private void init() {  
        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 contentWidth = 200;  
        int contentHeight = 200;  
  
        width = getMeasurement(widthMeasureSpec, contentWidth);  
        height = getMeasurement(heightMeasureSpec, contentHeight);  
        setMeasuredDimension(width, height);  
    }  
      
    private int getMeasurement(int measureSpec, int contentSize) {  
        int specSize = MeasureSpec.getSize(measureSpec);  
        switch (MeasureSpec.getMode(measureSpec)) {  
            case MeasureSpec.AT_MOST:  
                Log.v(TAG, specSize + "AT_MOST");  
                return Math.min(specSize, contentSize);  
            case MeasureSpec.UNSPECIFIED:  
                Log.v(TAG, "UNSPECIFIED");  
                return contentSize;  
            case MeasureSpec.EXACTLY:  
                Log.v(TAG, specSize + "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.BLUE);  
        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.RED);  
        canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.2f,  
                mPaint);  
    }  
}  

  

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical">  
    <com.osblog.androidrecipes.BullsEyeView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content" />  
  
</LinearLayout>  

技术分享

打印log日志为

 02-01 11:15:43.310 11521-11521/com.osblog.androidrecipes V/BullsEyeView: 720EXACTLY

 02-01 11:15:43.310 11521-11521/com.osblog.androidrecipes V/BullsEyeView: 1232EXACTLY

然后我们再将layout_width和layout_height改为wrap_content

运行效果:技术分享

 

打印log如下:

02-01 11:31:10.080 1886-1886/com.osblog.androidrecipes V/BullsEyeView: 720AT_MOST
02-01 11:31:10.080 1886-1886/com.osblog.androidrecipes V/BullsEyeView: 1160AT_MOST

然后我们再将layout_width和layout_height改为100dp

运行效果和上图一样,打印log日志为

02-01 11:32:35.440 4006-4006/com.osblog.androidrecipes V/BullsEyeView: 200EXACTLY
02-01 11:32:35.440 4006-4006/com.osblog.androidrecipes V/BullsEyeView: 200EXACTLY

通过查看官方API MeasureSpec有3种mode分别

 

UNSPECIFIED
The parent has not imposed any constraint on the child. It can be whatever size it wants.
EXACTLY
The parent has determined an exact size for the child. The child is going to be given those bounds regardless of how big it wants to be.
AT_MOST
The child can be as large as it wants up to the specified size.

MeasureSpecs are implemented as ints to reduce object allocation. This class is provided to pack and unpack the <size, mode> tuple into the int.

 

通过log和代码分析总结如下:

 

MeasureSpec.EXACTLY:当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="100dip",或者为match_patent(fill_parent),都是控件大小已经确定的情况,都是精确尺寸。

        MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为wrap_content时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。

        MeasureSpec.UNSPECIFIED是未指定尺寸,既控件可以得到它想要的最大尺寸,这种情况不多,一般都是父控件是AdapterView(listview或者scrollview),通过measure方法传入的模式。

通过代码我们可以看到当测量完成之后通过setMeasureDimension(weith,height)设置控件的宽高,在 onSizeChanged方法中已经得到了具体的宽高,我们在这里可以进行相关操作,例如本例我们计算画圆的圆心。

代码从上而下的三个构造方法,第一个是当我们使用 setContentView(new BullsEyeView())的时候调用,既直接用Java代码new一个view。

第二个是我们在布局文件中调用,第三个是在布局文件中并且使用了一个style。

  

BullsEyeView自定义view