blob: 1102a47984ee6e9b27fdc7bf1f6e9c2d96088f28 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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
17package android.view;
18
Chet Haase21cd1382010-09-01 17:42:29 -070019import android.animation.LayoutTransition;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.content.Context;
Dianne Hackborne36d6e22010-02-17 19:46:25 -080021import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.res.TypedArray;
23import android.graphics.Bitmap;
24import android.graphics.Canvas;
Adam Powell6e346362010-07-23 10:18:23 -070025import android.graphics.Matrix;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.graphics.Paint;
Christopher Tatea53146c2010-09-07 11:57:52 -070027import android.graphics.PointF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.graphics.Rect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.graphics.RectF;
svetoslavganov75986cf2009-05-14 22:28:01 -070030import android.graphics.Region;
Jeff Brown995e7742010-12-22 16:59:36 -080031import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.os.Parcelable;
33import android.os.SystemClock;
34import android.util.AttributeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.util.Log;
36import android.util.SparseArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070037import android.view.accessibility.AccessibilityEvent;
Svetoslav Ganov8643aa02011-04-20 12:12:33 -070038import android.view.accessibility.AccessibilityNodeInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.view.animation.Animation;
40import android.view.animation.AnimationUtils;
41import android.view.animation.LayoutAnimationController;
42import android.view.animation.Transformation;
Doug Feltcb3791202011-07-07 11:57:48 -070043
Romain Guy0211a0a2011-02-14 16:34:59 -080044import com.android.internal.R;
45import com.android.internal.util.Predicate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046
47import java.util.ArrayList;
Christopher Tate86cab1b2011-01-13 20:28:55 -080048import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
50/**
51 * <p>
52 * A <code>ViewGroup</code> is a special view that can contain other views
53 * (called children.) The view group is the base class for layouts and views
54 * containers. This class also defines the
55 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
56 * class for layouts parameters.
57 * </p>
58 *
59 * <p>
60 * Also see {@link LayoutParams} for layout attributes.
61 * </p>
Romain Guyd6a463a2009-05-21 23:10:10 -070062 *
Joe Fernandez558459f2011-10-13 16:47:36 -070063 * <div class="special reference">
64 * <h3>Developer Guides</h3>
65 * <p>For more information about creating user interface layouts, read the
66 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
67 * guide.</p></div>
68 *
Romain Guyd6a463a2009-05-21 23:10:10 -070069 * @attr ref android.R.styleable#ViewGroup_clipChildren
70 * @attr ref android.R.styleable#ViewGroup_clipToPadding
71 * @attr ref android.R.styleable#ViewGroup_layoutAnimation
72 * @attr ref android.R.styleable#ViewGroup_animationCache
73 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
74 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
75 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
76 * @attr ref android.R.styleable#ViewGroup_descendantFocusability
Chet Haase13cc1202010-09-03 15:39:20 -070077 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 */
79public abstract class ViewGroup extends View implements ViewParent, ViewManager {
Chet Haase21cd1382010-09-01 17:42:29 -070080
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 private static final boolean DBG = false;
Gilles Debunnecea45132011-11-24 02:19:27 +010082
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 /**
84 * Views which have been hidden or removed which need to be animated on
85 * their way out.
86 * This field should be made private, so it is hidden from the SDK.
87 * {@hide}
88 */
89 protected ArrayList<View> mDisappearingChildren;
90
91 /**
92 * Listener used to propagate events indicating when children are added
93 * and/or removed from a view group.
94 * This field should be made private, so it is hidden from the SDK.
95 * {@hide}
96 */
97 protected OnHierarchyChangeListener mOnHierarchyChangeListener;
98
99 // The view contained within this ViewGroup that has or contains focus.
100 private View mFocused;
101
Chet Haase48460322010-06-11 14:22:25 -0700102 /**
103 * A Transformation used when drawing children, to
104 * apply on the child being drawn.
105 */
106 private final Transformation mChildTransformation = new Transformation();
107
108 /**
109 * Used to track the current invalidation region.
110 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 private RectF mInvalidateRegion;
112
Chet Haase48460322010-06-11 14:22:25 -0700113 /**
114 * A Transformation used to calculate a correct
115 * invalidation area when the application is autoscaled.
116 */
117 private Transformation mInvalidationTransformation;
118
Christopher Tatea53146c2010-09-07 11:57:52 -0700119 // View currently under an ongoing drag
120 private View mCurrentDragView;
121
Christopher Tate86cab1b2011-01-13 20:28:55 -0800122 // Metadata about the ongoing drag
123 private DragEvent mCurrentDrag;
124 private HashSet<View> mDragNotifiedChildren;
125
Christopher Tatea53146c2010-09-07 11:57:52 -0700126 // Does this group have a child that can accept the current drag payload?
127 private boolean mChildAcceptsDrag;
128
129 // Used during drag dispatch
130 private final PointF mLocalPoint = new PointF();
131
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 // Layout animation
133 private LayoutAnimationController mLayoutAnimationController;
134 private Animation.AnimationListener mAnimationListener;
135
Jeff Brown20e987b2010-08-23 12:01:02 -0700136 // First touch target in the linked list of touch targets.
137 private TouchTarget mFirstTouchTarget;
138
Joe Onorato03ab0c72011-01-06 15:46:27 -0800139 // For debugging only. You can see these in hierarchyviewer.
Romain Guye95003e2011-01-09 13:53:06 -0800140 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800141 @ViewDebug.ExportedProperty(category = "events")
142 private long mLastTouchDownTime;
143 @ViewDebug.ExportedProperty(category = "events")
144 private int mLastTouchDownIndex = -1;
Romain Guye95003e2011-01-09 13:53:06 -0800145 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800146 @ViewDebug.ExportedProperty(category = "events")
147 private float mLastTouchDownX;
Romain Guye95003e2011-01-09 13:53:06 -0800148 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800149 @ViewDebug.ExportedProperty(category = "events")
150 private float mLastTouchDownY;
151
Jeff Brown87b7f802011-06-21 18:35:45 -0700152 // First hover target in the linked list of hover targets.
153 // The hover targets are children which have received ACTION_HOVER_ENTER.
154 // They might not have actually handled the hover event, but we will
155 // continue sending hover events to them as long as the pointer remains over
156 // their bounds and the view group does not intercept hover.
157 private HoverTarget mFirstHoverTarget;
Jeff Browna032cc02011-03-07 16:56:21 -0800158
Jeff Brown10b62902011-06-20 16:40:37 -0700159 // True if the view group itself received a hover event.
160 // It might not have actually handled the hover event.
161 private boolean mHoveredSelf;
162
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 /**
164 * Internal flags.
Romain Guy8506ab42009-06-11 17:35:47 -0700165 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 * This field should be made private, so it is hidden from the SDK.
167 * {@hide}
168 */
169 protected int mGroupFlags;
170
171 // When set, ViewGroup invalidates only the child's rectangle
172 // Set by default
173 private static final int FLAG_CLIP_CHILDREN = 0x1;
174
175 // When set, ViewGroup excludes the padding area from the invalidate rectangle
176 // Set by default
177 private static final int FLAG_CLIP_TO_PADDING = 0x2;
178
179 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
180 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
181 private static final int FLAG_INVALIDATE_REQUIRED = 0x4;
182
183 // When set, dispatchDraw() will run the layout animation and unset the flag
184 private static final int FLAG_RUN_ANIMATION = 0x8;
185
186 // When set, there is either no layout animation on the ViewGroup or the layout
187 // animation is over
188 // Set by default
189 private static final int FLAG_ANIMATION_DONE = 0x10;
190
191 // If set, this ViewGroup has padding; if unset there is no padding and we don't need
192 // to clip it, even if FLAG_CLIP_TO_PADDING is set
193 private static final int FLAG_PADDING_NOT_NULL = 0x20;
194
195 // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation
196 // Set by default
197 private static final int FLAG_ANIMATION_CACHE = 0x40;
198
199 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
200 // layout animation; this avoid clobbering the hierarchy
201 // Automatically set when the layout animation starts, depending on the animation's
202 // characteristics
203 private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
204
205 // When set, the next call to drawChild() will clear mChildTransformation's matrix
206 private static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
207
208 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
209 // the children's Bitmap caches if necessary
210 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
211 private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
212
213 /**
214 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
215 * to get the index of the child to draw for that iteration.
Romain Guy293451e2009-11-04 13:59:48 -0800216 *
217 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 */
219 protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
Romain Guy8506ab42009-06-11 17:35:47 -0700220
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 /**
222 * When set, this ViewGroup supports static transformations on children; this causes
223 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
224 * invoked when a child is drawn.
225 *
226 * Any subclass overriding
227 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
228 * set this flags in {@link #mGroupFlags}.
Romain Guy8506ab42009-06-11 17:35:47 -0700229 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 * {@hide}
231 */
232 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
233
234 // When the previous drawChild() invocation used an alpha value that was lower than
235 // 1.0 and set it in mCachePaint
236 private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000;
237
238 /**
239 * When set, this ViewGroup's drawable states also include those
240 * of its children.
241 */
242 private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
243
244 /**
245 * When set, this ViewGroup tries to always draw its children using their drawing cache.
246 */
247 private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
248
249 /**
250 * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to
251 * draw its children with their drawing cache.
252 */
253 private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
254
255 /**
256 * When set, this group will go through its list of children to notify them of
257 * any drawable state change.
258 */
259 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
260
261 private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
262
263 /**
264 * This view will get focus before any of its descendants.
265 */
266 public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
267
268 /**
269 * This view will get focus only if none of its descendants want it.
270 */
271 public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
272
273 /**
274 * This view will block any of its descendants from getting focus, even
275 * if they are focusable.
276 */
277 public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
278
279 /**
280 * Used to map between enum in attrubutes and flag values.
281 */
282 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
283 {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
284 FOCUS_BLOCK_DESCENDANTS};
285
286 /**
287 * When set, this ViewGroup should not intercept touch events.
Adam Powell110486f2010-06-22 17:14:44 -0700288 * {@hide}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 */
Adam Powell110486f2010-06-22 17:14:44 -0700290 protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
Romain Guy8506ab42009-06-11 17:35:47 -0700291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 /**
Adam Powell2b342f02010-08-18 18:14:13 -0700293 * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
294 */
Adam Powellf37df072010-09-17 16:22:49 -0700295 private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
Adam Powell2b342f02010-08-18 18:14:13 -0700296
297 /**
Adam Powell4b867882011-09-16 12:59:46 -0700298 * When set, this ViewGroup will not dispatch onAttachedToWindow calls
299 * to children when adding new views. This is used to prevent multiple
300 * onAttached calls when a ViewGroup adds children in its own onAttached method.
301 */
302 private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000;
303
304 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 * Indicates which types of drawing caches are to be kept in memory.
306 * This field should be made private, so it is hidden from the SDK.
307 * {@hide}
308 */
309 protected int mPersistentDrawingCache;
310
311 /**
312 * Used to indicate that no drawing cache should be kept in memory.
313 */
314 public static final int PERSISTENT_NO_CACHE = 0x0;
315
316 /**
317 * Used to indicate that the animation drawing cache should be kept in memory.
318 */
319 public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
320
321 /**
322 * Used to indicate that the scrolling drawing cache should be kept in memory.
323 */
324 public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
325
326 /**
327 * Used to indicate that all drawing caches should be kept in memory.
328 */
329 public static final int PERSISTENT_ALL_CACHES = 0x3;
330
331 /**
332 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
333 * are set at the same time.
334 */
335 protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
336
337 // Index of the child's left position in the mLocation array
338 private static final int CHILD_LEFT_INDEX = 0;
339 // Index of the child's top position in the mLocation array
340 private static final int CHILD_TOP_INDEX = 1;
341
342 // Child views of this ViewGroup
343 private View[] mChildren;
344 // Number of valid children in the mChildren array, the rest should be null or not
345 // considered as children
Chet Haase9c087442011-01-12 16:20:16 -0800346
347 private boolean mLayoutSuppressed = false;
348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 private int mChildrenCount;
350
351 private static final int ARRAY_INITIAL_CAPACITY = 12;
352 private static final int ARRAY_CAPACITY_INCREMENT = 12;
353
354 // Used to draw cached views
Dianne Hackborn0500b3c2011-11-01 15:28:43 -0700355 private Paint mCachePaint;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356
Chet Haase21cd1382010-09-01 17:42:29 -0700357 // Used to animate add/remove changes in layout
358 private LayoutTransition mTransition;
359
360 // The set of views that are currently being transitioned. This list is used to track views
361 // being removed that should not actually be removed from the parent yet because they are
362 // being animated.
363 private ArrayList<View> mTransitioningViews;
364
Chet Haase5e25c2c2010-09-16 11:15:56 -0700365 // List of children changing visibility. This is used to potentially keep rendering
366 // views during a transition when they otherwise would have become gone/invisible
367 private ArrayList<View> mVisibilityChangingChildren;
368
Romain Guy849d0a32011-02-01 17:20:48 -0800369 // Indicates whether this container will use its children layers to draw
370 @ViewDebug.ExportedProperty(category = "drawing")
371 private boolean mDrawLayers = true;
372
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 public ViewGroup(Context context) {
374 super(context);
375 initViewGroup();
376 }
377
378 public ViewGroup(Context context, AttributeSet attrs) {
379 super(context, attrs);
380 initViewGroup();
381 initFromAttributes(context, attrs);
382 }
383
384 public ViewGroup(Context context, AttributeSet attrs, int defStyle) {
385 super(context, attrs, defStyle);
386 initViewGroup();
387 initFromAttributes(context, attrs);
388 }
389
390 private void initViewGroup() {
391 // ViewGroup doesn't draw by default
392 setFlags(WILL_NOT_DRAW, DRAW_MASK);
393 mGroupFlags |= FLAG_CLIP_CHILDREN;
394 mGroupFlags |= FLAG_CLIP_TO_PADDING;
395 mGroupFlags |= FLAG_ANIMATION_DONE;
396 mGroupFlags |= FLAG_ANIMATION_CACHE;
397 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
398
Jeff Brown995e7742010-12-22 16:59:36 -0800399 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
400 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
401 }
402
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
404
405 mChildren = new View[ARRAY_INITIAL_CAPACITY];
406 mChildrenCount = 0;
407
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
409 }
410
411 private void initFromAttributes(Context context, AttributeSet attrs) {
412 TypedArray a = context.obtainStyledAttributes(attrs,
413 R.styleable.ViewGroup);
414
415 final int N = a.getIndexCount();
416 for (int i = 0; i < N; i++) {
417 int attr = a.getIndex(i);
418 switch (attr) {
419 case R.styleable.ViewGroup_clipChildren:
420 setClipChildren(a.getBoolean(attr, true));
421 break;
422 case R.styleable.ViewGroup_clipToPadding:
423 setClipToPadding(a.getBoolean(attr, true));
424 break;
425 case R.styleable.ViewGroup_animationCache:
426 setAnimationCacheEnabled(a.getBoolean(attr, true));
427 break;
428 case R.styleable.ViewGroup_persistentDrawingCache:
429 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
430 break;
431 case R.styleable.ViewGroup_addStatesFromChildren:
432 setAddStatesFromChildren(a.getBoolean(attr, false));
433 break;
434 case R.styleable.ViewGroup_alwaysDrawnWithCache:
435 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
436 break;
437 case R.styleable.ViewGroup_layoutAnimation:
438 int id = a.getResourceId(attr, -1);
439 if (id > 0) {
440 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
441 }
442 break;
443 case R.styleable.ViewGroup_descendantFocusability:
444 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
445 break;
Adam Powell2b342f02010-08-18 18:14:13 -0700446 case R.styleable.ViewGroup_splitMotionEvents:
447 setMotionEventSplittingEnabled(a.getBoolean(attr, false));
448 break;
Chet Haase13cc1202010-09-03 15:39:20 -0700449 case R.styleable.ViewGroup_animateLayoutChanges:
450 boolean animateLayoutChanges = a.getBoolean(attr, false);
451 if (animateLayoutChanges) {
452 setLayoutTransition(new LayoutTransition());
453 }
454 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 }
456 }
457
458 a.recycle();
459 }
460
461 /**
462 * Gets the descendant focusability of this view group. The descendant
463 * focusability defines the relationship between this view group and its
464 * descendants when looking for a view to take focus in
465 * {@link #requestFocus(int, android.graphics.Rect)}.
466 *
467 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
468 * {@link #FOCUS_BLOCK_DESCENDANTS}.
469 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700470 @ViewDebug.ExportedProperty(category = "focus", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
472 @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
473 @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
474 })
475 public int getDescendantFocusability() {
476 return mGroupFlags & FLAG_MASK_FOCUSABILITY;
477 }
478
479 /**
480 * Set the descendant focusability of this view group. This defines the relationship
481 * between this view group and its descendants when looking for a view to
482 * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
483 *
484 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
485 * {@link #FOCUS_BLOCK_DESCENDANTS}.
486 */
487 public void setDescendantFocusability(int focusability) {
488 switch (focusability) {
489 case FOCUS_BEFORE_DESCENDANTS:
490 case FOCUS_AFTER_DESCENDANTS:
491 case FOCUS_BLOCK_DESCENDANTS:
492 break;
493 default:
494 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
495 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
496 }
497 mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
498 mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
499 }
500
501 /**
502 * {@inheritDoc}
503 */
504 @Override
505 void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
506 if (mFocused != null) {
507 mFocused.unFocus();
508 mFocused = null;
509 }
510 super.handleFocusGainInternal(direction, previouslyFocusedRect);
511 }
512
513 /**
514 * {@inheritDoc}
515 */
516 public void requestChildFocus(View child, View focused) {
517 if (DBG) {
518 System.out.println(this + " requestChildFocus()");
519 }
520 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
521 return;
522 }
523
524 // Unfocus us, if necessary
525 super.unFocus();
526
527 // We had a previous notion of who had focus. Clear it.
528 if (mFocused != child) {
529 if (mFocused != null) {
530 mFocused.unFocus();
531 }
532
533 mFocused = child;
534 }
535 if (mParent != null) {
536 mParent.requestChildFocus(this, focused);
537 }
538 }
539
540 /**
541 * {@inheritDoc}
542 */
543 public void focusableViewAvailable(View v) {
544 if (mParent != null
545 // shortcut: don't report a new focusable view if we block our descendants from
546 // getting focus
547 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
548 // shortcut: don't report a new focusable view if we already are focused
549 // (and we don't prefer our descendants)
550 //
551 // note: knowing that mFocused is non-null is not a good enough reason
552 // to break the traversal since in that case we'd actually have to find
553 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
Joe Onoratoc6cc0f82011-04-12 11:53:13 -0700554 // an ancestor of v; this will get checked for at ViewAncestor
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
556 mParent.focusableViewAvailable(v);
557 }
558 }
559
560 /**
561 * {@inheritDoc}
562 */
563 public boolean showContextMenuForChild(View originalView) {
564 return mParent != null && mParent.showContextMenuForChild(originalView);
565 }
566
567 /**
Adam Powell6e346362010-07-23 10:18:23 -0700568 * {@inheritDoc}
569 */
570 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
571 return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null;
572 }
573
574 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 * Find the nearest view in the specified direction that wants to take
576 * focus.
577 *
578 * @param focused The view that currently has focus
579 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
580 * FOCUS_RIGHT, or 0 for not applicable.
581 */
582 public View focusSearch(View focused, int direction) {
583 if (isRootNamespace()) {
584 // root namespace means we should consider ourselves the top of the
585 // tree for focus searching; otherwise we could be focus searching
586 // into other tabs. see LocalActivityManager and TabHost for more info
587 return FocusFinder.getInstance().findNextFocus(this, focused, direction);
588 } else if (mParent != null) {
589 return mParent.focusSearch(focused, direction);
590 }
591 return null;
592 }
593
594 /**
595 * {@inheritDoc}
596 */
597 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
598 return false;
599 }
600
601 /**
602 * {@inheritDoc}
603 */
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700604 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
605 ViewParent parent = getParent();
606 if (parent == null) {
607 return false;
608 }
609 final boolean propagate = onRequestSendAccessibilityEvent(child, event);
Romain Guy02739a82011-05-16 11:43:18 -0700610 //noinspection SimplifiableIfStatement
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700611 if (!propagate) {
612 return false;
613 }
614 return parent.requestSendAccessibilityEvent(this, event);
615 }
616
617 /**
618 * Called when a child has requested sending an {@link AccessibilityEvent} and
619 * gives an opportunity to its parent to augment the event.
Svetoslav Ganov031d9c12011-09-09 16:41:13 -0700620 * <p>
Adam Powell2fcbbd02011-09-28 18:56:43 -0700621 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
622 * {@link android.view.View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
623 * {@link android.view.View.AccessibilityDelegate#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)}
Svetoslav Ganov031d9c12011-09-09 16:41:13 -0700624 * is responsible for handling this call.
625 * </p>
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700626 *
627 * @param child The child which requests sending the event.
628 * @param event The event to be sent.
629 * @return True if the event should be sent.
630 *
631 * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
632 */
633 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
Svetoslav Ganov031d9c12011-09-09 16:41:13 -0700634 if (mAccessibilityDelegate != null) {
635 return mAccessibilityDelegate.onRequestSendAccessibilityEvent(this, child, event);
636 } else {
637 return onRequestSendAccessibilityEventInternal(child, event);
638 }
639 }
640
641 /**
642 * @see #onRequestSendAccessibilityEvent(View, AccessibilityEvent)
643 *
644 * Note: Called from the default {@link View.AccessibilityDelegate}.
645 */
646 boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700647 return true;
648 }
649
650 /**
651 * {@inheritDoc}
652 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 @Override
654 public boolean dispatchUnhandledMove(View focused, int direction) {
655 return mFocused != null &&
656 mFocused.dispatchUnhandledMove(focused, direction);
657 }
658
659 /**
660 * {@inheritDoc}
661 */
662 public void clearChildFocus(View child) {
663 if (DBG) {
664 System.out.println(this + " clearChildFocus()");
665 }
666
667 mFocused = null;
668 if (mParent != null) {
669 mParent.clearChildFocus(this);
670 }
671 }
672
673 /**
674 * {@inheritDoc}
675 */
676 @Override
677 public void clearFocus() {
678 super.clearFocus();
679
680 // clear any child focus if it exists
681 if (mFocused != null) {
682 mFocused.clearFocus();
683 }
684 }
685
686 /**
687 * {@inheritDoc}
688 */
689 @Override
690 void unFocus() {
691 if (DBG) {
692 System.out.println(this + " unFocus()");
693 }
694
695 super.unFocus();
696 if (mFocused != null) {
697 mFocused.unFocus();
698 }
699 mFocused = null;
700 }
701
702 /**
703 * Returns the focused child of this view, if any. The child may have focus
704 * or contain focus.
705 *
706 * @return the focused child or null.
707 */
708 public View getFocusedChild() {
709 return mFocused;
710 }
711
712 /**
713 * Returns true if this view has or contains focus
714 *
715 * @return true if this view has or contains focus
716 */
717 @Override
718 public boolean hasFocus() {
719 return (mPrivateFlags & FOCUSED) != 0 || mFocused != null;
720 }
721
722 /*
723 * (non-Javadoc)
724 *
725 * @see android.view.View#findFocus()
726 */
727 @Override
728 public View findFocus() {
729 if (DBG) {
730 System.out.println("Find focus in " + this + ": flags="
731 + isFocused() + ", child=" + mFocused);
732 }
733
734 if (isFocused()) {
735 return this;
736 }
737
738 if (mFocused != null) {
739 return mFocused.findFocus();
740 }
741 return null;
742 }
743
744 /**
745 * {@inheritDoc}
746 */
747 @Override
748 public boolean hasFocusable() {
749 if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
750 return false;
751 }
752
753 if (isFocusable()) {
754 return true;
755 }
756
757 final int descendantFocusability = getDescendantFocusability();
758 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
759 final int count = mChildrenCount;
760 final View[] children = mChildren;
761
762 for (int i = 0; i < count; i++) {
763 final View child = children[i];
764 if (child.hasFocusable()) {
765 return true;
766 }
767 }
768 }
769
770 return false;
771 }
772
773 /**
774 * {@inheritDoc}
775 */
776 @Override
777 public void addFocusables(ArrayList<View> views, int direction) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700778 addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
779 }
780
781 /**
782 * {@inheritDoc}
783 */
784 @Override
785 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 final int focusableCount = views.size();
787
788 final int descendantFocusability = getDescendantFocusability();
789
790 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
791 final int count = mChildrenCount;
792 final View[] children = mChildren;
793
794 for (int i = 0; i < count; i++) {
795 final View child = children[i];
796 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700797 child.addFocusables(views, direction, focusableMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 }
799 }
800 }
801
802 // we add ourselves (if focusable) in all cases except for when we are
803 // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
804 // to avoid the focus search finding layouts when a more precise search
805 // among the focusable children would be more interesting.
806 if (
807 descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
808 // No focusable descendants
809 (focusableCount == views.size())) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700810 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 }
812 }
813
Svetoslav Ganov8643aa02011-04-20 12:12:33 -0700814 @Override
Svetoslav Ganovea515ae2011-09-14 18:15:32 -0700815 public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) {
816 super.findViewsWithText(outViews, text, flags);
Svetoslav Ganov8643aa02011-04-20 12:12:33 -0700817 final int childrenCount = mChildrenCount;
818 final View[] children = mChildren;
819 for (int i = 0; i < childrenCount; i++) {
820 View child = children[i];
Svetoslav Ganovea515ae2011-09-14 18:15:32 -0700821 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
822 && (child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
823 child.findViewsWithText(outViews, text, flags);
Svetoslav Ganov8643aa02011-04-20 12:12:33 -0700824 }
825 }
826 }
827
Svetoslav Ganov2cdedff2011-10-03 14:18:42 -0700828 @Override
829 View findViewByAccessibilityIdTraversal(int accessibilityId) {
830 View foundView = super.findViewByAccessibilityIdTraversal(accessibilityId);
831 if (foundView != null) {
832 return foundView;
833 }
834 final int childrenCount = mChildrenCount;
835 final View[] children = mChildren;
836 for (int i = 0; i < childrenCount; i++) {
837 View child = children[i];
838 foundView = child.findViewByAccessibilityIdTraversal(accessibilityId);
839 if (foundView != null) {
840 return foundView;
841 }
842 }
843 return null;
844 }
845
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846 /**
847 * {@inheritDoc}
848 */
849 @Override
850 public void dispatchWindowFocusChanged(boolean hasFocus) {
851 super.dispatchWindowFocusChanged(hasFocus);
852 final int count = mChildrenCount;
853 final View[] children = mChildren;
854 for (int i = 0; i < count; i++) {
855 children[i].dispatchWindowFocusChanged(hasFocus);
856 }
857 }
858
859 /**
860 * {@inheritDoc}
861 */
862 @Override
863 public void addTouchables(ArrayList<View> views) {
864 super.addTouchables(views);
865
866 final int count = mChildrenCount;
867 final View[] children = mChildren;
868
869 for (int i = 0; i < count; i++) {
870 final View child = children[i];
871 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
872 child.addTouchables(views);
873 }
874 }
875 }
Romain Guy43c9cdf2010-01-27 13:53:55 -0800876
877 /**
878 * {@inheritDoc}
879 */
880 @Override
881 public void dispatchDisplayHint(int hint) {
882 super.dispatchDisplayHint(hint);
883 final int count = mChildrenCount;
884 final View[] children = mChildren;
885 for (int i = 0; i < count; i++) {
886 children[i].dispatchDisplayHint(hint);
887 }
888 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889
890 /**
Chet Haase5e25c2c2010-09-16 11:15:56 -0700891 * @hide
892 * @param child
893 * @param visibility
894 */
Philip Milne350f0a62011-07-19 14:00:56 -0700895 protected void onChildVisibilityChanged(View child, int visibility) {
Chet Haase5e25c2c2010-09-16 11:15:56 -0700896 if (mTransition != null) {
897 if (visibility == VISIBLE) {
898 mTransition.showChild(this, child);
899 } else {
900 mTransition.hideChild(this, child);
901 }
902 if (visibility != VISIBLE) {
903 // Only track this on disappearing views - appearing views are already visible
904 // and don't need special handling during drawChild()
905 if (mVisibilityChangingChildren == null) {
906 mVisibilityChangingChildren = new ArrayList<View>();
907 }
908 mVisibilityChangingChildren.add(child);
909 if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
910 addDisappearingView(child);
911 }
912 }
913 }
Christopher Tate86cab1b2011-01-13 20:28:55 -0800914
915 // in all cases, for drags
916 if (mCurrentDrag != null) {
917 if (visibility == VISIBLE) {
918 notifyChildOfDrag(child);
919 }
920 }
Chet Haase5e25c2c2010-09-16 11:15:56 -0700921 }
922
923 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 * {@inheritDoc}
925 */
926 @Override
Adam Powell326d8082009-12-09 15:10:07 -0800927 protected void dispatchVisibilityChanged(View changedView, int visibility) {
928 super.dispatchVisibilityChanged(changedView, visibility);
929 final int count = mChildrenCount;
930 final View[] children = mChildren;
931 for (int i = 0; i < count; i++) {
932 children[i].dispatchVisibilityChanged(changedView, visibility);
933 }
934 }
935
936 /**
937 * {@inheritDoc}
938 */
939 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 public void dispatchWindowVisibilityChanged(int visibility) {
941 super.dispatchWindowVisibilityChanged(visibility);
942 final int count = mChildrenCount;
943 final View[] children = mChildren;
944 for (int i = 0; i < count; i++) {
945 children[i].dispatchWindowVisibilityChanged(visibility);
946 }
947 }
948
949 /**
950 * {@inheritDoc}
951 */
Dianne Hackborne36d6e22010-02-17 19:46:25 -0800952 @Override
953 public void dispatchConfigurationChanged(Configuration newConfig) {
954 super.dispatchConfigurationChanged(newConfig);
955 final int count = mChildrenCount;
956 final View[] children = mChildren;
957 for (int i = 0; i < count; i++) {
958 children[i].dispatchConfigurationChanged(newConfig);
959 }
960 }
961
962 /**
963 * {@inheritDoc}
964 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965 public void recomputeViewAttributes(View child) {
Joe Onorato664644d2011-01-23 17:53:23 -0800966 if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
967 ViewParent parent = mParent;
968 if (parent != null) parent.recomputeViewAttributes(this);
969 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 }
Romain Guy8506ab42009-06-11 17:35:47 -0700971
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 @Override
973 void dispatchCollectViewAttributes(int visibility) {
974 visibility |= mViewFlags&VISIBILITY_MASK;
975 super.dispatchCollectViewAttributes(visibility);
976 final int count = mChildrenCount;
977 final View[] children = mChildren;
978 for (int i = 0; i < count; i++) {
979 children[i].dispatchCollectViewAttributes(visibility);
980 }
981 }
982
983 /**
984 * {@inheritDoc}
985 */
986 public void bringChildToFront(View child) {
987 int index = indexOfChild(child);
988 if (index >= 0) {
989 removeFromArray(index);
990 addInArray(child, mChildrenCount);
991 child.mParent = this;
992 }
993 }
994
995 /**
996 * {@inheritDoc}
Christopher Tatea53146c2010-09-07 11:57:52 -0700997 *
998 * !!! TODO: write real docs
999 */
1000 @Override
1001 public boolean dispatchDragEvent(DragEvent event) {
1002 boolean retval = false;
1003 final float tx = event.mX;
1004 final float ty = event.mY;
1005
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07001006 ViewRootImpl root = getViewRootImpl();
Christopher Tatea53146c2010-09-07 11:57:52 -07001007
1008 // Dispatch down the view hierarchy
1009 switch (event.mAction) {
1010 case DragEvent.ACTION_DRAG_STARTED: {
1011 // clear state to recalculate which views we drag over
Chris Tate9d1ab882010-11-02 15:55:39 -07001012 mCurrentDragView = null;
Christopher Tatea53146c2010-09-07 11:57:52 -07001013
Christopher Tate86cab1b2011-01-13 20:28:55 -08001014 // Set up our tracking of drag-started notifications
1015 mCurrentDrag = DragEvent.obtain(event);
1016 if (mDragNotifiedChildren == null) {
1017 mDragNotifiedChildren = new HashSet<View>();
1018 } else {
1019 mDragNotifiedChildren.clear();
1020 }
1021
Christopher Tatea53146c2010-09-07 11:57:52 -07001022 // Now dispatch down to our children, caching the responses
1023 mChildAcceptsDrag = false;
1024 final int count = mChildrenCount;
1025 final View[] children = mChildren;
1026 for (int i = 0; i < count; i++) {
Christopher Tate2c095f32010-10-04 14:13:40 -07001027 final View child = children[i];
Christopher Tate3d4bf172011-03-28 16:16:46 -07001028 child.mPrivateFlags2 &= ~View.DRAG_MASK;
Christopher Tate2c095f32010-10-04 14:13:40 -07001029 if (child.getVisibility() == VISIBLE) {
Christopher Tate86cab1b2011-01-13 20:28:55 -08001030 final boolean handled = notifyChildOfDrag(children[i]);
Christopher Tate2c095f32010-10-04 14:13:40 -07001031 if (handled) {
1032 mChildAcceptsDrag = true;
1033 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001034 }
1035 }
1036
1037 // Return HANDLED if one of our children can accept the drag
1038 if (mChildAcceptsDrag) {
1039 retval = true;
1040 }
1041 } break;
1042
1043 case DragEvent.ACTION_DRAG_ENDED: {
Christopher Tate86cab1b2011-01-13 20:28:55 -08001044 // Release the bookkeeping now that the drag lifecycle has ended
Christopher Tate1fc014f2011-01-19 12:56:26 -08001045 if (mDragNotifiedChildren != null) {
1046 for (View child : mDragNotifiedChildren) {
1047 // If a child was notified about an ongoing drag, it's told that it's over
1048 child.dispatchDragEvent(event);
Christopher Tate3d4bf172011-03-28 16:16:46 -07001049 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1050 child.refreshDrawableState();
Christopher Tate1fc014f2011-01-19 12:56:26 -08001051 }
1052
1053 mDragNotifiedChildren.clear();
1054 mCurrentDrag.recycle();
1055 mCurrentDrag = null;
1056 }
Christopher Tate86cab1b2011-01-13 20:28:55 -08001057
Christopher Tatea53146c2010-09-07 11:57:52 -07001058 // We consider drag-ended to have been handled if one of our children
1059 // had offered to handle the drag.
1060 if (mChildAcceptsDrag) {
1061 retval = true;
1062 }
1063 } break;
1064
1065 case DragEvent.ACTION_DRAG_LOCATION: {
1066 // Find the [possibly new] drag target
1067 final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
1068
1069 // If we've changed apparent drag target, tell the view root which view
Chris Tate9d1ab882010-11-02 15:55:39 -07001070 // we're over now [for purposes of the eventual drag-recipient-changed
1071 // notifications to the framework] and tell the new target that the drag
1072 // has entered its bounds. The root will see setDragFocus() calls all
1073 // the way down to the final leaf view that is handling the LOCATION event
1074 // before reporting the new potential recipient to the framework.
Christopher Tatea53146c2010-09-07 11:57:52 -07001075 if (mCurrentDragView != target) {
Chris Tate9d1ab882010-11-02 15:55:39 -07001076 root.setDragFocus(target);
1077
1078 final int action = event.mAction;
1079 // If we've dragged off of a child view, send it the EXITED message
1080 if (mCurrentDragView != null) {
Christopher Tate3d4bf172011-03-28 16:16:46 -07001081 final View view = mCurrentDragView;
Chris Tate9d1ab882010-11-02 15:55:39 -07001082 event.mAction = DragEvent.ACTION_DRAG_EXITED;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001083 view.dispatchDragEvent(event);
1084 view.mPrivateFlags2 &= ~View.DRAG_HOVERED;
1085 view.refreshDrawableState();
Chris Tate9d1ab882010-11-02 15:55:39 -07001086 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001087 mCurrentDragView = target;
Chris Tate9d1ab882010-11-02 15:55:39 -07001088
1089 // If we've dragged over a new child view, send it the ENTERED message
1090 if (target != null) {
1091 event.mAction = DragEvent.ACTION_DRAG_ENTERED;
1092 target.dispatchDragEvent(event);
Christopher Tate3d4bf172011-03-28 16:16:46 -07001093 target.mPrivateFlags2 |= View.DRAG_HOVERED;
1094 target.refreshDrawableState();
Chris Tate9d1ab882010-11-02 15:55:39 -07001095 }
1096 event.mAction = action; // restore the event's original state
Christopher Tatea53146c2010-09-07 11:57:52 -07001097 }
Christopher Tate2c095f32010-10-04 14:13:40 -07001098
Christopher Tatea53146c2010-09-07 11:57:52 -07001099 // Dispatch the actual drag location notice, localized into its coordinates
1100 if (target != null) {
1101 event.mX = mLocalPoint.x;
1102 event.mY = mLocalPoint.y;
1103
1104 retval = target.dispatchDragEvent(event);
1105
1106 event.mX = tx;
1107 event.mY = ty;
1108 }
1109 } break;
1110
Chris Tate9d1ab882010-11-02 15:55:39 -07001111 /* Entered / exited dispatch
1112 *
1113 * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is
1114 * that we're about to get the corresponding LOCATION event, which we will use to
1115 * determine which of our children is the new target; at that point we will
1116 * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
1117 *
1118 * DRAG_EXITED *is* dispatched all the way down immediately: once we know the
1119 * drag has left this ViewGroup, we know by definition that every contained subview
1120 * is also no longer under the drag point.
1121 */
1122
1123 case DragEvent.ACTION_DRAG_EXITED: {
1124 if (mCurrentDragView != null) {
Christopher Tate3d4bf172011-03-28 16:16:46 -07001125 final View view = mCurrentDragView;
1126 view.dispatchDragEvent(event);
1127 view.mPrivateFlags2 &= ~View.DRAG_HOVERED;
1128 view.refreshDrawableState();
1129
Chris Tate9d1ab882010-11-02 15:55:39 -07001130 mCurrentDragView = null;
1131 }
1132 } break;
1133
Christopher Tatea53146c2010-09-07 11:57:52 -07001134 case DragEvent.ACTION_DROP: {
Christopher Tate2c095f32010-10-04 14:13:40 -07001135 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
Christopher Tatea53146c2010-09-07 11:57:52 -07001136 View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
1137 if (target != null) {
Christopher Tate5ada6cb2010-10-05 14:15:29 -07001138 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target);
Christopher Tatea53146c2010-09-07 11:57:52 -07001139 event.mX = mLocalPoint.x;
1140 event.mY = mLocalPoint.y;
1141 retval = target.dispatchDragEvent(event);
1142 event.mX = tx;
1143 event.mY = ty;
Christopher Tate5ada6cb2010-10-05 14:15:29 -07001144 } else {
1145 if (ViewDebug.DEBUG_DRAG) {
1146 Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view");
1147 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001148 }
1149 } break;
1150 }
1151
1152 // If none of our children could handle the event, try here
1153 if (!retval) {
Chris Tate32affef2010-10-18 15:29:21 -07001154 // Call up to the View implementation that dispatches to installed listeners
1155 retval = super.dispatchDragEvent(event);
Christopher Tatea53146c2010-09-07 11:57:52 -07001156 }
1157 return retval;
1158 }
1159
1160 // Find the frontmost child view that lies under the given point, and calculate
1161 // the position within its own local coordinate system.
1162 View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001163 final int count = mChildrenCount;
1164 final View[] children = mChildren;
1165 for (int i = count - 1; i >= 0; i--) {
1166 final View child = children[i];
Christopher Tate3d4bf172011-03-28 16:16:46 -07001167 if (!child.canAcceptDrag()) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001168 continue;
1169 }
1170
Christopher Tate2c095f32010-10-04 14:13:40 -07001171 if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001172 return child;
1173 }
1174 }
1175 return null;
1176 }
1177
Christopher Tate86cab1b2011-01-13 20:28:55 -08001178 boolean notifyChildOfDrag(View child) {
1179 if (ViewDebug.DEBUG_DRAG) {
1180 Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
1181 }
1182
Christopher Tate3d4bf172011-03-28 16:16:46 -07001183 boolean canAccept = false;
Christopher Tate86cab1b2011-01-13 20:28:55 -08001184 if (! mDragNotifiedChildren.contains(child)) {
1185 mDragNotifiedChildren.add(child);
Christopher Tate3d4bf172011-03-28 16:16:46 -07001186 canAccept = child.dispatchDragEvent(mCurrentDrag);
1187 if (canAccept && !child.canAcceptDrag()) {
1188 child.mPrivateFlags2 |= View.DRAG_CAN_ACCEPT;
1189 child.refreshDrawableState();
1190 }
Christopher Tate86cab1b2011-01-13 20:28:55 -08001191 }
Christopher Tate3d4bf172011-03-28 16:16:46 -07001192 return canAccept;
Christopher Tate86cab1b2011-01-13 20:28:55 -08001193 }
1194
Joe Onorato664644d2011-01-23 17:53:23 -08001195 @Override
1196 public void dispatchSystemUiVisibilityChanged(int visible) {
1197 super.dispatchSystemUiVisibilityChanged(visible);
1198
1199 final int count = mChildrenCount;
1200 final View[] children = mChildren;
1201 for (int i=0; i <count; i++) {
1202 final View child = children[i];
1203 child.dispatchSystemUiVisibilityChanged(visible);
1204 }
1205 }
1206
Dianne Hackborn9a230e02011-10-06 11:51:27 -07001207 @Override
1208 void updateLocalSystemUiVisibility(int localValue, int localChanges) {
1209 super.updateLocalSystemUiVisibility(localValue, localChanges);
1210
1211 final int count = mChildrenCount;
1212 final View[] children = mChildren;
1213 for (int i=0; i <count; i++) {
1214 final View child = children[i];
1215 child.updateLocalSystemUiVisibility(localValue, localChanges);
1216 }
1217 }
1218
Christopher Tatea53146c2010-09-07 11:57:52 -07001219 /**
1220 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001221 */
1222 @Override
1223 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1224 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1225 return super.dispatchKeyEventPreIme(event);
1226 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1227 return mFocused.dispatchKeyEventPreIme(event);
1228 }
1229 return false;
1230 }
1231
1232 /**
1233 * {@inheritDoc}
1234 */
1235 @Override
1236 public boolean dispatchKeyEvent(KeyEvent event) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001237 if (mInputEventConsistencyVerifier != null) {
1238 mInputEventConsistencyVerifier.onKeyEvent(event, 1);
1239 }
1240
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001242 if (super.dispatchKeyEvent(event)) {
1243 return true;
1244 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001246 if (mFocused.dispatchKeyEvent(event)) {
1247 return true;
1248 }
1249 }
1250
1251 if (mInputEventConsistencyVerifier != null) {
1252 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253 }
1254 return false;
1255 }
1256
1257 /**
1258 * {@inheritDoc}
1259 */
1260 @Override
1261 public boolean dispatchKeyShortcutEvent(KeyEvent event) {
1262 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1263 return super.dispatchKeyShortcutEvent(event);
1264 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1265 return mFocused.dispatchKeyShortcutEvent(event);
1266 }
1267 return false;
1268 }
1269
1270 /**
1271 * {@inheritDoc}
1272 */
1273 @Override
1274 public boolean dispatchTrackballEvent(MotionEvent event) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001275 if (mInputEventConsistencyVerifier != null) {
1276 mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
1277 }
1278
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001280 if (super.dispatchTrackballEvent(event)) {
1281 return true;
1282 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001284 if (mFocused.dispatchTrackballEvent(event)) {
1285 return true;
1286 }
1287 }
1288
1289 if (mInputEventConsistencyVerifier != null) {
1290 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001291 }
1292 return false;
1293 }
1294
Jeff Brown10b62902011-06-20 16:40:37 -07001295 /**
1296 * {@inheritDoc}
1297 */
Romain Guya9489272011-06-22 20:58:11 -07001298 @SuppressWarnings({"ConstantConditions"})
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299 @Override
Jeff Browna032cc02011-03-07 16:56:21 -08001300 protected boolean dispatchHoverEvent(MotionEvent event) {
Jeff Browna032cc02011-03-07 16:56:21 -08001301 final int action = event.getAction();
Jeff Browna032cc02011-03-07 16:56:21 -08001302
Jeff Brown10b62902011-06-20 16:40:37 -07001303 // First check whether the view group wants to intercept the hover event.
1304 final boolean interceptHover = onInterceptHoverEvent(event);
1305 event.setAction(action); // restore action in case it was changed
1306
Jeff Brown87b7f802011-06-21 18:35:45 -07001307 MotionEvent eventNoHistory = event;
1308 boolean handled = false;
1309
1310 // Send events to the hovered children and build a new list of hover targets until
1311 // one is found that handles the event.
1312 HoverTarget firstOldHoverTarget = mFirstHoverTarget;
1313 mFirstHoverTarget = null;
Jeff Brown10b62902011-06-20 16:40:37 -07001314 if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) {
Jeff Browna032cc02011-03-07 16:56:21 -08001315 final float x = event.getX();
1316 final float y = event.getY();
Jeff Brown33bbfd22011-02-24 20:55:35 -08001317 final int childrenCount = mChildrenCount;
1318 if (childrenCount != 0) {
1319 final View[] children = mChildren;
Jeff Brown87b7f802011-06-21 18:35:45 -07001320 HoverTarget lastHoverTarget = null;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001321 for (int i = childrenCount - 1; i >= 0; i--) {
1322 final View child = children[i];
Jeff Brown87b7f802011-06-21 18:35:45 -07001323 if (!canViewReceivePointerEvents(child)
1324 || !isTransformedTouchPointInView(x, y, child, null)) {
1325 continue;
1326 }
1327
1328 // Obtain a hover target for this child. Dequeue it from the
1329 // old hover target list if the child was previously hovered.
1330 HoverTarget hoverTarget = firstOldHoverTarget;
1331 final boolean wasHovered;
1332 for (HoverTarget predecessor = null; ;) {
1333 if (hoverTarget == null) {
1334 hoverTarget = HoverTarget.obtain(child);
1335 wasHovered = false;
1336 break;
1337 }
1338
1339 if (hoverTarget.child == child) {
1340 if (predecessor != null) {
1341 predecessor.next = hoverTarget.next;
1342 } else {
1343 firstOldHoverTarget = hoverTarget.next;
1344 }
1345 hoverTarget.next = null;
1346 wasHovered = true;
1347 break;
1348 }
1349
1350 predecessor = hoverTarget;
1351 hoverTarget = hoverTarget.next;
1352 }
1353
1354 // Enqueue the hover target onto the new hover target list.
1355 if (lastHoverTarget != null) {
1356 lastHoverTarget.next = hoverTarget;
1357 } else {
1358 lastHoverTarget = hoverTarget;
1359 mFirstHoverTarget = hoverTarget;
1360 }
1361
1362 // Dispatch the event to the child.
1363 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1364 if (!wasHovered) {
1365 // Send the enter as is.
1366 handled |= dispatchTransformedGenericPointerEvent(
1367 event, child); // enter
1368 }
1369 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1370 if (!wasHovered) {
1371 // Synthesize an enter from a move.
1372 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1373 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1374 handled |= dispatchTransformedGenericPointerEvent(
1375 eventNoHistory, child); // enter
1376 eventNoHistory.setAction(action);
1377
1378 handled |= dispatchTransformedGenericPointerEvent(
1379 eventNoHistory, child); // move
1380 } else {
1381 // Send the move as is.
1382 handled |= dispatchTransformedGenericPointerEvent(event, child);
1383 }
1384 }
1385 if (handled) {
Jeff Brown10b62902011-06-20 16:40:37 -07001386 break;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001387 }
Jeff Brown10b62902011-06-20 16:40:37 -07001388 }
1389 }
1390 }
Jeff Brown33bbfd22011-02-24 20:55:35 -08001391
Jeff Brown87b7f802011-06-21 18:35:45 -07001392 // Send exit events to all previously hovered children that are no longer hovered.
1393 while (firstOldHoverTarget != null) {
1394 final View child = firstOldHoverTarget.child;
Jeff Brown10b62902011-06-20 16:40:37 -07001395
Jeff Brown87b7f802011-06-21 18:35:45 -07001396 // Exit the old hovered child.
1397 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1398 // Send the exit as is.
1399 handled |= dispatchTransformedGenericPointerEvent(
1400 event, child); // exit
1401 } else {
1402 // Synthesize an exit from a move or enter.
1403 // Ignore the result because hover focus has moved to a different view.
1404 if (action == MotionEvent.ACTION_HOVER_MOVE) {
Jeff Brown10b62902011-06-20 16:40:37 -07001405 dispatchTransformedGenericPointerEvent(
Jeff Brown87b7f802011-06-21 18:35:45 -07001406 event, child); // move
Jeff Brown10b62902011-06-20 16:40:37 -07001407 }
Jeff Brown87b7f802011-06-21 18:35:45 -07001408 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1409 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1410 dispatchTransformedGenericPointerEvent(
1411 eventNoHistory, child); // exit
1412 eventNoHistory.setAction(action);
Jeff Brown10b62902011-06-20 16:40:37 -07001413 }
1414
Jeff Brown87b7f802011-06-21 18:35:45 -07001415 final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next;
1416 firstOldHoverTarget.recycle();
1417 firstOldHoverTarget = nextOldHoverTarget;
Jeff Brown10b62902011-06-20 16:40:37 -07001418 }
1419
Jeff Brown87b7f802011-06-21 18:35:45 -07001420 // Send events to the view group itself if no children have handled it.
Jeff Brown10b62902011-06-20 16:40:37 -07001421 boolean newHoveredSelf = !handled;
1422 if (newHoveredSelf == mHoveredSelf) {
1423 if (newHoveredSelf) {
1424 // Send event to the view group as before.
1425 handled |= super.dispatchHoverEvent(event);
1426 }
1427 } else {
1428 if (mHoveredSelf) {
1429 // Exit the view group.
1430 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1431 // Send the exit as is.
1432 handled |= super.dispatchHoverEvent(event); // exit
1433 } else {
1434 // Synthesize an exit from a move or enter.
1435 // Ignore the result because hover focus is moving to a different view.
1436 if (action == MotionEvent.ACTION_HOVER_MOVE) {
1437 super.dispatchHoverEvent(event); // move
1438 }
1439 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1440 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1441 super.dispatchHoverEvent(eventNoHistory); // exit
1442 eventNoHistory.setAction(action);
1443 }
1444 mHoveredSelf = false;
1445 }
1446
1447 if (newHoveredSelf) {
1448 // Enter the view group.
1449 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1450 // Send the enter as is.
1451 handled |= super.dispatchHoverEvent(event); // enter
1452 mHoveredSelf = true;
1453 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1454 // Synthesize an enter from a move.
1455 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1456 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1457 handled |= super.dispatchHoverEvent(eventNoHistory); // enter
1458 eventNoHistory.setAction(action);
1459
1460 handled |= super.dispatchHoverEvent(eventNoHistory); // move
1461 mHoveredSelf = true;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001462 }
1463 }
Jeff Brown33bbfd22011-02-24 20:55:35 -08001464 }
1465
Jeff Browna032cc02011-03-07 16:56:21 -08001466 // Recycle the copy of the event that we made.
1467 if (eventNoHistory != event) {
1468 eventNoHistory.recycle();
1469 }
1470
Jeff Browna032cc02011-03-07 16:56:21 -08001471 // Done.
1472 return handled;
1473 }
1474
Jeff Brown87b7f802011-06-21 18:35:45 -07001475 /** @hide */
1476 @Override
1477 protected boolean hasHoveredChild() {
1478 return mFirstHoverTarget != null;
1479 }
1480
Jeff Brown10b62902011-06-20 16:40:37 -07001481 /**
1482 * Implement this method to intercept hover events before they are handled
1483 * by child views.
1484 * <p>
1485 * This method is called before dispatching a hover event to a child of
1486 * the view group or to the view group's own {@link #onHoverEvent} to allow
1487 * the view group a chance to intercept the hover event.
1488 * This method can also be used to watch all pointer motions that occur within
1489 * the bounds of the view group even when the pointer is hovering over
1490 * a child of the view group rather than over the view group itself.
1491 * </p><p>
1492 * The view group can prevent its children from receiving hover events by
1493 * implementing this method and returning <code>true</code> to indicate
1494 * that it would like to intercept hover events. The view group must
1495 * continuously return <code>true</code> from {@link #onInterceptHoverEvent}
1496 * for as long as it wishes to continue intercepting hover events from
1497 * its children.
1498 * </p><p>
1499 * Interception preserves the invariant that at most one view can be
1500 * hovered at a time by transferring hover focus from the currently hovered
1501 * child to the view group or vice-versa as needed.
1502 * </p><p>
1503 * If this method returns <code>true</code> and a child is already hovered, then the
1504 * child view will first receive a hover exit event and then the view group
1505 * itself will receive a hover enter event in {@link #onHoverEvent}.
1506 * Likewise, if this method had previously returned <code>true</code> to intercept hover
1507 * events and instead returns <code>false</code> while the pointer is hovering
1508 * within the bounds of one of a child, then the view group will first receive a
1509 * hover exit event in {@link #onHoverEvent} and then the hovered child will
1510 * receive a hover enter event.
1511 * </p><p>
1512 * The default implementation always returns false.
1513 * </p>
1514 *
1515 * @param event The motion event that describes the hover.
1516 * @return True if the view group would like to intercept the hover event
1517 * and prevent its children from receiving it.
1518 */
1519 public boolean onInterceptHoverEvent(MotionEvent event) {
Svetoslav Ganov736c2752011-04-22 18:30:36 -07001520 return false;
1521 }
1522
Jeff Browna032cc02011-03-07 16:56:21 -08001523 private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
1524 if (event.getHistorySize() == 0) {
1525 return event;
1526 }
1527 return MotionEvent.obtainNoHistory(event);
1528 }
1529
Jeff Brown10b62902011-06-20 16:40:37 -07001530 /**
1531 * {@inheritDoc}
1532 */
Jeff Browna032cc02011-03-07 16:56:21 -08001533 @Override
1534 protected boolean dispatchGenericPointerEvent(MotionEvent event) {
1535 // Send the event to the child under the pointer.
1536 final int childrenCount = mChildrenCount;
1537 if (childrenCount != 0) {
1538 final View[] children = mChildren;
1539 final float x = event.getX();
1540 final float y = event.getY();
1541
1542 for (int i = childrenCount - 1; i >= 0; i--) {
1543 final View child = children[i];
1544 if (!canViewReceivePointerEvents(child)
1545 || !isTransformedTouchPointInView(x, y, child, null)) {
1546 continue;
1547 }
1548
1549 if (dispatchTransformedGenericPointerEvent(event, child)) {
1550 return true;
1551 }
1552 }
1553 }
1554
1555 // No child handled the event. Send it to this view group.
1556 return super.dispatchGenericPointerEvent(event);
1557 }
1558
Jeff Brown10b62902011-06-20 16:40:37 -07001559 /**
1560 * {@inheritDoc}
1561 */
Jeff Browna032cc02011-03-07 16:56:21 -08001562 @Override
1563 protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
Jeff Brown33bbfd22011-02-24 20:55:35 -08001564 // Send the event to the focused child or to this view group if it has focus.
Jeff Browncb1404e2011-01-15 18:14:15 -08001565 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
Jeff Browna032cc02011-03-07 16:56:21 -08001566 return super.dispatchGenericFocusedEvent(event);
Jeff Browncb1404e2011-01-15 18:14:15 -08001567 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1568 return mFocused.dispatchGenericMotionEvent(event);
1569 }
1570 return false;
1571 }
1572
1573 /**
Jeff Browna032cc02011-03-07 16:56:21 -08001574 * Dispatches a generic pointer event to a child, taking into account
1575 * transformations that apply to the child.
1576 *
1577 * @param event The event to send.
1578 * @param child The view to send the event to.
1579 * @return {@code true} if the child handled the event.
1580 */
1581 private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) {
1582 final float offsetX = mScrollX - child.mLeft;
1583 final float offsetY = mScrollY - child.mTop;
1584
1585 boolean handled;
1586 if (!child.hasIdentityMatrix()) {
1587 MotionEvent transformedEvent = MotionEvent.obtain(event);
1588 transformedEvent.offsetLocation(offsetX, offsetY);
1589 transformedEvent.transform(child.getInverseMatrix());
1590 handled = child.dispatchGenericMotionEvent(transformedEvent);
1591 transformedEvent.recycle();
1592 } else {
1593 event.offsetLocation(offsetX, offsetY);
1594 handled = child.dispatchGenericMotionEvent(event);
1595 event.offsetLocation(-offsetX, -offsetY);
1596 }
1597 return handled;
1598 }
1599
1600 /**
Jeff Browncb1404e2011-01-15 18:14:15 -08001601 * {@inheritDoc}
1602 */
1603 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001604 public boolean dispatchTouchEvent(MotionEvent ev) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001605 if (mInputEventConsistencyVerifier != null) {
1606 mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
1607 }
1608
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001609 boolean handled = false;
1610 if (onFilterTouchEventForSecurity(ev)) {
1611 final int action = ev.getAction();
1612 final int actionMasked = action & MotionEvent.ACTION_MASK;
Jeff Brown85a31762010-09-01 17:01:00 -07001613
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001614 // Handle an initial down.
1615 if (actionMasked == MotionEvent.ACTION_DOWN) {
1616 // Throw away all previous state when starting a new touch gesture.
1617 // The framework may have dropped the up or cancel event for the previous gesture
1618 // due to an app switch, ANR, or some other state change.
1619 cancelAndClearTouchTargets(ev);
1620 resetTouchState();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001621 }
Adam Powellb08013c2010-09-16 16:28:11 -07001622
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001623 // Check for interception.
1624 final boolean intercepted;
Jeff Brown20e987b2010-08-23 12:01:02 -07001625 if (actionMasked == MotionEvent.ACTION_DOWN
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001626 || mFirstTouchTarget != null) {
1627 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
1628 if (!disallowIntercept) {
1629 intercepted = onInterceptTouchEvent(ev);
1630 ev.setAction(action); // restore action in case it was changed
1631 } else {
1632 intercepted = false;
1633 }
1634 } else {
1635 // There are no touch targets and this action is not an initial down
1636 // so this view group continues to intercept touches.
1637 intercepted = true;
1638 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001639
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001640 // Check for cancelation.
1641 final boolean canceled = resetCancelNextUpFlag(this)
1642 || actionMasked == MotionEvent.ACTION_CANCEL;
Jeff Brown20e987b2010-08-23 12:01:02 -07001643
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001644 // Update list of touch targets for pointer down, if needed.
1645 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
1646 TouchTarget newTouchTarget = null;
1647 boolean alreadyDispatchedToNewTouchTarget = false;
1648 if (!canceled && !intercepted) {
1649 if (actionMasked == MotionEvent.ACTION_DOWN
1650 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
1651 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
1652 final int actionIndex = ev.getActionIndex(); // always 0 for down
1653 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
1654 : TouchTarget.ALL_POINTER_IDS;
Jeff Brown20e987b2010-08-23 12:01:02 -07001655
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001656 // Clean up earlier touch targets for this pointer id in case they
1657 // have become out of sync.
1658 removePointersFromTouchTargets(idBitsToAssign);
1659
1660 final int childrenCount = mChildrenCount;
1661 if (childrenCount != 0) {
1662 // Find a child that can receive the event.
1663 // Scan children from front to back.
1664 final View[] children = mChildren;
1665 final float x = ev.getX(actionIndex);
1666 final float y = ev.getY(actionIndex);
1667
1668 for (int i = childrenCount - 1; i >= 0; i--) {
1669 final View child = children[i];
1670 if (!canViewReceivePointerEvents(child)
1671 || !isTransformedTouchPointInView(x, y, child, null)) {
1672 continue;
1673 }
1674
1675 newTouchTarget = getTouchTarget(child);
1676 if (newTouchTarget != null) {
1677 // Child is already receiving touch within its bounds.
1678 // Give it the new pointer in addition to the ones it is handling.
1679 newTouchTarget.pointerIdBits |= idBitsToAssign;
1680 break;
1681 }
1682
1683 resetCancelNextUpFlag(child);
1684 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
1685 // Child wants to receive touch within its bounds.
1686 mLastTouchDownTime = ev.getDownTime();
1687 mLastTouchDownIndex = i;
1688 mLastTouchDownX = ev.getX();
1689 mLastTouchDownY = ev.getY();
1690 newTouchTarget = addTouchTarget(child, idBitsToAssign);
1691 alreadyDispatchedToNewTouchTarget = true;
1692 break;
1693 }
1694 }
1695 }
1696
1697 if (newTouchTarget == null && mFirstTouchTarget != null) {
1698 // Did not find a child to receive the event.
1699 // Assign the pointer to the least recently added target.
1700 newTouchTarget = mFirstTouchTarget;
1701 while (newTouchTarget.next != null) {
1702 newTouchTarget = newTouchTarget.next;
1703 }
1704 newTouchTarget.pointerIdBits |= idBitsToAssign;
1705 }
1706 }
1707 }
1708
1709 // Dispatch to touch targets.
1710 if (mFirstTouchTarget == null) {
1711 // No touch targets so treat this as an ordinary view.
1712 handled = dispatchTransformedTouchEvent(ev, canceled, null,
1713 TouchTarget.ALL_POINTER_IDS);
1714 } else {
1715 // Dispatch to touch targets, excluding the new touch target if we already
1716 // dispatched to it. Cancel touch targets if necessary.
1717 TouchTarget predecessor = null;
1718 TouchTarget target = mFirstTouchTarget;
1719 while (target != null) {
1720 final TouchTarget next = target.next;
1721 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
1722 handled = true;
1723 } else {
1724 final boolean cancelChild = resetCancelNextUpFlag(target.child)
1725 || intercepted;
1726 if (dispatchTransformedTouchEvent(ev, cancelChild,
1727 target.child, target.pointerIdBits)) {
1728 handled = true;
1729 }
1730 if (cancelChild) {
1731 if (predecessor == null) {
1732 mFirstTouchTarget = next;
1733 } else {
1734 predecessor.next = next;
1735 }
1736 target.recycle();
1737 target = next;
Jeff Brown20e987b2010-08-23 12:01:02 -07001738 continue;
1739 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001740 }
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001741 predecessor = target;
1742 target = next;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743 }
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001744 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001745
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001746 // Update list of touch targets for pointer up or cancel, if needed.
1747 if (canceled
1748 || actionMasked == MotionEvent.ACTION_UP
1749 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
1750 resetTouchState();
1751 } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
1752 final int actionIndex = ev.getActionIndex();
1753 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
1754 removePointersFromTouchTargets(idBitsToRemove);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001755 }
1756 }
Romain Guy8506ab42009-06-11 17:35:47 -07001757
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001758 if (!handled && mInputEventConsistencyVerifier != null) {
1759 mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
Jeff Brown20e987b2010-08-23 12:01:02 -07001760 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001761 return handled;
1762 }
1763
Romain Guy469b1db2010-10-05 11:49:57 -07001764 /**
1765 * Resets all touch state in preparation for a new cycle.
1766 */
1767 private void resetTouchState() {
Jeff Brown20e987b2010-08-23 12:01:02 -07001768 clearTouchTargets();
1769 resetCancelNextUpFlag(this);
1770 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
1771 }
1772
Romain Guy469b1db2010-10-05 11:49:57 -07001773 /**
1774 * Resets the cancel next up flag.
1775 * Returns true if the flag was previously set.
1776 */
1777 private boolean resetCancelNextUpFlag(View view) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001778 if ((view.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
1779 view.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
1780 return true;
Adam Cohen9b073942010-08-19 16:49:52 -07001781 }
1782 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001783 }
1784
Romain Guy469b1db2010-10-05 11:49:57 -07001785 /**
1786 * Clears all touch targets.
1787 */
1788 private void clearTouchTargets() {
Jeff Brown20e987b2010-08-23 12:01:02 -07001789 TouchTarget target = mFirstTouchTarget;
1790 if (target != null) {
1791 do {
1792 TouchTarget next = target.next;
1793 target.recycle();
1794 target = next;
1795 } while (target != null);
1796 mFirstTouchTarget = null;
1797 }
1798 }
1799
Romain Guy469b1db2010-10-05 11:49:57 -07001800 /**
1801 * Cancels and clears all touch targets.
1802 */
1803 private void cancelAndClearTouchTargets(MotionEvent event) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001804 if (mFirstTouchTarget != null) {
1805 boolean syntheticEvent = false;
1806 if (event == null) {
1807 final long now = SystemClock.uptimeMillis();
1808 event = MotionEvent.obtain(now, now,
1809 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
Jeff Brown2fdbc5a2011-06-30 12:25:54 -07001810 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
Jeff Brown20e987b2010-08-23 12:01:02 -07001811 syntheticEvent = true;
1812 }
1813
1814 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
1815 resetCancelNextUpFlag(target.child);
1816 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
1817 }
1818 clearTouchTargets();
1819
1820 if (syntheticEvent) {
1821 event.recycle();
1822 }
1823 }
1824 }
1825
Romain Guy469b1db2010-10-05 11:49:57 -07001826 /**
1827 * Gets the touch target for specified child view.
1828 * Returns null if not found.
1829 */
1830 private TouchTarget getTouchTarget(View child) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001831 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
1832 if (target.child == child) {
1833 return target;
1834 }
1835 }
1836 return null;
1837 }
1838
Romain Guy469b1db2010-10-05 11:49:57 -07001839 /**
1840 * Adds a touch target for specified child to the beginning of the list.
1841 * Assumes the target child is not already present.
1842 */
1843 private TouchTarget addTouchTarget(View child, int pointerIdBits) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001844 TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
1845 target.next = mFirstTouchTarget;
1846 mFirstTouchTarget = target;
1847 return target;
1848 }
1849
Romain Guy469b1db2010-10-05 11:49:57 -07001850 /**
1851 * Removes the pointer ids from consideration.
1852 */
1853 private void removePointersFromTouchTargets(int pointerIdBits) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001854 TouchTarget predecessor = null;
1855 TouchTarget target = mFirstTouchTarget;
1856 while (target != null) {
1857 final TouchTarget next = target.next;
1858 if ((target.pointerIdBits & pointerIdBits) != 0) {
1859 target.pointerIdBits &= ~pointerIdBits;
1860 if (target.pointerIdBits == 0) {
1861 if (predecessor == null) {
1862 mFirstTouchTarget = next;
1863 } else {
1864 predecessor.next = next;
1865 }
1866 target.recycle();
1867 target = next;
1868 continue;
1869 }
1870 }
1871 predecessor = target;
1872 target = next;
1873 }
1874 }
1875
Romain Guy469b1db2010-10-05 11:49:57 -07001876 /**
Jeff Browna032cc02011-03-07 16:56:21 -08001877 * Returns true if a child view can receive pointer events.
1878 * @hide
1879 */
1880 private static boolean canViewReceivePointerEvents(View child) {
1881 return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
1882 || child.getAnimation() != null;
1883 }
1884
1885 /**
Romain Guy469b1db2010-10-05 11:49:57 -07001886 * Returns true if a child view contains the specified point when transformed
Jeff Brown20e987b2010-08-23 12:01:02 -07001887 * into its coordinate space.
Romain Guy469b1db2010-10-05 11:49:57 -07001888 * Child must not be null.
Adam Cohena32edd42010-10-26 10:35:01 -07001889 * @hide
Romain Guy469b1db2010-10-05 11:49:57 -07001890 */
Adam Cohena32edd42010-10-26 10:35:01 -07001891 protected boolean isTransformedTouchPointInView(float x, float y, View child,
Christopher Tate2c095f32010-10-04 14:13:40 -07001892 PointF outLocalPoint) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001893 float localX = x + mScrollX - child.mLeft;
1894 float localY = y + mScrollY - child.mTop;
1895 if (! child.hasIdentityMatrix() && mAttachInfo != null) {
Adam Powell2b342f02010-08-18 18:14:13 -07001896 final float[] localXY = mAttachInfo.mTmpTransformLocation;
1897 localXY[0] = localX;
1898 localXY[1] = localY;
1899 child.getInverseMatrix().mapPoints(localXY);
1900 localX = localXY[0];
1901 localY = localXY[1];
1902 }
Christopher Tate2c095f32010-10-04 14:13:40 -07001903 final boolean isInView = child.pointInView(localX, localY);
1904 if (isInView && outLocalPoint != null) {
1905 outLocalPoint.set(localX, localY);
1906 }
1907 return isInView;
Adam Powell2b342f02010-08-18 18:14:13 -07001908 }
1909
Romain Guy469b1db2010-10-05 11:49:57 -07001910 /**
1911 * Transforms a motion event into the coordinate space of a particular child view,
Jeff Brown20e987b2010-08-23 12:01:02 -07001912 * filters out irrelevant pointer ids, and overrides its action if necessary.
Romain Guy469b1db2010-10-05 11:49:57 -07001913 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
1914 */
1915 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
Jeff Brown20e987b2010-08-23 12:01:02 -07001916 View child, int desiredPointerIdBits) {
1917 final boolean handled;
Adam Powell2b342f02010-08-18 18:14:13 -07001918
Jeff Brown20e987b2010-08-23 12:01:02 -07001919 // Canceling motions is a special case. We don't need to perform any transformations
1920 // or filtering. The important part is the action, not the contents.
1921 final int oldAction = event.getAction();
1922 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
1923 event.setAction(MotionEvent.ACTION_CANCEL);
1924 if (child == null) {
1925 handled = super.dispatchTouchEvent(event);
1926 } else {
1927 handled = child.dispatchTouchEvent(event);
1928 }
1929 event.setAction(oldAction);
1930 return handled;
1931 }
Adam Powell2b342f02010-08-18 18:14:13 -07001932
Jeff Brown20e987b2010-08-23 12:01:02 -07001933 // Calculate the number of pointers to deliver.
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07001934 final int oldPointerIdBits = event.getPointerIdBits();
1935 final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
Adam Powell2b342f02010-08-18 18:14:13 -07001936
Jeff Brown20e987b2010-08-23 12:01:02 -07001937 // If for some reason we ended up in an inconsistent state where it looks like we
1938 // might produce a motion event with no pointers in it, then drop the event.
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07001939 if (newPointerIdBits == 0) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001940 return false;
1941 }
Adam Powell2b342f02010-08-18 18:14:13 -07001942
Jeff Brown20e987b2010-08-23 12:01:02 -07001943 // If the number of pointers is the same and we don't need to perform any fancy
1944 // irreversible transformations, then we can reuse the motion event for this
1945 // dispatch as long as we are careful to revert any changes we make.
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07001946 // Otherwise we need to make a copy.
1947 final MotionEvent transformedEvent;
1948 if (newPointerIdBits == oldPointerIdBits) {
1949 if (child == null || child.hasIdentityMatrix()) {
1950 if (child == null) {
1951 handled = super.dispatchTouchEvent(event);
1952 } else {
1953 final float offsetX = mScrollX - child.mLeft;
1954 final float offsetY = mScrollY - child.mTop;
1955 event.offsetLocation(offsetX, offsetY);
Adam Powell2b342f02010-08-18 18:14:13 -07001956
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07001957 handled = child.dispatchTouchEvent(event);
Jeff Brown20e987b2010-08-23 12:01:02 -07001958
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07001959 event.offsetLocation(-offsetX, -offsetY);
1960 }
1961 return handled;
Jeff Brown20e987b2010-08-23 12:01:02 -07001962 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001963 transformedEvent = MotionEvent.obtain(event);
1964 } else {
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07001965 transformedEvent = event.split(newPointerIdBits);
Adam Powell2b342f02010-08-18 18:14:13 -07001966 }
1967
Jeff Brown20e987b2010-08-23 12:01:02 -07001968 // Perform any necessary transformations and dispatch.
1969 if (child == null) {
1970 handled = super.dispatchTouchEvent(transformedEvent);
1971 } else {
1972 final float offsetX = mScrollX - child.mLeft;
1973 final float offsetY = mScrollY - child.mTop;
1974 transformedEvent.offsetLocation(offsetX, offsetY);
1975 if (! child.hasIdentityMatrix()) {
1976 transformedEvent.transform(child.getInverseMatrix());
Adam Powell2b342f02010-08-18 18:14:13 -07001977 }
1978
Jeff Brown20e987b2010-08-23 12:01:02 -07001979 handled = child.dispatchTouchEvent(transformedEvent);
Adam Powell2b342f02010-08-18 18:14:13 -07001980 }
1981
Jeff Brown20e987b2010-08-23 12:01:02 -07001982 // Done.
1983 transformedEvent.recycle();
Adam Powell2b342f02010-08-18 18:14:13 -07001984 return handled;
1985 }
1986
Romain Guy469b1db2010-10-05 11:49:57 -07001987 /**
Adam Powell2b342f02010-08-18 18:14:13 -07001988 * Enable or disable the splitting of MotionEvents to multiple children during touch event
Jeff Brown995e7742010-12-22 16:59:36 -08001989 * dispatch. This behavior is enabled by default for applications that target an
1990 * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
Adam Powell2b342f02010-08-18 18:14:13 -07001991 *
1992 * <p>When this option is enabled MotionEvents may be split and dispatched to different child
1993 * views depending on where each pointer initially went down. This allows for user interactions
1994 * such as scrolling two panes of content independently, chording of buttons, and performing
1995 * independent gestures on different pieces of content.
1996 *
1997 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
1998 * child views. <code>false</code> to only allow one child view to be the target of
1999 * any MotionEvent received by this ViewGroup.
2000 */
2001 public void setMotionEventSplittingEnabled(boolean split) {
2002 // TODO Applications really shouldn't change this setting mid-touch event,
2003 // but perhaps this should handle that case and send ACTION_CANCELs to any child views
2004 // with gestures in progress when this is changed.
2005 if (split) {
Adam Powell2b342f02010-08-18 18:14:13 -07002006 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
2007 } else {
2008 mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
Adam Powell2b342f02010-08-18 18:14:13 -07002009 }
2010 }
2011
2012 /**
Jeff Brown995e7742010-12-22 16:59:36 -08002013 * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
Adam Powell2b342f02010-08-18 18:14:13 -07002014 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2015 */
2016 public boolean isMotionEventSplittingEnabled() {
2017 return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
2018 }
2019
2020 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002021 * {@inheritDoc}
2022 */
2023 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
Romain Guy8506ab42009-06-11 17:35:47 -07002024
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002025 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
2026 // We're already in this state, assume our ancestors are too
2027 return;
2028 }
Romain Guy8506ab42009-06-11 17:35:47 -07002029
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002030 if (disallowIntercept) {
2031 mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
2032 } else {
2033 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2034 }
Romain Guy8506ab42009-06-11 17:35:47 -07002035
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002036 // Pass it up to our parent
2037 if (mParent != null) {
2038 mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
2039 }
2040 }
2041
2042 /**
2043 * Implement this method to intercept all touch screen motion events. This
2044 * allows you to watch events as they are dispatched to your children, and
2045 * take ownership of the current gesture at any point.
2046 *
2047 * <p>Using this function takes some care, as it has a fairly complicated
2048 * interaction with {@link View#onTouchEvent(MotionEvent)
2049 * View.onTouchEvent(MotionEvent)}, and using it requires implementing
2050 * that method as well as this one in the correct way. Events will be
2051 * received in the following order:
2052 *
2053 * <ol>
2054 * <li> You will receive the down event here.
2055 * <li> The down event will be handled either by a child of this view
2056 * group, or given to your own onTouchEvent() method to handle; this means
2057 * you should implement onTouchEvent() to return true, so you will
2058 * continue to see the rest of the gesture (instead of looking for
2059 * a parent view to handle it). Also, by returning true from
2060 * onTouchEvent(), you will not receive any following
2061 * events in onInterceptTouchEvent() and all touch processing must
2062 * happen in onTouchEvent() like normal.
2063 * <li> For as long as you return false from this function, each following
2064 * event (up to and including the final up) will be delivered first here
2065 * and then to the target's onTouchEvent().
2066 * <li> If you return true from here, you will not receive any
2067 * following events: the target view will receive the same event but
2068 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
2069 * events will be delivered to your onTouchEvent() method and no longer
2070 * appear here.
2071 * </ol>
2072 *
2073 * @param ev The motion event being dispatched down the hierarchy.
2074 * @return Return true to steal motion events from the children and have
2075 * them dispatched to this ViewGroup through onTouchEvent().
2076 * The current target will receive an ACTION_CANCEL event, and no further
2077 * messages will be delivered here.
2078 */
2079 public boolean onInterceptTouchEvent(MotionEvent ev) {
2080 return false;
2081 }
2082
2083 /**
2084 * {@inheritDoc}
2085 *
2086 * Looks for a view to give focus to respecting the setting specified by
2087 * {@link #getDescendantFocusability()}.
2088 *
2089 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
2090 * find focus within the children of this group when appropriate.
2091 *
2092 * @see #FOCUS_BEFORE_DESCENDANTS
2093 * @see #FOCUS_AFTER_DESCENDANTS
2094 * @see #FOCUS_BLOCK_DESCENDANTS
Romain Guy02739a82011-05-16 11:43:18 -07002095 * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002096 */
2097 @Override
2098 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
2099 if (DBG) {
2100 System.out.println(this + " ViewGroup.requestFocus direction="
2101 + direction);
2102 }
2103 int descendantFocusability = getDescendantFocusability();
2104
2105 switch (descendantFocusability) {
2106 case FOCUS_BLOCK_DESCENDANTS:
2107 return super.requestFocus(direction, previouslyFocusedRect);
2108 case FOCUS_BEFORE_DESCENDANTS: {
2109 final boolean took = super.requestFocus(direction, previouslyFocusedRect);
2110 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
2111 }
2112 case FOCUS_AFTER_DESCENDANTS: {
2113 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
2114 return took ? took : super.requestFocus(direction, previouslyFocusedRect);
2115 }
2116 default:
2117 throw new IllegalStateException("descendant focusability must be "
2118 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
2119 + "but is " + descendantFocusability);
2120 }
2121 }
2122
2123 /**
2124 * Look for a descendant to call {@link View#requestFocus} on.
2125 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
2126 * when it wants to request focus within its children. Override this to
2127 * customize how your {@link ViewGroup} requests focus within its children.
2128 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
2129 * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
2130 * to give a finer grained hint about where focus is coming from. May be null
2131 * if there is no hint.
2132 * @return Whether focus was taken.
2133 */
2134 @SuppressWarnings({"ConstantConditions"})
2135 protected boolean onRequestFocusInDescendants(int direction,
2136 Rect previouslyFocusedRect) {
2137 int index;
2138 int increment;
2139 int end;
2140 int count = mChildrenCount;
2141 if ((direction & FOCUS_FORWARD) != 0) {
2142 index = 0;
2143 increment = 1;
2144 end = count;
2145 } else {
2146 index = count - 1;
2147 increment = -1;
2148 end = -1;
2149 }
2150 final View[] children = mChildren;
2151 for (int i = index; i != end; i += increment) {
2152 View child = children[i];
2153 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2154 if (child.requestFocus(direction, previouslyFocusedRect)) {
2155 return true;
2156 }
2157 }
2158 }
2159 return false;
2160 }
Chet Haase5c13d892010-10-08 08:37:55 -07002161
Romain Guya440b002010-02-24 15:57:54 -08002162 /**
2163 * {@inheritDoc}
Chet Haase5c13d892010-10-08 08:37:55 -07002164 *
Romain Guydcc490f2010-02-24 17:59:35 -08002165 * @hide
Romain Guya440b002010-02-24 15:57:54 -08002166 */
2167 @Override
2168 public void dispatchStartTemporaryDetach() {
2169 super.dispatchStartTemporaryDetach();
2170 final int count = mChildrenCount;
2171 final View[] children = mChildren;
2172 for (int i = 0; i < count; i++) {
2173 children[i].dispatchStartTemporaryDetach();
2174 }
2175 }
Chet Haase5c13d892010-10-08 08:37:55 -07002176
Romain Guya440b002010-02-24 15:57:54 -08002177 /**
2178 * {@inheritDoc}
Chet Haase5c13d892010-10-08 08:37:55 -07002179 *
Romain Guydcc490f2010-02-24 17:59:35 -08002180 * @hide
Romain Guya440b002010-02-24 15:57:54 -08002181 */
2182 @Override
2183 public void dispatchFinishTemporaryDetach() {
2184 super.dispatchFinishTemporaryDetach();
2185 final int count = mChildrenCount;
2186 final View[] children = mChildren;
2187 for (int i = 0; i < count; i++) {
2188 children[i].dispatchFinishTemporaryDetach();
2189 }
2190 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002191
2192 /**
2193 * {@inheritDoc}
2194 */
2195 @Override
2196 void dispatchAttachedToWindow(AttachInfo info, int visibility) {
Adam Powell4b867882011-09-16 12:59:46 -07002197 mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002198 super.dispatchAttachedToWindow(info, visibility);
Adam Powell4b867882011-09-16 12:59:46 -07002199 mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
2200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002201 visibility |= mViewFlags & VISIBILITY_MASK;
Adam Powell4b867882011-09-16 12:59:46 -07002202
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002203 final int count = mChildrenCount;
2204 final View[] children = mChildren;
2205 for (int i = 0; i < count; i++) {
2206 children[i].dispatchAttachedToWindow(info, visibility);
2207 }
2208 }
2209
svetoslavganov75986cf2009-05-14 22:28:01 -07002210 @Override
Svetoslav Ganov031d9c12011-09-09 16:41:13 -07002211 boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
Svetoslav Ganovb84b94e2011-09-22 19:26:56 -07002212 boolean handled = super.dispatchPopulateAccessibilityEventInternal(event);
2213 if (handled) {
2214 return handled;
2215 }
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002216 // Let our children have a shot in populating the event.
svetoslavganov75986cf2009-05-14 22:28:01 -07002217 for (int i = 0, count = getChildCount(); i < count; i++) {
Svetoslav Ganov6179ea32011-06-28 01:12:41 -07002218 View child = getChildAt(i);
2219 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
Svetoslav Ganovb84b94e2011-09-22 19:26:56 -07002220 handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event);
Svetoslav Ganov6179ea32011-06-28 01:12:41 -07002221 if (handled) {
2222 return handled;
2223 }
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002224 }
svetoslavganov75986cf2009-05-14 22:28:01 -07002225 }
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002226 return false;
svetoslavganov75986cf2009-05-14 22:28:01 -07002227 }
2228
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07002229 @Override
Svetoslav Ganov031d9c12011-09-09 16:41:13 -07002230 void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
2231 super.onInitializeAccessibilityNodeInfoInternal(info);
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07002232 for (int i = 0, count = mChildrenCount; i < count; i++) {
2233 View child = mChildren[i];
Svetoslav Ganov57f3b566db2011-10-31 17:59:14 -07002234 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
2235 && (child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
Svetoslav Ganovea1da3d2011-06-15 17:16:02 -07002236 info.addChild(child);
2237 }
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07002238 }
2239 }
2240
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002241 /**
2242 * {@inheritDoc}
2243 */
2244 @Override
2245 void dispatchDetachedFromWindow() {
Jeff Brown20e987b2010-08-23 12:01:02 -07002246 // If we still have a touch target, we are still in the process of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002247 // dispatching motion events to a child; we need to get rid of that
2248 // child to avoid dispatching events to it after the window is torn
2249 // down. To make sure we keep the child in a consistent state, we
2250 // first send it an ACTION_CANCEL motion event.
Jeff Brown20e987b2010-08-23 12:01:02 -07002251 cancelAndClearTouchTargets(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002252
Chet Haase9c087442011-01-12 16:20:16 -08002253 // In case view is detached while transition is running
2254 mLayoutSuppressed = false;
2255
Christopher Tate86cab1b2011-01-13 20:28:55 -08002256 // Tear down our drag tracking
2257 mDragNotifiedChildren = null;
2258 if (mCurrentDrag != null) {
2259 mCurrentDrag.recycle();
2260 mCurrentDrag = null;
2261 }
2262
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002263 final int count = mChildrenCount;
2264 final View[] children = mChildren;
2265 for (int i = 0; i < count; i++) {
2266 children[i].dispatchDetachedFromWindow();
2267 }
2268 super.dispatchDetachedFromWindow();
2269 }
2270
2271 /**
2272 * {@inheritDoc}
2273 */
2274 @Override
2275 public void setPadding(int left, int top, int right, int bottom) {
2276 super.setPadding(left, top, right, bottom);
2277
Romain Guy13f35f32011-03-24 12:03:17 -07002278 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002279 mGroupFlags |= FLAG_PADDING_NOT_NULL;
2280 } else {
2281 mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
2282 }
2283 }
2284
2285 /**
2286 * {@inheritDoc}
2287 */
2288 @Override
2289 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
2290 super.dispatchSaveInstanceState(container);
2291 final int count = mChildrenCount;
2292 final View[] children = mChildren;
2293 for (int i = 0; i < count; i++) {
Dianne Hackbornb4bc78b2010-05-12 18:59:50 -07002294 View c = children[i];
2295 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2296 c.dispatchSaveInstanceState(container);
2297 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002298 }
2299 }
2300
2301 /**
Romain Guy9fc27812011-04-27 14:21:41 -07002302 * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()}
2303 * to only this view, not to its children. For use when overriding
2304 * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow
2305 * subclasses to freeze their own state but not the state of their children.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002306 *
2307 * @param container the container
2308 */
2309 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
2310 super.dispatchSaveInstanceState(container);
2311 }
2312
2313 /**
2314 * {@inheritDoc}
2315 */
2316 @Override
2317 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
2318 super.dispatchRestoreInstanceState(container);
2319 final int count = mChildrenCount;
2320 final View[] children = mChildren;
2321 for (int i = 0; i < count; i++) {
Dianne Hackbornb4bc78b2010-05-12 18:59:50 -07002322 View c = children[i];
2323 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2324 c.dispatchRestoreInstanceState(container);
2325 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002326 }
2327 }
2328
2329 /**
Romain Guy02739a82011-05-16 11:43:18 -07002330 * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)}
2331 * to only this view, not to its children. For use when overriding
2332 * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow
2333 * subclasses to thaw their own state but not the state of their children.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002334 *
2335 * @param container the container
2336 */
2337 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
2338 super.dispatchRestoreInstanceState(container);
2339 }
2340
2341 /**
2342 * Enables or disables the drawing cache for each child of this view group.
2343 *
2344 * @param enabled true to enable the cache, false to dispose of it
2345 */
2346 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
2347 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
2348 final View[] children = mChildren;
2349 final int count = mChildrenCount;
2350 for (int i = 0; i < count; i++) {
2351 children[i].setDrawingCacheEnabled(enabled);
2352 }
2353 }
2354 }
2355
2356 @Override
2357 protected void onAnimationStart() {
2358 super.onAnimationStart();
2359
2360 // When this ViewGroup's animation starts, build the cache for the children
2361 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2362 final int count = mChildrenCount;
2363 final View[] children = mChildren;
Romain Guy0d9275e2010-10-26 14:22:30 -07002364 final boolean buildCache = !isHardwareAccelerated();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002365
2366 for (int i = 0; i < count; i++) {
2367 final View child = children[i];
2368 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2369 child.setDrawingCacheEnabled(true);
Romain Guy0d9275e2010-10-26 14:22:30 -07002370 if (buildCache) {
2371 child.buildDrawingCache(true);
2372 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002373 }
2374 }
2375
2376 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2377 }
2378 }
2379
2380 @Override
2381 protected void onAnimationEnd() {
2382 super.onAnimationEnd();
2383
2384 // When this ViewGroup's animation ends, destroy the cache of the children
2385 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2386 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2387
2388 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2389 setChildrenDrawingCacheEnabled(false);
2390 }
2391 }
2392 }
2393
Romain Guy223ff5c2010-03-02 17:07:47 -08002394 @Override
2395 Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
Romain Guy65554f22010-03-22 18:58:21 -07002396 int count = mChildrenCount;
2397 int[] visibilities = null;
2398
Romain Guy223ff5c2010-03-02 17:07:47 -08002399 if (skipChildren) {
Romain Guy65554f22010-03-22 18:58:21 -07002400 visibilities = new int[count];
2401 for (int i = 0; i < count; i++) {
2402 View child = getChildAt(i);
2403 visibilities[i] = child.getVisibility();
2404 if (visibilities[i] == View.VISIBLE) {
2405 child.setVisibility(INVISIBLE);
2406 }
2407 }
Romain Guy223ff5c2010-03-02 17:07:47 -08002408 }
2409
2410 Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
Romain Guy65554f22010-03-22 18:58:21 -07002411
2412 if (skipChildren) {
2413 for (int i = 0; i < count; i++) {
2414 getChildAt(i).setVisibility(visibilities[i]);
Chet Haase5c13d892010-10-08 08:37:55 -07002415 }
Romain Guy65554f22010-03-22 18:58:21 -07002416 }
Romain Guy223ff5c2010-03-02 17:07:47 -08002417
2418 return b;
2419 }
2420
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002421 /**
2422 * {@inheritDoc}
2423 */
2424 @Override
2425 protected void dispatchDraw(Canvas canvas) {
2426 final int count = mChildrenCount;
2427 final View[] children = mChildren;
2428 int flags = mGroupFlags;
2429
2430 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
2431 final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
2432
Romain Guy0d9275e2010-10-26 14:22:30 -07002433 final boolean buildCache = !isHardwareAccelerated();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002434 for (int i = 0; i < count; i++) {
2435 final View child = children[i];
2436 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2437 final LayoutParams params = child.getLayoutParams();
2438 attachLayoutAnimationParameters(child, params, i, count);
2439 bindLayoutAnimation(child);
2440 if (cache) {
2441 child.setDrawingCacheEnabled(true);
Romain Guy0d9275e2010-10-26 14:22:30 -07002442 if (buildCache) {
2443 child.buildDrawingCache(true);
2444 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002445 }
2446 }
2447 }
2448
2449 final LayoutAnimationController controller = mLayoutAnimationController;
2450 if (controller.willOverlap()) {
2451 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
2452 }
2453
2454 controller.start();
2455
2456 mGroupFlags &= ~FLAG_RUN_ANIMATION;
2457 mGroupFlags &= ~FLAG_ANIMATION_DONE;
2458
2459 if (cache) {
2460 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2461 }
2462
2463 if (mAnimationListener != null) {
2464 mAnimationListener.onAnimationStart(controller.getAnimation());
2465 }
2466 }
2467
2468 int saveCount = 0;
2469 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
2470 if (clipToPadding) {
2471 saveCount = canvas.save();
Romain Guy8f2d94f2009-03-25 18:04:42 -07002472 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
2473 mScrollX + mRight - mLeft - mPaddingRight,
2474 mScrollY + mBottom - mTop - mPaddingBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002475
2476 }
2477
2478 // We will draw our child's animation, let's reset the flag
2479 mPrivateFlags &= ~DRAW_ANIMATION;
2480 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
2481
2482 boolean more = false;
2483 final long drawingTime = getDrawingTime();
2484
2485 if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
2486 for (int i = 0; i < count; i++) {
2487 final View child = children[i];
2488 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2489 more |= drawChild(canvas, child, drawingTime);
2490 }
2491 }
2492 } else {
2493 for (int i = 0; i < count; i++) {
2494 final View child = children[getChildDrawingOrder(count, i)];
2495 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2496 more |= drawChild(canvas, child, drawingTime);
2497 }
2498 }
2499 }
2500
2501 // Draw any disappearing views that have animations
2502 if (mDisappearingChildren != null) {
2503 final ArrayList<View> disappearingChildren = mDisappearingChildren;
2504 final int disappearingCount = disappearingChildren.size() - 1;
2505 // Go backwards -- we may delete as animations finish
2506 for (int i = disappearingCount; i >= 0; i--) {
2507 final View child = disappearingChildren.get(i);
2508 more |= drawChild(canvas, child, drawingTime);
2509 }
2510 }
2511
2512 if (clipToPadding) {
2513 canvas.restoreToCount(saveCount);
2514 }
2515
2516 // mGroupFlags might have been updated by drawChild()
2517 flags = mGroupFlags;
2518
2519 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
Romain Guy849d0a32011-02-01 17:20:48 -08002520 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002521 }
2522
2523 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
2524 mLayoutAnimationController.isDone() && !more) {
2525 // We want to erase the drawing cache and notify the listener after the
2526 // next frame is drawn because one extra invalidate() is caused by
2527 // drawChild() after the animation is over
2528 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
2529 final Runnable end = new Runnable() {
2530 public void run() {
2531 notifyAnimationListener();
2532 }
2533 };
2534 post(end);
2535 }
2536 }
Romain Guy8506ab42009-06-11 17:35:47 -07002537
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002538 /**
2539 * Returns the index of the child to draw for this iteration. Override this
2540 * if you want to change the drawing order of children. By default, it
2541 * returns i.
2542 * <p>
Romain Guy293451e2009-11-04 13:59:48 -08002543 * NOTE: In order for this method to be called, you must enable child ordering
2544 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
Romain Guy8506ab42009-06-11 17:35:47 -07002545 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002546 * @param i The current iteration.
2547 * @return The index of the child to draw this iteration.
Chet Haase5c13d892010-10-08 08:37:55 -07002548 *
Romain Guy293451e2009-11-04 13:59:48 -08002549 * @see #setChildrenDrawingOrderEnabled(boolean)
2550 * @see #isChildrenDrawingOrderEnabled()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002551 */
2552 protected int getChildDrawingOrder(int childCount, int i) {
2553 return i;
2554 }
Romain Guy8506ab42009-06-11 17:35:47 -07002555
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002556 private void notifyAnimationListener() {
2557 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
2558 mGroupFlags |= FLAG_ANIMATION_DONE;
2559
2560 if (mAnimationListener != null) {
2561 final Runnable end = new Runnable() {
2562 public void run() {
2563 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
2564 }
2565 };
2566 post(end);
2567 }
2568
2569 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2570 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2571 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2572 setChildrenDrawingCacheEnabled(false);
2573 }
2574 }
2575
Romain Guy849d0a32011-02-01 17:20:48 -08002576 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002577 }
2578
2579 /**
Chet Haasedaf98e92011-01-10 14:10:36 -08002580 * This method is used to cause children of this ViewGroup to restore or recreate their
2581 * display lists. It is called by getDisplayList() when the parent ViewGroup does not need
2582 * to recreate its own display list, which would happen if it went through the normal
2583 * draw/dispatchDraw mechanisms.
2584 *
2585 * @hide
2586 */
2587 @Override
2588 protected void dispatchGetDisplayList() {
2589 final int count = mChildrenCount;
2590 final View[] children = mChildren;
2591 for (int i = 0; i < count; i++) {
2592 final View child = children[i];
Romain Guy59c7f802011-09-29 17:21:45 -07002593 if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) &&
2594 child.hasStaticLayer()) {
Romain Guy2f57ba52011-02-03 18:03:29 -08002595 child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED;
2596 child.mPrivateFlags &= ~INVALIDATED;
2597 child.getDisplayList();
2598 child.mRecreateDisplayList = false;
2599 }
Chet Haasedaf98e92011-01-10 14:10:36 -08002600 }
2601 }
2602
2603 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002604 * Draw one child of this View Group. This method is responsible for getting
2605 * the canvas in the right state. This includes clipping, translating so
2606 * that the child's scrolled origin is at 0, 0, and applying any animation
2607 * transformations.
2608 *
2609 * @param canvas The canvas on which to draw the child
2610 * @param child Who to draw
2611 * @param drawingTime The time at which draw is occuring
2612 * @return True if an invalidate() was issued
2613 */
2614 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
2615 boolean more = false;
2616
2617 final int cl = child.mLeft;
2618 final int ct = child.mTop;
2619 final int cr = child.mRight;
2620 final int cb = child.mBottom;
2621
Chet Haase70d4ba12010-10-06 09:46:45 -07002622 final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
2623
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002624 final int flags = mGroupFlags;
2625
2626 if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
Chet Haase48460322010-06-11 14:22:25 -07002627 mChildTransformation.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002628 mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION;
2629 }
2630
2631 Transformation transformToApply = null;
Chet Haase48460322010-06-11 14:22:25 -07002632 Transformation invalidationTransform;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002633 final Animation a = child.getAnimation();
2634 boolean concatMatrix = false;
2635
Chet Haase48460322010-06-11 14:22:25 -07002636 boolean scalingRequired = false;
Romain Guy171c5922011-01-06 10:04:23 -08002637 boolean caching;
Romain Guy849d0a32011-02-01 17:20:48 -08002638 int layerType = mDrawLayers ? child.getLayerType() : LAYER_TYPE_NONE;
Chet Haasee38ba4a2011-01-27 01:10:35 -08002639
2640 final boolean hardwareAccelerated = canvas.isHardwareAccelerated();
Romain Guyb051e892010-09-28 19:09:36 -07002641 if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
Chet Haase48460322010-06-11 14:22:25 -07002642 (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
2643 caching = true;
2644 if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
Romain Guy171c5922011-01-06 10:04:23 -08002645 } else {
Chet Haasee38ba4a2011-01-27 01:10:35 -08002646 caching = (layerType != LAYER_TYPE_NONE) || hardwareAccelerated;
Chet Haase48460322010-06-11 14:22:25 -07002647 }
2648
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002649 if (a != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002650 final boolean initialized = a.isInitialized();
2651 if (!initialized) {
Romain Guy8f2d94f2009-03-25 18:04:42 -07002652 a.initialize(cr - cl, cb - ct, getWidth(), getHeight());
2653 a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002654 child.onAnimationStart();
2655 }
2656
Chet Haase48460322010-06-11 14:22:25 -07002657 more = a.getTransformation(drawingTime, mChildTransformation,
2658 scalingRequired ? mAttachInfo.mApplicationScale : 1f);
2659 if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
2660 if (mInvalidationTransformation == null) {
2661 mInvalidationTransformation = new Transformation();
2662 }
2663 invalidationTransform = mInvalidationTransformation;
2664 a.getTransformation(drawingTime, invalidationTransform, 1f);
2665 } else {
2666 invalidationTransform = mChildTransformation;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002667 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002668 transformToApply = mChildTransformation;
2669
2670 concatMatrix = a.willChangeTransformationMatrix();
2671
2672 if (more) {
2673 if (!a.willChangeBounds()) {
2674 if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) ==
2675 FLAG_OPTIMIZE_INVALIDATE) {
2676 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
2677 } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) {
2678 // The child need to draw an animation, potentially offscreen, so
2679 // make sure we do not cancel invalidate requests
2680 mPrivateFlags |= DRAW_ANIMATION;
2681 invalidate(cl, ct, cr, cb);
2682 }
2683 } else {
Chet Haase48460322010-06-11 14:22:25 -07002684 if (mInvalidateRegion == null) {
2685 mInvalidateRegion = new RectF();
2686 }
2687 final RectF region = mInvalidateRegion;
2688 a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, invalidationTransform);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002689
2690 // The child need to draw an animation, potentially offscreen, so
2691 // make sure we do not cancel invalidate requests
2692 mPrivateFlags |= DRAW_ANIMATION;
The Android Open Source Project4df24232009-03-05 14:34:35 -08002693
2694 final int left = cl + (int) region.left;
2695 final int top = ct + (int) region.top;
Chet Haase530b22a2011-08-23 14:36:55 -07002696 invalidate(left, top, left + (int) (region.width() + .5f),
2697 top + (int) (region.height() + .5f));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002698 }
2699 }
2700 } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
2701 FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002702 final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation);
2703 if (hasTransform) {
2704 final int transformType = mChildTransformation.getTransformationType();
2705 transformToApply = transformType != Transformation.TYPE_IDENTITY ?
2706 mChildTransformation : null;
2707 concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
2708 }
2709 }
2710
Chet Haase70d4ba12010-10-06 09:46:45 -07002711 concatMatrix |= !childHasIdentityMatrix;
Chet Haasedf030d22010-07-30 17:22:38 -07002712
Romain Guy5bcdff42009-05-14 21:27:18 -07002713 // Sets the flag as early as possible to allow draw() implementations
Romain Guy986003d2009-03-25 17:42:35 -07002714 // to call invalidate() successfully when doing animations
Romain Guy5bcdff42009-05-14 21:27:18 -07002715 child.mPrivateFlags |= DRAWN;
Romain Guy986003d2009-03-25 17:42:35 -07002716
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002717 if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
2718 (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
2719 return more;
2720 }
Chet Haase5c13d892010-10-08 08:37:55 -07002721
Chet Haasee38ba4a2011-01-27 01:10:35 -08002722 if (hardwareAccelerated) {
Chet Haasedaf98e92011-01-10 14:10:36 -08002723 // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
2724 // retain the flag's value temporarily in the mRecreateDisplayList flag
2725 child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED;
2726 child.mPrivateFlags &= ~INVALIDATED;
2727 }
2728
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002729 child.computeScroll();
2730
2731 final int sx = child.mScrollX;
2732 final int sy = child.mScrollY;
2733
Romain Guyb051e892010-09-28 19:09:36 -07002734 DisplayList displayList = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002735 Bitmap cache = null;
Chet Haase678e0ad2011-01-25 09:37:18 -08002736 boolean hasDisplayList = false;
Chet Haase48460322010-06-11 14:22:25 -07002737 if (caching) {
Chet Haasee38ba4a2011-01-27 01:10:35 -08002738 if (!hardwareAccelerated) {
Romain Guy171c5922011-01-06 10:04:23 -08002739 if (layerType != LAYER_TYPE_NONE) {
Romain Guy6c319ca2011-01-11 14:29:25 -08002740 layerType = LAYER_TYPE_SOFTWARE;
Romain Guy171c5922011-01-06 10:04:23 -08002741 child.buildDrawingCache(true);
2742 }
Romain Guyb051e892010-09-28 19:09:36 -07002743 cache = child.getDrawingCache(true);
2744 } else {
Romain Guyd643bb52011-03-01 14:55:21 -08002745 switch (layerType) {
2746 case LAYER_TYPE_SOFTWARE:
2747 child.buildDrawingCache(true);
2748 cache = child.getDrawingCache(true);
2749 break;
2750 case LAYER_TYPE_NONE:
2751 // Delay getting the display list until animation-driven alpha values are
2752 // set up and possibly passed on to the view
2753 hasDisplayList = child.canHaveDisplayList();
2754 break;
Romain Guy171c5922011-01-06 10:04:23 -08002755 }
Romain Guyb051e892010-09-28 19:09:36 -07002756 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002757 }
2758
Romain Guyb051e892010-09-28 19:09:36 -07002759 final boolean hasNoCache = cache == null || hasDisplayList;
Romain Guyd643bb52011-03-01 14:55:21 -08002760 final boolean offsetForScroll = cache == null && !hasDisplayList &&
2761 layerType != LAYER_TYPE_HARDWARE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002762
2763 final int restoreTo = canvas.save();
Romain Guyd643bb52011-03-01 14:55:21 -08002764 if (offsetForScroll) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002765 canvas.translate(cl - sx, ct - sy);
2766 } else {
2767 canvas.translate(cl, ct);
Romain Guy8506ab42009-06-11 17:35:47 -07002768 if (scalingRequired) {
Romain Guycafdea62009-06-12 10:51:36 -07002769 // mAttachInfo cannot be null, otherwise scalingRequired == false
Romain Guy8506ab42009-06-11 17:35:47 -07002770 final float scale = 1.0f / mAttachInfo.mApplicationScale;
2771 canvas.scale(scale, scale);
2772 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002773 }
2774
Romain Guyc7ee3ca2011-12-07 19:06:28 -08002775 float alpha = child.getAlpha();
Romain Guy33e72ae2010-07-17 12:40:29 -07002776 if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
Chet Haase70d4ba12010-10-06 09:46:45 -07002777 if (transformToApply != null || !childHasIdentityMatrix) {
2778 int transX = 0;
2779 int transY = 0;
Romain Guy33e72ae2010-07-17 12:40:29 -07002780
Romain Guyd643bb52011-03-01 14:55:21 -08002781 if (offsetForScroll) {
Chet Haase70d4ba12010-10-06 09:46:45 -07002782 transX = -sx;
2783 transY = -sy;
2784 }
Romain Guy33e72ae2010-07-17 12:40:29 -07002785
Chet Haase70d4ba12010-10-06 09:46:45 -07002786 if (transformToApply != null) {
2787 if (concatMatrix) {
2788 // Undo the scroll translation, apply the transformation matrix,
2789 // then redo the scroll translate to get the correct result.
2790 canvas.translate(-transX, -transY);
2791 canvas.concat(transformToApply.getMatrix());
2792 canvas.translate(transX, transY);
2793 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
2794 }
2795
2796 float transformAlpha = transformToApply.getAlpha();
2797 if (transformAlpha < 1.0f) {
2798 alpha *= transformToApply.getAlpha();
2799 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
2800 }
2801 }
2802
2803 if (!childHasIdentityMatrix) {
Chet Haasec3aa3612010-06-17 08:50:37 -07002804 canvas.translate(-transX, -transY);
Chet Haase70d4ba12010-10-06 09:46:45 -07002805 canvas.concat(child.getMatrix());
Chet Haasec3aa3612010-06-17 08:50:37 -07002806 canvas.translate(transX, transY);
Chet Haasec3aa3612010-06-17 08:50:37 -07002807 }
Chet Haasec3aa3612010-06-17 08:50:37 -07002808 }
Romain Guy33e72ae2010-07-17 12:40:29 -07002809
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002810 if (alpha < 1.0f) {
2811 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
Romain Guy33e72ae2010-07-17 12:40:29 -07002812 if (hasNoCache) {
2813 final int multipliedAlpha = (int) (255 * alpha);
2814 if (!child.onSetAlpha(multipliedAlpha)) {
Romain Guyfd880422010-09-23 16:16:04 -07002815 int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
Romain Guy171c5922011-01-06 10:04:23 -08002816 if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN ||
2817 layerType != LAYER_TYPE_NONE) {
Romain Guyfd880422010-09-23 16:16:04 -07002818 layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
2819 }
Romain Guy54229ee2011-02-01 13:05:16 -08002820 if (layerType == LAYER_TYPE_NONE) {
Romain Guya7dabcd2011-03-01 15:44:21 -08002821 final int scrollX = hasDisplayList ? 0 : sx;
2822 final int scrollY = hasDisplayList ? 0 : sy;
2823 canvas.saveLayerAlpha(scrollX, scrollY, scrollX + cr - cl,
2824 scrollY + cb - ct, multipliedAlpha, layerFlags);
Romain Guy171c5922011-01-06 10:04:23 -08002825 }
Romain Guy33e72ae2010-07-17 12:40:29 -07002826 } else {
Romain Guy171c5922011-01-06 10:04:23 -08002827 // Alpha is handled by the child directly, clobber the layer's alpha
Romain Guy33e72ae2010-07-17 12:40:29 -07002828 child.mPrivateFlags |= ALPHA_SET;
2829 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002830 }
2831 }
2832 } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
2833 child.onSetAlpha(255);
Romain Guy9b34d452010-09-02 11:45:04 -07002834 child.mPrivateFlags &= ~ALPHA_SET;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002835 }
2836
2837 if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
Romain Guyd643bb52011-03-01 14:55:21 -08002838 if (offsetForScroll) {
Romain Guy8f2d94f2009-03-25 18:04:42 -07002839 canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002840 } else {
Chet Haasedaf98e92011-01-10 14:10:36 -08002841 if (!scalingRequired || cache == null) {
Romain Guy8506ab42009-06-11 17:35:47 -07002842 canvas.clipRect(0, 0, cr - cl, cb - ct);
2843 } else {
2844 canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
2845 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002846 }
2847 }
2848
Chet Haase678e0ad2011-01-25 09:37:18 -08002849 if (hasDisplayList) {
2850 displayList = child.getDisplayList();
Chet Haase6e6db612011-09-26 13:49:33 -07002851 if (!displayList.isValid()) {
2852 // Uncommon, but possible. If a view is removed from the hierarchy during the call
2853 // to getDisplayList(), the display list will be marked invalid and we should not
2854 // try to use it again.
2855 displayList = null;
2856 hasDisplayList = false;
2857 }
Chet Haase678e0ad2011-01-25 09:37:18 -08002858 }
2859
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002860 if (hasNoCache) {
Romain Guy6c319ca2011-01-11 14:29:25 -08002861 boolean layerRendered = false;
Romain Guyd6cd5722011-01-17 14:42:41 -08002862 if (layerType == LAYER_TYPE_HARDWARE) {
Romain Guy5e7f7662011-01-24 22:35:56 -08002863 final HardwareLayer layer = child.getHardwareLayer();
Romain Guy6c319ca2011-01-11 14:29:25 -08002864 if (layer != null && layer.isValid()) {
Romain Guy54229ee2011-02-01 13:05:16 -08002865 child.mLayerPaint.setAlpha((int) (alpha * 255));
Romain Guyada830f2011-01-13 12:13:20 -08002866 ((HardwareCanvas) canvas).drawHardwareLayer(layer, 0, 0, child.mLayerPaint);
Romain Guy6c319ca2011-01-11 14:29:25 -08002867 layerRendered = true;
Romain Guyb051e892010-09-28 19:09:36 -07002868 } else {
Romain Guya7dabcd2011-03-01 15:44:21 -08002869 final int scrollX = hasDisplayList ? 0 : sx;
2870 final int scrollY = hasDisplayList ? 0 : sy;
2871 canvas.saveLayer(scrollX, scrollY,
2872 scrollX + cr - cl, scrollY + cb - ct, child.mLayerPaint,
Romain Guy6c319ca2011-01-11 14:29:25 -08002873 Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002874 }
Romain Guy6c319ca2011-01-11 14:29:25 -08002875 }
2876
2877 if (!layerRendered) {
2878 if (!hasDisplayList) {
2879 // Fast path for layouts with no backgrounds
2880 if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
2881 if (ViewDebug.TRACE_HIERARCHY) {
2882 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
2883 }
2884 child.mPrivateFlags &= ~DIRTY_MASK;
2885 child.dispatchDraw(canvas);
2886 } else {
2887 child.draw(canvas);
2888 }
2889 } else {
2890 child.mPrivateFlags &= ~DIRTY_MASK;
Romain Guy7b5b6ab2011-03-14 18:05:08 -07002891 ((HardwareCanvas) canvas).drawDisplayList(displayList, cr - cl, cb - ct, null);
Romain Guy6c319ca2011-01-11 14:29:25 -08002892 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002893 }
Romain Guyb051e892010-09-28 19:09:36 -07002894 } else if (cache != null) {
Chet Haasef2f7d8f2010-12-03 14:08:14 -08002895 child.mPrivateFlags &= ~DIRTY_MASK;
Romain Guy171c5922011-01-06 10:04:23 -08002896 Paint cachePaint;
2897
Romain Guyd6cd5722011-01-17 14:42:41 -08002898 if (layerType == LAYER_TYPE_NONE) {
Romain Guy171c5922011-01-06 10:04:23 -08002899 cachePaint = mCachePaint;
Dianne Hackborn0500b3c2011-11-01 15:28:43 -07002900 if (cachePaint == null) {
2901 cachePaint = new Paint();
2902 cachePaint.setDither(false);
2903 mCachePaint = cachePaint;
2904 }
Romain Guy171c5922011-01-06 10:04:23 -08002905 if (alpha < 1.0f) {
2906 cachePaint.setAlpha((int) (alpha * 255));
2907 mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
2908 } else if ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
2909 cachePaint.setAlpha(255);
2910 mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
2911 }
2912 } else {
2913 cachePaint = child.mLayerPaint;
Romain Guyd6cd5722011-01-17 14:42:41 -08002914 cachePaint.setAlpha((int) (alpha * 255));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002915 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002916 canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
2917 }
2918
2919 canvas.restoreToCount(restoreTo);
2920
2921 if (a != null && !more) {
Chet Haasee38ba4a2011-01-27 01:10:35 -08002922 if (!hardwareAccelerated && !a.getFillAfter()) {
Chet Haase678e0ad2011-01-25 09:37:18 -08002923 child.onSetAlpha(255);
2924 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002925 finishAnimatingView(child, a);
2926 }
2927
Chet Haasee38ba4a2011-01-27 01:10:35 -08002928 if (more && hardwareAccelerated) {
Chet Haasedaf98e92011-01-10 14:10:36 -08002929 // invalidation is the trigger to recreate display lists, so if we're using
2930 // display lists to render, force an invalidate to allow the animation to
2931 // continue drawing another frame
Romain Guy849d0a32011-02-01 17:20:48 -08002932 invalidate(true);
Romain Guy29d23ec2011-07-25 14:42:24 -07002933 if (a.hasAlpha() && (child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
Chet Haase678e0ad2011-01-25 09:37:18 -08002934 // alpha animations should cause the child to recreate its display list
Romain Guy849d0a32011-02-01 17:20:48 -08002935 child.invalidate(true);
Chet Haase678e0ad2011-01-25 09:37:18 -08002936 }
Chet Haasedaf98e92011-01-10 14:10:36 -08002937 }
2938
2939 child.mRecreateDisplayList = false;
2940
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002941 return more;
2942 }
2943
2944 /**
Romain Guy849d0a32011-02-01 17:20:48 -08002945 *
2946 * @param enabled True if children should be drawn with layers, false otherwise.
2947 *
2948 * @hide
2949 */
2950 public void setChildrenLayersEnabled(boolean enabled) {
Romain Guy9d18f2d2011-02-03 11:25:51 -08002951 if (enabled != mDrawLayers) {
2952 mDrawLayers = enabled;
2953 invalidate(true);
2954
Romain Guy9c4b79a2011-11-10 19:23:58 -08002955 boolean flushLayers = !enabled;
2956 AttachInfo info = mAttachInfo;
2957 if (info != null && info.mHardwareRenderer != null &&
2958 info.mHardwareRenderer.isEnabled()) {
2959 if (!info.mHardwareRenderer.validate()) {
2960 flushLayers = false;
2961 }
2962 } else {
2963 flushLayers = false;
2964 }
2965
Romain Guy9d18f2d2011-02-03 11:25:51 -08002966 // We need to invalidate any child with a layer. For instance,
2967 // if a child is backed by a hardware layer and we disable layers
2968 // the child is marked as not dirty (flags cleared the last time
2969 // the child was drawn inside its layer.) However, that child might
2970 // never have created its own display list or have an obsolete
2971 // display list. By invalidating the child we ensure the display
2972 // list is in sync with the content of the hardware layer.
2973 for (int i = 0; i < mChildrenCount; i++) {
2974 View child = mChildren[i];
2975 if (child.mLayerType != LAYER_TYPE_NONE) {
Romain Guy9c4b79a2011-11-10 19:23:58 -08002976 if (flushLayers) child.flushLayer();
Romain Guy9d18f2d2011-02-03 11:25:51 -08002977 child.invalidate(true);
2978 }
2979 }
2980 }
Romain Guy849d0a32011-02-01 17:20:48 -08002981 }
2982
2983 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002984 * By default, children are clipped to their bounds before drawing. This
2985 * allows view groups to override this behavior for animations, etc.
2986 *
2987 * @param clipChildren true to clip children to their bounds,
2988 * false otherwise
2989 * @attr ref android.R.styleable#ViewGroup_clipChildren
2990 */
2991 public void setClipChildren(boolean clipChildren) {
2992 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
2993 }
2994
2995 /**
2996 * By default, children are clipped to the padding of the ViewGroup. This
2997 * allows view groups to override this behavior
2998 *
2999 * @param clipToPadding true to clip children to the padding of the
3000 * group, false otherwise
3001 * @attr ref android.R.styleable#ViewGroup_clipToPadding
3002 */
3003 public void setClipToPadding(boolean clipToPadding) {
3004 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
3005 }
3006
3007 /**
3008 * {@inheritDoc}
3009 */
3010 @Override
3011 public void dispatchSetSelected(boolean selected) {
3012 final View[] children = mChildren;
3013 final int count = mChildrenCount;
3014 for (int i = 0; i < count; i++) {
3015 children[i].setSelected(selected);
3016 }
3017 }
Romain Guy8506ab42009-06-11 17:35:47 -07003018
Dianne Hackbornd0fa3712010-09-14 18:57:14 -07003019 /**
3020 * {@inheritDoc}
3021 */
3022 @Override
3023 public void dispatchSetActivated(boolean activated) {
3024 final View[] children = mChildren;
3025 final int count = mChildrenCount;
3026 for (int i = 0; i < count; i++) {
Dianne Hackbornd0fa3712010-09-14 18:57:14 -07003027 children[i].setActivated(activated);
3028 }
3029 }
3030
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003031 @Override
3032 protected void dispatchSetPressed(boolean pressed) {
3033 final View[] children = mChildren;
3034 final int count = mChildrenCount;
3035 for (int i = 0; i < count; i++) {
3036 children[i].setPressed(pressed);
3037 }
3038 }
3039
3040 /**
3041 * When this property is set to true, this ViewGroup supports static transformations on
3042 * children; this causes
3043 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
3044 * invoked when a child is drawn.
3045 *
3046 * Any subclass overriding
3047 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
3048 * set this property to true.
3049 *
3050 * @param enabled True to enable static transformations on children, false otherwise.
3051 *
3052 * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS
3053 */
3054 protected void setStaticTransformationsEnabled(boolean enabled) {
3055 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
3056 }
3057
3058 /**
3059 * {@inheritDoc}
3060 *
Romain Guy8506ab42009-06-11 17:35:47 -07003061 * @see #setStaticTransformationsEnabled(boolean)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003062 */
3063 protected boolean getChildStaticTransformation(View child, Transformation t) {
3064 return false;
3065 }
3066
3067 /**
3068 * {@hide}
3069 */
3070 @Override
3071 protected View findViewTraversal(int id) {
3072 if (id == mID) {
3073 return this;
3074 }
3075
3076 final View[] where = mChildren;
3077 final int len = mChildrenCount;
3078
3079 for (int i = 0; i < len; i++) {
3080 View v = where[i];
3081
3082 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
3083 v = v.findViewById(id);
3084
3085 if (v != null) {
3086 return v;
3087 }
3088 }
3089 }
3090
3091 return null;
3092 }
3093
3094 /**
3095 * {@hide}
3096 */
3097 @Override
3098 protected View findViewWithTagTraversal(Object tag) {
3099 if (tag != null && tag.equals(mTag)) {
3100 return this;
3101 }
3102
3103 final View[] where = mChildren;
3104 final int len = mChildrenCount;
3105
3106 for (int i = 0; i < len; i++) {
3107 View v = where[i];
3108
3109 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
3110 v = v.findViewWithTag(tag);
3111
3112 if (v != null) {
3113 return v;
3114 }
3115 }
3116 }
3117
3118 return null;
3119 }
3120
3121 /**
Jeff Brown4e6319b2010-12-13 10:36:51 -08003122 * {@hide}
3123 */
3124 @Override
Jeff Brown4dfbec22011-08-15 14:55:37 -07003125 protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
Jeff Brown4e6319b2010-12-13 10:36:51 -08003126 if (predicate.apply(this)) {
3127 return this;
3128 }
3129
3130 final View[] where = mChildren;
3131 final int len = mChildrenCount;
3132
3133 for (int i = 0; i < len; i++) {
3134 View v = where[i];
3135
Jeff Brown4dfbec22011-08-15 14:55:37 -07003136 if (v != childToSkip && (v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
Jeff Brown4e6319b2010-12-13 10:36:51 -08003137 v = v.findViewByPredicate(predicate);
3138
3139 if (v != null) {
3140 return v;
3141 }
3142 }
3143 }
3144
3145 return null;
3146 }
3147
3148 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003149 * Adds a child view. If no layout parameters are already set on the child, the
3150 * default parameters for this ViewGroup are set on the child.
3151 *
3152 * @param child the child view to add
3153 *
3154 * @see #generateDefaultLayoutParams()
3155 */
3156 public void addView(View child) {
3157 addView(child, -1);
3158 }
3159
3160 /**
3161 * Adds a child view. If no layout parameters are already set on the child, the
3162 * default parameters for this ViewGroup are set on the child.
3163 *
3164 * @param child the child view to add
3165 * @param index the position at which to add the child
3166 *
3167 * @see #generateDefaultLayoutParams()
3168 */
3169 public void addView(View child, int index) {
3170 LayoutParams params = child.getLayoutParams();
3171 if (params == null) {
3172 params = generateDefaultLayoutParams();
3173 if (params == null) {
3174 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
3175 }
3176 }
3177 addView(child, index, params);
3178 }
3179
3180 /**
3181 * Adds a child view with this ViewGroup's default layout parameters and the
3182 * specified width and height.
3183 *
3184 * @param child the child view to add
3185 */
3186 public void addView(View child, int width, int height) {
3187 final LayoutParams params = generateDefaultLayoutParams();
3188 params.width = width;
3189 params.height = height;
3190 addView(child, -1, params);
3191 }
3192
3193 /**
3194 * Adds a child view with the specified layout parameters.
3195 *
3196 * @param child the child view to add
3197 * @param params the layout parameters to set on the child
3198 */
3199 public void addView(View child, LayoutParams params) {
3200 addView(child, -1, params);
3201 }
3202
3203 /**
3204 * Adds a child view with the specified layout parameters.
3205 *
3206 * @param child the child view to add
3207 * @param index the position at which to add the child
3208 * @param params the layout parameters to set on the child
3209 */
3210 public void addView(View child, int index, LayoutParams params) {
3211 if (DBG) {
3212 System.out.println(this + " addView");
3213 }
3214
3215 // addViewInner() will call child.requestLayout() when setting the new LayoutParams
3216 // therefore, we call requestLayout() on ourselves before, so that the child's request
3217 // will be blocked at our level
3218 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003219 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003220 addViewInner(child, index, params, false);
3221 }
3222
3223 /**
3224 * {@inheritDoc}
3225 */
3226 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
3227 if (!checkLayoutParams(params)) {
3228 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
3229 }
3230 if (view.mParent != this) {
3231 throw new IllegalArgumentException("Given view not a child of " + this);
3232 }
3233 view.setLayoutParams(params);
3234 }
3235
3236 /**
3237 * {@inheritDoc}
3238 */
3239 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3240 return p != null;
3241 }
3242
3243 /**
3244 * Interface definition for a callback to be invoked when the hierarchy
3245 * within this view changed. The hierarchy changes whenever a child is added
3246 * to or removed from this view.
3247 */
3248 public interface OnHierarchyChangeListener {
3249 /**
3250 * Called when a new child is added to a parent view.
3251 *
3252 * @param parent the view in which a child was added
3253 * @param child the new child view added in the hierarchy
3254 */
3255 void onChildViewAdded(View parent, View child);
3256
3257 /**
3258 * Called when a child is removed from a parent view.
3259 *
3260 * @param parent the view from which the child was removed
3261 * @param child the child removed from the hierarchy
3262 */
3263 void onChildViewRemoved(View parent, View child);
3264 }
3265
3266 /**
3267 * Register a callback to be invoked when a child is added to or removed
3268 * from this view.
3269 *
3270 * @param listener the callback to invoke on hierarchy change
3271 */
3272 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
3273 mOnHierarchyChangeListener = listener;
3274 }
3275
3276 /**
Philip Milnef51d91c2011-07-18 16:12:19 -07003277 * @hide
3278 */
3279 protected void onViewAdded(View child) {
3280 if (mOnHierarchyChangeListener != null) {
3281 mOnHierarchyChangeListener.onChildViewAdded(this, child);
3282 }
3283 }
3284
3285 /**
3286 * @hide
3287 */
3288 protected void onViewRemoved(View child) {
3289 if (mOnHierarchyChangeListener != null) {
3290 mOnHierarchyChangeListener.onChildViewRemoved(this, child);
3291 }
3292 }
3293
3294 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003295 * Adds a view during layout. This is useful if in your onLayout() method,
3296 * you need to add more views (as does the list view for example).
3297 *
3298 * If index is negative, it means put it at the end of the list.
3299 *
3300 * @param child the view to add to the group
3301 * @param index the index at which the child must be added
3302 * @param params the layout parameters to associate with the child
3303 * @return true if the child was added, false otherwise
3304 */
3305 protected boolean addViewInLayout(View child, int index, LayoutParams params) {
3306 return addViewInLayout(child, index, params, false);
3307 }
3308
3309 /**
3310 * Adds a view during layout. This is useful if in your onLayout() method,
3311 * you need to add more views (as does the list view for example).
3312 *
3313 * If index is negative, it means put it at the end of the list.
3314 *
3315 * @param child the view to add to the group
3316 * @param index the index at which the child must be added
3317 * @param params the layout parameters to associate with the child
3318 * @param preventRequestLayout if true, calling this method will not trigger a
3319 * layout request on child
3320 * @return true if the child was added, false otherwise
3321 */
3322 protected boolean addViewInLayout(View child, int index, LayoutParams params,
3323 boolean preventRequestLayout) {
3324 child.mParent = null;
3325 addViewInner(child, index, params, preventRequestLayout);
Romain Guy24443ea2009-05-11 11:56:30 -07003326 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003327 return true;
3328 }
3329
3330 /**
3331 * Prevents the specified child to be laid out during the next layout pass.
3332 *
3333 * @param child the child on which to perform the cleanup
3334 */
3335 protected void cleanupLayoutState(View child) {
3336 child.mPrivateFlags &= ~View.FORCE_LAYOUT;
3337 }
3338
3339 private void addViewInner(View child, int index, LayoutParams params,
3340 boolean preventRequestLayout) {
3341
Chet Haasee8e45d32011-03-02 17:07:35 -08003342 if (mTransition != null) {
3343 // Don't prevent other add transitions from completing, but cancel remove
3344 // transitions to let them complete the process before we add to the container
3345 mTransition.cancel(LayoutTransition.DISAPPEARING);
Chet Haaseadd65772011-02-09 16:47:29 -08003346 }
3347
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003348 if (child.getParent() != null) {
3349 throw new IllegalStateException("The specified child already has a parent. " +
3350 "You must call removeView() on the child's parent first.");
3351 }
3352
Chet Haase21cd1382010-09-01 17:42:29 -07003353 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003354 mTransition.addChild(this, child);
Chet Haase21cd1382010-09-01 17:42:29 -07003355 }
3356
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003357 if (!checkLayoutParams(params)) {
3358 params = generateLayoutParams(params);
3359 }
3360
3361 if (preventRequestLayout) {
3362 child.mLayoutParams = params;
3363 } else {
3364 child.setLayoutParams(params);
3365 }
3366
3367 if (index < 0) {
3368 index = mChildrenCount;
3369 }
3370
3371 addInArray(child, index);
3372
3373 // tell our children
3374 if (preventRequestLayout) {
3375 child.assignParent(this);
3376 } else {
3377 child.mParent = this;
3378 }
3379
3380 if (child.hasFocus()) {
3381 requestChildFocus(child, child.findFocus());
3382 }
Romain Guy8506ab42009-06-11 17:35:47 -07003383
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003384 AttachInfo ai = mAttachInfo;
Adam Powell4b867882011-09-16 12:59:46 -07003385 if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
Romain Guy8506ab42009-06-11 17:35:47 -07003386 boolean lastKeepOn = ai.mKeepScreenOn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003387 ai.mKeepScreenOn = false;
3388 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
3389 if (ai.mKeepScreenOn) {
3390 needGlobalAttributesUpdate(true);
3391 }
3392 ai.mKeepScreenOn = lastKeepOn;
3393 }
3394
Philip Milnef51d91c2011-07-18 16:12:19 -07003395 onViewAdded(child);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003396
3397 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
3398 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
3399 }
3400 }
3401
3402 private void addInArray(View child, int index) {
3403 View[] children = mChildren;
3404 final int count = mChildrenCount;
3405 final int size = children.length;
3406 if (index == count) {
3407 if (size == count) {
3408 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3409 System.arraycopy(children, 0, mChildren, 0, size);
3410 children = mChildren;
3411 }
3412 children[mChildrenCount++] = child;
3413 } else if (index < count) {
3414 if (size == count) {
3415 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3416 System.arraycopy(children, 0, mChildren, 0, index);
3417 System.arraycopy(children, index, mChildren, index + 1, count - index);
3418 children = mChildren;
3419 } else {
3420 System.arraycopy(children, index, children, index + 1, count - index);
3421 }
3422 children[index] = child;
3423 mChildrenCount++;
Joe Onorato03ab0c72011-01-06 15:46:27 -08003424 if (mLastTouchDownIndex >= index) {
3425 mLastTouchDownIndex++;
3426 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003427 } else {
3428 throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
3429 }
3430 }
3431
3432 // This method also sets the child's mParent to null
3433 private void removeFromArray(int index) {
3434 final View[] children = mChildren;
Chet Haase21cd1382010-09-01 17:42:29 -07003435 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
3436 children[index].mParent = null;
3437 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003438 final int count = mChildrenCount;
3439 if (index == count - 1) {
3440 children[--mChildrenCount] = null;
3441 } else if (index >= 0 && index < count) {
3442 System.arraycopy(children, index + 1, children, index, count - index - 1);
3443 children[--mChildrenCount] = null;
3444 } else {
3445 throw new IndexOutOfBoundsException();
3446 }
Joe Onorato03ab0c72011-01-06 15:46:27 -08003447 if (mLastTouchDownIndex == index) {
3448 mLastTouchDownTime = 0;
3449 mLastTouchDownIndex = -1;
3450 } else if (mLastTouchDownIndex > index) {
3451 mLastTouchDownIndex--;
3452 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003453 }
3454
3455 // This method also sets the children's mParent to null
3456 private void removeFromArray(int start, int count) {
3457 final View[] children = mChildren;
3458 final int childrenCount = mChildrenCount;
3459
3460 start = Math.max(0, start);
3461 final int end = Math.min(childrenCount, start + count);
3462
3463 if (start == end) {
3464 return;
3465 }
3466
3467 if (end == childrenCount) {
3468 for (int i = start; i < end; i++) {
3469 children[i].mParent = null;
3470 children[i] = null;
3471 }
3472 } else {
3473 for (int i = start; i < end; i++) {
3474 children[i].mParent = null;
3475 }
3476
3477 // Since we're looping above, we might as well do the copy, but is arraycopy()
3478 // faster than the extra 2 bounds checks we would do in the loop?
3479 System.arraycopy(children, end, children, start, childrenCount - end);
3480
3481 for (int i = childrenCount - (end - start); i < childrenCount; i++) {
3482 children[i] = null;
3483 }
3484 }
3485
3486 mChildrenCount -= (end - start);
3487 }
3488
3489 private void bindLayoutAnimation(View child) {
3490 Animation a = mLayoutAnimationController.getAnimationForView(child);
3491 child.setAnimation(a);
3492 }
3493
3494 /**
3495 * Subclasses should override this method to set layout animation
3496 * parameters on the supplied child.
3497 *
3498 * @param child the child to associate with animation parameters
3499 * @param params the child's layout parameters which hold the animation
3500 * parameters
3501 * @param index the index of the child in the view group
3502 * @param count the number of children in the view group
3503 */
3504 protected void attachLayoutAnimationParameters(View child,
3505 LayoutParams params, int index, int count) {
3506 LayoutAnimationController.AnimationParameters animationParams =
3507 params.layoutAnimationParameters;
3508 if (animationParams == null) {
3509 animationParams = new LayoutAnimationController.AnimationParameters();
3510 params.layoutAnimationParameters = animationParams;
3511 }
3512
3513 animationParams.count = count;
3514 animationParams.index = index;
3515 }
3516
3517 /**
3518 * {@inheritDoc}
3519 */
3520 public void removeView(View view) {
3521 removeViewInternal(view);
3522 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003523 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003524 }
3525
3526 /**
3527 * Removes a view during layout. This is useful if in your onLayout() method,
3528 * you need to remove more views.
3529 *
3530 * @param view the view to remove from the group
3531 */
3532 public void removeViewInLayout(View view) {
3533 removeViewInternal(view);
3534 }
3535
3536 /**
3537 * Removes a range of views during layout. This is useful if in your onLayout() method,
3538 * you need to remove more views.
3539 *
3540 * @param start the index of the first view to remove from the group
3541 * @param count the number of views to remove from the group
3542 */
3543 public void removeViewsInLayout(int start, int count) {
3544 removeViewsInternal(start, count);
3545 }
3546
3547 /**
3548 * Removes the view at the specified position in the group.
3549 *
3550 * @param index the position in the group of the view to remove
3551 */
3552 public void removeViewAt(int index) {
3553 removeViewInternal(index, getChildAt(index));
3554 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003555 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003556 }
3557
3558 /**
3559 * Removes the specified range of views from the group.
3560 *
3561 * @param start the first position in the group of the range of views to remove
3562 * @param count the number of views to remove
3563 */
3564 public void removeViews(int start, int count) {
3565 removeViewsInternal(start, count);
3566 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003567 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003568 }
3569
3570 private void removeViewInternal(View view) {
3571 final int index = indexOfChild(view);
3572 if (index >= 0) {
3573 removeViewInternal(index, view);
3574 }
3575 }
3576
3577 private void removeViewInternal(int index, View view) {
Chet Haase21cd1382010-09-01 17:42:29 -07003578
3579 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003580 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003581 }
3582
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003583 boolean clearChildFocus = false;
3584 if (view == mFocused) {
3585 view.clearFocusForRemoval();
3586 clearChildFocus = true;
3587 }
3588
Chet Haase21cd1382010-09-01 17:42:29 -07003589 if (view.getAnimation() != null ||
3590 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003591 addDisappearingView(view);
3592 } else if (view.mAttachInfo != null) {
3593 view.dispatchDetachedFromWindow();
3594 }
3595
Philip Milnef51d91c2011-07-18 16:12:19 -07003596 onViewRemoved(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003597
3598 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003599
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003600 removeFromArray(index);
3601
3602 if (clearChildFocus) {
3603 clearChildFocus(view);
3604 }
3605 }
3606
Chet Haase21cd1382010-09-01 17:42:29 -07003607 /**
3608 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
3609 * not null, changes in layout which occur because of children being added to or removed from
3610 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
3611 * object. By default, the transition object is null (so layout changes are not animated).
3612 *
3613 * @param transition The LayoutTransition object that will animated changes in layout. A value
3614 * of <code>null</code> means no transition will run on layout changes.
Chet Haase13cc1202010-09-03 15:39:20 -07003615 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
Chet Haase21cd1382010-09-01 17:42:29 -07003616 */
3617 public void setLayoutTransition(LayoutTransition transition) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07003618 if (mTransition != null) {
3619 mTransition.removeTransitionListener(mLayoutTransitionListener);
3620 }
Chet Haase21cd1382010-09-01 17:42:29 -07003621 mTransition = transition;
Chet Haase13cc1202010-09-03 15:39:20 -07003622 if (mTransition != null) {
3623 mTransition.addTransitionListener(mLayoutTransitionListener);
3624 }
3625 }
3626
3627 /**
3628 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
3629 * not null, changes in layout which occur because of children being added to or removed from
3630 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
3631 * object. By default, the transition object is null (so layout changes are not animated).
3632 *
3633 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
3634 * A value of <code>null</code> means no transition will run on layout changes.
3635 */
3636 public LayoutTransition getLayoutTransition() {
3637 return mTransition;
Chet Haase21cd1382010-09-01 17:42:29 -07003638 }
3639
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003640 private void removeViewsInternal(int start, int count) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003641 final View focused = mFocused;
3642 final boolean detach = mAttachInfo != null;
3643 View clearChildFocus = null;
3644
3645 final View[] children = mChildren;
3646 final int end = start + count;
3647
3648 for (int i = start; i < end; i++) {
3649 final View view = children[i];
3650
Chet Haase21cd1382010-09-01 17:42:29 -07003651 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003652 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003653 }
3654
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003655 if (view == focused) {
3656 view.clearFocusForRemoval();
3657 clearChildFocus = view;
3658 }
3659
Chet Haase21cd1382010-09-01 17:42:29 -07003660 if (view.getAnimation() != null ||
3661 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003662 addDisappearingView(view);
3663 } else if (detach) {
3664 view.dispatchDetachedFromWindow();
3665 }
3666
3667 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003668
Philip Milnef51d91c2011-07-18 16:12:19 -07003669 onViewRemoved(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003670 }
3671
3672 removeFromArray(start, count);
3673
3674 if (clearChildFocus != null) {
3675 clearChildFocus(clearChildFocus);
3676 }
3677 }
3678
3679 /**
3680 * Call this method to remove all child views from the
3681 * ViewGroup.
3682 */
3683 public void removeAllViews() {
3684 removeAllViewsInLayout();
3685 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003686 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003687 }
3688
3689 /**
3690 * Called by a ViewGroup subclass to remove child views from itself,
3691 * when it must first know its size on screen before it can calculate how many
3692 * child views it will render. An example is a Gallery or a ListView, which
3693 * may "have" 50 children, but actually only render the number of children
3694 * that can currently fit inside the object on screen. Do not call
3695 * this method unless you are extending ViewGroup and understand the
3696 * view measuring and layout pipeline.
3697 */
3698 public void removeAllViewsInLayout() {
3699 final int count = mChildrenCount;
3700 if (count <= 0) {
3701 return;
3702 }
3703
3704 final View[] children = mChildren;
3705 mChildrenCount = 0;
3706
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003707 final View focused = mFocused;
3708 final boolean detach = mAttachInfo != null;
3709 View clearChildFocus = null;
3710
3711 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003712
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003713 for (int i = count - 1; i >= 0; i--) {
3714 final View view = children[i];
3715
Chet Haase21cd1382010-09-01 17:42:29 -07003716 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003717 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003718 }
3719
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003720 if (view == focused) {
3721 view.clearFocusForRemoval();
3722 clearChildFocus = view;
3723 }
3724
Chet Haase21cd1382010-09-01 17:42:29 -07003725 if (view.getAnimation() != null ||
3726 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003727 addDisappearingView(view);
3728 } else if (detach) {
3729 view.dispatchDetachedFromWindow();
3730 }
3731
Philip Milnef51d91c2011-07-18 16:12:19 -07003732 onViewRemoved(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003733
3734 view.mParent = null;
3735 children[i] = null;
3736 }
3737
3738 if (clearChildFocus != null) {
3739 clearChildFocus(clearChildFocus);
3740 }
3741 }
3742
3743 /**
3744 * Finishes the removal of a detached view. This method will dispatch the detached from
3745 * window event and notify the hierarchy change listener.
3746 *
3747 * @param child the child to be definitely removed from the view hierarchy
3748 * @param animate if true and the view has an animation, the view is placed in the
3749 * disappearing views list, otherwise, it is detached from the window
3750 *
3751 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3752 * @see #detachAllViewsFromParent()
3753 * @see #detachViewFromParent(View)
3754 * @see #detachViewFromParent(int)
3755 */
3756 protected void removeDetachedView(View child, boolean animate) {
Chet Haase21cd1382010-09-01 17:42:29 -07003757 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003758 mTransition.removeChild(this, child);
Chet Haase21cd1382010-09-01 17:42:29 -07003759 }
3760
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003761 if (child == mFocused) {
3762 child.clearFocus();
3763 }
Romain Guy8506ab42009-06-11 17:35:47 -07003764
Chet Haase21cd1382010-09-01 17:42:29 -07003765 if ((animate && child.getAnimation() != null) ||
3766 (mTransitioningViews != null && mTransitioningViews.contains(child))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003767 addDisappearingView(child);
3768 } else if (child.mAttachInfo != null) {
3769 child.dispatchDetachedFromWindow();
3770 }
3771
Philip Milnef51d91c2011-07-18 16:12:19 -07003772 onViewRemoved(child);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003773 }
3774
3775 /**
3776 * Attaches a view to this view group. Attaching a view assigns this group as the parent,
3777 * sets the layout parameters and puts the view in the list of children so it can be retrieved
3778 * by calling {@link #getChildAt(int)}.
3779 *
3780 * This method should be called only for view which were detached from their parent.
3781 *
3782 * @param child the child to attach
3783 * @param index the index at which the child should be attached
3784 * @param params the layout parameters of the child
3785 *
3786 * @see #removeDetachedView(View, boolean)
3787 * @see #detachAllViewsFromParent()
3788 * @see #detachViewFromParent(View)
3789 * @see #detachViewFromParent(int)
3790 */
3791 protected void attachViewToParent(View child, int index, LayoutParams params) {
3792 child.mLayoutParams = params;
3793
3794 if (index < 0) {
3795 index = mChildrenCount;
3796 }
3797
3798 addInArray(child, index);
3799
3800 child.mParent = this;
Chet Haase3b2b0fc2011-01-24 17:53:52 -08003801 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK & ~DRAWING_CACHE_VALID) |
3802 DRAWN | INVALIDATED;
3803 this.mPrivateFlags |= INVALIDATED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003804
3805 if (child.hasFocus()) {
3806 requestChildFocus(child, child.findFocus());
3807 }
3808 }
3809
3810 /**
3811 * Detaches a view from its parent. Detaching a view should be temporary and followed
3812 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3813 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3814 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3815 *
3816 * @param child the child to detach
3817 *
3818 * @see #detachViewFromParent(int)
3819 * @see #detachViewsFromParent(int, int)
3820 * @see #detachAllViewsFromParent()
3821 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3822 * @see #removeDetachedView(View, boolean)
3823 */
3824 protected void detachViewFromParent(View child) {
3825 removeFromArray(indexOfChild(child));
3826 }
3827
3828 /**
3829 * Detaches a view from its parent. Detaching a view should be temporary and followed
3830 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3831 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3832 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3833 *
3834 * @param index the index of the child to detach
3835 *
3836 * @see #detachViewFromParent(View)
3837 * @see #detachAllViewsFromParent()
3838 * @see #detachViewsFromParent(int, int)
3839 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3840 * @see #removeDetachedView(View, boolean)
3841 */
3842 protected void detachViewFromParent(int index) {
3843 removeFromArray(index);
3844 }
3845
3846 /**
3847 * Detaches a range of view from their parent. Detaching a view should be temporary and followed
3848 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3849 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its
3850 * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3851 *
3852 * @param start the first index of the childrend range to detach
3853 * @param count the number of children to detach
3854 *
3855 * @see #detachViewFromParent(View)
3856 * @see #detachViewFromParent(int)
3857 * @see #detachAllViewsFromParent()
3858 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3859 * @see #removeDetachedView(View, boolean)
3860 */
3861 protected void detachViewsFromParent(int start, int count) {
3862 removeFromArray(start, count);
3863 }
3864
3865 /**
3866 * Detaches all views from the parent. Detaching a view should be temporary and followed
3867 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3868 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3869 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3870 *
3871 * @see #detachViewFromParent(View)
3872 * @see #detachViewFromParent(int)
3873 * @see #detachViewsFromParent(int, int)
3874 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3875 * @see #removeDetachedView(View, boolean)
3876 */
3877 protected void detachAllViewsFromParent() {
3878 final int count = mChildrenCount;
3879 if (count <= 0) {
3880 return;
3881 }
3882
3883 final View[] children = mChildren;
3884 mChildrenCount = 0;
3885
3886 for (int i = count - 1; i >= 0; i--) {
3887 children[i].mParent = null;
3888 children[i] = null;
3889 }
3890 }
3891
3892 /**
3893 * Don't call or override this method. It is used for the implementation of
3894 * the view hierarchy.
3895 */
3896 public final void invalidateChild(View child, final Rect dirty) {
3897 if (ViewDebug.TRACE_HIERARCHY) {
3898 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
3899 }
3900
3901 ViewParent parent = this;
3902
3903 final AttachInfo attachInfo = mAttachInfo;
3904 if (attachInfo != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003905 // If the child is drawing an animation, we want to copy this flag onto
3906 // ourselves and the parent to make sure the invalidate request goes
3907 // through
3908 final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
Romain Guy24443ea2009-05-11 11:56:30 -07003909
Chet Haase70d4ba12010-10-06 09:46:45 -07003910 if (dirty == null) {
Chet Haasedaf98e92011-01-10 14:10:36 -08003911 if (child.mLayerType != LAYER_TYPE_NONE) {
3912 mPrivateFlags |= INVALIDATED;
3913 mPrivateFlags &= ~DRAWING_CACHE_VALID;
Romain Guy3a3133d2011-02-01 22:59:58 -08003914 child.mLocalDirtyRect.setEmpty();
Chet Haasedaf98e92011-01-10 14:10:36 -08003915 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003916 do {
3917 View view = null;
3918 if (parent instanceof View) {
3919 view = (View) parent;
Romain Guy3a3133d2011-02-01 22:59:58 -08003920 if (view.mLayerType != LAYER_TYPE_NONE) {
3921 view.mLocalDirtyRect.setEmpty();
3922 if (view.getParent() instanceof View) {
3923 final View grandParent = (View) view.getParent();
3924 grandParent.mPrivateFlags |= INVALIDATED;
3925 grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3926 }
Chet Haasedaf98e92011-01-10 14:10:36 -08003927 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003928 if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
3929 // already marked dirty - we're done
3930 break;
3931 }
3932 }
3933
3934 if (drawAnimation) {
3935 if (view != null) {
3936 view.mPrivateFlags |= DRAW_ANIMATION;
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07003937 } else if (parent instanceof ViewRootImpl) {
3938 ((ViewRootImpl) parent).mIsAnimating = true;
Chet Haase70d4ba12010-10-06 09:46:45 -07003939 }
3940 }
3941
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07003942 if (parent instanceof ViewRootImpl) {
3943 ((ViewRootImpl) parent).invalidate();
Chet Haase70d4ba12010-10-06 09:46:45 -07003944 parent = null;
3945 } else if (view != null) {
Chet Haase77785f92011-01-25 23:22:09 -08003946 if ((view.mPrivateFlags & DRAWN) == DRAWN ||
3947 (view.mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
Chet Haase70d4ba12010-10-06 09:46:45 -07003948 view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
Chet Haase0d200832010-11-05 15:36:16 -07003949 view.mPrivateFlags |= DIRTY;
Chet Haase70d4ba12010-10-06 09:46:45 -07003950 parent = view.mParent;
3951 } else {
3952 parent = null;
3953 }
3954 }
3955 } while (parent != null);
3956 } else {
Chet Haase0d200832010-11-05 15:36:16 -07003957 // Check whether the child that requests the invalidate is fully opaque
Chet Haase8c39def2011-11-29 13:40:39 -08003958 // Views being animated or transformed are not considered opaque because we may
3959 // be invalidating their old position and need the parent to paint behind them.
3960 Matrix childMatrix = child.getMatrix();
Chet Haase0d200832010-11-05 15:36:16 -07003961 final boolean isOpaque = child.isOpaque() && !drawAnimation &&
Chet Haase8c39def2011-11-29 13:40:39 -08003962 child.getAnimation() == null && childMatrix.isIdentity();
Chet Haase0d200832010-11-05 15:36:16 -07003963 // Mark the child as dirty, using the appropriate flag
3964 // Make sure we do not set both flags at the same time
Romain Guy7e68efb2011-01-07 14:50:27 -08003965 int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
Chet Haase0d200832010-11-05 15:36:16 -07003966
Romain Guybeff8d82011-02-01 23:53:34 -08003967 if (child.mLayerType != LAYER_TYPE_NONE) {
3968 mPrivateFlags |= INVALIDATED;
3969 mPrivateFlags &= ~DRAWING_CACHE_VALID;
3970 child.mLocalDirtyRect.union(dirty);
3971 }
3972
Chet Haase70d4ba12010-10-06 09:46:45 -07003973 final int[] location = attachInfo.mInvalidateChildLocation;
3974 location[CHILD_LEFT_INDEX] = child.mLeft;
3975 location[CHILD_TOP_INDEX] = child.mTop;
Chet Haase70d4ba12010-10-06 09:46:45 -07003976 if (!childMatrix.isIdentity()) {
3977 RectF boundingRect = attachInfo.mTmpTransformRect;
3978 boundingRect.set(dirty);
Romain Guya9489272011-06-22 20:58:11 -07003979 //boundingRect.inset(-0.5f, -0.5f);
Chet Haase70d4ba12010-10-06 09:46:45 -07003980 childMatrix.mapRect(boundingRect);
Romain Guya9489272011-06-22 20:58:11 -07003981 dirty.set((int) (boundingRect.left - 0.5f),
3982 (int) (boundingRect.top - 0.5f),
Chet Haase70d4ba12010-10-06 09:46:45 -07003983 (int) (boundingRect.right + 0.5f),
3984 (int) (boundingRect.bottom + 0.5f));
Romain Guy24443ea2009-05-11 11:56:30 -07003985 }
3986
Chet Haase70d4ba12010-10-06 09:46:45 -07003987 do {
3988 View view = null;
3989 if (parent instanceof View) {
3990 view = (View) parent;
Romain Guy7d7b5492011-01-24 16:33:45 -08003991 if (view.mLayerType != LAYER_TYPE_NONE &&
3992 view.getParent() instanceof View) {
3993 final View grandParent = (View) view.getParent();
3994 grandParent.mPrivateFlags |= INVALIDATED;
3995 grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3996 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003997 }
3998
3999 if (drawAnimation) {
4000 if (view != null) {
4001 view.mPrivateFlags |= DRAW_ANIMATION;
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07004002 } else if (parent instanceof ViewRootImpl) {
4003 ((ViewRootImpl) parent).mIsAnimating = true;
Chet Haase70d4ba12010-10-06 09:46:45 -07004004 }
4005 }
4006
4007 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
4008 // flag coming from the child that initiated the invalidate
Romain Guy7e68efb2011-01-07 14:50:27 -08004009 if (view != null) {
4010 if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
Romain Guy2243e552011-03-08 11:46:28 -08004011 view.getSolidColor() == 0) {
Romain Guy7e68efb2011-01-07 14:50:27 -08004012 opaqueFlag = DIRTY;
4013 }
4014 if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
4015 view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
4016 }
Chet Haase70d4ba12010-10-06 09:46:45 -07004017 }
4018
4019 parent = parent.invalidateChildInParent(location, dirty);
Romain Guy24443ea2009-05-11 11:56:30 -07004020 if (view != null) {
Chet Haase70d4ba12010-10-06 09:46:45 -07004021 // Account for transform on current parent
4022 Matrix m = view.getMatrix();
4023 if (!m.isIdentity()) {
4024 RectF boundingRect = attachInfo.mTmpTransformRect;
4025 boundingRect.set(dirty);
4026 m.mapRect(boundingRect);
4027 dirty.set((int) boundingRect.left, (int) boundingRect.top,
4028 (int) (boundingRect.right + 0.5f),
4029 (int) (boundingRect.bottom + 0.5f));
4030 }
Romain Guybb93d552009-03-24 21:04:15 -07004031 }
Chet Haase70d4ba12010-10-06 09:46:45 -07004032 } while (parent != null);
4033 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004034 }
4035 }
4036
4037 /**
4038 * Don't call or override this method. It is used for the implementation of
4039 * the view hierarchy.
4040 *
4041 * This implementation returns null if this ViewGroup does not have a parent,
4042 * if this ViewGroup is already fully invalidated or if the dirty rectangle
4043 * does not intersect with this ViewGroup's bounds.
4044 */
4045 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
4046 if (ViewDebug.TRACE_HIERARCHY) {
4047 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
4048 }
4049
Chet Haase77785f92011-01-25 23:22:09 -08004050 if ((mPrivateFlags & DRAWN) == DRAWN ||
4051 (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004052 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
4053 FLAG_OPTIMIZE_INVALIDATE) {
4054 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
4055 location[CHILD_TOP_INDEX] - mScrollY);
4056
4057 final int left = mLeft;
4058 final int top = mTop;
4059
Chet Haasea3db8662011-07-19 10:36:05 -07004060 if ((mGroupFlags & FLAG_CLIP_CHILDREN) != FLAG_CLIP_CHILDREN ||
4061 dirty.intersect(0, 0, mRight - left, mBottom - top) ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004062 (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
4063 mPrivateFlags &= ~DRAWING_CACHE_VALID;
4064
4065 location[CHILD_LEFT_INDEX] = left;
4066 location[CHILD_TOP_INDEX] = top;
4067
Romain Guy3a3133d2011-02-01 22:59:58 -08004068 if (mLayerType != LAYER_TYPE_NONE) {
4069 mLocalDirtyRect.union(dirty);
4070 }
Romain Guybeff8d82011-02-01 23:53:34 -08004071
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004072 return mParent;
4073 }
4074 } else {
4075 mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
4076
4077 location[CHILD_LEFT_INDEX] = mLeft;
4078 location[CHILD_TOP_INDEX] = mTop;
Chet Haasea3db8662011-07-19 10:36:05 -07004079 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
4080 dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
4081 } else {
4082 // in case the dirty rect extends outside the bounds of this container
4083 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
4084 }
Romain Guy3a3133d2011-02-01 22:59:58 -08004085
4086 if (mLayerType != LAYER_TYPE_NONE) {
4087 mLocalDirtyRect.union(dirty);
4088 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004089
4090 return mParent;
4091 }
4092 }
4093
4094 return null;
4095 }
4096
4097 /**
4098 * Offset a rectangle that is in a descendant's coordinate
4099 * space into our coordinate space.
4100 * @param descendant A descendant of this view
4101 * @param rect A rectangle defined in descendant's coordinate space.
4102 */
4103 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
4104 offsetRectBetweenParentAndChild(descendant, rect, true, false);
4105 }
4106
4107 /**
4108 * Offset a rectangle that is in our coordinate space into an ancestor's
4109 * coordinate space.
4110 * @param descendant A descendant of this view
4111 * @param rect A rectangle defined in descendant's coordinate space.
4112 */
4113 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
4114 offsetRectBetweenParentAndChild(descendant, rect, false, false);
4115 }
4116
4117 /**
4118 * Helper method that offsets a rect either from parent to descendant or
4119 * descendant to parent.
4120 */
4121 void offsetRectBetweenParentAndChild(View descendant, Rect rect,
4122 boolean offsetFromChildToParent, boolean clipToBounds) {
4123
4124 // already in the same coord system :)
4125 if (descendant == this) {
4126 return;
4127 }
4128
4129 ViewParent theParent = descendant.mParent;
4130
4131 // search and offset up to the parent
4132 while ((theParent != null)
4133 && (theParent instanceof View)
4134 && (theParent != this)) {
4135
4136 if (offsetFromChildToParent) {
4137 rect.offset(descendant.mLeft - descendant.mScrollX,
4138 descendant.mTop - descendant.mScrollY);
4139 if (clipToBounds) {
4140 View p = (View) theParent;
4141 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4142 }
4143 } else {
4144 if (clipToBounds) {
4145 View p = (View) theParent;
4146 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4147 }
4148 rect.offset(descendant.mScrollX - descendant.mLeft,
4149 descendant.mScrollY - descendant.mTop);
4150 }
4151
4152 descendant = (View) theParent;
4153 theParent = descendant.mParent;
4154 }
4155
4156 // now that we are up to this view, need to offset one more time
4157 // to get into our coordinate space
4158 if (theParent == this) {
4159 if (offsetFromChildToParent) {
4160 rect.offset(descendant.mLeft - descendant.mScrollX,
4161 descendant.mTop - descendant.mScrollY);
4162 } else {
4163 rect.offset(descendant.mScrollX - descendant.mLeft,
4164 descendant.mScrollY - descendant.mTop);
4165 }
4166 } else {
4167 throw new IllegalArgumentException("parameter must be a descendant of this view");
4168 }
4169 }
4170
4171 /**
4172 * Offset the vertical location of all children of this view by the specified number of pixels.
4173 *
4174 * @param offset the number of pixels to offset
4175 *
4176 * @hide
4177 */
4178 public void offsetChildrenTopAndBottom(int offset) {
4179 final int count = mChildrenCount;
4180 final View[] children = mChildren;
4181
4182 for (int i = 0; i < count; i++) {
4183 final View v = children[i];
4184 v.mTop += offset;
4185 v.mBottom += offset;
4186 }
4187 }
4188
4189 /**
4190 * {@inheritDoc}
4191 */
4192 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
Gilles Debunnecea45132011-11-24 02:19:27 +01004193 // The View is not attached to a window, 'visible' does not make sense, return false
4194 if (mAttachInfo == null) return false;
4195
4196 final RectF rect = mAttachInfo.mTmpTransformRect;
4197 rect.set(r);
4198
4199 if (!child.hasIdentityMatrix()) {
4200 child.getMatrix().mapRect(rect);
4201 }
4202
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004203 int dx = child.mLeft - mScrollX;
4204 int dy = child.mTop - mScrollY;
Gilles Debunnecea45132011-11-24 02:19:27 +01004205
4206 rect.offset(dx, dy);
4207
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004208 if (offset != null) {
Gilles Debunnecea45132011-11-24 02:19:27 +01004209 if (!child.hasIdentityMatrix()) {
4210 float[] position = mAttachInfo.mTmpTransformLocation;
4211 position[0] = offset.x;
4212 position[1] = offset.y;
4213 child.getMatrix().mapPoints(position);
4214 offset.x = (int) (position[0] + 0.5f);
4215 offset.y = (int) (position[1] + 0.5f);
4216 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004217 offset.x += dx;
4218 offset.y += dy;
4219 }
Gilles Debunnecea45132011-11-24 02:19:27 +01004220
4221 if (rect.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
4222 if (mParent == null) return true;
4223 r.set((int) (rect.left + 0.5f), (int) (rect.top + 0.5f),
4224 (int) (rect.right + 0.5f), (int) (rect.bottom + 0.5f));
4225 return mParent.getChildVisibleRect(this, r, offset);
4226 }
4227
4228 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004229 }
4230
4231 /**
4232 * {@inheritDoc}
4233 */
4234 @Override
Chet Haase9c087442011-01-12 16:20:16 -08004235 public final void layout(int l, int t, int r, int b) {
4236 if (mTransition == null || !mTransition.isChangingLayout()) {
4237 super.layout(l, t, r, b);
4238 } else {
4239 // record the fact that we noop'd it; request layout when transition finishes
4240 mLayoutSuppressed = true;
4241 }
4242 }
4243
4244 /**
4245 * {@inheritDoc}
4246 */
4247 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004248 protected abstract void onLayout(boolean changed,
4249 int l, int t, int r, int b);
4250
4251 /**
4252 * Indicates whether the view group has the ability to animate its children
4253 * after the first layout.
4254 *
4255 * @return true if the children can be animated, false otherwise
4256 */
4257 protected boolean canAnimate() {
4258 return mLayoutAnimationController != null;
4259 }
4260
4261 /**
4262 * Runs the layout animation. Calling this method triggers a relayout of
4263 * this view group.
4264 */
4265 public void startLayoutAnimation() {
4266 if (mLayoutAnimationController != null) {
4267 mGroupFlags |= FLAG_RUN_ANIMATION;
4268 requestLayout();
4269 }
4270 }
4271
4272 /**
4273 * Schedules the layout animation to be played after the next layout pass
4274 * of this view group. This can be used to restart the layout animation
4275 * when the content of the view group changes or when the activity is
4276 * paused and resumed.
4277 */
4278 public void scheduleLayoutAnimation() {
4279 mGroupFlags |= FLAG_RUN_ANIMATION;
4280 }
4281
4282 /**
4283 * Sets the layout animation controller used to animate the group's
4284 * children after the first layout.
4285 *
4286 * @param controller the animation controller
4287 */
4288 public void setLayoutAnimation(LayoutAnimationController controller) {
4289 mLayoutAnimationController = controller;
4290 if (mLayoutAnimationController != null) {
4291 mGroupFlags |= FLAG_RUN_ANIMATION;
4292 }
4293 }
4294
4295 /**
4296 * Returns the layout animation controller used to animate the group's
4297 * children.
4298 *
4299 * @return the current animation controller
4300 */
4301 public LayoutAnimationController getLayoutAnimation() {
4302 return mLayoutAnimationController;
4303 }
4304
4305 /**
4306 * Indicates whether the children's drawing cache is used during a layout
4307 * animation. By default, the drawing cache is enabled but this will prevent
4308 * nested layout animations from working. To nest animations, you must disable
4309 * the cache.
4310 *
4311 * @return true if the animation cache is enabled, false otherwise
4312 *
4313 * @see #setAnimationCacheEnabled(boolean)
4314 * @see View#setDrawingCacheEnabled(boolean)
4315 */
4316 @ViewDebug.ExportedProperty
4317 public boolean isAnimationCacheEnabled() {
4318 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
4319 }
4320
4321 /**
4322 * Enables or disables the children's drawing cache during a layout animation.
4323 * By default, the drawing cache is enabled but this will prevent nested
4324 * layout animations from working. To nest animations, you must disable the
4325 * cache.
4326 *
4327 * @param enabled true to enable the animation cache, false otherwise
4328 *
4329 * @see #isAnimationCacheEnabled()
4330 * @see View#setDrawingCacheEnabled(boolean)
4331 */
4332 public void setAnimationCacheEnabled(boolean enabled) {
4333 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
4334 }
4335
4336 /**
4337 * Indicates whether this ViewGroup will always try to draw its children using their
4338 * drawing cache. By default this property is enabled.
4339 *
4340 * @return true if the animation cache is enabled, false otherwise
4341 *
4342 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4343 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4344 * @see View#setDrawingCacheEnabled(boolean)
4345 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004346 @ViewDebug.ExportedProperty(category = "drawing")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004347 public boolean isAlwaysDrawnWithCacheEnabled() {
4348 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
4349 }
4350
4351 /**
4352 * Indicates whether this ViewGroup will always try to draw its children using their
4353 * drawing cache. This property can be set to true when the cache rendering is
4354 * slightly different from the children's normal rendering. Renderings can be different,
4355 * for instance, when the cache's quality is set to low.
4356 *
4357 * When this property is disabled, the ViewGroup will use the drawing cache of its
4358 * children only when asked to. It's usually the task of subclasses to tell ViewGroup
4359 * when to start using the drawing cache and when to stop using it.
4360 *
4361 * @param always true to always draw with the drawing cache, false otherwise
4362 *
4363 * @see #isAlwaysDrawnWithCacheEnabled()
4364 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4365 * @see View#setDrawingCacheEnabled(boolean)
4366 * @see View#setDrawingCacheQuality(int)
4367 */
4368 public void setAlwaysDrawnWithCacheEnabled(boolean always) {
4369 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
4370 }
4371
4372 /**
4373 * Indicates whether the ViewGroup is currently drawing its children using
4374 * their drawing cache.
4375 *
4376 * @return true if children should be drawn with their cache, false otherwise
4377 *
4378 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4379 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4380 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004381 @ViewDebug.ExportedProperty(category = "drawing")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004382 protected boolean isChildrenDrawnWithCacheEnabled() {
4383 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
4384 }
4385
4386 /**
4387 * Tells the ViewGroup to draw its children using their drawing cache. This property
4388 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
4389 * will be used only if it has been enabled.
4390 *
4391 * Subclasses should call this method to start and stop using the drawing cache when
4392 * they perform performance sensitive operations, like scrolling or animating.
4393 *
4394 * @param enabled true if children should be drawn with their cache, false otherwise
4395 *
4396 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4397 * @see #isChildrenDrawnWithCacheEnabled()
4398 */
4399 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
4400 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
4401 }
4402
Romain Guy293451e2009-11-04 13:59:48 -08004403 /**
4404 * Indicates whether the ViewGroup is drawing its children in the order defined by
4405 * {@link #getChildDrawingOrder(int, int)}.
4406 *
4407 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
4408 * false otherwise
4409 *
4410 * @see #setChildrenDrawingOrderEnabled(boolean)
4411 * @see #getChildDrawingOrder(int, int)
4412 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004413 @ViewDebug.ExportedProperty(category = "drawing")
Romain Guy293451e2009-11-04 13:59:48 -08004414 protected boolean isChildrenDrawingOrderEnabled() {
4415 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
4416 }
4417
4418 /**
4419 * Tells the ViewGroup whether to draw its children in the order defined by the method
4420 * {@link #getChildDrawingOrder(int, int)}.
4421 *
4422 * @param enabled true if the order of the children when drawing is determined by
4423 * {@link #getChildDrawingOrder(int, int)}, false otherwise
4424 *
4425 * @see #isChildrenDrawingOrderEnabled()
4426 * @see #getChildDrawingOrder(int, int)
4427 */
4428 protected void setChildrenDrawingOrderEnabled(boolean enabled) {
4429 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
4430 }
4431
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004432 private void setBooleanFlag(int flag, boolean value) {
4433 if (value) {
4434 mGroupFlags |= flag;
4435 } else {
4436 mGroupFlags &= ~flag;
4437 }
4438 }
4439
4440 /**
4441 * Returns an integer indicating what types of drawing caches are kept in memory.
4442 *
4443 * @see #setPersistentDrawingCache(int)
4444 * @see #setAnimationCacheEnabled(boolean)
4445 *
4446 * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
4447 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
4448 * and {@link #PERSISTENT_ALL_CACHES}
4449 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004450 @ViewDebug.ExportedProperty(category = "drawing", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004451 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
Romain Guy203688c2010-05-12 15:41:32 -07004452 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004453 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
4454 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL")
4455 })
4456 public int getPersistentDrawingCache() {
4457 return mPersistentDrawingCache;
4458 }
4459
4460 /**
4461 * Indicates what types of drawing caches should be kept in memory after
4462 * they have been created.
4463 *
4464 * @see #getPersistentDrawingCache()
4465 * @see #setAnimationCacheEnabled(boolean)
4466 *
4467 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
4468 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
4469 * and {@link #PERSISTENT_ALL_CACHES}
4470 */
4471 public void setPersistentDrawingCache(int drawingCacheToKeep) {
4472 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
4473 }
4474
4475 /**
4476 * Returns a new set of layout parameters based on the supplied attributes set.
4477 *
4478 * @param attrs the attributes to build the layout parameters from
4479 *
4480 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
4481 * of its descendants
4482 */
4483 public LayoutParams generateLayoutParams(AttributeSet attrs) {
4484 return new LayoutParams(getContext(), attrs);
4485 }
4486
4487 /**
4488 * Returns a safe set of layout parameters based on the supplied layout params.
4489 * When a ViewGroup is passed a View whose layout params do not pass the test of
4490 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
4491 * is invoked. This method should return a new set of layout params suitable for
4492 * this ViewGroup, possibly by copying the appropriate attributes from the
4493 * specified set of layout params.
4494 *
4495 * @param p The layout parameters to convert into a suitable set of layout parameters
4496 * for this ViewGroup.
4497 *
4498 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
4499 * of its descendants
4500 */
4501 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4502 return p;
4503 }
4504
4505 /**
4506 * Returns a set of default layout parameters. These parameters are requested
4507 * when the View passed to {@link #addView(View)} has no layout parameters
4508 * already set. If null is returned, an exception is thrown from addView.
4509 *
4510 * @return a set of default layout parameters or null
4511 */
4512 protected LayoutParams generateDefaultLayoutParams() {
4513 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
4514 }
4515
4516 /**
Romain Guy13922e02009-05-12 17:56:14 -07004517 * @hide
4518 */
4519 @Override
4520 protected boolean dispatchConsistencyCheck(int consistency) {
4521 boolean result = super.dispatchConsistencyCheck(consistency);
4522
4523 final int count = mChildrenCount;
4524 final View[] children = mChildren;
4525 for (int i = 0; i < count; i++) {
4526 if (!children[i].dispatchConsistencyCheck(consistency)) result = false;
4527 }
4528
4529 return result;
4530 }
4531
4532 /**
4533 * @hide
4534 */
4535 @Override
4536 protected boolean onConsistencyCheck(int consistency) {
4537 boolean result = super.onConsistencyCheck(consistency);
4538
4539 final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
4540 final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0;
4541
4542 if (checkLayout) {
4543 final int count = mChildrenCount;
4544 final View[] children = mChildren;
4545 for (int i = 0; i < count; i++) {
4546 if (children[i].getParent() != this) {
4547 result = false;
4548 android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
4549 "View " + children[i] + " has no parent/a parent that is not " + this);
4550 }
4551 }
4552 }
4553
4554 if (checkDrawing) {
4555 // If this group is dirty, check that the parent is dirty as well
4556 if ((mPrivateFlags & DIRTY_MASK) != 0) {
4557 final ViewParent parent = getParent();
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07004558 if (parent != null && !(parent instanceof ViewRootImpl)) {
Romain Guy13922e02009-05-12 17:56:14 -07004559 if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) {
4560 result = false;
4561 android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
4562 "ViewGroup " + this + " is dirty but its parent is not: " + this);
4563 }
4564 }
4565 }
4566 }
4567
4568 return result;
4569 }
4570
4571 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004572 * {@inheritDoc}
4573 */
4574 @Override
4575 protected void debug(int depth) {
4576 super.debug(depth);
4577 String output;
4578
4579 if (mFocused != null) {
4580 output = debugIndent(depth);
4581 output += "mFocused";
4582 Log.d(VIEW_LOG_TAG, output);
4583 }
4584 if (mChildrenCount != 0) {
4585 output = debugIndent(depth);
4586 output += "{";
4587 Log.d(VIEW_LOG_TAG, output);
4588 }
4589 int count = mChildrenCount;
4590 for (int i = 0; i < count; i++) {
4591 View child = mChildren[i];
4592 child.debug(depth + 1);
4593 }
4594
4595 if (mChildrenCount != 0) {
4596 output = debugIndent(depth);
4597 output += "}";
4598 Log.d(VIEW_LOG_TAG, output);
4599 }
4600 }
4601
4602 /**
4603 * Returns the position in the group of the specified child view.
4604 *
4605 * @param child the view for which to get the position
4606 * @return a positive integer representing the position of the view in the
4607 * group, or -1 if the view does not exist in the group
4608 */
4609 public int indexOfChild(View child) {
4610 final int count = mChildrenCount;
4611 final View[] children = mChildren;
4612 for (int i = 0; i < count; i++) {
4613 if (children[i] == child) {
4614 return i;
4615 }
4616 }
4617 return -1;
4618 }
4619
4620 /**
4621 * Returns the number of children in the group.
4622 *
4623 * @return a positive integer representing the number of children in
4624 * the group
4625 */
4626 public int getChildCount() {
4627 return mChildrenCount;
4628 }
4629
4630 /**
4631 * Returns the view at the specified position in the group.
4632 *
4633 * @param index the position at which to get the view from
4634 * @return the view at the specified position or null if the position
4635 * does not exist within the group
4636 */
4637 public View getChildAt(int index) {
Adam Powell3ba8f5d62011-03-07 15:36:33 -08004638 if (index < 0 || index >= mChildrenCount) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004639 return null;
4640 }
Adam Powell3ba8f5d62011-03-07 15:36:33 -08004641 return mChildren[index];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004642 }
4643
4644 /**
4645 * Ask all of the children of this view to measure themselves, taking into
4646 * account both the MeasureSpec requirements for this view and its padding.
4647 * We skip children that are in the GONE state The heavy lifting is done in
4648 * getChildMeasureSpec.
4649 *
4650 * @param widthMeasureSpec The width requirements for this view
4651 * @param heightMeasureSpec The height requirements for this view
4652 */
4653 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
4654 final int size = mChildrenCount;
4655 final View[] children = mChildren;
4656 for (int i = 0; i < size; ++i) {
4657 final View child = children[i];
4658 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
4659 measureChild(child, widthMeasureSpec, heightMeasureSpec);
4660 }
4661 }
4662 }
4663
4664 /**
4665 * Ask one of the children of this view to measure itself, taking into
4666 * account both the MeasureSpec requirements for this view and its padding.
4667 * The heavy lifting is done in getChildMeasureSpec.
4668 *
4669 * @param child The child to measure
4670 * @param parentWidthMeasureSpec The width requirements for this view
4671 * @param parentHeightMeasureSpec The height requirements for this view
4672 */
4673 protected void measureChild(View child, int parentWidthMeasureSpec,
4674 int parentHeightMeasureSpec) {
4675 final LayoutParams lp = child.getLayoutParams();
4676
4677 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
4678 mPaddingLeft + mPaddingRight, lp.width);
4679 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
4680 mPaddingTop + mPaddingBottom, lp.height);
4681
4682 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4683 }
4684
4685 /**
4686 * Ask one of the children of this view to measure itself, taking into
4687 * account both the MeasureSpec requirements for this view and its padding
4688 * and margins. The child must have MarginLayoutParams The heavy lifting is
4689 * done in getChildMeasureSpec.
4690 *
4691 * @param child The child to measure
4692 * @param parentWidthMeasureSpec The width requirements for this view
4693 * @param widthUsed Extra space that has been used up by the parent
4694 * horizontally (possibly by other children of the parent)
4695 * @param parentHeightMeasureSpec The height requirements for this view
4696 * @param heightUsed Extra space that has been used up by the parent
4697 * vertically (possibly by other children of the parent)
4698 */
4699 protected void measureChildWithMargins(View child,
4700 int parentWidthMeasureSpec, int widthUsed,
4701 int parentHeightMeasureSpec, int heightUsed) {
4702 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
4703
4704 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
4705 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
4706 + widthUsed, lp.width);
4707 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
4708 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
4709 + heightUsed, lp.height);
4710
4711 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4712 }
4713
4714 /**
4715 * Does the hard part of measureChildren: figuring out the MeasureSpec to
4716 * pass to a particular child. This method figures out the right MeasureSpec
4717 * for one dimension (height or width) of one child view.
4718 *
4719 * The goal is to combine information from our MeasureSpec with the
4720 * LayoutParams of the child to get the best possible results. For example,
4721 * if the this view knows its size (because its MeasureSpec has a mode of
4722 * EXACTLY), and the child has indicated in its LayoutParams that it wants
4723 * to be the same size as the parent, the parent should ask the child to
4724 * layout given an exact size.
4725 *
4726 * @param spec The requirements for this view
4727 * @param padding The padding of this view for the current dimension and
4728 * margins, if applicable
4729 * @param childDimension How big the child wants to be in the current
4730 * dimension
4731 * @return a MeasureSpec integer for the child
4732 */
4733 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
4734 int specMode = MeasureSpec.getMode(spec);
4735 int specSize = MeasureSpec.getSize(spec);
4736
4737 int size = Math.max(0, specSize - padding);
4738
4739 int resultSize = 0;
4740 int resultMode = 0;
4741
4742 switch (specMode) {
4743 // Parent has imposed an exact size on us
4744 case MeasureSpec.EXACTLY:
4745 if (childDimension >= 0) {
4746 resultSize = childDimension;
4747 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08004748 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004749 // Child wants to be our size. So be it.
4750 resultSize = size;
4751 resultMode = MeasureSpec.EXACTLY;
4752 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4753 // Child wants to determine its own size. It can't be
4754 // bigger than us.
4755 resultSize = size;
4756 resultMode = MeasureSpec.AT_MOST;
4757 }
4758 break;
4759
4760 // Parent has imposed a maximum size on us
4761 case MeasureSpec.AT_MOST:
4762 if (childDimension >= 0) {
4763 // Child wants a specific size... so be it
4764 resultSize = childDimension;
4765 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08004766 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004767 // Child wants to be our size, but our size is not fixed.
4768 // Constrain child to not be bigger than us.
4769 resultSize = size;
4770 resultMode = MeasureSpec.AT_MOST;
4771 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4772 // Child wants to determine its own size. It can't be
4773 // bigger than us.
4774 resultSize = size;
4775 resultMode = MeasureSpec.AT_MOST;
4776 }
4777 break;
4778
4779 // Parent asked to see how big we want to be
4780 case MeasureSpec.UNSPECIFIED:
4781 if (childDimension >= 0) {
4782 // Child wants a specific size... let him have it
4783 resultSize = childDimension;
4784 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08004785 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004786 // Child wants to be our size... find out how big it should
4787 // be
4788 resultSize = 0;
4789 resultMode = MeasureSpec.UNSPECIFIED;
4790 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4791 // Child wants to determine its own size.... find out how
4792 // big it should be
4793 resultSize = 0;
4794 resultMode = MeasureSpec.UNSPECIFIED;
4795 }
4796 break;
4797 }
4798 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
4799 }
4800
4801
4802 /**
4803 * Removes any pending animations for views that have been removed. Call
4804 * this if you don't want animations for exiting views to stack up.
4805 */
4806 public void clearDisappearingChildren() {
4807 if (mDisappearingChildren != null) {
4808 mDisappearingChildren.clear();
4809 }
4810 }
4811
4812 /**
4813 * Add a view which is removed from mChildren but still needs animation
4814 *
4815 * @param v View to add
4816 */
4817 private void addDisappearingView(View v) {
4818 ArrayList<View> disappearingChildren = mDisappearingChildren;
4819
4820 if (disappearingChildren == null) {
4821 disappearingChildren = mDisappearingChildren = new ArrayList<View>();
4822 }
4823
4824 disappearingChildren.add(v);
4825 }
4826
4827 /**
4828 * Cleanup a view when its animation is done. This may mean removing it from
4829 * the list of disappearing views.
4830 *
4831 * @param view The view whose animation has finished
4832 * @param animation The animation, cannot be null
4833 */
4834 private void finishAnimatingView(final View view, Animation animation) {
4835 final ArrayList<View> disappearingChildren = mDisappearingChildren;
4836 if (disappearingChildren != null) {
4837 if (disappearingChildren.contains(view)) {
4838 disappearingChildren.remove(view);
4839
4840 if (view.mAttachInfo != null) {
4841 view.dispatchDetachedFromWindow();
4842 }
4843
4844 view.clearAnimation();
4845 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4846 }
4847 }
4848
4849 if (animation != null && !animation.getFillAfter()) {
4850 view.clearAnimation();
4851 }
4852
4853 if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) {
4854 view.onAnimationEnd();
4855 // Should be performed by onAnimationEnd() but this avoid an infinite loop,
4856 // so we'd rather be safe than sorry
4857 view.mPrivateFlags &= ~ANIMATION_STARTED;
4858 // Draw one more frame after the animation is done
4859 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4860 }
4861 }
4862
Chet Haaseb20db3e2010-09-10 13:07:30 -07004863 /**
Chet Haaseaceafe62011-08-26 15:44:33 -07004864 * Utility function called by View during invalidation to determine whether a view that
4865 * is invisible or gone should still be invalidated because it is being transitioned (and
4866 * therefore still needs to be drawn).
4867 */
4868 boolean isViewTransitioning(View view) {
4869 return (mTransitioningViews != null && mTransitioningViews.contains(view));
4870 }
4871
4872 /**
Chet Haaseb20db3e2010-09-10 13:07:30 -07004873 * This method tells the ViewGroup that the given View object, which should have this
4874 * ViewGroup as its parent,
4875 * should be kept around (re-displayed when the ViewGroup draws its children) even if it
4876 * is removed from its parent. This allows animations, such as those used by
4877 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
4878 * the removal of views. A call to this method should always be accompanied by a later call
4879 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
4880 * so that the View finally gets removed.
4881 *
4882 * @param view The View object to be kept visible even if it gets removed from its parent.
4883 */
4884 public void startViewTransition(View view) {
4885 if (view.mParent == this) {
4886 if (mTransitioningViews == null) {
4887 mTransitioningViews = new ArrayList<View>();
4888 }
4889 mTransitioningViews.add(view);
4890 }
4891 }
4892
4893 /**
4894 * This method should always be called following an earlier call to
4895 * {@link #startViewTransition(View)}. The given View is finally removed from its parent
4896 * and will no longer be displayed. Note that this method does not perform the functionality
4897 * of removing a view from its parent; it just discontinues the display of a View that
4898 * has previously been removed.
4899 *
4900 * @return view The View object that has been removed but is being kept around in the visible
4901 * hierarchy by an earlier call to {@link #startViewTransition(View)}.
4902 */
4903 public void endViewTransition(View view) {
4904 if (mTransitioningViews != null) {
4905 mTransitioningViews.remove(view);
4906 final ArrayList<View> disappearingChildren = mDisappearingChildren;
4907 if (disappearingChildren != null && disappearingChildren.contains(view)) {
4908 disappearingChildren.remove(view);
Chet Haase5e25c2c2010-09-16 11:15:56 -07004909 if (mVisibilityChangingChildren != null &&
4910 mVisibilityChangingChildren.contains(view)) {
4911 mVisibilityChangingChildren.remove(view);
4912 } else {
4913 if (view.mAttachInfo != null) {
4914 view.dispatchDetachedFromWindow();
4915 }
4916 if (view.mParent != null) {
4917 view.mParent = null;
4918 }
Chet Haaseb20db3e2010-09-10 13:07:30 -07004919 }
4920 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4921 }
4922 }
4923 }
4924
Chet Haase21cd1382010-09-01 17:42:29 -07004925 private LayoutTransition.TransitionListener mLayoutTransitionListener =
4926 new LayoutTransition.TransitionListener() {
4927 @Override
4928 public void startTransition(LayoutTransition transition, ViewGroup container,
4929 View view, int transitionType) {
4930 // We only care about disappearing items, since we need special logic to keep
4931 // those items visible after they've been 'removed'
4932 if (transitionType == LayoutTransition.DISAPPEARING) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07004933 startViewTransition(view);
Chet Haase21cd1382010-09-01 17:42:29 -07004934 }
4935 }
4936
4937 @Override
4938 public void endTransition(LayoutTransition transition, ViewGroup container,
4939 View view, int transitionType) {
Chet Haase9c087442011-01-12 16:20:16 -08004940 if (mLayoutSuppressed && !transition.isChangingLayout()) {
4941 requestLayout();
4942 mLayoutSuppressed = false;
4943 }
Chet Haase21cd1382010-09-01 17:42:29 -07004944 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07004945 endViewTransition(view);
Chet Haase21cd1382010-09-01 17:42:29 -07004946 }
4947 }
4948 };
4949
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004950 /**
4951 * {@inheritDoc}
4952 */
4953 @Override
4954 public boolean gatherTransparentRegion(Region region) {
4955 // If no transparent regions requested, we are always opaque.
4956 final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0;
4957 if (meOpaque && region == null) {
4958 // The caller doesn't care about the region, so stop now.
4959 return true;
4960 }
4961 super.gatherTransparentRegion(region);
4962 final View[] children = mChildren;
4963 final int count = mChildrenCount;
4964 boolean noneOfTheChildrenAreTransparent = true;
4965 for (int i = 0; i < count; i++) {
4966 final View child = children[i];
Mathias Agopiane3381152010-12-02 15:19:36 -08004967 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004968 if (!child.gatherTransparentRegion(region)) {
4969 noneOfTheChildrenAreTransparent = false;
4970 }
4971 }
4972 }
4973 return meOpaque || noneOfTheChildrenAreTransparent;
4974 }
4975
4976 /**
4977 * {@inheritDoc}
4978 */
4979 public void requestTransparentRegion(View child) {
4980 if (child != null) {
4981 child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
4982 if (mParent != null) {
4983 mParent.requestTransparentRegion(this);
4984 }
4985 }
4986 }
Romain Guy8506ab42009-06-11 17:35:47 -07004987
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004988
4989 @Override
4990 protected boolean fitSystemWindows(Rect insets) {
4991 boolean done = super.fitSystemWindows(insets);
4992 if (!done) {
4993 final int count = mChildrenCount;
4994 final View[] children = mChildren;
4995 for (int i = 0; i < count; i++) {
4996 done = children[i].fitSystemWindows(insets);
4997 if (done) {
4998 break;
4999 }
5000 }
5001 }
5002 return done;
5003 }
5004
5005 /**
5006 * Returns the animation listener to which layout animation events are
5007 * sent.
5008 *
5009 * @return an {@link android.view.animation.Animation.AnimationListener}
5010 */
5011 public Animation.AnimationListener getLayoutAnimationListener() {
5012 return mAnimationListener;
5013 }
5014
5015 @Override
5016 protected void drawableStateChanged() {
5017 super.drawableStateChanged();
5018
5019 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
5020 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
5021 throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
5022 + " child has duplicateParentState set to true");
5023 }
5024
5025 final View[] children = mChildren;
5026 final int count = mChildrenCount;
5027
5028 for (int i = 0; i < count; i++) {
5029 final View child = children[i];
5030 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
5031 child.refreshDrawableState();
5032 }
5033 }
5034 }
5035 }
5036
5037 @Override
Dianne Hackborne2136772010-11-04 15:08:59 -07005038 public void jumpDrawablesToCurrentState() {
5039 super.jumpDrawablesToCurrentState();
5040 final View[] children = mChildren;
5041 final int count = mChildrenCount;
5042 for (int i = 0; i < count; i++) {
5043 children[i].jumpDrawablesToCurrentState();
5044 }
5045 }
5046
5047 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005048 protected int[] onCreateDrawableState(int extraSpace) {
5049 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
5050 return super.onCreateDrawableState(extraSpace);
5051 }
5052
5053 int need = 0;
5054 int n = getChildCount();
5055 for (int i = 0; i < n; i++) {
5056 int[] childState = getChildAt(i).getDrawableState();
5057
5058 if (childState != null) {
5059 need += childState.length;
5060 }
5061 }
5062
5063 int[] state = super.onCreateDrawableState(extraSpace + need);
5064
5065 for (int i = 0; i < n; i++) {
5066 int[] childState = getChildAt(i).getDrawableState();
5067
5068 if (childState != null) {
5069 state = mergeDrawableStates(state, childState);
5070 }
5071 }
5072
5073 return state;
5074 }
5075
5076 /**
5077 * Sets whether this ViewGroup's drawable states also include
5078 * its children's drawable states. This is used, for example, to
5079 * make a group appear to be focused when its child EditText or button
5080 * is focused.
5081 */
5082 public void setAddStatesFromChildren(boolean addsStates) {
5083 if (addsStates) {
5084 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
5085 } else {
5086 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
5087 }
5088
5089 refreshDrawableState();
5090 }
5091
5092 /**
5093 * Returns whether this ViewGroup's drawable states also include
5094 * its children's drawable states. This is used, for example, to
5095 * make a group appear to be focused when its child EditText or button
5096 * is focused.
5097 */
5098 public boolean addStatesFromChildren() {
5099 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
5100 }
5101
5102 /**
5103 * If {link #addStatesFromChildren} is true, refreshes this group's
5104 * drawable state (to include the states from its children).
5105 */
5106 public void childDrawableStateChanged(View child) {
5107 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
5108 refreshDrawableState();
5109 }
5110 }
5111
5112 /**
5113 * Specifies the animation listener to which layout animation events must
5114 * be sent. Only
5115 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
5116 * and
5117 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
5118 * are invoked.
5119 *
5120 * @param animationListener the layout animation listener
5121 */
5122 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
5123 mAnimationListener = animationListener;
5124 }
5125
5126 /**
Chet Haasecca2c982011-05-20 14:34:18 -07005127 * This method is called by LayoutTransition when there are 'changing' animations that need
5128 * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
5129 * starts all pending transitions prior to the drawing phase in the current traversal.
5130 *
5131 * @param transition The LayoutTransition to be started on the next traversal.
5132 *
5133 * @hide
5134 */
5135 public void requestTransitionStart(LayoutTransition transition) {
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07005136 ViewRootImpl viewAncestor = getViewRootImpl();
Chet Haase1abf7fa2011-08-17 18:31:56 -07005137 if (viewAncestor != null) {
5138 viewAncestor.requestTransitionStart(transition);
5139 }
Chet Haasecca2c982011-05-20 14:34:18 -07005140 }
5141
Fabrice Di Meglio80dc53d2011-06-21 18:36:33 -07005142 @Override
Fabrice Di Meglio7f86c802011-07-01 15:09:24 -07005143 protected void resetResolvedLayoutDirection() {
5144 super.resetResolvedLayoutDirection();
Fabrice Di Meglio80dc53d2011-06-21 18:36:33 -07005145
5146 // Take care of resetting the children resolution too
5147 final int count = getChildCount();
5148 for (int i = 0; i < count; i++) {
5149 final View child = getChildAt(i);
5150 if (child.getLayoutDirection() == LAYOUT_DIRECTION_INHERIT) {
Fabrice Di Meglio7f86c802011-07-01 15:09:24 -07005151 child.resetResolvedLayoutDirection();
Fabrice Di Meglio80dc53d2011-06-21 18:36:33 -07005152 }
5153 }
5154 }
5155
Fabrice Di Meglio22268862011-06-27 18:13:18 -07005156 @Override
5157 protected void resetResolvedTextDirection() {
5158 super.resetResolvedTextDirection();
5159
5160 // Take care of resetting the children resolution too
5161 final int count = getChildCount();
5162 for (int i = 0; i < count; i++) {
5163 final View child = getChildAt(i);
5164 if (child.getTextDirection() == TEXT_DIRECTION_INHERIT) {
5165 child.resetResolvedTextDirection();
5166 }
5167 }
5168 }
5169
5170 /**
Patrick Dubroye0a799a2011-05-04 16:19:22 -07005171 * Return true if the pressed state should be delayed for children or descendants of this
5172 * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
5173 * This prevents the pressed state from appearing when the user is actually trying to scroll
5174 * the content.
5175 *
5176 * The default implementation returns true for compatibility reasons. Subclasses that do
5177 * not scroll should generally override this method and return false.
5178 */
5179 public boolean shouldDelayChildPressedState() {
5180 return true;
5181 }
5182
5183 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005184 * LayoutParams are used by views to tell their parents how they want to be
5185 * laid out. See
5186 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
5187 * for a list of all child view attributes that this class supports.
Romain Guy8506ab42009-06-11 17:35:47 -07005188 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005189 * <p>
5190 * The base LayoutParams class just describes how big the view wants to be
5191 * for both width and height. For each dimension, it can specify one of:
5192 * <ul>
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005193 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
5194 * means that the view wants to be as big as its parent (minus padding)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005195 * <li> WRAP_CONTENT, which means that the view wants to be just big enough
5196 * to enclose its content (plus padding)
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005197 * <li> an exact number
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005198 * </ul>
5199 * There are subclasses of LayoutParams for different subclasses of
5200 * ViewGroup. For example, AbsoluteLayout has its own subclass of
Joe Fernandez558459f2011-10-13 16:47:36 -07005201 * LayoutParams which adds an X and Y value.</p>
5202 *
5203 * <div class="special reference">
5204 * <h3>Developer Guides</h3>
5205 * <p>For more information about creating user interface layouts, read the
5206 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
5207 * guide.</p></div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005208 *
5209 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
5210 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
5211 */
5212 public static class LayoutParams {
5213 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005214 * Special value for the height or width requested by a View.
5215 * FILL_PARENT means that the view wants to be as big as its parent,
5216 * minus the parent's padding, if any. This value is deprecated
5217 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005218 */
Romain Guy980a9382010-01-08 15:06:28 -08005219 @SuppressWarnings({"UnusedDeclaration"})
5220 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005221 public static final int FILL_PARENT = -1;
5222
5223 /**
5224 * Special value for the height or width requested by a View.
Gilles Debunnef5c6eff2010-02-09 19:08:36 -08005225 * MATCH_PARENT means that the view wants to be as big as its parent,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005226 * minus the parent's padding, if any. Introduced in API Level 8.
Romain Guy980a9382010-01-08 15:06:28 -08005227 */
5228 public static final int MATCH_PARENT = -1;
5229
5230 /**
5231 * Special value for the height or width requested by a View.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005232 * WRAP_CONTENT means that the view wants to be just large enough to fit
5233 * its own internal content, taking its own padding into account.
5234 */
5235 public static final int WRAP_CONTENT = -2;
5236
5237 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005238 * Information about how wide the view wants to be. Can be one of the
5239 * constants FILL_PARENT (replaced by MATCH_PARENT ,
5240 * in API Level 8) or WRAP_CONTENT. or an exact size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005241 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005242 @ViewDebug.ExportedProperty(category = "layout", mapping = {
Romain Guy980a9382010-01-08 15:06:28 -08005243 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005244 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
5245 })
5246 public int width;
5247
5248 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005249 * Information about how tall the view wants to be. Can be one of the
5250 * constants FILL_PARENT (replaced by MATCH_PARENT ,
5251 * in API Level 8) or WRAP_CONTENT. or an exact size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005252 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005253 @ViewDebug.ExportedProperty(category = "layout", mapping = {
Romain Guy980a9382010-01-08 15:06:28 -08005254 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005255 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
5256 })
5257 public int height;
5258
5259 /**
5260 * Used to animate layouts.
5261 */
5262 public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
5263
5264 /**
5265 * Creates a new set of layout parameters. The values are extracted from
5266 * the supplied attributes set and context. The XML attributes mapped
5267 * to this set of layout parameters are:
5268 *
5269 * <ul>
5270 * <li><code>layout_width</code>: the width, either an exact value,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005271 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
5272 * {@link #MATCH_PARENT} in API Level 8)</li>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005273 * <li><code>layout_height</code>: the height, either an exact value,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005274 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
5275 * {@link #MATCH_PARENT} in API Level 8)</li>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005276 * </ul>
5277 *
5278 * @param c the application environment
5279 * @param attrs the set of attributes from which to extract the layout
5280 * parameters' values
5281 */
5282 public LayoutParams(Context c, AttributeSet attrs) {
5283 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
5284 setBaseAttributes(a,
5285 R.styleable.ViewGroup_Layout_layout_width,
5286 R.styleable.ViewGroup_Layout_layout_height);
5287 a.recycle();
5288 }
5289
5290 /**
5291 * Creates a new set of layout parameters with the specified width
5292 * and height.
5293 *
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005294 * @param width the width, either {@link #WRAP_CONTENT},
5295 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
5296 * API Level 8), or a fixed size in pixels
5297 * @param height the height, either {@link #WRAP_CONTENT},
5298 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
5299 * API Level 8), or a fixed size in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005300 */
5301 public LayoutParams(int width, int height) {
5302 this.width = width;
5303 this.height = height;
5304 }
5305
5306 /**
5307 * Copy constructor. Clones the width and height values of the source.
5308 *
5309 * @param source The layout params to copy from.
5310 */
5311 public LayoutParams(LayoutParams source) {
5312 this.width = source.width;
5313 this.height = source.height;
5314 }
5315
5316 /**
5317 * Used internally by MarginLayoutParams.
5318 * @hide
5319 */
5320 LayoutParams() {
5321 }
5322
5323 /**
5324 * Extracts the layout parameters from the supplied attributes.
5325 *
5326 * @param a the style attributes to extract the parameters from
5327 * @param widthAttr the identifier of the width attribute
5328 * @param heightAttr the identifier of the height attribute
5329 */
5330 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
5331 width = a.getLayoutDimension(widthAttr, "layout_width");
5332 height = a.getLayoutDimension(heightAttr, "layout_height");
5333 }
5334
5335 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005336 * Resolve layout parameters depending on the layout direction. Subclasses that care about
5337 * layoutDirection changes should override this method. The default implementation does
5338 * nothing.
5339 *
5340 * @param layoutDirection the direction of the layout
5341 *
5342 * {@link View#LAYOUT_DIRECTION_LTR}
5343 * {@link View#LAYOUT_DIRECTION_RTL}
5344 *
5345 * @hide
5346 */
5347 protected void resolveWithDirection(int layoutDirection) {
5348 }
5349
5350 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005351 * Returns a String representation of this set of layout parameters.
5352 *
5353 * @param output the String to prepend to the internal representation
5354 * @return a String with the following format: output +
5355 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
Romain Guy8506ab42009-06-11 17:35:47 -07005356 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005357 * @hide
5358 */
5359 public String debug(String output) {
5360 return output + "ViewGroup.LayoutParams={ width="
5361 + sizeToString(width) + ", height=" + sizeToString(height) + " }";
5362 }
5363
5364 /**
5365 * Converts the specified size to a readable String.
5366 *
5367 * @param size the size to convert
5368 * @return a String instance representing the supplied size
Romain Guy8506ab42009-06-11 17:35:47 -07005369 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005370 * @hide
5371 */
5372 protected static String sizeToString(int size) {
5373 if (size == WRAP_CONTENT) {
5374 return "wrap-content";
5375 }
Romain Guy980a9382010-01-08 15:06:28 -08005376 if (size == MATCH_PARENT) {
5377 return "match-parent";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005378 }
5379 return String.valueOf(size);
5380 }
5381 }
5382
5383 /**
5384 * Per-child layout information for layouts that support margins.
5385 * See
5386 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
5387 * for a list of all child view attributes that this class supports.
5388 */
5389 public static class MarginLayoutParams extends ViewGroup.LayoutParams {
5390 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005391 * The left margin in pixels of the child. Whenever this value is changed, a call to
5392 * {@link android.view.View#requestLayout()} needs to be done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005393 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005394 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005395 public int leftMargin;
5396
5397 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005398 * The top margin in pixels of the child. Whenever this value is changed, a call to
5399 * {@link android.view.View#requestLayout()} needs to be done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005400 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005401 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005402 public int topMargin;
5403
5404 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005405 * The right margin in pixels of the child. Whenever this value is changed, a call to
5406 * {@link android.view.View#requestLayout()} needs to be done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005407 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005408 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005409 public int rightMargin;
5410
5411 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005412 * The bottom margin in pixels of the child. Whenever this value is changed, a call to
5413 * {@link android.view.View#requestLayout()} needs to be done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005414 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005415 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005416 public int bottomMargin;
5417
5418 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005419 * The start margin in pixels of the child.
5420 *
5421 * @hide
5422 *
5423 */
5424 @ViewDebug.ExportedProperty(category = "layout")
5425 protected int startMargin = DEFAULT_RELATIVE;
5426
5427 /**
5428 * The end margin in pixels of the child.
5429 *
5430 * @hide
5431 */
5432 @ViewDebug.ExportedProperty(category = "layout")
5433 protected int endMargin = DEFAULT_RELATIVE;
5434
5435 /**
5436 * The default start and end margin.
5437 */
5438 static private final int DEFAULT_RELATIVE = Integer.MIN_VALUE;
5439
5440 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005441 * Creates a new set of layout parameters. The values are extracted from
5442 * the supplied attributes set and context.
5443 *
5444 * @param c the application environment
5445 * @param attrs the set of attributes from which to extract the layout
5446 * parameters' values
5447 */
5448 public MarginLayoutParams(Context c, AttributeSet attrs) {
5449 super();
5450
5451 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
5452 setBaseAttributes(a,
5453 R.styleable.ViewGroup_MarginLayout_layout_width,
5454 R.styleable.ViewGroup_MarginLayout_layout_height);
5455
5456 int margin = a.getDimensionPixelSize(
5457 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
5458 if (margin >= 0) {
5459 leftMargin = margin;
5460 topMargin = margin;
5461 rightMargin= margin;
5462 bottomMargin = margin;
5463 } else {
5464 leftMargin = a.getDimensionPixelSize(
5465 R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
5466 topMargin = a.getDimensionPixelSize(
5467 R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
5468 rightMargin = a.getDimensionPixelSize(
5469 R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
5470 bottomMargin = a.getDimensionPixelSize(
5471 R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005472 startMargin = a.getDimensionPixelSize(
5473 R.styleable.ViewGroup_MarginLayout_layout_marginStart, DEFAULT_RELATIVE);
5474 endMargin = a.getDimensionPixelSize(
5475 R.styleable.ViewGroup_MarginLayout_layout_marginEnd, DEFAULT_RELATIVE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005476 }
5477
5478 a.recycle();
5479 }
5480
5481 /**
5482 * {@inheritDoc}
5483 */
5484 public MarginLayoutParams(int width, int height) {
5485 super(width, height);
5486 }
5487
5488 /**
5489 * Copy constructor. Clones the width, height and margin values of the source.
5490 *
5491 * @param source The layout params to copy from.
5492 */
5493 public MarginLayoutParams(MarginLayoutParams source) {
5494 this.width = source.width;
5495 this.height = source.height;
5496
5497 this.leftMargin = source.leftMargin;
5498 this.topMargin = source.topMargin;
5499 this.rightMargin = source.rightMargin;
5500 this.bottomMargin = source.bottomMargin;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005501 this.startMargin = source.startMargin;
5502 this.endMargin = source.endMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005503 }
5504
5505 /**
5506 * {@inheritDoc}
5507 */
5508 public MarginLayoutParams(LayoutParams source) {
5509 super(source);
5510 }
5511
5512 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005513 * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
5514 * to be done so that the new margins are taken into account. Left and right margins may be
5515 * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005516 *
5517 * @param left the left margin size
5518 * @param top the top margin size
5519 * @param right the right margin size
5520 * @param bottom the bottom margin size
5521 *
5522 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
5523 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
5524 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
5525 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
5526 */
5527 public void setMargins(int left, int top, int right, int bottom) {
5528 leftMargin = left;
5529 topMargin = top;
5530 rightMargin = right;
5531 bottomMargin = bottom;
5532 }
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005533
5534 /**
5535 * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
5536 * needs to be done so that the new relative margins are taken into account. Left and right
5537 * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout
5538 * direction.
5539 *
5540 * @param start the start margin size
5541 * @param top the top margin size
5542 * @param end the right margin size
5543 * @param bottom the bottom margin size
5544 *
5545 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5546 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
5547 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5548 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
5549 *
5550 * @hide
5551 */
5552 public void setMarginsRelative(int start, int top, int end, int bottom) {
5553 startMargin = start;
5554 topMargin = top;
5555 endMargin = end;
5556 bottomMargin = bottom;
5557 }
5558
5559 /**
5560 * Returns the start margin in pixels.
5561 *
5562 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5563 *
5564 * @return the start margin in pixels.
5565 *
5566 * @hide
5567 */
5568 public int getMarginStart() {
5569 return startMargin;
5570 }
5571
5572 /**
5573 * Returns the end margin in pixels.
5574 *
5575 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5576 *
5577 * @return the end margin in pixels.
5578 *
5579 * @hide
5580 */
5581 public int getMarginEnd() {
5582 return endMargin;
5583 }
5584
5585 /**
5586 * Check if margins are relative.
5587 *
5588 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5589 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5590 *
5591 * @return true if either marginStart or marginEnd has been set
5592 *
5593 * @hide
5594 */
5595 public boolean isMarginRelative() {
5596 return (startMargin != DEFAULT_RELATIVE) || (endMargin != DEFAULT_RELATIVE);
5597 }
5598
5599 /**
5600 * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
5601 * maybe overriden depending on layout direction.
5602 *
5603 * @hide
5604 */
5605 @Override
5606 protected void resolveWithDirection(int layoutDirection) {
5607 switch(layoutDirection) {
5608 case View.LAYOUT_DIRECTION_RTL:
5609 leftMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : leftMargin;
5610 rightMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : rightMargin;
5611 break;
5612 case View.LAYOUT_DIRECTION_LTR:
5613 default:
5614 leftMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : leftMargin;
5615 rightMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : rightMargin;
5616 break;
5617 }
5618 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005619 }
Adam Powell2b342f02010-08-18 18:14:13 -07005620
Jeff Brown20e987b2010-08-23 12:01:02 -07005621 /* Describes a touched view and the ids of the pointers that it has captured.
5622 *
5623 * This code assumes that pointer ids are always in the range 0..31 such that
5624 * it can use a bitfield to track which pointer ids are present.
5625 * As it happens, the lower layers of the input dispatch pipeline also use the
5626 * same trick so the assumption should be safe here...
5627 */
5628 private static final class TouchTarget {
5629 private static final int MAX_RECYCLED = 32;
5630 private static final Object sRecycleLock = new Object();
5631 private static TouchTarget sRecycleBin;
5632 private static int sRecycledCount;
Adam Powell2b342f02010-08-18 18:14:13 -07005633
Jeff Brown20e987b2010-08-23 12:01:02 -07005634 public static final int ALL_POINTER_IDS = -1; // all ones
Adam Powell2b342f02010-08-18 18:14:13 -07005635
Jeff Brown20e987b2010-08-23 12:01:02 -07005636 // The touched child view.
5637 public View child;
5638
5639 // The combined bit mask of pointer ids for all pointers captured by the target.
5640 public int pointerIdBits;
5641
5642 // The next target in the target list.
5643 public TouchTarget next;
5644
5645 private TouchTarget() {
Adam Powell2b342f02010-08-18 18:14:13 -07005646 }
5647
Jeff Brown20e987b2010-08-23 12:01:02 -07005648 public static TouchTarget obtain(View child, int pointerIdBits) {
5649 final TouchTarget target;
5650 synchronized (sRecycleLock) {
Adam Powell816c3be2010-08-23 18:00:05 -07005651 if (sRecycleBin == null) {
Jeff Brown20e987b2010-08-23 12:01:02 -07005652 target = new TouchTarget();
Adam Powell816c3be2010-08-23 18:00:05 -07005653 } else {
Jeff Brown20e987b2010-08-23 12:01:02 -07005654 target = sRecycleBin;
5655 sRecycleBin = target.next;
5656 sRecycledCount--;
5657 target.next = null;
Adam Powell816c3be2010-08-23 18:00:05 -07005658 }
Adam Powell816c3be2010-08-23 18:00:05 -07005659 }
Jeff Brown20e987b2010-08-23 12:01:02 -07005660 target.child = child;
5661 target.pointerIdBits = pointerIdBits;
5662 return target;
5663 }
Adam Powell816c3be2010-08-23 18:00:05 -07005664
Jeff Brown20e987b2010-08-23 12:01:02 -07005665 public void recycle() {
5666 synchronized (sRecycleLock) {
5667 if (sRecycledCount < MAX_RECYCLED) {
5668 next = sRecycleBin;
5669 sRecycleBin = this;
5670 sRecycledCount += 1;
Patrick Dubroyfb0547d22010-10-19 17:36:18 -07005671 } else {
5672 next = null;
Adam Powell816c3be2010-08-23 18:00:05 -07005673 }
Patrick Dubroyfb0547d22010-10-19 17:36:18 -07005674 child = null;
Adam Powell816c3be2010-08-23 18:00:05 -07005675 }
5676 }
Adam Powell2b342f02010-08-18 18:14:13 -07005677 }
Jeff Brown87b7f802011-06-21 18:35:45 -07005678
5679 /* Describes a hovered view. */
5680 private static final class HoverTarget {
5681 private static final int MAX_RECYCLED = 32;
5682 private static final Object sRecycleLock = new Object();
5683 private static HoverTarget sRecycleBin;
5684 private static int sRecycledCount;
5685
5686 // The hovered child view.
5687 public View child;
5688
5689 // The next target in the target list.
5690 public HoverTarget next;
5691
5692 private HoverTarget() {
5693 }
5694
5695 public static HoverTarget obtain(View child) {
5696 final HoverTarget target;
5697 synchronized (sRecycleLock) {
5698 if (sRecycleBin == null) {
5699 target = new HoverTarget();
5700 } else {
5701 target = sRecycleBin;
5702 sRecycleBin = target.next;
5703 sRecycledCount--;
5704 target.next = null;
5705 }
5706 }
5707 target.child = child;
5708 return target;
5709 }
5710
5711 public void recycle() {
5712 synchronized (sRecycleLock) {
5713 if (sRecycledCount < MAX_RECYCLED) {
5714 next = sRecycleBin;
5715 sRecycleBin = this;
5716 sRecycledCount += 1;
5717 } else {
5718 next = null;
5719 }
5720 child = null;
5721 }
5722 }
5723 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005724}