FileDocCategorySizeDatePackage
GhostView.javaAPI DocAndroid 5.1 API13322Thu Mar 12 22:22:10 GMT 2015android.view

GhostView

public class GhostView extends View
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

Fields Summary
private final View
mView
private int
mReferences
private boolean
mBeingMoved
Constructors Summary
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();
    
Methods Summary
public static android.view.GhostViewaddGhost(View view, ViewGroup viewGroup, android.graphics.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);
            ArrayList<View> tempViews = new ArrayList<View>();
            int firstGhost = moveGhostViewsToTop(overlay.mOverlayViewGroup, tempViews);
            insertIntoOverlay(overlay.mOverlayViewGroup, parent, ghostView, tempViews, firstGhost);
            ghostView.mReferences = previousRefCount;
        } else if (matrix != null) {
            ghostView.setMatrix(matrix);
        }
        ghostView.mReferences++;
        return ghostView;
    
public static android.view.GhostViewaddGhost(View view, ViewGroup viewGroup)

        return addGhost(view, viewGroup, null);
    
public static voidcalculateMatrix(View view, ViewGroup host, android.graphics.Matrix matrix)

        ViewGroup parent = (ViewGroup) view.getParent();
        matrix.reset();
        parent.transformMatrixToGlobal(matrix);
        matrix.preTranslate(-parent.getScrollX(), -parent.getScrollY());
        host.transformMatrixToLocal(matrix);
    
private static voidcopySize(View from, View to)

        to.setLeft(0);
        to.setTop(0);
        to.setRight(from.getWidth());
        to.setBottom(from.getHeight());
    
public static android.view.GhostViewgetGhost(View view)

        return view.mGhostView;
    
private static intgetInsertIndex(ViewGroup overlayViewGroup, java.util.ArrayList viewParents, java.util.ArrayList tempParents, int firstGhost)
Find the index into the overlay to insert the GhostView based on the order that the views should be drawn. This keeps GhostViews layered in the same order that they are ordered in the UI.

        int low = firstGhost;
        int high = overlayViewGroup.getChildCount() - 1;

        while (low <= high) {
            int mid = (low + high) / 2;
            ViewGroup wrapper = (ViewGroup) overlayViewGroup.getChildAt(mid);
            GhostView midView = (GhostView) wrapper.getChildAt(0);
            getParents(midView.mView, tempParents);
            if (isOnTop(viewParents, tempParents)) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
            tempParents.clear();
        }

        return low;
    
private static voidgetParents(View view, java.util.ArrayList parents)
Adds all the parents, grandparents, etc. of view to parents.

        ViewParent parent = view.getParent();
        if (parent != null && parent instanceof ViewGroup) {
            getParents((View) parent, parents);
        }
        parents.add(view);
    
private static voidinsertIntoOverlay(ViewGroup viewGroup, ViewGroup wrapper, android.view.GhostView ghostView, java.util.ArrayList tempParents, int firstGhost)
Inserts a GhostView into the overlay's ViewGroup in the order in which they should be displayed by the UI.

        if (firstGhost == -1) {
            viewGroup.addView(wrapper);
        } else {
            ArrayList<View> viewParents = new ArrayList<View>();
            getParents(ghostView.mView, viewParents);

            int index = getInsertIndex(viewGroup, viewParents, tempParents, firstGhost);
            if (index < 0 || index >= viewGroup.getChildCount()) {
                viewGroup.addView(wrapper);
            } else {
                viewGroup.addView(wrapper, index);
            }
        }
    
private static booleanisGhostWrapper(View view)
Returns true if view is a GhostView's FrameLayout wrapper.

        if (view instanceof FrameLayout) {
            FrameLayout frameLayout = (FrameLayout) view;
            if (frameLayout.getChildCount() == 1) {
                View child = frameLayout.getChildAt(0);
                return child instanceof GhostView;
            }
        }
        return false;
    
private static booleanisOnTop(java.util.ArrayList viewParents, java.util.ArrayList comparedWith)
Returns true if viewParents is from a View that is on top of the comparedWith's view. The ArrayLists contain the ancestors of views in order from top most grandparent, to the view itself, in order. The goal is to find the first matching parent and then compare the draw order of the siblings.

        if (viewParents.isEmpty() || comparedWith.isEmpty() ||
                viewParents.get(0) != comparedWith.get(0)) {
            // Not the same decorView -- arbitrary ordering
            return true;
        }
        int depth = Math.min(viewParents.size(), comparedWith.size());
        for (int i = 1; i < depth; i++) {
            View viewParent = viewParents.get(i);
            View comparedWithParent = comparedWith.get(i);

            if (viewParent != comparedWithParent) {
                // i - 1 is the same parent, but these are different children.
                return isOnTop(viewParent, comparedWithParent);
            }
        }

        // one of these is the parent of the other
        boolean isComparedWithTheParent = (comparedWith.size() == depth);
        return isComparedWithTheParent;
    
private static booleanisOnTop(View view, View comparedWith)
Returns true if view would be drawn on top of comparedWith or false otherwise. view and comparedWith are siblings with the same parent. This uses the logic that dispatchDraw uses to determine which View should be drawn first.

        ViewGroup parent = (ViewGroup) view.getParent();

        final int childrenCount = parent.getChildCount();
        final ArrayList<View> preorderedList = parent.buildOrderedChildList();
        final boolean customOrder = preorderedList == null
                && parent.isChildrenDrawingOrderEnabled();

        // This default value shouldn't be used because both view and comparedWith
        // should be in the list. If there is an error, then just return an arbitrary
        // view is on top.
        boolean isOnTop = true;
        for (int i = 0; i < childrenCount; i++) {
            int childIndex = customOrder ? parent.getChildDrawingOrder(childrenCount, i) : i;
            final View child = (preorderedList == null)
                    ? parent.getChildAt(childIndex) : preorderedList.get(childIndex);
            if (child == view) {
                isOnTop = false;
                break;
            } else if (child == comparedWith) {
                isOnTop = true;
                break;
            }
        }

        if (preorderedList != null) {
            preorderedList.clear();
        }
        return isOnTop;
    
private static intmoveGhostViewsToTop(ViewGroup viewGroup, java.util.ArrayList tempViews)
Move the GhostViews to the end so that they are on top of other views and it is easier to do binary search for the correct location for the GhostViews in insertIntoOverlay.

return
The index of the first GhostView or -1 if no GhostView is in the ViewGroup

        final int numChildren = viewGroup.getChildCount();
        if (numChildren == 0) {
            return -1;
        } else if (isGhostWrapper(viewGroup.getChildAt(numChildren - 1))) {
            // GhostViews are already at the end
            int firstGhost = numChildren - 1;
            for (int i = numChildren - 2; i >= 0; i--) {
                if (!isGhostWrapper(viewGroup.getChildAt(i))) {
                    break;
                }
                firstGhost = i;
            }
            return firstGhost;
        }

        // Remove all GhostViews from the middle
        for (int i = numChildren - 2; i >= 0; i--) {
            View child = viewGroup.getChildAt(i);
            if (isGhostWrapper(child)) {
                tempViews.add(child);
                GhostView ghostView = (GhostView)((ViewGroup)child).getChildAt(0);
                ghostView.mBeingMoved = true;
                viewGroup.removeViewAt(i);
                ghostView.mBeingMoved = false;
            }
        }

        final int firstGhost;
        if (tempViews.isEmpty()) {
            firstGhost = -1;
        } else {
            firstGhost = viewGroup.getChildCount();
            // Add the GhostViews to the end
            for (int i = tempViews.size() - 1; i >= 0; i--) {
                viewGroup.addView(tempViews.get(i));
            }
            tempViews.clear();
        }
        return firstGhost;
    
protected voidonDetachedFromWindow()

        super.onDetachedFromWindow();
        if (!mBeingMoved) {
            setGhostedVisibility(View.VISIBLE);
            mView.mGhostView = null;
            final ViewGroup parent = (ViewGroup) mView.getParent();
            if (parent != null) {
                parent.mRecreateDisplayList = true;
                parent.getDisplayList();
            }
        }
    
protected voidonDraw(android.graphics.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 static voidremoveGhost(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);
            }
        }
    
private voidsetGhostedVisibility(int visibility)

        mView.mViewFlags = (mView.mViewFlags & ~View.VISIBILITY_MASK) | visibility;
    
public voidsetMatrix(android.graphics.Matrix matrix)

        mRenderNode.setAnimationMatrix(matrix);
    
public voidsetVisibility(int visibility)

        super.setVisibility(visibility);
        if (mView.mGhostView == this) {
            int inverseVisibility = (visibility == View.VISIBLE) ? View.INVISIBLE : View.VISIBLE;
            setGhostedVisibility(inverseVisibility);
        }