强制移动window窗口可以通过在PhoneWindowManager中强制指定window的显示区域即可, 但我需要的是一个有过渡动画的位移.
查找相关代码发现窗口动画有一个位移的函数:
|--frameworks/base/server/java/com/android/server/wm/WindowManagerService.java
// "Something has changed! Let's make it correct now." private final void performLayoutAndPlaceSurfacesLockedInner(boolean recoveringMemory) { ... if (w.mHasSurface && w.shouldAnimateMove()) { // Frame has moved, containing content frame // has also moved, and we're not currently animating... // let's do something. Animation a = AnimationUtils.loadAnimation(mContext, com.android.internal.R.anim.window_move_from_decor); winAnimator.setAnimation(a); winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left; winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top; try { w.mClient.moved(w.mFrame.left, w.mFrame.top); } catch (RemoteException e) { } } ... }
|--frameworks/base/core/java/android/view/ViewRootImpl.java
static class W extends IWindow.Stub { ... @Override public void moved(int newX, int newY) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.dispatchMoved(newX, newY); } } ... } public void dispatchMoved(int newX, int newY) { if (DEBUG_LAYOUT) Log.v(TAG, "Window moved " + this + ": newX=" + newX + " newY=" + newY); if (mTranslator != null) { PointF point = new PointF(newX, newY); mTranslator.translatePointInScreenToAppWindow(point); newX = (int) (point.x + 0.5); newY = (int) (point.y + 0.5); } Message msg = mHandler.obtainMessage(MSG_WINDOW_MOVED, newX, newY); mHandler.sendMessage(msg); } ... case MSG_WINDOW_MOVED: if (mAdded) { final int w = mWinFrame.width(); final int h = mWinFrame.height(); final int l = msg.arg1; final int t = msg.arg2; mWinFrame.left = l; mWinFrame.right = l + w; mWinFrame.top = t; mWinFrame.bottom = t + h; if (mView != null) { forceLayout(mView); } requestLayout(); } break; ... private void performTraversals() {} ->WindowManagerService.relayoutWindow(). ->WindowManagerService.performLayoutAndPlaceSurfacesLocked().
|--frameworks/base/service/java/com/android/server/wm/WindowState.java
/** * Return whether this window is wanting to have a translation * animation applied to it for an in-progress move. (Only makes * sense to call from performLayoutAndPlaceSurfacesLockedInner().) */ boolean shouldAnimateMove() { //有可能不满足的一个条件 return mContentChanged && !mExiting && !mWinAnimator.mLastHidden && mService.okToDisplay() && (mFrame.top != mLastFrame.top || mFrame.left != mLastFrame.left) && (mAttrs.privateFlags&PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0 && (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove()); } @Override public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf) { ... if (!mParentFrame.equals(pf)) { //Slog.i(TAG, "Window " + this + " content frame from " + mParentFrame // + " to " + pf); mParentFrame.set(pf); mContentChanged = true; } ... }
//也就是说, 只需要pf的值变化, 即可触发.
|--frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
//转了一圈还是回到了这里. /** {@inheritDoc} */ @Override public void layoutWindowLw(WindowState win, WindowManager.LayoutParams attrs, WindowState attached) { ... //AnsonCode 2016.12.21 String mapPkg = "com.autonavi.amapauto"; if(mapPkg.equals(attrs.packageName) && deadareaSide > 0){ /****** resolution 1 //pf parentFrame //vf visibleFrame //df displayFrame //cf contentFrame //of overScanFrame //dcf decorContentFrame ************/ /******** resolution 2 ************/ //0:off; 5:left; 6:right int offset = 480; String b = pf.toShortString(); if(deadareaSide == 5){ pf.left += offset; pf.right += offset; }else if(deadareaSide == 6){ pf.left -= offset; pf.right -= offset; } Log.d("ALog", "PhoneWindowManager.layoutWindowLw pfBefore(" + b + "): pfAfter(" + pf.toShortString() + ")"); } win.computeFrameLw(pf, df, of, cf, vf, dcf); // Dock windows carve out the bottom of the screen, so normal windows // can't appear underneath them. if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleOrBehindKeyguardLw() && !win.getGivenInsetsPendingLw()) { setLastInputMethodWindowLw(null, null); offsetInputMethodWindowLw(win); } }
OK, 完成
测试流程如下:
新增APP并把主题设置为Theme.Halo.Dialog.
APP打开后会显示在屏幕中间;
通过发送广播告诉PhoneWindowManager, 去修改pf的值.
带过渡动画的位移实现.