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;
}
}
首先是聲明瞭回調接口類:callBacks 這個類第一個是playSoundEffect ,這個用於播放按鍵聲音,參數是這個點擊事件的類型,可以看SoundEffectConstants中的聲明,一般是SoundEffectConstants中幾個常量中的一個,在AttachInfo有一個CallBack對象
:mRootCallBacks 這個的實現可以看ViewRootImpl類,ViewRootImpl中,我們可以看到: 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,因爲這個對話框是有父窗口的。