| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package android.view; |
| |
| import android.graphics.Canvas; |
| import android.graphics.Matrix; |
| import android.widget.FrameLayout; |
| |
| /** |
| * This view draws another View in an Overlay without changing the parent. It will not be drawn |
| * by its parent because its visibility is set to INVISIBLE, but will be drawn |
| * here using its render node. When the GhostView is set to INVISIBLE, the View it is |
| * shadowing will become VISIBLE and when the GhostView becomes VISIBLE, the shadowed |
| * view becomes INVISIBLE. |
| * @hide |
| */ |
| public class GhostView extends View { |
| private final View mView; |
| private int mReferences; |
| |
| private GhostView(View view) { |
| super(view.getContext()); |
| mView = view; |
| mView.mGhostView = this; |
| final ViewGroup parent = (ViewGroup) mView.getParent(); |
| setGhostedVisibility(View.INVISIBLE); |
| parent.mRecreateDisplayList = true; |
| parent.getDisplayList(); |
| } |
| |
| @Override |
| protected void onDraw(Canvas canvas) { |
| if (canvas instanceof HardwareCanvas) { |
| HardwareCanvas hwCanvas = (HardwareCanvas) canvas; |
| mView.mRecreateDisplayList = true; |
| RenderNode renderNode = mView.getDisplayList(); |
| if (renderNode.isValid()) { |
| hwCanvas.insertReorderBarrier(); // enable shadow for this rendernode |
| hwCanvas.drawRenderNode(renderNode); |
| hwCanvas.insertInorderBarrier(); // re-disable reordering/shadows |
| } |
| } |
| } |
| |
| public void setMatrix(Matrix matrix) { |
| mRenderNode.setAnimationMatrix(matrix); |
| } |
| |
| @Override |
| public void setVisibility(@Visibility int visibility) { |
| super.setVisibility(visibility); |
| if (mView.mGhostView == this) { |
| int inverseVisibility = (visibility == View.VISIBLE) ? View.INVISIBLE : View.VISIBLE; |
| setGhostedVisibility(inverseVisibility); |
| } |
| } |
| |
| private void setGhostedVisibility(int visibility) { |
| mView.mViewFlags = (mView.mViewFlags & ~View.VISIBILITY_MASK) | visibility; |
| } |
| |
| @Override |
| protected void onDetachedFromWindow() { |
| super.onDetachedFromWindow(); |
| setGhostedVisibility(View.VISIBLE); |
| mView.mGhostView = null; |
| final ViewGroup parent = (ViewGroup) mView.getParent(); |
| if (parent != null) { |
| parent.mRecreateDisplayList = true; |
| parent.getDisplayList(); |
| } |
| } |
| |
| public static void calculateMatrix(View view, ViewGroup host, Matrix matrix) { |
| ViewGroup parent = (ViewGroup) view.getParent(); |
| matrix.reset(); |
| parent.transformMatrixToGlobal(matrix); |
| matrix.preTranslate(-parent.getScrollX(), -parent.getScrollY()); |
| host.transformMatrixToLocal(matrix); |
| } |
| |
| public static GhostView addGhost(View view, ViewGroup viewGroup, Matrix matrix) { |
| if (!(view.getParent() instanceof ViewGroup)) { |
| throw new IllegalArgumentException("Ghosted views must be parented by a ViewGroup"); |
| } |
| ViewGroupOverlay overlay = viewGroup.getOverlay(); |
| ViewOverlay.OverlayViewGroup overlayViewGroup = overlay.mOverlayViewGroup; |
| GhostView ghostView = view.mGhostView; |
| int previousRefCount = 0; |
| if (ghostView != null) { |
| View oldParent = (View) ghostView.getParent(); |
| ViewGroup oldGrandParent = (ViewGroup) oldParent.getParent(); |
| if (oldGrandParent != overlayViewGroup) { |
| previousRefCount = ghostView.mReferences; |
| oldGrandParent.removeView(oldParent); |
| ghostView = null; |
| } |
| } |
| if (ghostView == null) { |
| if (matrix == null) { |
| matrix = new Matrix(); |
| calculateMatrix(view, viewGroup, matrix); |
| } |
| ghostView = new GhostView(view); |
| ghostView.setMatrix(matrix); |
| FrameLayout parent = new FrameLayout(view.getContext()); |
| parent.setClipChildren(false); |
| copySize(viewGroup, parent); |
| copySize(viewGroup, ghostView); |
| parent.addView(ghostView); |
| overlay.add(parent); |
| ghostView.mReferences = previousRefCount; |
| } else if (matrix != null) { |
| ghostView.setMatrix(matrix); |
| } |
| ghostView.mReferences++; |
| return ghostView; |
| } |
| |
| public static GhostView addGhost(View view, ViewGroup viewGroup) { |
| return addGhost(view, viewGroup, null); |
| } |
| |
| public static void removeGhost(View view) { |
| GhostView ghostView = view.mGhostView; |
| if (ghostView != null) { |
| ghostView.mReferences--; |
| if (ghostView.mReferences == 0) { |
| ViewGroup parent = (ViewGroup) ghostView.getParent(); |
| ViewGroup grandParent = (ViewGroup) parent.getParent(); |
| grandParent.removeView(parent); |
| } |
| } |
| } |
| |
| public static GhostView getGhost(View view) { |
| return view.mGhostView; |
| } |
| |
| private static void copySize(View from, View to) { |
| to.setLeft(0); |
| to.setTop(0); |
| to.setRight(from.getWidth()); |
| to.setBottom(from.getHeight()); |
| } |
| } |