blob: 55109397f5959b84695e45a714527013c6621c46 [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
18import android.content.Context;
19import android.graphics.Canvas;
20import android.graphics.Rect;
21import android.graphics.drawable.Drawable;
22
23import java.util.ArrayList;
24
25/**
Chet Haaseedf6f4b2013-03-26 07:55:30 -070026 * 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.
Chet Haase91cedf12013-03-11 07:56:30 -070030 *
Chet Haaseedf6f4b2013-03-26 07:55:30 -070031 * <p>An overlay requested from a ViewGroup is of type {@link ViewGroupOverlay},
32 * which also supports adding and removing views.</p>
Chet Haase91cedf12013-03-11 07:56:30 -070033 *
Chet Haaseedf6f4b2013-03-26 07:55:30 -070034 * @see View#getOverlay() View.getOverlay()
35 * @see ViewGroup#getOverlay() ViewGroup.getOverlay()
36 * @see ViewGroupOverlay
Chet Haase91cedf12013-03-11 07:56:30 -070037 */
Chet Haaseedf6f4b2013-03-26 07:55:30 -070038public class ViewOverlay {
Chet Haase91cedf12013-03-11 07:56:30 -070039
40 /**
Chet Haaseedf6f4b2013-03-26 07:55:30 -070041 * 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.
Chet Haase91cedf12013-03-11 07:56:30 -070044 */
Chet Haaseedf6f4b2013-03-26 07:55:30 -070045 OverlayViewGroup mOverlayViewGroup;
Chet Haase91cedf12013-03-11 07:56:30 -070046
Chet Haaseedf6f4b2013-03-26 07:55:30 -070047 ViewOverlay(Context context, View hostView) {
48 mOverlayViewGroup = new OverlayViewGroup(context, hostView);
Chet Haase91cedf12013-03-11 07:56:30 -070049 }
50
Chet Haaseedf6f4b2013-03-26 07:55:30 -070051 /**
52 * Used internally by View and ViewGroup to handle drawing and invalidation
53 * of the overlay
54 * @return
55 */
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 */
Chet Haase91cedf12013-03-11 07:56:30 -070069 public void add(Drawable drawable) {
Chet Haaseedf6f4b2013-03-26 07:55:30 -070070 mOverlayViewGroup.add(drawable);
Chet Haase91cedf12013-03-11 07:56:30 -070071 }
72
Chet Haaseedf6f4b2013-03-26 07:55:30 -070073 /**
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 */
Chet Haase91cedf12013-03-11 07:56:30 -070079 public void remove(Drawable drawable) {
Chet Haaseedf6f4b2013-03-26 07:55:30 -070080 mOverlayViewGroup.remove(drawable);
Chet Haase91cedf12013-03-11 07:56:30 -070081 }
82
Chet Haaseedf6f4b2013-03-26 07:55:30 -070083 /**
84 * Removes all content from the overlay.
85 */
Chet Haase91cedf12013-03-11 07:56:30 -070086 public void clear() {
Chet Haaseedf6f4b2013-03-26 07:55:30 -070087 mOverlayViewGroup.clear();
Chet Haase91cedf12013-03-11 07:56:30 -070088 }
89
90 boolean isEmpty() {
Chet Haaseedf6f4b2013-03-26 07:55:30 -070091 return mOverlayViewGroup.isEmpty();
Chet Haase91cedf12013-03-11 07:56:30 -070092 }
93
Chet Haaseedf6f4b2013-03-26 07:55:30 -070094 /**
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()
Chet Haase91cedf12013-03-11 07:56:30 -0700114 */
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700115 static class OverlayViewGroup extends ViewGroup {
Chet Haase91cedf12013-03-11 07:56:30 -0700116
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700117 /**
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
128 OverlayViewGroup(Context context, View hostView) {
129 super(context);
130 mHostView = hostView;
131 mAttachInfo = mHostView.mAttachInfo;
132 mRight = hostView.getWidth();
133 mBottom = hostView.getHeight();
Chet Haase91cedf12013-03-11 07:56:30 -0700134 }
Chet Haase91cedf12013-03-11 07:56:30 -0700135
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700136 public void add(Drawable drawable) {
137 if (mDrawables == null) {
Chet Haase91cedf12013-03-11 07:56:30 -0700138
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700139 mDrawables = new ArrayList<Drawable>();
Chet Haase91cedf12013-03-11 07:56:30 -0700140 }
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700141 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);
Chet Haase9c17fe62013-03-22 17:05:55 -0700146 }
Chet Haase91cedf12013-03-11 07:56:30 -0700147 }
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700148
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
157 public void add(View child) {
158 if (child.getParent() instanceof ViewGroup) {
159 ViewGroup parent = (ViewGroup) child.getParent();
Chet Haaseface7422013-04-15 15:15:59 -0700160 if (parent != mHostView && parent.getParent() != null &&
161 parent.mAttachInfo != null) {
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700162 // 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
176 public void remove(View view) {
177 super.removeView(view);
178 }
179
180 public void clear() {
181 removeAllViews();
Chet Haased8b0b232013-05-20 06:46:11 -0700182 if (mDrawables != null) {
183 mDrawables.clear();
184 }
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700185 }
186
187 boolean isEmpty() {
188 if (getChildCount() == 0 &&
189 (mDrawables == null || mDrawables.size() == 0)) {
190 return true;
191 }
192 return false;
193 }
194
195 @Override
196 public void invalidateDrawable(Drawable drawable) {
197 invalidate(drawable.getBounds());
198 }
199
200 @Override
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
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
224 public void invalidate(Rect dirty) {
225 super.invalidate(dirty);
226 if (mHostView != null) {
227 mHostView.invalidate(dirty);
228 }
229 }
230
231 @Override
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
240 public void invalidate() {
241 super.invalidate();
242 if (mHostView != null) {
243 mHostView.invalidate();
244 }
245 }
246
247 @Override
248 void invalidate(boolean invalidateCache) {
249 super.invalidate(invalidateCache);
250 if (mHostView != null) {
251 mHostView.invalidate(invalidateCache);
252 }
253 }
254
255 @Override
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
264 protected void invalidateParentCaches() {
265 super.invalidateParentCaches();
266 if (mHostView != null) {
267 mHostView.invalidateParentCaches();
268 }
269 }
270
271 @Override
272 protected void invalidateParentIfNeeded() {
273 super.invalidateParentIfNeeded();
274 if (mHostView != null) {
275 mHostView.invalidateParentIfNeeded();
276 }
277 }
278
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
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 }
Chet Haase91cedf12013-03-11 07:56:30 -0700309 }
Chet Haaseedf6f4b2013-03-26 07:55:30 -0700310
Chet Haase91cedf12013-03-11 07:56:30 -0700311}