您的位置:首页 > 移动开发 > Android开发

Android:View的测量/onMeasure()方法解析

2016-06-24 14:15 459 查看

为了不做伸手党,就开始分享了。

参考书籍:《疯狂Android讲义 P48》

    《Android群英传 P34——P37》

参考博客:http://www.cnblogs.com/zhuchengyi/articles/4875873.html

在现实生活中,如果我们需要去画一个图形,就要知道他的大小和位置。同样,Android系统在绘制View前,也必须对View进行测量,在onMeasure()方法中进行。

这里介绍测量模式,都是MesasureSpec中的静态常量。

1.EXACTLY

空间的layout_width、layout_height属性被设置为具体数值时候使用,比如android:layout_width = "300dp";

或者被指定为match_parent.(PS:fill_parent已经被谷歌废了,恩。)

2.AT_MOST

属性设置为wrap_content的时候使用的模式。

3.UNSPECIFIED

这个属性很奇怪,它不指定其大小测量模式,View想多大就多大,通常情况下在绘制“自定义View”的时候才会使用。

关于代码就在代码里面说

package com.example.liujianbo.test;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
* Created by liujianbo on 16/6/24.
*/
public class MyView extends View {

private Paint mPaint = new Paint();
private float currentX = 0;
private float currentY = 0;

//如果是在java文件里用控件,就需要这个构造方法站在这里。
public MyView(Context context) {
super(context);
}

//如果是在XML文件里直接使用控件,那么久需要这个构造方法出现在这里。
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}

//这个构造方法有什么卵用,没搞明白,自己去查,貌似查到了也怎么用不到。
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

/**
* 开始的onMeasure()方法是这个样子的,如下
* protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
*      super.onMeasure(widthMeasureSpec,heightMeasureSpec);
* }
* 在IDE中按住Ctrl键查看super.onMeasure()方法。可以发现,系统最终会调用
* setMeasuredDimension(int measuredWidth,int MeasuredHeight)方法将测量后的设置进去,从而完成工作。
* 所以在重写onMeasure()方法后,要做的工作就是把测量后的宽高值作为参数设置给setMeasuredDimension()方法。
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(
measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec)
);
}

/**
* 这是对宽的重新测量。
* 如果是EXACTLY模式,也就是match_parent/layout_width = "100dp"不用测了,直接使用给定的measureSpec,也就是XML文件你设置的那个数字。
* 如果是UNSPECIFIC模式,需要给定一个默认的值,当你用wrap_content属性的时候,就会使用这个你自己设置的属性,不会那么没安全感了。
* 如果是AT_MOST模式,就会比较默认的和你设置的谁更小,默认的就是指的是你设置wrap_content时,图片的那个大小。
*/
private int measureWidth(int measureSpec){
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY){
result = specSize;
}else {
result = 200;
if (specMode == MeasureSpec.AT_MOST){
result = Math.min(result,specSize);
}
}
return result;
}

private int measureHeight(int measureSpec){
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY){
result = specSize;
}else {
result = 200;
if (specMode == MeasureSpec.AT_MOST){
result = Math.min(result,specSize);
}
}
return result;
}

/**
*
* @param canvas
* 接下来是一个跟随手指的小球,就是在画布上画一个小球,然后根据点击事件动态更新。
* 变量currentX,currentY,mPaint都在上面初始化过了。
* 小白可以去查,参考资料或者网上都行。
*/

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(Color.RED);
canvas.drawCircle(currentX,currentY,15,mPaint);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
currentX = event.getX();
currentY = event.getY();
invalidate();
return true;
}
}


关于XML文件,是这么处理的,用了一个wrap_content,就会使用上面设置的200dp和系统帮你设置的大小中更小的一个,这次用的是200dp,系统想给全屏的。
你把两个Math.min都改成Math.max就知道系统想给多大的了。

<com.example.liujianbo.test.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#A000"/>

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="----我是一条分割线----"/>


效果图



这时候改变XML文件,设置为比如300dp和500dp,就会忽略设置,执行if (specMode = MeasureSpec.EXACTLY)判断里面的代码。
如果用match_parent就是充满屏幕,都是EXACTLY模式的。

效果图



看起来代码里的200和这里的300dp差距有点大,个人猜测代码里的数字会被配上px的单位,然后在XML文件里把300dp改成300px就差不多清晰了。

效果图



PS:录屏软件用的是icecap,可以录制屏幕,输出GIF动态图,大概就是这样,恩。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android