• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.view;
17 
18 import android.content.Context;
19 import android.graphics.Canvas;
20 import android.graphics.Rect;
21 import android.graphics.drawable.Drawable;
22 
23 import java.util.ArrayList;
24 
25 /**
26  * An overlay is an extra layer that sits on top of a View (the "host view")
27  * which is drawn after all other content in that view (including children,
28  * if the view is a ViewGroup). Interaction with the overlay layer is done
29  * by adding and removing drawables.
30  *
31  * <p>An overlay requested from a ViewGroup is of type {@link ViewGroupOverlay},
32  * which also supports adding and removing views.</p>
33  *
34  * @see View#getOverlay() View.getOverlay()
35  * @see ViewGroup#getOverlay() ViewGroup.getOverlay()
36  * @see ViewGroupOverlay
37  */
38 public class ViewOverlay {
39 
40     /**
41      * The actual container for the drawables (and views, if it's a ViewGroupOverlay).
42      * All of the management and rendering details for the overlay are handled in
43      * OverlayViewGroup.
44      */
45     OverlayViewGroup mOverlayViewGroup;
46 
ViewOverlay(Context context, View hostView)47     ViewOverlay(Context context, View hostView) {
48         mOverlayViewGroup = new OverlayViewGroup(context, hostView);
49     }
50 
51     /**
52      * Used internally by View and ViewGroup to handle drawing and invalidation
53      * of the overlay
54      * @return
55      */
getOverlayView()56     ViewGroup getOverlayView() {
57         return mOverlayViewGroup;
58     }
59 
60     /**
61      * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
62      * the host view. Any drawable added to the overlay should be removed when it is no longer
63      * needed or no longer visible.
64      *
65      * @param drawable The Drawable to be added to the overlay. This drawable will be
66      * drawn when the view redraws its overlay.
67      * @see #remove(Drawable)
68      */
add(Drawable drawable)69     public void add(Drawable drawable) {
70         mOverlayViewGroup.add(drawable);
71     }
72 
73     /**
74      * Removes the specified Drawable from the overlay.
75      *
76      * @param drawable The Drawable to be removed from the overlay.
77      * @see #add(Drawable)
78      */
remove(Drawable drawable)79     public void remove(Drawable drawable) {
80         mOverlayViewGroup.remove(drawable);
81     }
82 
83     /**
84      * Removes all content from the overlay.
85      */
clear()86     public void clear() {
87         mOverlayViewGroup.clear();
88     }
89 
isEmpty()90     boolean isEmpty() {
91         return mOverlayViewGroup.isEmpty();
92     }
93 
94     /**
95      * OverlayViewGroup is a container that View and ViewGroup use to host
96      * drawables and views added to their overlays  ({@link ViewOverlay} and
97      * {@link ViewGroupOverlay}, respectively). Drawables are added to the overlay
98      * via the add/remove methods in ViewOverlay, Views are added/removed via
99      * ViewGroupOverlay. These drawable and view objects are
100      * drawn whenever the view itself is drawn; first the view draws its own
101      * content (and children, if it is a ViewGroup), then it draws its overlay
102      * (if it has one).
103      *
104      * <p>Besides managing and drawing the list of drawables, this class serves
105      * two purposes:
106      * (1) it noops layout calls because children are absolutely positioned and
107      * (2) it forwards all invalidation calls to its host view. The invalidation
108      * redirect is necessary because the overlay is not a child of the host view
109      * and invalidation cannot therefore follow the normal path up through the
110      * parent hierarchy.</p>
111      *
112      * @see View#getOverlay()
113      * @see ViewGroup#getOverlay()
114      */
115     static class OverlayViewGroup extends ViewGroup {
116 
117         /**
118          * The View for which this is an overlay. Invalidations of the overlay are redirected to
119          * this host view.
120          */
121         View mHostView;
122 
123         /**
124          * The set of drawables to draw when the overlay is rendered.
125          */
126         ArrayList<Drawable> mDrawables = null;
127 
OverlayViewGroup(Context context, View hostView)128         OverlayViewGroup(Context context, View hostView) {
129             super(context);
130             mHostView = hostView;
131             mAttachInfo = mHostView.mAttachInfo;
132             mRight = hostView.getWidth();
133             mBottom = hostView.getHeight();
134         }
135 
add(Drawable drawable)136         public void add(Drawable drawable) {
137             if (mDrawables == null) {
138 
139                 mDrawables = new ArrayList<Drawable>();
140             }
141             if (!mDrawables.contains(drawable)) {
142                 // Make each drawable unique in the overlay; can't add it more than once
143                 mDrawables.add(drawable);
144                 invalidate(drawable.getBounds());
145                 drawable.setCallback(this);
146             }
147         }
148 
remove(Drawable drawable)149         public void remove(Drawable drawable) {
150             if (mDrawables != null) {
151                 mDrawables.remove(drawable);
152                 invalidate(drawable.getBounds());
153                 drawable.setCallback(null);
154             }
155         }
156 
add(View child)157         public void add(View child) {
158             if (child.getParent() instanceof ViewGroup) {
159                 ViewGroup parent = (ViewGroup) child.getParent();
160                 if (parent != mHostView && parent.getParent() != null &&
161                         parent.mAttachInfo != null) {
162                     // Moving to different container; figure out how to position child such that
163                     // it is in the same location on the screen
164                     int[] parentLocation = new int[2];
165                     int[] hostViewLocation = new int[2];
166                     parent.getLocationOnScreen(parentLocation);
167                     mHostView.getLocationOnScreen(hostViewLocation);
168                     child.offsetLeftAndRight(parentLocation[0] - hostViewLocation[0]);
169                     child.offsetTopAndBottom(parentLocation[1] - hostViewLocation[1]);
170                 }
171                 parent.removeView(child);
172             }
173             super.addView(child);
174         }
175 
remove(View view)176         public void remove(View view) {
177             super.removeView(view);
178         }
179 
clear()180         public void clear() {
181             removeAllViews();
182             if (mDrawables != null) {
183                 mDrawables.clear();
184             }
185         }
186 
isEmpty()187         boolean isEmpty() {
188             if (getChildCount() == 0 &&
189                     (mDrawables == null || mDrawables.size() == 0)) {
190                 return true;
191             }
192             return false;
193         }
194 
195         @Override
invalidateDrawable(Drawable drawable)196         public void invalidateDrawable(Drawable drawable) {
197             invalidate(drawable.getBounds());
198         }
199 
200         @Override
dispatchDraw(Canvas canvas)201         protected void dispatchDraw(Canvas canvas) {
202             super.dispatchDraw(canvas);
203             final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size();
204             for (int i = 0; i < numDrawables; ++i) {
205                 mDrawables.get(i).draw(canvas);
206             }
207         }
208 
209         @Override
onLayout(boolean changed, int l, int t, int r, int b)210         protected void onLayout(boolean changed, int l, int t, int r, int b) {
211             // Noop: children are positioned absolutely
212         }
213 
214         /*
215          The following invalidation overrides exist for the purpose of redirecting invalidation to
216          the host view. The overlay is not parented to the host view (since a View cannot be a
217          parent), so the invalidation cannot proceed through the normal parent hierarchy.
218          There is a built-in assumption that the overlay exactly covers the host view, therefore
219          the invalidation rectangles received do not need to be adjusted when forwarded to
220          the host view.
221          */
222 
223         @Override
invalidate(Rect dirty)224         public void invalidate(Rect dirty) {
225             super.invalidate(dirty);
226             if (mHostView != null) {
227                 mHostView.invalidate(dirty);
228             }
229         }
230 
231         @Override
invalidate(int l, int t, int r, int b)232         public void invalidate(int l, int t, int r, int b) {
233             super.invalidate(l, t, r, b);
234             if (mHostView != null) {
235                 mHostView.invalidate(l, t, r, b);
236             }
237         }
238 
239         @Override
invalidate()240         public void invalidate() {
241             super.invalidate();
242             if (mHostView != null) {
243                 mHostView.invalidate();
244             }
245         }
246 
247         @Override
invalidate(boolean invalidateCache)248         void invalidate(boolean invalidateCache) {
249             super.invalidate(invalidateCache);
250             if (mHostView != null) {
251                 mHostView.invalidate(invalidateCache);
252             }
253         }
254 
255         @Override
invalidateViewProperty(boolean invalidateParent, boolean forceRedraw)256         void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
257             super.invalidateViewProperty(invalidateParent, forceRedraw);
258             if (mHostView != null) {
259                 mHostView.invalidateViewProperty(invalidateParent, forceRedraw);
260             }
261         }
262 
263         @Override
invalidateParentCaches()264         protected void invalidateParentCaches() {
265             super.invalidateParentCaches();
266             if (mHostView != null) {
267                 mHostView.invalidateParentCaches();
268             }
269         }
270 
271         @Override
invalidateParentIfNeeded()272         protected void invalidateParentIfNeeded() {
273             super.invalidateParentIfNeeded();
274             if (mHostView != null) {
275                 mHostView.invalidateParentIfNeeded();
276             }
277         }
278 
invalidateChildFast(View child, final Rect dirty)279         public void invalidateChildFast(View child, final Rect dirty) {
280             if (mHostView != null) {
281                 // Note: This is not a "fast" invalidation. Would be nice to instead invalidate
282                 // using DisplayList properties and a dirty rect instead of causing a real
283                 // invalidation of the host view
284                 int left = child.mLeft;
285                 int top = child.mTop;
286                 if (!child.getMatrix().isIdentity()) {
287                     child.transformRect(dirty);
288                 }
289                 dirty.offset(left, top);
290                 mHostView.invalidate(dirty);
291             }
292         }
293 
294         @Override
invalidateChildInParent(int[] location, Rect dirty)295         public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
296             if (mHostView != null) {
297                 dirty.offset(location[0], location[1]);
298                 if (mHostView instanceof ViewGroup) {
299                     location[0] = 0;
300                     location[1] = 0;
301                     super.invalidateChildInParent(location, dirty);
302                     return ((ViewGroup) mHostView).invalidateChildInParent(location, dirty);
303                 } else {
304                     invalidate(dirty);
305                 }
306             }
307             return null;
308         }
309     }
310 
311 }
312