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

自定义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
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值