View类两万一千多行,在这里逐步分析。建议这篇blog对照Android View源码观看!!

/*     <tr> *         <td><code>{@link #onFinishInflate()}code>td> *         <td>Called after a view and all of its children has been inflated *         from XML.td> *     tr> * *     <tr> *         <td rowspan="3">Layouttd> *         <td><code>{@link #onMeasure(int, int)}code>td> *         <td>Called to determine the size requirements for this view and all *         of its children. *         td> *     tr> *     <tr> *         <td><code>{@link #onLayout(boolean, int, int, int, int)}code>td> *         <td>Called when this view should assign a size and position to all *         of its children. *         td> *     tr> *     <tr> *         <td><code>{@link #onSizeChanged(int, int, int, int)}code>td> *         <td>Called when the size of this view has changed. *         td> *     tr> * *     <tr> *         <td>Drawingtd> *         <td><code>{@link #onDraw(android.graphics.Canvas)}code>td> *         <td>Called when the view should render its content. *         td> *     tr> * *     <tr> *         <td rowspan="4">Event processingtd> *         <td><code>{@link #onKeyDown(int, KeyEvent)}code>td> *         <td>Called when a new hardware key event occurs. *         td> *     tr> *     <tr> *         <td><code>{@link #onKeyUp(int, KeyEvent)}code>td> *         <td>Called when a hardware key up event occurs. *         td> *     tr> *     <tr> *         <td><code>{@link #onTrackballEvent(MotionEvent)}code>td> *         <td>Called when a trackball motion event occurs. *         td> *     tr> *     <tr> *         <td><code>{@link #onTouchEvent(MotionEvent)}code>td> *         <td>Called when a touch screen motion event occurs. *         td> *     tr> * *     <tr> *         <td rowspan="2">Focustd> *         <td><code>{@link #onFocusChanged(boolean, int, android.graphics.Rect)}code>td> *         <td>Called when the view gains or loses focus. *         td> *     tr> * *     <tr> *         <td><code>{@link #onWindowFocusChanged(boolean)}code>td> *         <td>Called when the window containing the view gains or loses focus. *         td> *     tr> * *     <tr> *         <td rowspan="3">Attachingtd> *         <td><code>{@link #onAttachedToWindow()}code>td> *         <td>Called when the view is attached to a window. *         td> *     tr> * *     <tr> *         <td><code>{@link #onDetachedFromWindow}code>td> *         <td>Called when the view is detached from its window. *         td> *     tr> * *     <tr> *         <td><code>{@link #onWindowVisibilityChanged(int)}code>td> *         <td>Called when the visibility of the window containing the view *         has changed. *         td> *     tr> */

View通过构造方法创建,onMeasure测量,Layout方法测量需要测量自身和子View,onLayout方法分配子View,onSizeChanged方法当视图大小发生改变时调用,onDraw方法用于Canvas的绘制内容,onTouchEvent触屏时调用,onFocusChanged焦点改变时调用,onWindowFocusChanged同理,onWindowVisibilityChanged视图可见性onDetachedFromWindow取消监听和相关事件,onAttachedToWindow绑定相关事件监听。

/*     android:id="@+id/my_button" *     android:layout_width="wrap_content" *     android:layout_height="wrap_content" *     android:text="@string/my_button_text" */

XML引用View控件,并分配ID

public class View implements Drawable.Callback, KeyEvent.Callback,        AccessibilityEventSource {              .......        }

这里实现的三个接口,先来看Drawable.Callback

 /**     * Implement this interface if you want to create an animated drawable that     * extends {@link android.graphics.drawable.Drawable Drawable}.     * Upon retrieving a drawable, use     * {@link Drawable#setCallback(android.graphics.drawable.Drawable.Callback)}     * to supply your implementation of the interface to the drawable; it uses     * this interface to schedule and execute animation changes.     */    public static interface Callback {        /**         * Called when the drawable needs to be redrawn.  A view at this point         * should invalidate itself (or at least the part of itself where the         * drawable appears).         *         * @param who The drawable that is requesting the update.         */        public void invalidateDrawable(Drawable who);        /**         * A Drawable can call this to schedule the next frame of its         * animation.  An implementation can generally simply call         * {@link android.os.Handler#postAtTime(Runnable, Object, long)} with         * the parameters (what, who, when) to perform the         * scheduling.         *         * @param who The drawable being scheduled.         * @param what The action to execute.         * @param when The time (in milliseconds) to run.  The timebase is         *             {@link android.os.SystemClock#uptimeMillis}         */        public void scheduleDrawable(Drawable who, Runnable what, long when);        /**         * A Drawable can call this to unschedule an action previously         * scheduled with {@link #scheduleDrawable}.  An implementation can         * generally simply call         * {@link android.os.Handler#removeCallbacks(Runnable, Object)} with         * the parameters (what, who) to unschedule the drawable.         *         * @param who The drawable being unscheduled.         * @param what The action being unscheduled.         */        public void unscheduleDrawable(Drawable who, Runnable what);    }

View内部对外公开两个方法setCallback 、getCallback

 /**     * Bind a {@link Callback} object to this Drawable.  Required for clients     * that want to support animated drawables.     *     * @param cb The client's Callback implementation.     *     * @see #getCallback()     */    public final void setCallback(Callback cb) {        mCallback = new WeakReference(cb);    }    /**     * Return the current {@link Callback} implementation attached to this     * Drawable.     *     * @return A {@link Callback} instance or null if no callback was set.     *     * @see #setCallback(android.graphics.drawable.Drawable.Callback)     */    public Callback getCallback() {        if (mCallback != null) {            return mCallback.get();        }        return null;    }

如果你想扩展View动画,可以实现Drawable.Callback,然后调用setCallback传入Callback的实现类即可invalidateDrawable()方法,在drawable重画时触发,展示Drawable部分将不可用。scheduleDrawable()用于控制动画的下一帧,用户可以通过mHandler.postAtTime(Runnable,Object)调用,unscheduleDrawable()方法则是取消scheduleDrawable()该方法定义的下一帧动画。这里的setCallback并不是我们常用的this.mCallback=mCallBack而是通过弱引用(引用类型:强引用 、软引用、弱引用、虚引用)引用,以便于GC回收,避免了内存泄露。getCallback()方法通过软引用调用抽象父类的get方法间接的调用native方法getReferent()。下面再来看KeyEvent.Callback接口定义。

 public interface Callback {        /**         * Called when a key down event has occurred.  If you return true,         * you can first call {@link KeyEvent#startTracking()         * KeyEvent.startTracking()} to have the framework track the event         * through its {@link #onKeyUp(int, KeyEvent)} and also call your         * {@link #onKeyLongPress(int, KeyEvent)} if it occurs.         *         * @param keyCode The value in event.getKeyCode().         * @param event Description of the key event.         *         * @return If you handled the event, return true.  If you want to allow         *         the event to be handled by the next receiver, return false.         */        boolean onKeyDown(int keyCode, KeyEvent event);        /**         * Called when a long press has occurred.  If you return true,         * the final key up will have {@link KeyEvent#FLAG_CANCELED} and         * {@link KeyEvent#FLAG_CANCELED_LONG_PRESS} set.  Note that in         * order to receive this callback, someone in the event change         * must return true from {@link #onKeyDown} and         * call {@link KeyEvent#startTracking()} on the event.         *         * @param keyCode The value in event.getKeyCode().         * @param event Description of the key event.         *         * @return If you handled the event, return true.  If you want to allow         *         the event to be handled by the next receiver, return false.         */        boolean onKeyLongPress(int keyCode, KeyEvent event);        /**         * Called when a key up event has occurred.         *         * @param keyCode The value in event.getKeyCode().         * @param event Description of the key event.         *         * @return If you handled the event, return true.  If you want to allow         *         the event to be handled by the next receiver, return false.         */        boolean onKeyUp(int keyCode, KeyEvent event);        /**         * Called when multiple down/up pairs of the same key have occurred         * in a row.         *         * @param keyCode The value in event.getKeyCode().         * @param count Number of pairs as returned by event.getRepeatCount().         * @param event Description of the key event.         *         * @return If you handled the event, return true.  If you want to allow         *         the event to be handled by the next receiver, return false.         */        boolean onKeyMultiple(int keyCode, int count, KeyEvent event);    }

KeyEnvent类继承自InputEvent,该类定义了许多keyCode值,例如:

 public static final int KEYCODE_SOFT_LEFT       = 1;    /** Key code constant: Soft Right key.     * Usually situated below the display on phones and used as a multi-function     * feature key for selecting a software defined function shown on the bottom right     * of the display. */    public static final int KEYCODE_SOFT_RIGHT      = 2;    /** Key code constant: Home key.     * This key is handled by the framework and is never delivered to applications. */    public static final int KEYCODE_HOME            = 3;    /** Key code constant: Back key. */    public static final int KEYCODE_BACK            = 4;    /** Key code constant: Call key. */    public static final int KEYCODE_CALL            = 5;

用户操作是只执行相应的回调函数,例如按住返回键,那么就会执行KeyEvent.Callback 的onKeyDown(KEYCODE_BACK,KeyEvent),在我们开发时,判断KeyEvent.getAction判断Action类型和keyCode值选择执行相应函数,return boolean 如果返回值为true代表处理该事件不在向下分发,false则不处理该事件。(Action是在KeyEvent构造参数时传入)Action在KeyEvent的定义如下:

 /**     * {@link #getAction} value: the key has been pressed down.     */    public static final int ACTION_DOWN             = 0;    /**     * {@link #getAction} value: the key has been released.     */    public static final int ACTION_UP               = 1;    /**     * {@link #getAction} value: multiple duplicate key events have     * occurred in a row, or a complex string is being delivered.  If the     * key code is not {#link {@link #KEYCODE_UNKNOWN} then the     * {#link {@link #getRepeatCount()} method returns the number of times     * the given key code should be executed.     * Otherwise, if the key code is {@link #KEYCODE_UNKNOWN}, then     * this is a sequence of characters as returned by {@link #getCharacters}.     */    public static final int ACTION_MULTIPLE         = 2;    //..........................此处略....................................

KeyEvent.Callback的onKeyUp方法也onKeyDown差别不大,区别在于事件的响应时间,一个是按下一个是抬起。View和Activity同时重写onKeyDown onkeyUp方法会先执行View的。

/** * This interface is implemented by classes source of {@link AccessibilityEvent}s. * *  * 

Developer Guides

*

For more information about making applications accessible, read the * Accessibility * developer guide.

* */
public interface AccessibilityEventSource { /** * Handles the request for sending an {@link AccessibilityEvent} given * the event type. The method must first check if accessibility is on * via calling {@link AccessibilityManager#isEnabled() AccessibilityManager.isEnabled()}, * obtain an {@link AccessibilityEvent} from the event pool through calling * {@link AccessibilityEvent#obtain(int) AccessibilityEvent.obtain(int)}, populate the * event, and send it for dispatch via calling * {@link AccessibilityManager#sendAccessibilityEvent(AccessibilityEvent) * AccessibilityManager.sendAccessibilityEvent(AccessibilityEvent)}. * * @see AccessibilityEvent * @see AccessibilityManager * * @param eventType The event type. */ public void sendAccessibilityEvent(int eventType); /** * Handles the request for sending an {@link AccessibilityEvent}. The * method does not guarantee to check if accessibility is on before * sending the event for dispatch. It is responsibility of the caller * to do the check via calling {@link AccessibilityManager#isEnabled() * AccessibilityManager.isEnabled()}. * * @see AccessibilityEvent * @see AccessibilityManager * * @param event The event. */ public void sendAccessibilityEventUnchecked(AccessibilityEvent event);}

sendAccessibilityEventUnchecked() (API级别4)当调用代码需要直接控制检查辅助功能设备的启用时,调用该方法。 sendAccessibilityEvent()(API级别4)当用户在一个视图操作时调用此方法。事件是按照用户操作类型分类,如TYPE_VIEW_CLICKED。你通常不需要实现该方法,除非你是创建一个自定义视图。

  private static final boolean DBG = false;

定义全局final变量DBG,如果是调试模式,在调用某些方法调试时输出日志,例如:

 protected boolean setFrame(int left, int top, int right, int bottom) {        boolean changed = false;        if (DBG) {            Log.d("View", this + " View.setFrame(" + left + "," + top + ","                    + right + "," + bottom + ")");        }        //.......................此处略.......................  }

Log.i/e/w(TAG,”“)方法中TAG定义:

 /**     * The logging tag used by this class with android.util.Log.     */    protected static final String VIEW_LOG_TAG = "View";

下面的再来看一组变量定义:

   /**     * When set to true, apps will draw debugging information about their layouts.     *     * @hide     */    public static final String DEBUG_LAYOUT_PROPERTY = "debug.layout";   /**     * Show where the margins, bounds and layout bounds are for each view.     */    boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false);

SystemProperties类位置在framework层,查看请点击SystemProperties,这里面的内容简单的封装了set get 方法,set get方法内部分别调用了native 方法代码如下:

/** * Gives access to the system properties store.  The system properties * store contains a list of string key-value pairs. * * {@hide} */public class SystemProperties{    public static final int PROP_NAME_MAX = 31;    public static final int PROP_VALUE_MAX = 91;    private static final ArrayList sChangeCallbacks = new ArrayList();    private static native String native_get(String key);    private static native String native_get(String key, String def);    private static native int native_get_int(String key, int def);    private static native long native_get_long(String key, long def);    private static native boolean native_get_boolean(String key, boolean def);    private static native void native_set(String key, String def);    private static native void native_add_change_callback();      /**     * Get the value for the given key, returned as a boolean.     * Values 'n', 'no', '0', 'false' or 'off' are considered false.     * Values 'y', 'yes', '1', 'true' or 'on' are considered true.     * (case sensitive).     * If the key does not exist, or has any other value, then the default     * result is returned.     * @param key the key to lookup     * @param def a default value to return     * @return the key parsed as a boolean, or def if the key isn't found or is     *         not able to be parsed as a boolean.     * @throws IllegalArgumentException if the key exceeds 32 characters     */    public static boolean getBoolean(String key, boolean def) {        if (key.length() > PROP_NAME_MAX) {            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);        }        return native_get_boolean(key, def);    }    //.......................其他略...............................}

该类的作用是获取设置系统属性,进行系统属性设置的程序也必须有system或root权限,
如何将android程序的权限提升到system权限?方法是这样的:
1.在AndroidManifest.xml中,在manifest加入android:sharedUserId=”android.uid.system”。
2.在Android.mk中,將LOCAL_CERTIFICATE := XXX修改成LOCAL_CERTIFICATE :=platform。
该类参考资料:http://blog.csdn.net/ameyume/article/details/8056492

 /**     * Used to mark a View that has no ID.     */    public static final int NO_ID = -1;

NO_ID变量让属性的初始值引用,代码实例如下:

public View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {    //.............此处略.......................     case com.android.internal.R.styleable.View_id:       mID = a.getResourceId(attr, NO_ID);       break;     case com.android.internal.R.styleable.View_tag:       mTag = a.getText(attr);       break;}

再来看一下三个变量定义:sCompatibilityDone 、sUseBrokenMakeMeasureSpec 、sIgnoreMeasureCache

   /**     * Signals that compatibility booleans have been initialized according to     * target SDK versions.     */    private static boolean sCompatibilityDone = false;     /**     * Use the old (broken) way of building MeasureSpecs.     */    private static boolean sUseBrokenMakeMeasureSpec = false;    /**     * Ignore any optimizations using the measure cache.     */    private static boolean sIgnoreMeasureCache = false;

这三个参数在构造方法里面进行了初始化赋值

 public View(Context context) {  //..............此处略..................  if (!sCompatibilityDone && context != null) {          final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;     // Older apps may need this compatibility hack for measurement.     sUseBrokenMakeMeasureSpec = targetSdkVersion <= JELLY_BEAN_MR1;     // Older apps expect onMeasure() to always be called on a layout pass, regardless     // of whether a layout was requested on that View.     sIgnoreMeasureCache = targetSdkVersion < KITKAT;     sCompatibilityDone = true;     }  }

sCompatibilityDone 有何意义我还没发现,待后续研究,sUseBrokenMakeMeasureSpec 变量用于判断使用哪一种方式(老版本的方法还是新版本的方法)构建MeasureSpec,MeasureSpec是测量相关,稍后再提。

 public static int makeMeasureSpec(int size, int mode) {            if (sUseBrokenMakeMeasureSpec) {                return size + mode;            } else {                return (size & ~MODE_MASK) | (mode & MODE_MASK);            }        }

sIgnoreMeasureCache 忽略任何优化使用缓存,在measure测量方法里会用到该参数,稍后再细说measure方法。接着来看一组焦点相关的变量定义:

  /**     * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when     * calling setFlags.     */    private static final int NOT_FOCUSABLE = 0x00000000;    /**     * This view wants keystrokes. Use with TAKES_FOCUS_MASK when calling     * setFlags.     */    private static final int FOCUSABLE = 0x00000001;    /**     * Mask for use with setFlags indicating bits used for focus.     */    private static final int FOCUSABLE_MASK = 0x00000001;

在我们平时控制View的焦点响应方法setFocusable(boolean focusable)其本质就是在根据传入参数获取一个int 型 flag值,随后调用了setFlags隐藏方法;

 /**     * Set whether this view can receive the focus.     *     * Setting this to false will also ensure that this view is not focusable     * in touch mode.     *     * @param focusable If true, this view can receive the focus.     *     * @see #setFocusableInTouchMode(boolean)     * @attr ref android.R.styleable#View_focusable     */    public void setFocusable(boolean focusable) {        if (!focusable) {            setFlags(0, FOCUSABLE_IN_TOUCH_MODE);        }        setFlags(focusable ? FOCUSABLE : NOT_FOCUSABLE, FOCUSABLE_MASK);    }

View.setVisibility也是如此(同时还判断选择调用Drawale的setVisibilty,通过回调接口Drawable.Callback的 invalidateDrawable(this)方法控制重绘)

 /**     * Set the enabled state of this view.     *     * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.     * @attr ref android.R.styleable#View_visibility     */    @RemotableViewMethod    public void setVisibility(@Visibility int visibility) {        setFlags(visibility, VISIBILITY_MASK);        if (mBackground != null) mBackground.setVisible(visibility == VISIBLE, false);    }

隐藏方法setFlag对我来说无疑是神秘的,位运算搞得我头大,查了一番资料,马马虎虎明白其原理,位运算相关知识链接点击这里,该方法的具体实现如下:

 /**     * Set flags controlling behavior of this view.     *     * @param flags Constant indicating the value which should be set     * @param mask Constant indicating the bit range that should be changed     */    void setFlags(int flags, int mask) {        final boolean accessibilityEnabled =                AccessibilityManager.getInstance(mContext).isEnabled();        final boolean oldIncludeForAccessibility = accessibilityEnabled && includeForAccessibility();        //old保存mViewFlags之前的值, (mViewFlags在构造方法里初始化了)             int old = mViewFlags;        mViewFlags = (mViewFlags & ~mask) | (flags & mask);        //old和mViewFlags异或后得到一个新的changed 凡是为1 说明发送了变化, 就需要View系统进行调整了!        int changed = mViewFlags ^ old;        if (changed == 0) {         /*********            什么时候能有中文版注释的系统源码,偶滴神啊,头大了            flags = 0x0000000100             int old = mViewFlags; //旧的flag  = 0x00000000              mViewFlags = (mViewFlags & ~mask) | (flags & mask); //新的flag               0x00000000  & ~(00000001100) | & 00000001100   =   0x0000000100             int changed = mViewFlags ^ old;  = 0x0000000100             if (changed == 0) {             return; //hey ! 没有变化! 直接return!           ************/            return;        }        //...............此处略..................        /* Check if the GONE bit has changed */        if ((changed & GONE) != 0) {            needGlobalAttributesUpdate(false);            requestLayout();            if (((mViewFlags & VISIBILITY_MASK) == GONE)) {                //清除view的cache                if (hasFocus()) clearFocus();                clearAccessibilityFocus();                destroyDrawingCache();                if (mParent instanceof View) {                    // GONE views noop invalidation, so invalidate the parent                    //重绘                    ((View) mParent).invalidate(true);                }                // Mark the view drawn to ensure that it gets invalidated properly the next                // time it is visible and gets invalidated                mPrivateFlags |= PFLAG_DRAWN;            }            if (mAttachInfo != null) {                mAttachInfo.mViewVisibilityChanged = true;            }        }          //........此处略....方法说明requestLayout():重新布局;invalidate(true):重绘..........    }

半天多时间已去,第一篇View blog也出来了,我也先学先写,差了一些资料发现了很多以前不懂的知识,向下等看完View的所有源代码,对自己会有一个很大的提升,逗比的一天接近尾声,看会儿爱哥的设计模式去,坐等下班。

更多相关文章

  1. 【工具类】Android自定义Dialog
  2. Android 获取mac地址方法
  3. android videoView播放视频,对播放结束的监听方法
  4. Android 获取本机唯一序列号 和可变UUID方法

随机推荐

  1. Android下载repo文件报错
  2. 全球十个最佳Android应用
  3. [置顶] android 实现发送彩信方法 (MMS),
  4. Android(安卓)Dialog设置TYPE_SYSTEM_ALE
  5. android 笔记 --- Intent 应用
  6. android Java BASE64编码和解码一:基础
  7. ProgressBar
  8. android 应用的网络请求工具
  9. 手把手教你搭建 Android(安卓)SVN 环境
  10. android task与back stack 开发文档翻译