View是安卓一切視圖的基礎(chǔ),我覺得先從這個(gè)點(diǎn)開始學(xué)習(xí)坷襟,向下擴(kuò)展會(huì)讓我更容易理解生年。
//包
package android.view;
//類
android.view.View
1抱婉、 實(shí)現(xiàn)接口 Drawable.Callback
蒸绩, 用來創(chuàng)建動(dòng)畫繪制時(shí)铃肯,用來實(shí)現(xiàn)調(diào)度和動(dòng)畫修改操作的,分為以下三個(gè)接口:
//繪制自身
void invalidateDrawable(@NonNull Drawable who);
//計(jì)劃執(zhí)行下一次動(dòng)畫
void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when);
//取消執(zhí)行前面計(jì)劃
void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what);
這個(gè)接口是在 setBackground(Drawable background)
中注冊(cè)監(jiān)聽的惦界。其實(shí)現(xiàn)如下:
public void setBackground(Drawable background) {
setBackgroundDrawable(background);
}
@Deprecated
public void setBackgroundDrawable(Drawable background) {
...
boolean requestLayout = false;
mBackgroundResource = 0;
//如果已有背景沾歪,如果此View綁定到了Window上雾消,則先使其不可見。
if (mBackground != null) {
if (isAttachedToWindow()) {
mBackground.setVisible(false, false);
}
//然后再清空回調(diào)監(jiān)聽
mBackground.setCallback(null);
//再清空原背景的所有繪制任務(wù)
unscheduleDrawable(mBackground);
}
if (background != null) {
...
//設(shè)置當(dāng)前背景的布局方向
background.setLayoutDirection(getLayoutDirection());
//這里獲取背景的padding值并根據(jù)布局方向設(shè)置布局參數(shù)。
if (background.getPadding(padding)) {
resetResolvedPaddingInternal();
switch (background.getLayoutDirection()) {
case LAYOUT_DIRECTION_RTL:
mUserPaddingLeftInitial = padding.right;
mUserPaddingRightInitial = padding.left;
internalSetPadding(padding.right, padding.top, padding.left, padding.bottom);
break;
case LAYOUT_DIRECTION_LTR:
default:
mUserPaddingLeftInitial = padding.left;
mUserPaddingRightInitial = padding.right;
internalSetPadding(padding.left, padding.top, padding.right, padding.bottom);
}
mLeftPaddingDefined = false;
mRightPaddingDefined = false;
}
// 判斷當(dāng)前背景大小是否與上一次繪制的背景大小相等泉哈,不想等則標(biāo)記為要重新繪制旨巷。
if (mBackground == null
|| mBackground.getMinimumHeight() != background.getMinimumHeight()
|| mBackground.getMinimumWidth() != background.getMinimumWidth()) {
requestLayout = true;
}
//賦值
mBackground = background;
//判斷是否需要狀態(tài)更新
if (background.isStateful()) {
background.setState(getDrawableState());
}
//判斷是否已添加到窗口中
if (isAttachedToWindow()) {
//設(shè)置是否可見
background.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
}
applyBackgroundTint();
// 最后設(shè)置監(jiān)聽
background.setCallback(this);
if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
requestLayout = true;
}
} else {
//這里的分支是做移除背景操作
mBackground = null;
if ((mViewFlags & WILL_NOT_DRAW) != 0
&& (mForegroundInfo == null || mForegroundInfo.mDrawable == null)) {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
//這里會(huì)請(qǐng)求布局一次
requestLayout = true;
}
//再次計(jì)算一次透明度
computeOpaqueFlags();
//是否需要重新布局
if (requestLayout) {
requestLayout();
}
//標(biāo)記背景大小發(fā)生改變
mBackgroundSizeChanged = true;
//繪制
invalidate(true);
invalidateOutline();
}
2采呐、 實(shí)現(xiàn)接口 KeyEvent.Callback
斧吐, 實(shí)現(xiàn)所有按鍵監(jiān)聽回調(diào)仲器,接口如下:
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
具體實(shí)現(xiàn)方式如下:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.isConfirmKey(keyCode)) {//檢測(cè)
if ((mViewFlags & ENABLED_MASK) == DISABLED) {//檢測(cè)是否標(biāo)記為禁用
return true;
}
//判斷是否可點(diǎn)擊,或可長(zhǎng)點(diǎn)擊
if (((mViewFlags & CLICKABLE) == CLICKABLE
|| (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
&& (event.getRepeatCount() == 0)) {
// 計(jì)算出中心點(diǎn)
final float x = getWidth() / 2f;
final float y = getHeight() / 2f;
setPressed(true, x, y);//按壓
checkForLongClick(0, x, y);//檢測(cè)是否為長(zhǎng)點(diǎn)擊
return true;
}
}
return false;
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (KeyEvent.isConfirmKey(keyCode)) {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return true;
}
if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) {
setPressed(false);//取消按壓狀態(tài)
if (!mHasPerformedLongPress) {
// 如果有長(zhǎng)按乏冀,則將長(zhǎng)按回調(diào)移除
removeLongPressCallback();
return performClick();
}
}
}
return false;
}
3蝶糯、 下面開始從View的構(gòu)造函數(shù)開始正是入手,部分源碼如下:
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
this(context);//處理context賦值等操作
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
···
//初始化XML參數(shù)
···
a.recycle();
// Needs to be called after mViewFlags is set
if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) {
recomputePadding();
}
if (x != 0 || y != 0) {//是否需要滾動(dòng)
scrollTo(x, y);
}
if (transformSet) {//設(shè)置一些偏移辆沦,旋轉(zhuǎn)放大縮小等參數(shù)
setTranslationX(tx);
setTranslationY(ty);
setTranslationZ(tz);
setElevation(elevation);
setRotation(rotation);
setRotationX(rotationX);
setRotationY(rotationY);
setScaleX(sx);
setScaleY(sy);
}
if (!setScrollContainer && (viewFlagValues&SCROLLBARS_VERTICAL) != 0) {
setScrollContainer(true);//設(shè)置內(nèi)容是否可以滾動(dòng)
}
}
以下 performClick()
用來實(shí)現(xiàn)OnClickListener操作昼捍,并作出一系列關(guān)聯(lián)操作:
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {//如果設(shè)置了監(jiān)聽,則播放聲音肢扯,并實(shí)現(xiàn)
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
//發(fā)送事件類型狀態(tài)
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
而 callOnClick()
只調(diào)用實(shí)現(xiàn)監(jiān)聽,并不做一系列相關(guān)操作:
public boolean callOnClick() {
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
li.mOnClickListener.onClick(this);
return true;
}
return false;
}
4蔚晨、 這里有個(gè)厲害的了 onTouchEvent(MotionEvent event)
乍钻,處理觸摸事件,通常自定義View都會(huì)重寫它,這里來按下源碼银择。
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
return (((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
}
if (mTouchDelegate != null) {//代理分發(fā)
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
//判斷是否可用點(diǎn)擊多糠、長(zhǎng)按、還是上下文點(diǎn)擊
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP:
boolean focusTaken = false;//焦點(diǎn)獲取
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
···
if (!post(mPerformClick)) {//分發(fā)點(diǎn)擊事件
performClick();
}
...
break;
case MotionEvent.ACTION_DOWN:
if (isInScrollingContainer) {//這里會(huì)先判斷是否為內(nèi)部滾動(dòng)事件
...
} else {
// 如果不是則欢摄,直接反饋按壓狀態(tài)熬丧,并檢測(cè)是否為長(zhǎng)按操作
setPressed(true, x, y);
checkForLongClick(0, x, y);
}
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);//解除按壓狀態(tài)
removeTapCallback();移除回調(diào)
removeLongPressCallback();移除長(zhǎng)按回調(diào)
//重置狀態(tài)
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_MOVE:
// Be lenient about moving outside of buttons
//這里有個(gè)mTouchSlop,是通過ViewConfiguration.get(context).getScaledTouchSlop();得到的怀挠。
if (!pointInView(x, y, mTouchSlop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
setPressed(false);
}
}
break;
}
return true;
}
return false;
}
5析蝴、 其實(shí)我覺得最牛的方法應(yīng)該是 void setFlags(int flags, int mask)
雖然他是包類方法,但是這個(gè)方法的作用太強(qiáng)大了绿淋,所有的標(biāo)志位設(shè)置闷畸,幾乎都經(jīng)過它, flags
表示應(yīng)該設(shè)置的值吞滞,mask
表示改變的值的范圍佑菩,源碼部分分析如下:
void setFlags(int flags, int mask) {
final boolean accessibilityEnabled =
AccessibilityManager.getInstance(mContext).isEnabled();
final boolean oldIncludeForAccessibility = accessibilityEnabled && includeForAccessibility();
int old = mViewFlags;//先臨時(shí)保存一份舊的
mViewFlags = (mViewFlags & ~mask) | (flags & mask);
int changed = mViewFlags ^ old;//判斷舊的和新的是否發(fā)生改變
if (changed == 0) {
return;//如果沒有改變則返回
}
int privateFlags = mPrivateFlags;
/* Check if the FOCUSABLE bit has changed */
if (((changed & FOCUSABLE_MASK) != 0) &&
((privateFlags & PFLAG_HAS_BOUNDS) !=0)) {//判斷是否改變了焦點(diǎn)
...
}
final int newVisibility = flags & VISIBILITY_MASK;
if (newVisibility == VISIBLE) {//是否為VISIBLE
if ((changed & VISIBILITY_MASK) != 0) {
//是否發(fā)生改變
mPrivateFlags |= PFLAG_DRAWN;
invalidate(true);//重新繪制
...
}
}
/* Check if the GONE bit has changed */
if ((changed & GONE) != 0) {
needGlobalAttributesUpdate(false);
requestLayout();
...
}
/* Check if the VISIBLE bit has changed */
if ((changed & INVISIBLE) != 0) {
needGlobalAttributesUpdate(false);
...
}
if ((changed & VISIBILITY_MASK) != 0) {
// If the view is invisible, cleanup its display list to free up resources
if (newVisibility != VISIBLE && mAttachInfo != null) {
cleanupDraw();
}
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).onChildVisibilityChanged(this,
(changed & VISIBILITY_MASK), newVisibility);
((View) mParent).invalidate(true);
} else if (mParent != null) {
mParent.invalidateChild(this, null);
}
...
}
//后面是針對(duì)其它標(biāo)記位進(jìn)行判斷
if ((changed & WILL_NOT_CACHE_DRAWING) != 0) {
destroyDrawingCache();
}
if ((changed & DRAWING_CACHE_ENABLED) != 0) {
destroyDrawingCache();
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
invalidateParentCaches();
}
if ((changed & DRAWING_CACHE_QUALITY_MASK) != 0) {
destroyDrawingCache();
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
if ((changed & DRAW_MASK) != 0) {
if ((mViewFlags & WILL_NOT_DRAW) != 0) {
if (mBackground != null
|| (mForegroundInfo != null && mForegroundInfo.mDrawable != null)) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
} else {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
} else {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
}
requestLayout();
invalidate(true);
}
if ((changed & KEEP_SCREEN_ON) != 0) {
if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
mParent.recomputeViewAttributes(this);
}
}
if (accessibilityEnabled) {
...
//通知
}
}
6渠概、 bringToFront()
將視圖置頂诗眨,這個(gè)方法有時(shí)也很常用忧换,我們來看下它的內(nèi)部魄衅,調(diào)用父類方法,如下:
public void bringChildToFront(View child) {
final int index = indexOfChild(child);//查詢孩子的腳標(biāo)
if (index >= 0) {//判斷是否存在
removeFromArray(index);//先移除拢肆,然后再添加進(jìn)來
addInArray(child, mChildrenCount);
child.mParent = this;
requestLayout();//重新布局
invalidate();//重新繪制
}
}
7嵌戈、 dispatchAttachedToWindow(AttachInfo info, int visibility)
這個(gè)方法應(yīng)該是當(dāng)View綁定上窗口時(shí)则披,系統(tǒng)自動(dòng)調(diào)用的一忱。部分分析如下:
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
}
mWindowAttachCount++;記錄綁定次數(shù)
...
// Transfer all pending runnables.
if (mRunQueue != null) {對(duì)綁定前操作的所有任務(wù)隊(duì)列莲蜘,在這里進(jìn)行統(tǒng)一處理。
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
performCollectViewAttributes(mAttachInfo, visibility);
onAttachedToWindow();//調(diào)用此方法帘营,View子類都可重寫此方法票渠。
ListenerInfo li = mListenerInfo;
final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
li != null ? li.mOnAttachStateChangeListeners : null;
if (listeners != null && listeners.size() > 0) {
//通知監(jiān)聽
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewAttachedToWindow(this);
}
}
int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(vis);
if (isShown()) {
// Calling onVisibilityAggregated directly here since the subtree will also
// receive dispatchAttachedToWindow and this same call
onVisibilityAggregated(vis == VISIBLE);
}
}
// Send onVisibilityChanged directly instead of dispatchVisibilityChanged.
// As all views in the subtree will already receive dispatchAttachedToWindow
// traversing the subtree again here is not desired.
onVisibilityChanged(this, visibility);
if ((mPrivateFlags&PFLAG_DRAWABLE_STATE_DIRTY) != 0) {
// If nobody has evaluated the drawable state yet, then do it now.
refreshDrawableState();
}
needGlobalAttributesUpdate(false);
}
8、 相對(duì)綁定芬迄,肯定會(huì)有個(gè)解綁 dispatchDetachedFromWindow()
问顷,如下:
void dispatchDetachedFromWindow() {
AttachInfo info = mAttachInfo;
if (info != null) {
int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(GONE);
if (isShown()) {
// Invoking onVisibilityAggregated directly here since the subtree
// will also receive detached from window
onVisibilityAggregated(false);
}
}
}
onDetachedFromWindow();//對(duì)外繼承使用
onDetachedFromWindowInternal();//內(nèi)部調(diào)用
...
//其它通知
}
9、 實(shí)在是看不下去了禀梳,最后來討論下 getViewTreeObserver()
, 這是一個(gè)注冊(cè)監(jiān)聽視圖樹的觀察者(observer)择诈,在視圖樹種全局事件改變時(shí)得到通知。這個(gè)全局事件不僅還包括整個(gè)樹的布局出皇,從繪畫過程開始,觸摸模式的改變等哗戈。
內(nèi)部接口
interface ViewTreeObserver.OnGlobalFocusChangeListener
//當(dāng)在一個(gè)視圖樹中的焦點(diǎn)狀態(tài)發(fā)生改變時(shí)郊艘,所要調(diào)用的回調(diào)函數(shù)的接口類
interface ViewTreeObserver.OnGlobalLayoutListener
//當(dāng)在一個(gè)視圖樹中全局布局發(fā)生改變或者視圖樹中的某個(gè)視圖的可視狀態(tài)發(fā)生改變時(shí),所要調(diào)用的回調(diào)函數(shù)的接口類
interface ViewTreeObserver.OnPreDrawListener
//當(dāng)一個(gè)視圖樹將要繪制時(shí),所要調(diào)用的回調(diào)函數(shù)的接口類
interface ViewTreeObserver.OnScrollChangedListener
//當(dāng)一個(gè)視圖樹中的一些組件發(fā)生滾動(dòng)時(shí)纱注,所要調(diào)用的回調(diào)函數(shù)的接口類
interface ViewTreeObserver.OnTouchModeChangeListener
//當(dāng)一個(gè)視圖樹的觸摸模式發(fā)生改變時(shí)畏浆,所要調(diào)用的回調(diào)函數(shù)的接口類
可使用的公共方法
/**注冊(cè)一個(gè)回調(diào)函數(shù),當(dāng)在一個(gè)視圖樹中的焦點(diǎn)狀態(tài)發(fā)生改變時(shí)調(diào)用這個(gè)回調(diào)函數(shù)狞贱。
* 參數(shù) listener 將要被添加的回調(diào)函數(shù)
*異常 IllegalStateException 如果isAlive() 返回false
*/
public void addOnGlobalFocusChangeListener (ViewTreeObserver.OnGlobalFocusChangeListener listener)
/**注冊(cè)一個(gè)回調(diào)函數(shù)刻获,當(dāng)在一個(gè)視圖樹中全局布局發(fā)生改變或者視圖樹中的某個(gè)視圖的可視狀態(tài)發(fā)生改變時(shí)調(diào)用這個(gè)回調(diào)函數(shù)。
*參數(shù) listener 將要被添加的回調(diào)函數(shù)
*異常 IllegalStateException 如果isAlive() 返回false
*/
public void addOnGlobalLayoutListener (ViewTreeObserver.OnGlobalLayoutListener listener)
/**注冊(cè)一個(gè)回調(diào)函數(shù)瞎嬉,當(dāng)一個(gè)視圖樹將要繪制時(shí)調(diào)用這個(gè)回調(diào)函數(shù)蝎毡。
*參數(shù) listener 將要被添加的回調(diào)函數(shù)
*異常 IllegalStateException 如果isAlive() 返回false
*/
public void addOnPreDrawListener (ViewTreeObserver.OnPreDrawListener listener)
/**注冊(cè)一個(gè)回調(diào)函數(shù),當(dāng)一個(gè)視圖發(fā)生滾動(dòng)時(shí)調(diào)用這個(gè)回調(diào)函數(shù)氧枣。
*參數(shù) listener 將要被添加的回調(diào)函數(shù)
*異常 IllegalStateException 如果isAlive() 返回false
*/
public void addOnScrollChangedListener (ViewTreeObserver.OnScrollChangedListener listener)
/**注冊(cè)一個(gè)回調(diào)函數(shù)沐兵,當(dāng)一個(gè)觸摸模式發(fā)生改變時(shí)調(diào)用這個(gè)回調(diào)函數(shù)。
*參數(shù) listener 將要被添加的回調(diào)函數(shù)
*異常 IllegalStateException 如果isAlive() 返回false
*/
public void addOnTouchModeChangeListener (ViewTreeObserver.OnTouchModeChangeListener listener)
//當(dāng)整個(gè)布局發(fā)生改變時(shí)通知相應(yīng)的注冊(cè)監(jiān)聽器便监。如果你強(qiáng)制對(duì)視圖布局或者在一個(gè)沒有附加到一個(gè)窗口的視圖的層次結(jié)構(gòu)或者在GONE狀態(tài)下扎谎,它可以被手動(dòng)的調(diào)用
public final void dispatchOnGlobalLayout ()
/**當(dāng)一個(gè)視圖樹將要繪制時(shí)通知相應(yīng)的注冊(cè)監(jiān)聽器。如果這個(gè)監(jiān)聽器返回true烧董,則這個(gè)繪制將被取消并重新計(jì)劃毁靶。如果你強(qiáng)制對(duì)視圖布局或者在一個(gè)沒有附加到一個(gè)窗口的視圖的層次結(jié)構(gòu)或者在一個(gè)GONE狀態(tài)下,它可以被手動(dòng)的調(diào)用
*返回值 當(dāng)前繪制能夠取消并重新計(jì)劃則返回true逊移,否則返回false预吆。
*/
public final boolean dispatchOnPreDraw ()
/**指示當(dāng)前的ViewTreeObserver是否可用(alive)。當(dāng)observer不可用時(shí)螟左,任何方法的調(diào)用(除了這個(gè)方法)都將拋出一個(gè)異常啡浊。如果一個(gè)應(yīng)用程序保持和ViewTreeObserver一個(gè)歷時(shí)較長(zhǎng)的引用,它應(yīng)該總是需要在調(diào)用別的方法之前去檢測(cè)這個(gè)方法的返回值胶背。
*返回值 但這個(gè)對(duì)象可用則返回true巷嚣,否則返回false
*/
public boolean isAlive ()
/**移除之前已經(jīng)注冊(cè)的全局布局回調(diào)函數(shù)。
*參數(shù) victim 將要被移除的回調(diào)函數(shù)
*異常 IllegalStateException 如果isAlive() 返回false
*/
public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)
/**移除之前已經(jīng)注冊(cè)的焦點(diǎn)改變回調(diào)函數(shù)钳吟。
*參數(shù) victim 將要被移除的回調(diào)函數(shù)
*異常 IllegalStateException 如果isAlive() 返回false
*/
public void removeOnGlobalFocusChangeListener (ViewTreeObserver.OnGlobalFocusChangeListener victim)
/**移除之前已經(jīng)注冊(cè)的預(yù)繪制回調(diào)函數(shù)廷粒。
*參數(shù) victim 將要被移除的回調(diào)函數(shù)
*異常 IllegalStateException 如果isAlive() 返回false
*/
public void removeOnPreDrawListener (ViewTreeObserver.OnPreDrawListener victim)
/**移除之前已經(jīng)注冊(cè)的滾動(dòng)改變回調(diào)函數(shù)。
*參數(shù) victim 將要被移除的回調(diào)函數(shù)
*異常 IllegalStateException 如果isAlive() 返回false
*/
public void removeOnScrollChangedListener (ViewTreeObserver.OnScrollChangedListener victim)
/**移除之前已經(jīng)注冊(cè)的觸摸模式改變回調(diào)函數(shù)
*參數(shù) victim 將要被移除的回調(diào)函數(shù)
*異常 IllegalStateException 如果isAlive() 返回false
*/
public void removeOnTouchModeChangeListener (ViewTreeObserver.OnTouchModeChangeListener victim)
作者:Zyao89红且;轉(zhuǎn)載請(qǐng)保留此行坝茎,謝謝;
個(gè)人博客:http://zyao89.me