android自定义View一(基础和原理)

来源:互联网 发布:云计算板块 编辑:程序博客网 时间:2024/06/07 05:58

自定义View基础和入门
http://blog.csdn.net/androidxiaogang/article/details/51849136

1、自定义View的步骤和原理

这里写图片描述

2、构造方法

自定义view的第一步是写构造方法,构造方法是用来初始化对象的,包括view也是对象。
构造方法在这里一般要写三个甚至四个,这样写的原因:我们在不同的情况下创建View的方式不同,可能需要从xml文件中填充布局,也可能不需要,或者也需要一样style之类的,因此不同情况下,使用的构造可能存在差异。因此构造方法也有这么多种类。从API上描述我们一定要有第二个构造方法。(在实际开发中也可以第一个调用第二个,第二个调用第三个构造,确保使用了每一种)

  1. 第一个构造:是在java创建视图的时候调用,如果从xml文件中填充,则不会调用这个构造方法;
  2. 第二个构造方法 :用于layout文件实例化,会把xml中的参数通过attrs带入
  3. 第三个构造方法:这个构造方法是在第二个基础上再传入style的
  //是在java创建视图的时候调用,如果从xml文件中填充,则不会调用这个构造方法;    public MyView(Context context) {        super(context);        Log.i(TAG,"一个参数构造");    }    //用于layout文件实例化,会把xml中的参数通过attrs带入    public MyView(Context context, AttributeSet attrs) {        super(context, attrs);        Log.i(TAG, "二个参数构造");    }    //这个构造方法是在第二个基础上再传入style的。    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        Log.i(TAG, "三个参数构造");    }

3、 onMeasure()方法

这里写图片描述
在上图中我们指定了
setMeasuredDimension(900000000, 900000000);
但在实际onlayout()中大小为10807552

1、onMeasure计算视图大小的过程
measure是测量的意思,那么onMeasure()方法顾名思义就是用于测量视图的大小的。View系统的绘制流程会从ViewRoot的performTraversals()方法中开始,在其内部调用View的measure()方法。measure()方法接收两个参数,widthMeasureSpec和heightMeasureSpec,这两个值分别用于确定视图的宽度和高度的规格和大小。

MeasureSpec的值由specSize和specMode共同组成的,其中specSize记录的是大小,specMode记录的是规格。specMode一共有三种类型,如下所示:

1.MeasureSpec.EXACTLY
”确定是“:表示父视图希望子视图大小应该是specSize中指定的大小

2.MeasureSpec.AT_MOST
“最大是”:子视图的大小最大是specSize中指定的大小。

3.MeasureSpec.UNSPECIFIED
“没有限制”:此时View的设计者可以根据自身的特性设置视图的大小。


 /**     * <p>     * Measure the view and its content to determine the measured width and the     * measured height. This method is invoked by {@link #measure(int, int)} and     * should be overridden by subclasses to provide accurate and efficient     * measurement of their contents.     * </p>     *     * <p>     * <strong>CONTRACT:</strong> When overriding this method, you     * <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the     * measured width and height of this view. Failure to do so will trigger an     * <code>IllegalStateException</code>, thrown by     * {@link #measure(int, int)}. Calling the superclass'     * {@link #onMeasure(int, int)} is a valid use.     * </p>     *     * <p>     * The base class implementation of measure defaults to the background size,     * unless a larger size is allowed by the MeasureSpec. Subclasses should     * override {@link #onMeasure(int, int)} to provide better measurements of     * their content.     * </p>     *     * <p>     * If this method is overridden, it is the subclass's responsibility to make     * sure the measured height and width are at least the view's minimum height     * and width ({@link #getSuggestedMinimumHeight()} and     * {@link #getSuggestedMinimumWidth()}).     * </p>     *     * @param widthMeasureSpec horizontal space requirements as imposed by the parent.     *                         The requirements are encoded with     *                         {@link android.view.View.MeasureSpec}.     * @param heightMeasureSpec vertical space requirements as imposed by the parent.     *                         The requirements are encoded with     *                         {@link android.view.View.MeasureSpec}.     *     * @see #getMeasuredWidth()     * @see #getMeasuredHeight()     * @see #setMeasuredDimension(int, int)     * @see #getSuggestedMinimumHeight()     * @see #getSuggestedMinimumWidth()     * @see android.view.View.MeasureSpec#getMode(int)     * @see android.view.View.MeasureSpec#getSize(int)     */    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));    }

它调用了一个getDefaultSize()的方法,再来看这个方法

  /**     * Utility to return a default size. Uses the supplied size if the     * MeasureSpec imposed no constraints. Will get larger if allowed     * by the MeasureSpec.     *     * @param size Default size for this view     * @param measureSpec Constraints imposed by the parent     * @return The size this view should be.     */    public static int getDefaultSize(int size, int measureSpec) {        int result = size;        int specMode = MeasureSpec.getMode(measureSpec);        int specSize = MeasureSpec.getSize(measureSpec);        switch (specMode) {        case MeasureSpec.UNSPECIFIED:            result = size;            break;        case MeasureSpec.AT_MOST:        case MeasureSpec.EXACTLY:            result = specSize;            break;        }        return result;    }

通过onMeasure()的代码可以看出:这个方法的作用是根据父View中具体能够提供的空间大小来指定子View的视图大小,正常情况下,父View会满足子View所需要的大小,但是如果子View超过父View的最大空间,父View也只能给子View自已最大的空间。而不能无限制的满足子View。

4、onLayout()方法

onLayout()方法用于指定view在视图中的位置

    /**     * Called from layout when this view should     * assign a size and position to each of its children.     *     * Derived classes with children should override     * this method and call layout on each of     * their children.     * @param changed This is a new size or position for this view     * @param left Left position, relative to parent     * @param top Top position, relative to parent     * @param right Right position, relative to parent     * @param bottom Bottom position, relative to parent     */    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {    }

5、为什么onMeasure(),onLayout()执行二次或者多次。

这里写图片描述
父视图可以不止一次的调用onMeasure(),onLayout()方法,如果子视图的个数为0,那么只会执行一次,如果不止一个的话,就可能执行2次或者多次。因为从xml文件或者用java代码添加子View的时候,父视图也会重新测量(给子view多大的空间)如上:第一次执行了onMeasure(),onLayout(),这时候view的宽高就发生了改变,会重新调用一次onMeasure()和onLayout()

6、onDraw()绘制内容

onDraw()把需要的内容,颜色,背景绘制到屏幕中。
demo代码

package view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.util.AttributeSet;import android.util.Log;import android.view.View;/** * Created by yu on 2016/7/7. */public class MyView extends View {    public static final String TAG = "MyView";    //是在java创建视图的时候调用,如果从xml文件中填充,则不会调用这个构造方法;    public MyView(Context context) {        super(context);        Log.i(TAG, "一个参数构造");    }    //用于layout文件实例化,会把xml中的参数通过attrs带入    public MyView(Context context, AttributeSet attrs) {        super(context, attrs);        Log.i(TAG, "二个参数构造");    }    //这个构造方法是在第二个基础上再传入style的。    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        Log.i(TAG, "三个参数构造");    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        setMeasuredDimension(400, 200);        Log.i(TAG, "onMeasure()");    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);        Log.i(TAG, "onLayout()" + left + " : " + top + " : " + right + " : " + bottom);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        Log.i(TAG, "onDraw()");        canvas.drawColor(Color.RED);    }}

这里写图片描述

3 0
原创粉丝点击