blob: 47de780d83c0ab1bc0d0844e58b480ea88539187 [file] [log] [blame]
Chet Haase91cedf12013-03-11 07:56:30 -07001/*
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 */
16package android.view;
17
Chet Haasece08ce52013-06-06 07:27:53 -070018import android.animation.LayoutTransition;
Chet Haase91cedf12013-03-11 07:56:30 -070019import android.content.Context;
20import android.graphics.Canvas;
21import android.graphics.Rect;
22import android.graphics.drawable.Drawable;
23
24import java.util.ArrayList;
25
26/**
Chet Haaseedf6f4b2013-03-26 07:55:30 -070027 * An overlay is an extra layer that sits on top of a View (the "host view")
28 * which is drawn after all other content in that view (including children,
29 * if the view is a ViewGroup). Interaction with the overlay layer is done
30 * by adding and removing drawables.
Chet Haase91cedf12013-03-11 07:56:30 -070031 *
Chet Haaseedf6f4b2013-03-26 07:55:30 -070032 * <p>An overlay requested from a ViewGroup is of type {@link ViewGroupOverlay},
33 * which also supports adding and removing views.</p>
Chet Haase91cedf12013-03-11 07:56:30 -070034 *
Chet Haaseedf6f4b2013-03-26 07:55:30 -070035 * @see View#getOverlay() View.getOverlay()
36 * @see ViewGroup#getOverlay() ViewGroup.getOverlay()
37 * @see ViewGroupOverlay
Chet Haase91cedf12013-03-11 07:56:30 -070038 */
Chet Haaseedf6f4b2013-03-26 07:55:30 -070039public class ViewOverlay {
Chet Haase91cedf12013-03-11 07:56:30 -070040
41 /**
Chet Haaseedf6f4b2013-03-26 07:55:30 -070042 * The actual container for the drawables (and views, if it's a ViewGroupOverlay).
43 * All of the management and rendering details for the overlay are handled in
44 * OverlayViewGroup.
Chet Haase91cedf12013-03-11 07:56:30 -070045 */
Chet Haaseedf6f4b2013-03-26 07:55:30 -070046 OverlayViewGroup mOverlayViewGroup;
Chet Haase91cedf12013-03-11 07:56:30 -070047
Chet Haaseedf6f4b2013-03-26 07:55:30 -070048 ViewOverlay(Context context, View hostView) {
49 mOverlayViewGroup = new OverlayViewGroup(context, hostView);
Chet Haase91cedf12013-03-11 07:56:30 -070050 }
51
Chet Haaseedf6f4b2013-03-26 07:55:30 -070052 /**
53 * Used internally by View and ViewGroup to handle drawing and invalidation
54 * of the overlay
55 * @return
56 */
57 ViewGroup getOverlayView() {
58 return mOverlayViewGroup;
59 }
60
61 /**
62 * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
63 * the host view. Any drawable added to the overlay should be removed when it is no longer
64 * needed or no longer visible.
65 *
66 * @param drawable The Drawable to be added to the overlay. This drawable will be
67 * drawn when the view redraws its overlay.
68 * @see #remove(Drawable)
69 */
Chet Haase91cedf12013-03-11 07:56:30 -070070 public void add(Drawable drawable) {
Chet Haaseedf6f4b2013-03-26 07:55:30 -070071 mOverlayViewGroup.add(drawable);
Chet Haase91cedf12013-03-11 07:56:30 -070072 }
73
Chet Haaseedf6f4b2013-03-26 07:55:30 -070074 /**
75 * Removes the specified Drawable from the overlay.
76 *
77 * @param drawable The Drawable to be removed from the overlay.
78 * @see #add(Drawable)
79 */
Chet Haase91cedf12013-03-11 07:56:30 -070080 public void remove(Drawable drawable) {
Chet Haaseedf6f4b2013-03-26 07:55:30 -070081 mOverlayViewGroup.remove(drawable);
Chet Haase91cedf12013-03-11 07:56:30 -070082 }
83
Chet Haaseedf6f4b2013-03-26 07:55:30 -070084 /**
85 * Removes all content from the overlay.
86 */
Chet Haase91cedf12013-03-11 07:56:30 -070087 public void clear() {
Chet Haaseedf6f4b2013-03-26 07:55:30 -070088 mOverlayViewGroup.clear();
Chet Haase91cedf12013-03-11 07:56:30 -070089 }
90
91 boolean isEmpty() {
Chet Haaseedf6f4b2013-03-26 07:55:30 -070092 return mOverlayViewGroup.isEmpty();
Chet Haase91cedf12013-03-11 07:56:30 -070093 }
94
Chet Haaseedf6f4b2013-03-26 07:55:30 -070095 /**
96 * OverlayViewGroup is a container that View and ViewGroup use to host
97 * drawables and views added to their overlays ({@link ViewOverlay} and
98 * {@link ViewGroupOverlay}, respectively). Drawables are added to the overlay
99 * via the add/remove methods in ViewOverlay, Views are added/removed via
100 * ViewGroupOverlay. These drawable and view objects are
101 * drawn whenever the view itself is drawn; first the view draws its own
102 * content (and children, if it is a ViewGroup), then it draws its overlay
103 * (if it has one).
104 *
105 * <p>Besides managing and drawing the list of drawables, this class serves
106 * two purposes:
107 * (1) it noops layout calls because children are absolutely positioned and
108 * (2) it forwards all invalidation calls to its host view. The invalidation
109 * redirect is necessary because the overlay is not a child of the host view
110 * and invalidation cannot therefore follow the normal path up through the
111 * parent hierarchy.</p>
112 *
113 * @see View#getOverlay()
114 * @see ViewGroup#getOverlay()
Chet Haase91cedf12013-03-11 07:56:30 -0700115 */
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700116 static class OverlayViewGroup extends ViewGroup {
Chet Haase91cedf12013-03-11 07:56:30 -0700117
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700118 /**
119 * The View for which this is an overlay. Invalidations of the overlay are redirected to
120 * this host view.
121 */
122 View mHostView;
123
124 /**
125 * The set of drawables to draw when the overlay is rendered.
126 */
127 ArrayList<Drawable> mDrawables = null;
128
129 OverlayViewGroup(Context context, View hostView) {
130 super(context);
131 mHostView = hostView;
132 mAttachInfo = mHostView.mAttachInfo;
133 mRight = hostView.getWidth();
134 mBottom = hostView.getHeight();
Chet Haase91cedf12013-03-11 07:56:30 -0700135 }
Chet Haase91cedf12013-03-11 07:56:30 -0700136
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700137 public void add(Drawable drawable) {
138 if (mDrawables == null) {
Chet Haase91cedf12013-03-11 07:56:30 -0700139
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700140 mDrawables = new ArrayList<Drawable>();
Chet Haase91cedf12013-03-11 07:56:30 -0700141 }
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700142 if (!mDrawables.contains(drawable)) {
143 // Make each drawable unique in the overlay; can't add it more than once
144 mDrawables.add(drawable);
145 invalidate(drawable.getBounds());
146 drawable.setCallback(this);
Chet Haase9c17fe62013-03-22 17:05:55 -0700147 }
Chet Haase91cedf12013-03-11 07:56:30 -0700148 }
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700149
150 public void remove(Drawable drawable) {
151 if (mDrawables != null) {
152 mDrawables.remove(drawable);
153 invalidate(drawable.getBounds());
154 drawable.setCallback(null);
155 }
156 }
157
Alan Viverette39de9bf2013-12-11 13:15:47 -0800158 @Override
159 protected boolean verifyDrawable(Drawable who) {
160 return super.verifyDrawable(who) || (mDrawables != null && mDrawables.contains(who));
161 }
162
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700163 public void add(View child) {
164 if (child.getParent() instanceof ViewGroup) {
165 ViewGroup parent = (ViewGroup) child.getParent();
Chet Haaseface7422013-04-15 15:15:59 -0700166 if (parent != mHostView && parent.getParent() != null &&
167 parent.mAttachInfo != null) {
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700168 // Moving to different container; figure out how to position child such that
169 // it is in the same location on the screen
170 int[] parentLocation = new int[2];
171 int[] hostViewLocation = new int[2];
172 parent.getLocationOnScreen(parentLocation);
173 mHostView.getLocationOnScreen(hostViewLocation);
174 child.offsetLeftAndRight(parentLocation[0] - hostViewLocation[0]);
175 child.offsetTopAndBottom(parentLocation[1] - hostViewLocation[1]);
176 }
177 parent.removeView(child);
Chet Haasece08ce52013-06-06 07:27:53 -0700178 if (parent.getLayoutTransition() != null) {
179 // LayoutTransition will cause the child to delay removal - cancel it
180 parent.getLayoutTransition().cancel(LayoutTransition.DISAPPEARING);
181 }
182 // fail-safe if view is still attached for any reason
183 if (child.getParent() != null) {
184 child.mParent = null;
185 }
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700186 }
187 super.addView(child);
188 }
189
190 public void remove(View view) {
191 super.removeView(view);
192 }
193
194 public void clear() {
195 removeAllViews();
Chet Haased8b0b232013-05-20 06:46:11 -0700196 if (mDrawables != null) {
197 mDrawables.clear();
198 }
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700199 }
200
201 boolean isEmpty() {
202 if (getChildCount() == 0 &&
203 (mDrawables == null || mDrawables.size() == 0)) {
204 return true;
205 }
206 return false;
207 }
208
209 @Override
210 public void invalidateDrawable(Drawable drawable) {
211 invalidate(drawable.getBounds());
212 }
213
214 @Override
215 protected void dispatchDraw(Canvas canvas) {
216 super.dispatchDraw(canvas);
217 final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size();
218 for (int i = 0; i < numDrawables; ++i) {
219 mDrawables.get(i).draw(canvas);
220 }
221 }
222
223 @Override
224 protected void onLayout(boolean changed, int l, int t, int r, int b) {
225 // Noop: children are positioned absolutely
226 }
227
228 /*
229 The following invalidation overrides exist for the purpose of redirecting invalidation to
230 the host view. The overlay is not parented to the host view (since a View cannot be a
231 parent), so the invalidation cannot proceed through the normal parent hierarchy.
232 There is a built-in assumption that the overlay exactly covers the host view, therefore
233 the invalidation rectangles received do not need to be adjusted when forwarded to
234 the host view.
235 */
236
237 @Override
238 public void invalidate(Rect dirty) {
239 super.invalidate(dirty);
240 if (mHostView != null) {
241 mHostView.invalidate(dirty);
242 }
243 }
244
245 @Override
246 public void invalidate(int l, int t, int r, int b) {
247 super.invalidate(l, t, r, b);
248 if (mHostView != null) {
249 mHostView.invalidate(l, t, r, b);
250 }
251 }
252
253 @Override
254 public void invalidate() {
255 super.invalidate();
256 if (mHostView != null) {
257 mHostView.invalidate();
258 }
259 }
260
261 @Override
262 void invalidate(boolean invalidateCache) {
263 super.invalidate(invalidateCache);
264 if (mHostView != null) {
265 mHostView.invalidate(invalidateCache);
266 }
267 }
268
269 @Override
270 void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
271 super.invalidateViewProperty(invalidateParent, forceRedraw);
272 if (mHostView != null) {
273 mHostView.invalidateViewProperty(invalidateParent, forceRedraw);
274 }
275 }
276
277 @Override
278 protected void invalidateParentCaches() {
279 super.invalidateParentCaches();
280 if (mHostView != null) {
281 mHostView.invalidateParentCaches();
282 }
283 }
284
285 @Override
286 protected void invalidateParentIfNeeded() {
287 super.invalidateParentIfNeeded();
288 if (mHostView != null) {
289 mHostView.invalidateParentIfNeeded();
290 }
291 }
292
293 public void invalidateChildFast(View child, final Rect dirty) {
294 if (mHostView != null) {
295 // Note: This is not a "fast" invalidation. Would be nice to instead invalidate
296 // using DisplayList properties and a dirty rect instead of causing a real
297 // invalidation of the host view
298 int left = child.mLeft;
299 int top = child.mTop;
300 if (!child.getMatrix().isIdentity()) {
301 child.transformRect(dirty);
302 }
303 dirty.offset(left, top);
304 mHostView.invalidate(dirty);
305 }
306 }
307
Chet Haasee4a2d7c2013-06-21 17:49:36 -0700308 /**
309 * @hide
310 */
311 @Override
312 protected ViewParent invalidateChildInParentFast(int left, int top, Rect dirty) {
313 if (mHostView instanceof ViewGroup) {
314 return ((ViewGroup) mHostView).invalidateChildInParentFast(left, top, dirty);
315 }
316 return null;
317 }
318
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700319 @Override
320 public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
321 if (mHostView != null) {
322 dirty.offset(location[0], location[1]);
323 if (mHostView instanceof ViewGroup) {
324 location[0] = 0;
325 location[1] = 0;
326 super.invalidateChildInParent(location, dirty);
327 return ((ViewGroup) mHostView).invalidateChildInParent(location, dirty);
328 } else {
329 invalidate(dirty);
330 }
331 }
332 return null;
333 }
Chet Haase91cedf12013-03-11 07:56:30 -0700334 }
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700335
Chet Haase91cedf12013-03-11 07:56:30 -0700336}