017.View與視窗:AttachInfo
阿新 • • 發佈:2018-11-15
每一個View都需要依賴於視窗來顯示,而View和視窗的關係則是放在View.AttachInfo中,關於View.AttachInfo的文章少,因為這個是View的內部類而且不是公共的,在應用層用的很少,只有在ViewRootImpl等類中才用到了,不過我覺得這個還是有點學習的必要,因此在這篇文章中就從原始碼入手學習下AttachInfo這個類。
AttachInfo 看到這個類名,我們就知道,他是代表著繫結的資訊,View.AttachInfo 裡面的資訊,就是View和Window之間的資訊。每一個被新增到視窗上的View我們都會看到有一個AttachInfo,比如我們看DecorView和Window的繫結,可以在ViewRootImpl#perfromTraversals方法中看到:
AttachInfo 會通過View的diapatchAttachedTowWindow分發給View。如果是一個ViewGroup 那麼這個這個AttachInfo也會分發給所有子View,以引用的方式。 下面我們可以看下AttachInfo這個類,這個類是View的內部類,不是宣告為public的,所以只有view這個包中的類才用到。
一般來說,IWinodwSession是通過: WindowManagerGlobal.peekWindowSession() 或者是 WindowManagerGlobal.getWindowSession() 來獲取的 兩者一般來說是差不多的,就是peek返回的可能為空 get一般返回是不為空的。另外,IWindowSession 、IWindow 、mWindowToken 都是從 IWindowManager.Stub.asInterface( ServiceManager.getService("window")) 獲取到IWindowManager來獲取的。 相關的可以看前面的第10篇:Binder進階:系統服務中的Binder 以及ServiceManager的原始碼來看。 mPanelParentWindowToken 如果該視窗時子視窗,那麼該值就是父視窗的W物件,如果mWindowToken不為空,則說明沒有父視窗…嗯,和mWindowToken有點相對的意思。比如說Activity 的DecorView 的AttachInfo這個值就是null,而我們彈出了一個對話方塊,這個對話方塊的這個就不為null,因為這個對話方塊是有父視窗的。
AttachInfo 看到這個類名,我們就知道,他是代表著繫結的資訊,View.AttachInfo 裡面的資訊,就是View和Window之間的資訊。每一個被新增到視窗上的View我們都會看到有一個AttachInfo,比如我們看DecorView和Window的繫結,可以在ViewRootImpl#perfromTraversals方法中看到:
final View.AttachInfo attachInfo = mAttachInfo; final int viewVisibility = getHostVisibility(); boolean viewVisibilityChanged = mViewVisibility != viewVisibility || mNewSurfaceNeeded; WindowManager.LayoutParams params = null; if (mWindowAttributesChanged) { mWindowAttributesChanged = false; surfaceChanged = true; params = lp; } CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo(); if (compatibilityInfo.supportsScreen() == mLastInCompatMode) { params = lp; mFullRedrawNeeded = true; mLayoutRequested = true; if (mLastInCompatMode) { params.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; mLastInCompatMode = false; } else { params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; mLastInCompatMode = true; } } mWindowAttributesChangesFlag = 0; Rect frame = mWinFrame; if (mFirst) { mFullRedrawNeeded = true; mLayoutRequested = true; if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) { // NOTE -- system code, won't try to do compat mode. Point size = new Point(); mDisplay.getRealSize(size); desiredWindowWidth = size.x; desiredWindowHeight = size.y; } else { DisplayMetrics packageMetrics = mView.getContext().getResources().getDisplayMetrics(); desiredWindowWidth = packageMetrics.widthPixels; desiredWindowHeight = packageMetrics.heightPixels; } // For the very first time, tell the view hierarchy that it // is attached to the window. Note that at this point the surface // object is not initialized to its backing store, but soon it // will be (assuming the window is visible). attachInfo.mSurface = mSurface; // We used to use the following condition to choose 32 bits drawing caches: // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888 // However, windows are now always 32 bits by default, so choose 32 bits attachInfo.mUse32BitDrawingCache = true; attachInfo.mHasWindowFocus = false; attachInfo.mWindowVisibility = viewVisibility; attachInfo.mRecomputeGlobalAttributes = false; viewVisibilityChanged = false; mLastConfiguration.setTo(host.getResources().getConfiguration()); mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; // Set the layout direction if it has not been set before (inherit is the default) if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) { host.setLayoutDirection(mLastConfiguration.getLayoutDirection()); } host.dispatchAttachedToWindow(attachInfo, 0); }
AttachInfo 會通過View的diapatchAttachedTowWindow分發給View。如果是一個ViewGroup 那麼這個這個AttachInfo也會分發給所有子View,以引用的方式。 下面我們可以看下AttachInfo這個類,這個類是View的內部類,不是宣告為public的,所以只有view這個包中的類才用到。
首先是聲明瞭回撥介面類:callBacks 這個類第一個是playSoundEffect ,這個用於播放按鍵聲音,引數是這個點選事件的型別,可以看SoundEffectConstants中的宣告,一般是SoundEffectConstants中幾個常量中的一個,在AttachInfo有一個CallBack物件 :mRootCallBacks 這個的實現可以看ViewRootImpl類,ViewRootImpl中,我們可以看到:static class AttachInfo { interface Callbacks { void playSoundEffect(int effectId); boolean performHapticFeedback(int effectId, boolean always); } /** * InvalidateInfo is used to post invalidate(int, int, int, int) messages * to a Handler. This class contains the target (View) to invalidate and * the coordinates of the dirty rectangle. * * For performance purposes, this class also implements a pool of up to * POOL_LIMIT objects that get reused. This reduces memory allocations * whenever possible. */ static class InvalidateInfo { private static final int POOL_LIMIT = 10; private static final SynchronizedPool<InvalidateInfo> sPool = new SynchronizedPool<InvalidateInfo>(POOL_LIMIT); View target; int left; int top; int right; int bottom; public static InvalidateInfo obtain() { InvalidateInfo instance = sPool.acquire(); return (instance != null) ? instance : new InvalidateInfo(); } public void recycle() { target = null; sPool.release(this); } } final IWindowSession mSession; final IWindow mWindow; final IBinder mWindowToken; final Display mDisplay; final Callbacks mRootCallbacks; HardwareCanvas mHardwareCanvas; IWindowId mIWindowId; WindowId mWindowId; /** * The top view of the hierarchy. */ View mRootView; IBinder mPanelParentWindowToken; Surface mSurface; boolean mHardwareAccelerated; boolean mHardwareAccelerationRequested; HardwareRenderer mHardwareRenderer; boolean mScreenOn; /** * Scale factor used by the compatibility mode */ float mApplicationScale; /** * Indicates whether the application is in compatibility mode */ boolean mScalingRequired; /** * If set, ViewRootImpl doesn't use its lame animation for when the window resizes. */ boolean mTurnOffWindowResizeAnim; /** * Left position of this view's window */ int mWindowLeft; /** * Top position of this view's window */ int mWindowTop; /** * Indicates whether views need to use 32-bit drawing caches */ boolean mUse32BitDrawingCache; /** * For windows that are full-screen but using insets to layout inside * of the screen areas, these are the current insets to appear inside * the overscan area of the display. */ final Rect mOverscanInsets = new Rect(); /** * For windows that are full-screen but using insets to layout inside * of the screen decorations, these are the current insets for the * content of the window. */ final Rect mContentInsets = new Rect(); /** * For windows that are full-screen but using insets to layout inside * of the screen decorations, these are the current insets for the * actual visible parts of the window. */ final Rect mVisibleInsets = new Rect(); /** * The internal insets given by this window. This value is * supplied by the client (through * {@link ViewTreeObserver.OnComputeInternalInsetsListener}) and will * be given to the window manager when changed to be used in laying * out windows behind it. */ final ViewTreeObserver.InternalInsetsInfo mGivenInternalInsets = new ViewTreeObserver.InternalInsetsInfo(); /** * Set to true when mGivenInternalInsets is non-empty. */ boolean mHasNonEmptyGivenInternalInsets; /** * All views in the window's hierarchy that serve as scroll containers, * used to determine if the window can be resized or must be panned * to adjust for a soft input area. */ final ArrayList<View> mScrollContainers = new ArrayList<View>(); final KeyEvent.DispatcherState mKeyDispatchState = new KeyEvent.DispatcherState(); /** * Indicates whether the view's window currently has the focus. */ boolean mHasWindowFocus; /** * The current visibility of the window. */ int mWindowVisibility; /** * Indicates the time at which drawing started to occur. */ long mDrawingTime; /** * Indicates whether or not ignoring the DIRTY_MASK flags. */ boolean mIgnoreDirtyState; /** * This flag tracks when the mIgnoreDirtyState flag is set during draw(), * to avoid clearing that flag prematurely. */ boolean mSetIgnoreDirtyState = false; /** * Indicates whether the view's window is currently in touch mode. */ boolean mInTouchMode; /** * Indicates that ViewAncestor should trigger a global layout change * the next time it performs a traversal */ boolean mRecomputeGlobalAttributes; /** * Always report new attributes at next traversal. */ boolean mForceReportNewAttributes; /** * Set during a traveral if any views want to keep the screen on. */ boolean mKeepScreenOn; /** * Bitwise-or of all of the values that views have passed to setSystemUiVisibility(). */ int mSystemUiVisibility; /** * Hack to force certain system UI visibility flags to be cleared. */ int mDisabledSystemUiVisibility; /** * Last global system UI visibility reported by the window manager. */ int mGlobalSystemUiVisibility; /** * True if a view in this hierarchy has an OnSystemUiVisibilityChangeListener * attached. */ boolean mHasSystemUiListeners; /** * Set if the window has requested to extend into the overscan region * via WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN. */ boolean mOverscanRequested; /** * Set if the visibility of any views has changed. */ boolean mViewVisibilityChanged; /** * Set to true if a view has been scrolled. */ boolean mViewScrollChanged; /** * Global to the view hierarchy used as a temporary for dealing with * x/y points in the transparent region computations. */ final int[] mTransparentLocation = new int[2]; /** * Global to the view hierarchy used as a temporary for dealing with * x/y points in the ViewGroup.invalidateChild implementation. */ final int[] mInvalidateChildLocation = new int[2]; /** * Global to the view hierarchy used as a temporary for dealing with * x/y location when view is transformed. */ final float[] mTmpTransformLocation = new float[2]; /** * The view tree observer used to dispatch global events like * layout, pre-draw, touch mode change, etc. */ final ViewTreeObserver mTreeObserver = new ViewTreeObserver(); /** * A Canvas used by the view hierarchy to perform bitmap caching. */ Canvas mCanvas; /** * The view root impl. */ final ViewRootImpl mViewRootImpl; /** * A Handler supplied by a view's {@link android.view.ViewRootImpl}. This * handler can be used to pump events in the UI events queue. */ final Handler mHandler; /** * Temporary for use in computing invalidate rectangles while * calling up the hierarchy. */ final Rect mTmpInvalRect = new Rect(); /** * Temporary for use in computing hit areas with transformed views */ final RectF mTmpTransformRect = new RectF(); /** * Temporary for use in transforming invalidation rect */ final Matrix mTmpMatrix = new Matrix(); /** * Temporary for use in transforming invalidation rect */ final Transformation mTmpTransformation = new Transformation(); /** * Temporary list for use in collecting focusable descendents of a view. */ final ArrayList<View> mTempArrayList = new ArrayList<View>(24); /** * The id of the window for accessibility purposes. */ int mAccessibilityWindowId = View.NO_ID; /** * Flags related to accessibility processing. * * @see AccessibilityNodeInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS * @see AccessibilityNodeInfo#FLAG_REPORT_VIEW_IDS */ int mAccessibilityFetchFlags; /** * The drawable for highlighting accessibility focus. */ Drawable mAccessibilityFocusDrawable; /** * Show where the margins, bounds and layout bounds are for each view. */ boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false); /** * Point used to compute visible regions. */ final Point mPoint = new Point(); /** * Used to track which View originated a requestLayout() call, used when * requestLayout() is called during layout. */ View mViewRequestingLayout; /** * Creates a new set of attachment information with the specified * events handler and thread. * * @param handler the events handler the view must use */ AttachInfo(IWindowSession session, IWindow window, Display display, ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) { mSession = session; mWindow = window; mWindowToken = window.asBinder(); mDisplay = display; mViewRootImpl = viewRootImpl; mHandler = handler; mRootCallbacks = effectPlayer; } }
public void playSoundEffect(int effectId) {
checkThread();
if (mMediaDisabled) {
return;
}
try {
final AudioManager audioManager = getAudioManager();
switch (effectId) {
case SoundEffectConstants.CLICK:
audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
return;
case SoundEffectConstants.NAVIGATION_DOWN:
audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);
return;
case SoundEffectConstants.NAVIGATION_LEFT:
audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);
return;
case SoundEffectConstants.NAVIGATION_RIGHT:
audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);
return;
case SoundEffectConstants.NAVIGATION_UP:
audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);
return;
default:
throw new IllegalArgumentException("unknown effect id " + effectId +
" not defined in " + SoundEffectConstants.class.getCanonicalName());
}
} catch (IllegalStateException e) {
// Exception thrown by getAudioManager() when mView is null
Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e);
e.printStackTrace();
}
}
是的,這個就是控制AudioManager來播放我們系統的點選聲音,如果我們需要修改按鈕的點選聲音,就有如下幾個方法,
如果是修改Framework 那麼我們可以修改AudioManager的這個playSoundEffect方法,修改對應型別,也可以直接修改ViewRootImpl中這個方法(不建議),不過最後一個改動 基本上就是改動全域性,如果我們自定義控制元件想要觸發點選聲音不同呢?
那麼我們在自定義的View上,重寫playSoundEffect方法就可以了。每一個View都有playSoundEffect方法,我們可以改動這個方法。
CallBack中還有一個方法:
performHapticFeedback這個意思就是觸感反饋,引數可以看HapticFeedBack這個類,當用戶在系統開啟觸感反饋選項,我們View的
performHapticFeedback(int feedBackContants )這個方法,當然,如果我們呼叫
performHapticFeedback(int feedbackConstant, int flags) 的時候,把引數FLAG_IGNORE_GLOBAL_SETTING 就可以忽略全域性設定,而如果我們HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING 就可以忽略我們在View裡面設定的android:hapticFeedbackEnabled
關於CallBack就講到這裡,接下來我們繼續往下看InvalidateInfo。
InvalidateInfo用於重新整理UI,當我們重新整理UI的時候,會生成一個新的
InvalidateInfo物件,然後根據這個來重新整理UI。這個比較簡單,就不詳細說了。
在AttachInfo中,還有其他的資訊,在這邊,我們可以拿到和Window相關的資訊:
<span style="white-space:pre"> </span>final IWindowSession mSession;
<span style="white-space:pre"> </span>
final IWindow mWindow;
final IBinder mWindowToken;
IBinder mPanelParentWindowToken ;
一般來說,IWinodwSession是通過: WindowManagerGlobal.peekWindowSession() 或者是 WindowManagerGlobal.getWindowSession() 來獲取的 兩者一般來說是差不多的,就是peek返回的可能為空 get一般返回是不為空的。另外,IWindowSession 、IWindow 、mWindowToken 都是從 IWindowManager.Stub.asInterface( ServiceManager.getService("window")) 獲取到IWindowManager來獲取的。 相關的可以看前面的第10篇:Binder進階:系統服務中的Binder 以及ServiceManager的原始碼來看。 mPanelParentWindowToken 如果該視窗時子視窗,那麼該值就是父視窗的W物件,如果mWindowToken不為空,則說明沒有父視窗…嗯,和mWindowToken有點相對的意思。比如說Activity 的DecorView 的AttachInfo這個值就是null,而我們彈出了一個對話方塊,這個對話方塊的這個就不為null,因為這個對話方塊是有父視窗的。