强制移动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的值.
带过渡动画的位移实现.