自定义View知识基础准备(一)

来源:互联网 发布:php 全局命名空间 编辑:程序博客网 时间:2024/05/17 06:44

转载请标明出处:http://blog.csdn.net/u010145900/article/details/45197133
安卓的火爆不仅仅是因为它是廉价的智能机,更多的是因为它多种多样的软件应用和酷炫的界面效果。当然这些都是程序猿们辛苦编码出来的成果。
google源生框架封装的控件经常不能满足苛刻的用户需求,google大神将android framework层开源,也很好的为程序猿们提供了如何自定义控件的范例。
我这也提供两位知名博主的自定义view的帖子,以供读者学习和参考。他们写的很详细也很到位。
http://blog.csdn.net/lmj623565791/article/details/24252901
http://blog.csdn.net/guolin_blog/article/details/12921889
下面我们从源码和api的角度来看看google是怎么教我们自定义view的。本人英文水平有限,翻译内容仅供参考。
我们打开android api和源码(这里使用的是android 20版本)。

android api中View的第一段英文就对view类进行了功能描述:

This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling. View is the base class for widgets, which are used to create interactive UI components (buttons, text fields, etc.). The ViewGroup subclass is the base class for layouts, which are invisible containers that hold other Views (or other ViewGroups) and define their layout properties.

View类是用户界面组件的基本构建块。一个view占据屏幕的一块矩形区域,并负责绘制和处理事件。View类是控件(用于人机交互的界面组件,比如按钮,文本框等)的基类。它的子类ViewGroup是布局容器(用于盛装其他view或者ViewGroup)的基类,并可定义它的子view的属性。
然后api中的第二段就告诉我们View的基本用法,告诉我们View可以做的操作有:设置属性,焦点,监听器,可视性(是否可见),并在其中列举了一些相关的操作方法。
接下来,就是我们最关注的内容了:实现自定义view(Implementing a Custom View)android api给我们提供列举了自定义view时可能用到的回调方法:
这里写图片描述

google官方将自定义View的回调方法分为了6类。

我们一一的来细看一下。

一、Creation

创建方法有:构造方法和onFinishInflate()

我们查看View的源码,发现View有3个公开的构造方法,google都做了详细的注释:

     1.public View(Context context) {       ......       }    Simple constructor to use when creating a view from code.

用于从代码进行创建View对象的简单构造方法

2.public View(Context context, AttributeSet attrs) {             this(context, attrs, 0);             }Constructor that is called when inflating a view from XML. This is called  when a view is being constructed from an XML file, supplying attributes  that were specified in the XML file. This version uses a default style of       0, so the only attribute values applied are those in the Context's Theme  and the given AttributeSet. The method onFinishInflate() will be called after all children have been added.

当从xml布局映射一个view时会调用该构造方法。该构造方法是在当一个view是从xml布局文件中构建,

并且在xml布局文件中提供了该view的指定的属性时被调用。使用默认的样式0,因此,只有该版本上下文的主题和属性值可以被使用。
在所有的子view被添加到该view(布局容器)中后,回调方法 onFinishInflate()方法将被调用。

我们注意到,这里使用的是this(context,attrs,0) 调用了另外一个view的构造方法。就是下面这个:

  3.public View(Context context, AttributeSet attrs, int defStyleAttr) {             this(context);         ......  } Perform inflation from XML and apply a class-specific base style. This  constructor of View allows subclasses to use their own base style when  they are inflating. For example, a Button class's constructor would call        this version of the super class constructor and supply    R.attr.buttonStyle for defStyle; this allows  the theme's button style to modify all of the base view attributes (in particular its background) as well as the Button class's attributes.

从xml中进行映射并应用指定的基本样式。该构造方法允许View的子类在进行加载时使用自己的风格样式。例如:一个按钮类在创建时会调用父类的这一个构造方法并使用指定的R.attr.buttonStyle样式。这将允许这个主题风格的按钮样式来修改基类View 的所有属性(尤其是是它的背景)和该按钮类的属性。如果我们仔细的看View该构造方法的源代码,
会发现这个构造方法中的代码有400多行,使用switch语句对定义了的58种xml中使用的属性,进行分支判断和相应的操作。使用的case语句要么是:com.android.internal.R.styleable.要么是R.styleable.作为前缀,如果我们看过其他人写的自定义View实现特效的博客就会发现:
xml布局文件中各种控件属性的设置原来就是这样做的,原来google大神已经在源代码中给我们做了很多的范例。
关于这58个view的属性解释可以参考博客:http://www.eoeandroid.com/thread-294226-1-1.html
4.onFinishInflate()方法

Finalize inflating a view from XML.  This is called as the last phase  of inflation, after all child views have been added.Even if the subclass overrides onFinishInflate, they should always be  sure to call the super method, so that we get called.

当View中所有的子控件 均被映射成xml后会触发该回调方法。
子类在重写该方法时,需要使用super关键字来调用view父类的该回调方法,从而可以获取到系统的该回调通知

这个方法我还基本没有使用过,参考了一些博客关于该方法的理解:

http://www.incoding.org/admin/archives/199.html

我们一般使用View的流程是在onCreate中使用setContentView来设置要显示Layout文件或直接创建一个View,在当设置了ContentView之后系统会对这个View进行解析,然后回调当前视图View中的onFinishInflate方法。只有解析了这个View我们才能在这个View容器中
获取到拥有Id的组件,同样因为系统解析完View之后才会调用onFinishInflate方法,所以我们自定义组件时可以onFinishInflate方法中获取指定子View的引用。
也就是说,在自定义组合型控件时需要使用该回调方法,注意不要忘了super关键字。

二、layout

View布局时有3个回调方法:

onMeasure(int,int)                                             onLayout(booleanintintintint)onSizeChanged(intintintint

我们打开View的源码来看看这3个回调方法的注释:

  1.onMeasure:           protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {           ......                         }    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 overriden by subclasses to provide accurate and efficient  measurement of their contents. When overriding this method, you must call {@link #setMeasuredDimension(int, int)} to store the measured width and height of this view. Failure to do so will trigger an IllegalStateException, thrown by {@link #measure(int, int)}. Calling the superclass'  {@link #onMeasure(int, int)} is a valid use. 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. 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()}).

又是很长的一大段英文注释,说了一大堆。
简单解释下:View的子类应当重写该方法以为其内容提供准确且有效的测量值。重写该方法必须要调用 setMeasuredDimension(int, int)方法来保存测量的宽高值。 View的onMeasure回调方法已经实现了测量默认的背景尺寸,直到被MeasureSpec允许的更大的尺寸来替代。View子类应当重写onMeasure方法来为其提供一个更适合其内容的尺寸,注意:重写该方法时,宽高值应当至少是这个view的最小宽高值。

 2.onLayout     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}     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.

当该View为其子view分配控件和位置时会调用该方法,带有child的派生子类应当重写该方法来为其每一个child进行布局。实际上,带有child 的view都是在指布局容器ViewGroup,只是ViewGroup也是View的子类。我们从ViewGroup的源代码会看到:protected abstract void onLayout(boolean changed, int l, int t, int r, int b);
ViewGroup将onLayout方法重写并设置为了abstract抽象方法,这样只要是继承自ViewGroup的自定义View都必须要重写该回调方法来为其子view进行尺寸和位置的布局。具体的onLayout的操作参考可以查看LinearLayout,RelativeLayout等常用布局容器的源代码。

3.onSizeChanged(int, int, int, int)

     /**     * This is called during layout when the size of this view has changed. If     * you were just added to the view hierarchy, you're called with the old     * values of 0.     *     * @param w Current width of this view.     * @param h Current height of this view.     * @param oldw Old width of this view.     * @param oldh Old height of this view.     */    protected void onSizeChanged(int w, int h, int oldw, int oldh) {    }

当view在进行布局时发生了尺寸变化会调用该回调方法。当你的view刚刚添加到布局中时,该方法的old参数都是0.即:oldw和oldh初始为0。

限于篇幅,这里我们先介绍到这里,未完待续。

0 0
原创粉丝点击