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的所有源代码,对自己会有一个很大的提升,逗比的一天接近尾声,看会儿爱哥的设计模式去,坐等下班。