blob: 35cca4f3b98a9b4db232cf126ae63a70acb3621a [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;
Romain Guy0211a0a2011-02-14 16:34:59 -080038import android.view.animation.AlphaAnimation;
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;
Romain Guy0211a0a2011-02-14 16:34:59 -080043import com.android.internal.R;
44import com.android.internal.util.Predicate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045
46import java.util.ArrayList;
Christopher Tate86cab1b2011-01-13 20:28:55 -080047import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
49/**
50 * <p>
51 * A <code>ViewGroup</code> is a special view that can contain other views
52 * (called children.) The view group is the base class for layouts and views
53 * containers. This class also defines the
54 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
55 * class for layouts parameters.
56 * </p>
57 *
58 * <p>
59 * Also see {@link LayoutParams} for layout attributes.
60 * </p>
Romain Guyd6a463a2009-05-21 23:10:10 -070061 *
62 * @attr ref android.R.styleable#ViewGroup_clipChildren
63 * @attr ref android.R.styleable#ViewGroup_clipToPadding
64 * @attr ref android.R.styleable#ViewGroup_layoutAnimation
65 * @attr ref android.R.styleable#ViewGroup_animationCache
66 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
67 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
68 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
69 * @attr ref android.R.styleable#ViewGroup_descendantFocusability
Chet Haase13cc1202010-09-03 15:39:20 -070070 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 */
72public abstract class ViewGroup extends View implements ViewParent, ViewManager {
Chet Haase21cd1382010-09-01 17:42:29 -070073
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 private static final boolean DBG = false;
Romain Guydbc26d22010-10-11 17:58:29 -070075
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 /**
77 * Views which have been hidden or removed which need to be animated on
78 * their way out.
79 * This field should be made private, so it is hidden from the SDK.
80 * {@hide}
81 */
82 protected ArrayList<View> mDisappearingChildren;
83
84 /**
85 * Listener used to propagate events indicating when children are added
86 * and/or removed from a view group.
87 * This field should be made private, so it is hidden from the SDK.
88 * {@hide}
89 */
90 protected OnHierarchyChangeListener mOnHierarchyChangeListener;
91
92 // The view contained within this ViewGroup that has or contains focus.
93 private View mFocused;
94
Chet Haase48460322010-06-11 14:22:25 -070095 /**
96 * A Transformation used when drawing children, to
97 * apply on the child being drawn.
98 */
99 private final Transformation mChildTransformation = new Transformation();
100
101 /**
102 * Used to track the current invalidation region.
103 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 private RectF mInvalidateRegion;
105
Chet Haase48460322010-06-11 14:22:25 -0700106 /**
107 * A Transformation used to calculate a correct
108 * invalidation area when the application is autoscaled.
109 */
110 private Transformation mInvalidationTransformation;
111
Christopher Tatea53146c2010-09-07 11:57:52 -0700112 // View currently under an ongoing drag
113 private View mCurrentDragView;
114
Christopher Tate86cab1b2011-01-13 20:28:55 -0800115 // Metadata about the ongoing drag
116 private DragEvent mCurrentDrag;
117 private HashSet<View> mDragNotifiedChildren;
118
Christopher Tatea53146c2010-09-07 11:57:52 -0700119 // Does this group have a child that can accept the current drag payload?
120 private boolean mChildAcceptsDrag;
121
122 // Used during drag dispatch
123 private final PointF mLocalPoint = new PointF();
124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 // Layout animation
126 private LayoutAnimationController mLayoutAnimationController;
127 private Animation.AnimationListener mAnimationListener;
128
Jeff Brown20e987b2010-08-23 12:01:02 -0700129 // First touch target in the linked list of touch targets.
130 private TouchTarget mFirstTouchTarget;
131
132 // Temporary arrays for splitting pointers.
133 private int[] mTmpPointerIndexMap;
134 private int[] mTmpPointerIds;
135 private MotionEvent.PointerCoords[] mTmpPointerCoords;
136
Joe Onorato03ab0c72011-01-06 15:46:27 -0800137 // For debugging only. You can see these in hierarchyviewer.
Romain Guye95003e2011-01-09 13:53:06 -0800138 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800139 @ViewDebug.ExportedProperty(category = "events")
140 private long mLastTouchDownTime;
141 @ViewDebug.ExportedProperty(category = "events")
142 private int mLastTouchDownIndex = -1;
Romain Guye95003e2011-01-09 13:53:06 -0800143 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800144 @ViewDebug.ExportedProperty(category = "events")
145 private float mLastTouchDownX;
Romain Guye95003e2011-01-09 13:53:06 -0800146 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800147 @ViewDebug.ExportedProperty(category = "events")
148 private float mLastTouchDownY;
149
Jeff Browna032cc02011-03-07 16:56:21 -0800150 // Child which last received ACTION_HOVER_ENTER and ACTION_HOVER_MOVE.
151 private View mHoveredChild;
152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 /**
154 * Internal flags.
Romain Guy8506ab42009-06-11 17:35:47 -0700155 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 * This field should be made private, so it is hidden from the SDK.
157 * {@hide}
158 */
159 protected int mGroupFlags;
160
161 // When set, ViewGroup invalidates only the child's rectangle
162 // Set by default
163 private static final int FLAG_CLIP_CHILDREN = 0x1;
164
165 // When set, ViewGroup excludes the padding area from the invalidate rectangle
166 // Set by default
167 private static final int FLAG_CLIP_TO_PADDING = 0x2;
168
169 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
170 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
171 private static final int FLAG_INVALIDATE_REQUIRED = 0x4;
172
173 // When set, dispatchDraw() will run the layout animation and unset the flag
174 private static final int FLAG_RUN_ANIMATION = 0x8;
175
176 // When set, there is either no layout animation on the ViewGroup or the layout
177 // animation is over
178 // Set by default
179 private static final int FLAG_ANIMATION_DONE = 0x10;
180
181 // If set, this ViewGroup has padding; if unset there is no padding and we don't need
182 // to clip it, even if FLAG_CLIP_TO_PADDING is set
183 private static final int FLAG_PADDING_NOT_NULL = 0x20;
184
185 // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation
186 // Set by default
187 private static final int FLAG_ANIMATION_CACHE = 0x40;
188
189 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
190 // layout animation; this avoid clobbering the hierarchy
191 // Automatically set when the layout animation starts, depending on the animation's
192 // characteristics
193 private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
194
195 // When set, the next call to drawChild() will clear mChildTransformation's matrix
196 private static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
197
198 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
199 // the children's Bitmap caches if necessary
200 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
201 private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
202
203 /**
204 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
205 * to get the index of the child to draw for that iteration.
Romain Guy293451e2009-11-04 13:59:48 -0800206 *
207 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 */
209 protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
Romain Guy8506ab42009-06-11 17:35:47 -0700210
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 /**
212 * When set, this ViewGroup supports static transformations on children; this causes
213 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
214 * invoked when a child is drawn.
215 *
216 * Any subclass overriding
217 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
218 * set this flags in {@link #mGroupFlags}.
Romain Guy8506ab42009-06-11 17:35:47 -0700219 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 * {@hide}
221 */
222 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
223
224 // When the previous drawChild() invocation used an alpha value that was lower than
225 // 1.0 and set it in mCachePaint
226 private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000;
227
228 /**
229 * When set, this ViewGroup's drawable states also include those
230 * of its children.
231 */
232 private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
233
234 /**
235 * When set, this ViewGroup tries to always draw its children using their drawing cache.
236 */
237 private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
238
239 /**
240 * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to
241 * draw its children with their drawing cache.
242 */
243 private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
244
245 /**
246 * When set, this group will go through its list of children to notify them of
247 * any drawable state change.
248 */
249 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
250
251 private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
252
253 /**
254 * This view will get focus before any of its descendants.
255 */
256 public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
257
258 /**
259 * This view will get focus only if none of its descendants want it.
260 */
261 public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
262
263 /**
264 * This view will block any of its descendants from getting focus, even
265 * if they are focusable.
266 */
267 public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
268
269 /**
270 * Used to map between enum in attrubutes and flag values.
271 */
272 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
273 {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
274 FOCUS_BLOCK_DESCENDANTS};
275
276 /**
277 * When set, this ViewGroup should not intercept touch events.
Adam Powell110486f2010-06-22 17:14:44 -0700278 * {@hide}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 */
Adam Powell110486f2010-06-22 17:14:44 -0700280 protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
Romain Guy8506ab42009-06-11 17:35:47 -0700281
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 /**
Adam Powell2b342f02010-08-18 18:14:13 -0700283 * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
284 */
Adam Powellf37df072010-09-17 16:22:49 -0700285 private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
Adam Powell2b342f02010-08-18 18:14:13 -0700286
287 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 * Indicates which types of drawing caches are to be kept in memory.
289 * This field should be made private, so it is hidden from the SDK.
290 * {@hide}
291 */
292 protected int mPersistentDrawingCache;
293
294 /**
295 * Used to indicate that no drawing cache should be kept in memory.
296 */
297 public static final int PERSISTENT_NO_CACHE = 0x0;
298
299 /**
300 * Used to indicate that the animation drawing cache should be kept in memory.
301 */
302 public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
303
304 /**
305 * Used to indicate that the scrolling drawing cache should be kept in memory.
306 */
307 public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
308
309 /**
310 * Used to indicate that all drawing caches should be kept in memory.
311 */
312 public static final int PERSISTENT_ALL_CACHES = 0x3;
313
314 /**
315 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
316 * are set at the same time.
317 */
318 protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
319
320 // Index of the child's left position in the mLocation array
321 private static final int CHILD_LEFT_INDEX = 0;
322 // Index of the child's top position in the mLocation array
323 private static final int CHILD_TOP_INDEX = 1;
324
325 // Child views of this ViewGroup
326 private View[] mChildren;
327 // Number of valid children in the mChildren array, the rest should be null or not
328 // considered as children
Chet Haase9c087442011-01-12 16:20:16 -0800329
330 private boolean mLayoutSuppressed = false;
331
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 private int mChildrenCount;
333
334 private static final int ARRAY_INITIAL_CAPACITY = 12;
335 private static final int ARRAY_CAPACITY_INCREMENT = 12;
336
337 // Used to draw cached views
338 private final Paint mCachePaint = new Paint();
339
Chet Haase21cd1382010-09-01 17:42:29 -0700340 // Used to animate add/remove changes in layout
341 private LayoutTransition mTransition;
342
343 // The set of views that are currently being transitioned. This list is used to track views
344 // being removed that should not actually be removed from the parent yet because they are
345 // being animated.
346 private ArrayList<View> mTransitioningViews;
347
Chet Haase5e25c2c2010-09-16 11:15:56 -0700348 // List of children changing visibility. This is used to potentially keep rendering
349 // views during a transition when they otherwise would have become gone/invisible
350 private ArrayList<View> mVisibilityChangingChildren;
351
Romain Guy849d0a32011-02-01 17:20:48 -0800352 // Indicates whether this container will use its children layers to draw
353 @ViewDebug.ExportedProperty(category = "drawing")
354 private boolean mDrawLayers = true;
355
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 public ViewGroup(Context context) {
357 super(context);
358 initViewGroup();
359 }
360
361 public ViewGroup(Context context, AttributeSet attrs) {
362 super(context, attrs);
363 initViewGroup();
364 initFromAttributes(context, attrs);
365 }
366
367 public ViewGroup(Context context, AttributeSet attrs, int defStyle) {
368 super(context, attrs, defStyle);
369 initViewGroup();
370 initFromAttributes(context, attrs);
371 }
372
373 private void initViewGroup() {
374 // ViewGroup doesn't draw by default
375 setFlags(WILL_NOT_DRAW, DRAW_MASK);
376 mGroupFlags |= FLAG_CLIP_CHILDREN;
377 mGroupFlags |= FLAG_CLIP_TO_PADDING;
378 mGroupFlags |= FLAG_ANIMATION_DONE;
379 mGroupFlags |= FLAG_ANIMATION_CACHE;
380 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
381
Jeff Brown995e7742010-12-22 16:59:36 -0800382 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
383 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
384 }
385
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
387
388 mChildren = new View[ARRAY_INITIAL_CAPACITY];
389 mChildrenCount = 0;
390
391 mCachePaint.setDither(false);
392
393 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
394 }
395
396 private void initFromAttributes(Context context, AttributeSet attrs) {
397 TypedArray a = context.obtainStyledAttributes(attrs,
398 R.styleable.ViewGroup);
399
400 final int N = a.getIndexCount();
401 for (int i = 0; i < N; i++) {
402 int attr = a.getIndex(i);
403 switch (attr) {
404 case R.styleable.ViewGroup_clipChildren:
405 setClipChildren(a.getBoolean(attr, true));
406 break;
407 case R.styleable.ViewGroup_clipToPadding:
408 setClipToPadding(a.getBoolean(attr, true));
409 break;
410 case R.styleable.ViewGroup_animationCache:
411 setAnimationCacheEnabled(a.getBoolean(attr, true));
412 break;
413 case R.styleable.ViewGroup_persistentDrawingCache:
414 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
415 break;
416 case R.styleable.ViewGroup_addStatesFromChildren:
417 setAddStatesFromChildren(a.getBoolean(attr, false));
418 break;
419 case R.styleable.ViewGroup_alwaysDrawnWithCache:
420 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
421 break;
422 case R.styleable.ViewGroup_layoutAnimation:
423 int id = a.getResourceId(attr, -1);
424 if (id > 0) {
425 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
426 }
427 break;
428 case R.styleable.ViewGroup_descendantFocusability:
429 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
430 break;
Adam Powell2b342f02010-08-18 18:14:13 -0700431 case R.styleable.ViewGroup_splitMotionEvents:
432 setMotionEventSplittingEnabled(a.getBoolean(attr, false));
433 break;
Chet Haase13cc1202010-09-03 15:39:20 -0700434 case R.styleable.ViewGroup_animateLayoutChanges:
435 boolean animateLayoutChanges = a.getBoolean(attr, false);
436 if (animateLayoutChanges) {
437 setLayoutTransition(new LayoutTransition());
438 }
439 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 }
441 }
442
443 a.recycle();
444 }
445
446 /**
447 * Gets the descendant focusability of this view group. The descendant
448 * focusability defines the relationship between this view group and its
449 * descendants when looking for a view to take focus in
450 * {@link #requestFocus(int, android.graphics.Rect)}.
451 *
452 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
453 * {@link #FOCUS_BLOCK_DESCENDANTS}.
454 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700455 @ViewDebug.ExportedProperty(category = "focus", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
457 @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
458 @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
459 })
460 public int getDescendantFocusability() {
461 return mGroupFlags & FLAG_MASK_FOCUSABILITY;
462 }
463
464 /**
465 * Set the descendant focusability of this view group. This defines the relationship
466 * between this view group and its descendants when looking for a view to
467 * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
468 *
469 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
470 * {@link #FOCUS_BLOCK_DESCENDANTS}.
471 */
472 public void setDescendantFocusability(int focusability) {
473 switch (focusability) {
474 case FOCUS_BEFORE_DESCENDANTS:
475 case FOCUS_AFTER_DESCENDANTS:
476 case FOCUS_BLOCK_DESCENDANTS:
477 break;
478 default:
479 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
480 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
481 }
482 mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
483 mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
484 }
485
486 /**
487 * {@inheritDoc}
488 */
489 @Override
490 void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
491 if (mFocused != null) {
492 mFocused.unFocus();
493 mFocused = null;
494 }
495 super.handleFocusGainInternal(direction, previouslyFocusedRect);
496 }
497
498 /**
499 * {@inheritDoc}
500 */
501 public void requestChildFocus(View child, View focused) {
502 if (DBG) {
503 System.out.println(this + " requestChildFocus()");
504 }
505 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
506 return;
507 }
508
509 // Unfocus us, if necessary
510 super.unFocus();
511
512 // We had a previous notion of who had focus. Clear it.
513 if (mFocused != child) {
514 if (mFocused != null) {
515 mFocused.unFocus();
516 }
517
518 mFocused = child;
519 }
520 if (mParent != null) {
521 mParent.requestChildFocus(this, focused);
522 }
523 }
524
525 /**
526 * {@inheritDoc}
527 */
528 public void focusableViewAvailable(View v) {
529 if (mParent != null
530 // shortcut: don't report a new focusable view if we block our descendants from
531 // getting focus
532 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
533 // shortcut: don't report a new focusable view if we already are focused
534 // (and we don't prefer our descendants)
535 //
536 // note: knowing that mFocused is non-null is not a good enough reason
537 // to break the traversal since in that case we'd actually have to find
538 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
Joe Onoratoc6cc0f82011-04-12 11:53:13 -0700539 // an ancestor of v; this will get checked for at ViewAncestor
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
541 mParent.focusableViewAvailable(v);
542 }
543 }
544
545 /**
546 * {@inheritDoc}
547 */
548 public boolean showContextMenuForChild(View originalView) {
549 return mParent != null && mParent.showContextMenuForChild(originalView);
550 }
551
552 /**
Adam Powell6e346362010-07-23 10:18:23 -0700553 * {@inheritDoc}
554 */
555 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
556 return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null;
557 }
558
559 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 * Find the nearest view in the specified direction that wants to take
561 * focus.
562 *
563 * @param focused The view that currently has focus
564 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
565 * FOCUS_RIGHT, or 0 for not applicable.
566 */
567 public View focusSearch(View focused, int direction) {
568 if (isRootNamespace()) {
569 // root namespace means we should consider ourselves the top of the
570 // tree for focus searching; otherwise we could be focus searching
571 // into other tabs. see LocalActivityManager and TabHost for more info
572 return FocusFinder.getInstance().findNextFocus(this, focused, direction);
573 } else if (mParent != null) {
574 return mParent.focusSearch(focused, direction);
575 }
576 return null;
577 }
578
579 /**
580 * {@inheritDoc}
581 */
582 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
583 return false;
584 }
585
586 /**
587 * {@inheritDoc}
588 */
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700589 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
590 ViewParent parent = getParent();
591 if (parent == null) {
592 return false;
593 }
594 final boolean propagate = onRequestSendAccessibilityEvent(child, event);
595 if (!propagate) {
596 return false;
597 }
598 return parent.requestSendAccessibilityEvent(this, event);
599 }
600
601 /**
602 * Called when a child has requested sending an {@link AccessibilityEvent} and
603 * gives an opportunity to its parent to augment the event.
604 *
605 * @param child The child which requests sending the event.
606 * @param event The event to be sent.
607 * @return True if the event should be sent.
608 *
609 * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
610 */
611 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
612 return true;
613 }
614
615 /**
616 * {@inheritDoc}
617 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 @Override
619 public boolean dispatchUnhandledMove(View focused, int direction) {
620 return mFocused != null &&
621 mFocused.dispatchUnhandledMove(focused, direction);
622 }
623
624 /**
625 * {@inheritDoc}
626 */
627 public void clearChildFocus(View child) {
628 if (DBG) {
629 System.out.println(this + " clearChildFocus()");
630 }
631
632 mFocused = null;
633 if (mParent != null) {
634 mParent.clearChildFocus(this);
635 }
636 }
637
638 /**
639 * {@inheritDoc}
640 */
641 @Override
642 public void clearFocus() {
643 super.clearFocus();
644
645 // clear any child focus if it exists
646 if (mFocused != null) {
647 mFocused.clearFocus();
648 }
649 }
650
651 /**
652 * {@inheritDoc}
653 */
654 @Override
655 void unFocus() {
656 if (DBG) {
657 System.out.println(this + " unFocus()");
658 }
659
660 super.unFocus();
661 if (mFocused != null) {
662 mFocused.unFocus();
663 }
664 mFocused = null;
665 }
666
667 /**
668 * Returns the focused child of this view, if any. The child may have focus
669 * or contain focus.
670 *
671 * @return the focused child or null.
672 */
673 public View getFocusedChild() {
674 return mFocused;
675 }
676
677 /**
678 * Returns true if this view has or contains focus
679 *
680 * @return true if this view has or contains focus
681 */
682 @Override
683 public boolean hasFocus() {
684 return (mPrivateFlags & FOCUSED) != 0 || mFocused != null;
685 }
686
687 /*
688 * (non-Javadoc)
689 *
690 * @see android.view.View#findFocus()
691 */
692 @Override
693 public View findFocus() {
694 if (DBG) {
695 System.out.println("Find focus in " + this + ": flags="
696 + isFocused() + ", child=" + mFocused);
697 }
698
699 if (isFocused()) {
700 return this;
701 }
702
703 if (mFocused != null) {
704 return mFocused.findFocus();
705 }
706 return null;
707 }
708
709 /**
710 * {@inheritDoc}
711 */
712 @Override
713 public boolean hasFocusable() {
714 if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
715 return false;
716 }
717
718 if (isFocusable()) {
719 return true;
720 }
721
722 final int descendantFocusability = getDescendantFocusability();
723 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
724 final int count = mChildrenCount;
725 final View[] children = mChildren;
726
727 for (int i = 0; i < count; i++) {
728 final View child = children[i];
729 if (child.hasFocusable()) {
730 return true;
731 }
732 }
733 }
734
735 return false;
736 }
737
738 /**
739 * {@inheritDoc}
740 */
741 @Override
742 public void addFocusables(ArrayList<View> views, int direction) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700743 addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
744 }
745
746 /**
747 * {@inheritDoc}
748 */
749 @Override
750 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 final int focusableCount = views.size();
752
753 final int descendantFocusability = getDescendantFocusability();
754
755 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
756 final int count = mChildrenCount;
757 final View[] children = mChildren;
758
759 for (int i = 0; i < count; i++) {
760 final View child = children[i];
761 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700762 child.addFocusables(views, direction, focusableMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800763 }
764 }
765 }
766
767 // we add ourselves (if focusable) in all cases except for when we are
768 // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
769 // to avoid the focus search finding layouts when a more precise search
770 // among the focusable children would be more interesting.
771 if (
772 descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
773 // No focusable descendants
774 (focusableCount == views.size())) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700775 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 }
777 }
778
779 /**
780 * {@inheritDoc}
781 */
782 @Override
783 public void dispatchWindowFocusChanged(boolean hasFocus) {
784 super.dispatchWindowFocusChanged(hasFocus);
785 final int count = mChildrenCount;
786 final View[] children = mChildren;
787 for (int i = 0; i < count; i++) {
788 children[i].dispatchWindowFocusChanged(hasFocus);
789 }
790 }
791
792 /**
793 * {@inheritDoc}
794 */
795 @Override
796 public void addTouchables(ArrayList<View> views) {
797 super.addTouchables(views);
798
799 final int count = mChildrenCount;
800 final View[] children = mChildren;
801
802 for (int i = 0; i < count; i++) {
803 final View child = children[i];
804 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
805 child.addTouchables(views);
806 }
807 }
808 }
Romain Guy43c9cdf2010-01-27 13:53:55 -0800809
810 /**
811 * {@inheritDoc}
812 */
813 @Override
814 public void dispatchDisplayHint(int hint) {
815 super.dispatchDisplayHint(hint);
816 final int count = mChildrenCount;
817 final View[] children = mChildren;
818 for (int i = 0; i < count; i++) {
819 children[i].dispatchDisplayHint(hint);
820 }
821 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822
823 /**
Chet Haase5e25c2c2010-09-16 11:15:56 -0700824 * @hide
825 * @param child
826 * @param visibility
827 */
828 void onChildVisibilityChanged(View child, int visibility) {
829 if (mTransition != null) {
830 if (visibility == VISIBLE) {
831 mTransition.showChild(this, child);
832 } else {
833 mTransition.hideChild(this, child);
834 }
835 if (visibility != VISIBLE) {
836 // Only track this on disappearing views - appearing views are already visible
837 // and don't need special handling during drawChild()
838 if (mVisibilityChangingChildren == null) {
839 mVisibilityChangingChildren = new ArrayList<View>();
840 }
841 mVisibilityChangingChildren.add(child);
842 if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
843 addDisappearingView(child);
844 }
845 }
846 }
Christopher Tate86cab1b2011-01-13 20:28:55 -0800847
848 // in all cases, for drags
849 if (mCurrentDrag != null) {
850 if (visibility == VISIBLE) {
851 notifyChildOfDrag(child);
852 }
853 }
Chet Haase5e25c2c2010-09-16 11:15:56 -0700854 }
855
856 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 * {@inheritDoc}
858 */
859 @Override
Adam Powell326d8082009-12-09 15:10:07 -0800860 protected void dispatchVisibilityChanged(View changedView, int visibility) {
861 super.dispatchVisibilityChanged(changedView, visibility);
862 final int count = mChildrenCount;
863 final View[] children = mChildren;
864 for (int i = 0; i < count; i++) {
865 children[i].dispatchVisibilityChanged(changedView, visibility);
866 }
867 }
868
869 /**
870 * {@inheritDoc}
871 */
872 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873 public void dispatchWindowVisibilityChanged(int visibility) {
874 super.dispatchWindowVisibilityChanged(visibility);
875 final int count = mChildrenCount;
876 final View[] children = mChildren;
877 for (int i = 0; i < count; i++) {
878 children[i].dispatchWindowVisibilityChanged(visibility);
879 }
880 }
881
882 /**
883 * {@inheritDoc}
884 */
Dianne Hackborne36d6e22010-02-17 19:46:25 -0800885 @Override
886 public void dispatchConfigurationChanged(Configuration newConfig) {
887 super.dispatchConfigurationChanged(newConfig);
888 final int count = mChildrenCount;
889 final View[] children = mChildren;
890 for (int i = 0; i < count; i++) {
891 children[i].dispatchConfigurationChanged(newConfig);
892 }
893 }
894
895 /**
896 * {@inheritDoc}
897 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 public void recomputeViewAttributes(View child) {
Joe Onorato664644d2011-01-23 17:53:23 -0800899 if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
900 ViewParent parent = mParent;
901 if (parent != null) parent.recomputeViewAttributes(this);
902 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 }
Romain Guy8506ab42009-06-11 17:35:47 -0700904
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 @Override
906 void dispatchCollectViewAttributes(int visibility) {
907 visibility |= mViewFlags&VISIBILITY_MASK;
908 super.dispatchCollectViewAttributes(visibility);
909 final int count = mChildrenCount;
910 final View[] children = mChildren;
911 for (int i = 0; i < count; i++) {
912 children[i].dispatchCollectViewAttributes(visibility);
913 }
914 }
915
916 /**
917 * {@inheritDoc}
918 */
919 public void bringChildToFront(View child) {
920 int index = indexOfChild(child);
921 if (index >= 0) {
922 removeFromArray(index);
923 addInArray(child, mChildrenCount);
924 child.mParent = this;
925 }
926 }
927
928 /**
929 * {@inheritDoc}
Christopher Tatea53146c2010-09-07 11:57:52 -0700930 *
931 * !!! TODO: write real docs
932 */
933 @Override
934 public boolean dispatchDragEvent(DragEvent event) {
935 boolean retval = false;
936 final float tx = event.mX;
937 final float ty = event.mY;
938
Joe Onoratoc6cc0f82011-04-12 11:53:13 -0700939 ViewAncestor root = getViewAncestor();
Christopher Tatea53146c2010-09-07 11:57:52 -0700940
941 // Dispatch down the view hierarchy
942 switch (event.mAction) {
943 case DragEvent.ACTION_DRAG_STARTED: {
944 // clear state to recalculate which views we drag over
Chris Tate9d1ab882010-11-02 15:55:39 -0700945 mCurrentDragView = null;
Christopher Tatea53146c2010-09-07 11:57:52 -0700946
Christopher Tate86cab1b2011-01-13 20:28:55 -0800947 // Set up our tracking of drag-started notifications
948 mCurrentDrag = DragEvent.obtain(event);
949 if (mDragNotifiedChildren == null) {
950 mDragNotifiedChildren = new HashSet<View>();
951 } else {
952 mDragNotifiedChildren.clear();
953 }
954
Christopher Tatea53146c2010-09-07 11:57:52 -0700955 // Now dispatch down to our children, caching the responses
956 mChildAcceptsDrag = false;
957 final int count = mChildrenCount;
958 final View[] children = mChildren;
959 for (int i = 0; i < count; i++) {
Christopher Tate2c095f32010-10-04 14:13:40 -0700960 final View child = children[i];
Christopher Tate3d4bf172011-03-28 16:16:46 -0700961 child.mPrivateFlags2 &= ~View.DRAG_MASK;
Christopher Tate2c095f32010-10-04 14:13:40 -0700962 if (child.getVisibility() == VISIBLE) {
Christopher Tate86cab1b2011-01-13 20:28:55 -0800963 final boolean handled = notifyChildOfDrag(children[i]);
Christopher Tate2c095f32010-10-04 14:13:40 -0700964 if (handled) {
965 mChildAcceptsDrag = true;
966 }
Christopher Tatea53146c2010-09-07 11:57:52 -0700967 }
968 }
969
970 // Return HANDLED if one of our children can accept the drag
971 if (mChildAcceptsDrag) {
972 retval = true;
973 }
974 } break;
975
976 case DragEvent.ACTION_DRAG_ENDED: {
Christopher Tate86cab1b2011-01-13 20:28:55 -0800977 // Release the bookkeeping now that the drag lifecycle has ended
Christopher Tate1fc014f2011-01-19 12:56:26 -0800978 if (mDragNotifiedChildren != null) {
979 for (View child : mDragNotifiedChildren) {
980 // If a child was notified about an ongoing drag, it's told that it's over
981 child.dispatchDragEvent(event);
Christopher Tate3d4bf172011-03-28 16:16:46 -0700982 child.mPrivateFlags2 &= ~View.DRAG_MASK;
983 child.refreshDrawableState();
Christopher Tate1fc014f2011-01-19 12:56:26 -0800984 }
985
986 mDragNotifiedChildren.clear();
987 mCurrentDrag.recycle();
988 mCurrentDrag = null;
989 }
Christopher Tate86cab1b2011-01-13 20:28:55 -0800990
Christopher Tatea53146c2010-09-07 11:57:52 -0700991 // We consider drag-ended to have been handled if one of our children
992 // had offered to handle the drag.
993 if (mChildAcceptsDrag) {
994 retval = true;
995 }
996 } break;
997
998 case DragEvent.ACTION_DRAG_LOCATION: {
999 // Find the [possibly new] drag target
1000 final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
1001
1002 // If we've changed apparent drag target, tell the view root which view
Chris Tate9d1ab882010-11-02 15:55:39 -07001003 // we're over now [for purposes of the eventual drag-recipient-changed
1004 // notifications to the framework] and tell the new target that the drag
1005 // has entered its bounds. The root will see setDragFocus() calls all
1006 // the way down to the final leaf view that is handling the LOCATION event
1007 // before reporting the new potential recipient to the framework.
Christopher Tatea53146c2010-09-07 11:57:52 -07001008 if (mCurrentDragView != target) {
Chris Tate9d1ab882010-11-02 15:55:39 -07001009 root.setDragFocus(target);
1010
1011 final int action = event.mAction;
1012 // If we've dragged off of a child view, send it the EXITED message
1013 if (mCurrentDragView != null) {
Christopher Tate3d4bf172011-03-28 16:16:46 -07001014 final View view = mCurrentDragView;
Chris Tate9d1ab882010-11-02 15:55:39 -07001015 event.mAction = DragEvent.ACTION_DRAG_EXITED;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001016 view.dispatchDragEvent(event);
1017 view.mPrivateFlags2 &= ~View.DRAG_HOVERED;
1018 view.refreshDrawableState();
Chris Tate9d1ab882010-11-02 15:55:39 -07001019 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001020 mCurrentDragView = target;
Chris Tate9d1ab882010-11-02 15:55:39 -07001021
1022 // If we've dragged over a new child view, send it the ENTERED message
1023 if (target != null) {
1024 event.mAction = DragEvent.ACTION_DRAG_ENTERED;
1025 target.dispatchDragEvent(event);
Christopher Tate3d4bf172011-03-28 16:16:46 -07001026 target.mPrivateFlags2 |= View.DRAG_HOVERED;
1027 target.refreshDrawableState();
Chris Tate9d1ab882010-11-02 15:55:39 -07001028 }
1029 event.mAction = action; // restore the event's original state
Christopher Tatea53146c2010-09-07 11:57:52 -07001030 }
Christopher Tate2c095f32010-10-04 14:13:40 -07001031
Christopher Tatea53146c2010-09-07 11:57:52 -07001032 // Dispatch the actual drag location notice, localized into its coordinates
1033 if (target != null) {
1034 event.mX = mLocalPoint.x;
1035 event.mY = mLocalPoint.y;
1036
1037 retval = target.dispatchDragEvent(event);
1038
1039 event.mX = tx;
1040 event.mY = ty;
1041 }
1042 } break;
1043
Chris Tate9d1ab882010-11-02 15:55:39 -07001044 /* Entered / exited dispatch
1045 *
1046 * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is
1047 * that we're about to get the corresponding LOCATION event, which we will use to
1048 * determine which of our children is the new target; at that point we will
1049 * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
1050 *
1051 * DRAG_EXITED *is* dispatched all the way down immediately: once we know the
1052 * drag has left this ViewGroup, we know by definition that every contained subview
1053 * is also no longer under the drag point.
1054 */
1055
1056 case DragEvent.ACTION_DRAG_EXITED: {
1057 if (mCurrentDragView != null) {
Christopher Tate3d4bf172011-03-28 16:16:46 -07001058 final View view = mCurrentDragView;
1059 view.dispatchDragEvent(event);
1060 view.mPrivateFlags2 &= ~View.DRAG_HOVERED;
1061 view.refreshDrawableState();
1062
Chris Tate9d1ab882010-11-02 15:55:39 -07001063 mCurrentDragView = null;
1064 }
1065 } break;
1066
Christopher Tatea53146c2010-09-07 11:57:52 -07001067 case DragEvent.ACTION_DROP: {
Christopher Tate2c095f32010-10-04 14:13:40 -07001068 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
Christopher Tatea53146c2010-09-07 11:57:52 -07001069 View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
1070 if (target != null) {
Christopher Tate5ada6cb2010-10-05 14:15:29 -07001071 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target);
Christopher Tatea53146c2010-09-07 11:57:52 -07001072 event.mX = mLocalPoint.x;
1073 event.mY = mLocalPoint.y;
1074 retval = target.dispatchDragEvent(event);
1075 event.mX = tx;
1076 event.mY = ty;
Christopher Tate5ada6cb2010-10-05 14:15:29 -07001077 } else {
1078 if (ViewDebug.DEBUG_DRAG) {
1079 Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view");
1080 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001081 }
1082 } break;
1083 }
1084
1085 // If none of our children could handle the event, try here
1086 if (!retval) {
Chris Tate32affef2010-10-18 15:29:21 -07001087 // Call up to the View implementation that dispatches to installed listeners
1088 retval = super.dispatchDragEvent(event);
Christopher Tatea53146c2010-09-07 11:57:52 -07001089 }
1090 return retval;
1091 }
1092
1093 // Find the frontmost child view that lies under the given point, and calculate
1094 // the position within its own local coordinate system.
1095 View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001096 final int count = mChildrenCount;
1097 final View[] children = mChildren;
1098 for (int i = count - 1; i >= 0; i--) {
1099 final View child = children[i];
Christopher Tate3d4bf172011-03-28 16:16:46 -07001100 if (!child.canAcceptDrag()) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001101 continue;
1102 }
1103
Christopher Tate2c095f32010-10-04 14:13:40 -07001104 if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001105 return child;
1106 }
1107 }
1108 return null;
1109 }
1110
Christopher Tate86cab1b2011-01-13 20:28:55 -08001111 boolean notifyChildOfDrag(View child) {
1112 if (ViewDebug.DEBUG_DRAG) {
1113 Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
1114 }
1115
Christopher Tate3d4bf172011-03-28 16:16:46 -07001116 boolean canAccept = false;
Christopher Tate86cab1b2011-01-13 20:28:55 -08001117 if (! mDragNotifiedChildren.contains(child)) {
1118 mDragNotifiedChildren.add(child);
Christopher Tate3d4bf172011-03-28 16:16:46 -07001119 canAccept = child.dispatchDragEvent(mCurrentDrag);
1120 if (canAccept && !child.canAcceptDrag()) {
1121 child.mPrivateFlags2 |= View.DRAG_CAN_ACCEPT;
1122 child.refreshDrawableState();
1123 }
Christopher Tate86cab1b2011-01-13 20:28:55 -08001124 }
Christopher Tate3d4bf172011-03-28 16:16:46 -07001125 return canAccept;
Christopher Tate86cab1b2011-01-13 20:28:55 -08001126 }
1127
Joe Onorato664644d2011-01-23 17:53:23 -08001128 @Override
1129 public void dispatchSystemUiVisibilityChanged(int visible) {
1130 super.dispatchSystemUiVisibilityChanged(visible);
1131
1132 final int count = mChildrenCount;
1133 final View[] children = mChildren;
1134 for (int i=0; i <count; i++) {
1135 final View child = children[i];
1136 child.dispatchSystemUiVisibilityChanged(visible);
1137 }
1138 }
1139
Christopher Tatea53146c2010-09-07 11:57:52 -07001140 /**
1141 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 */
1143 @Override
1144 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1145 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1146 return super.dispatchKeyEventPreIme(event);
1147 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1148 return mFocused.dispatchKeyEventPreIme(event);
1149 }
1150 return false;
1151 }
1152
1153 /**
1154 * {@inheritDoc}
1155 */
1156 @Override
1157 public boolean dispatchKeyEvent(KeyEvent event) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001158 if (mInputEventConsistencyVerifier != null) {
1159 mInputEventConsistencyVerifier.onKeyEvent(event, 1);
1160 }
1161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001163 if (super.dispatchKeyEvent(event)) {
1164 return true;
1165 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001167 if (mFocused.dispatchKeyEvent(event)) {
1168 return true;
1169 }
1170 }
1171
1172 if (mInputEventConsistencyVerifier != null) {
1173 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 }
1175 return false;
1176 }
1177
1178 /**
1179 * {@inheritDoc}
1180 */
1181 @Override
1182 public boolean dispatchKeyShortcutEvent(KeyEvent event) {
1183 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1184 return super.dispatchKeyShortcutEvent(event);
1185 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1186 return mFocused.dispatchKeyShortcutEvent(event);
1187 }
1188 return false;
1189 }
1190
1191 /**
1192 * {@inheritDoc}
1193 */
1194 @Override
1195 public boolean dispatchTrackballEvent(MotionEvent event) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001196 if (mInputEventConsistencyVerifier != null) {
1197 mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
1198 }
1199
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001201 if (super.dispatchTrackballEvent(event)) {
1202 return true;
1203 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001205 if (mFocused.dispatchTrackballEvent(event)) {
1206 return true;
1207 }
1208 }
1209
1210 if (mInputEventConsistencyVerifier != null) {
1211 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 }
1213 return false;
1214 }
1215
Jeff Browna032cc02011-03-07 16:56:21 -08001216 /** @hide */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001217 @Override
Jeff Browna032cc02011-03-07 16:56:21 -08001218 protected boolean dispatchHoverEvent(MotionEvent event) {
1219 // Send the hover enter or hover move event to the view group first.
1220 // If it handles the event then a hovered child should receive hover exit.
1221 boolean handled = false;
1222 final boolean interceptHover;
1223 final int action = event.getAction();
1224 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1225 interceptHover = true;
1226 } else {
1227 handled = super.dispatchHoverEvent(event);
1228 interceptHover = handled;
1229 }
1230
1231 // Send successive hover events to the hovered child as long as the pointer
1232 // remains within the child's bounds.
1233 MotionEvent eventNoHistory = event;
1234 if (mHoveredChild != null) {
1235 final float x = event.getX();
1236 final float y = event.getY();
1237
1238 if (interceptHover
1239 || !isTransformedTouchPointInView(x, y, mHoveredChild, null)) {
1240 // Pointer exited the child.
1241 // Send it a hover exit with only the most recent coordinates. We could
1242 // try to find the exact point in history when the pointer left the view
1243 // but it is not worth the effort.
1244 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1245 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1246 handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, mHoveredChild);
1247 eventNoHistory.setAction(action);
Jeff Browna032cc02011-03-07 16:56:21 -08001248 mHoveredChild = null;
Svetoslav Ganov736c2752011-04-22 18:30:36 -07001249 } else {
Jeff Browna032cc02011-03-07 16:56:21 -08001250 // Pointer is still within the child.
1251 handled |= dispatchTransformedGenericPointerEvent(event, mHoveredChild);
1252 }
1253 }
1254
1255 // Find a new hovered child if needed.
1256 if (!interceptHover && mHoveredChild == null
1257 && (action == MotionEvent.ACTION_HOVER_ENTER
1258 || action == MotionEvent.ACTION_HOVER_MOVE)) {
Jeff Brown33bbfd22011-02-24 20:55:35 -08001259 final int childrenCount = mChildrenCount;
1260 if (childrenCount != 0) {
1261 final View[] children = mChildren;
1262 final float x = event.getX();
1263 final float y = event.getY();
1264
1265 for (int i = childrenCount - 1; i >= 0; i--) {
1266 final View child = children[i];
Jeff Browna032cc02011-03-07 16:56:21 -08001267 if (!canViewReceivePointerEvents(child)
1268 || !isTransformedTouchPointInView(x, y, child, null)) {
Jeff Brown33bbfd22011-02-24 20:55:35 -08001269 continue;
1270 }
1271
Jeff Browna032cc02011-03-07 16:56:21 -08001272 // Found the hovered child.
1273 mHoveredChild = child;
1274 if (action == MotionEvent.ACTION_HOVER_MOVE) {
1275 // Pointer was moving within the view group and entered the child.
1276 // Send it a hover enter and hover move with only the most recent
1277 // coordinates. We could try to find the exact point in history when
1278 // the pointer entered the view but it is not worth the effort.
1279 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1280 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1281 handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, child);
1282 eventNoHistory.setAction(action);
Jeff Brown33bbfd22011-02-24 20:55:35 -08001283
Jeff Browna032cc02011-03-07 16:56:21 -08001284 handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, child);
1285 } else { /* must be ACTION_HOVER_ENTER */
1286 // Pointer entered the child.
1287 handled |= dispatchTransformedGenericPointerEvent(event, child);
Jeff Brown33bbfd22011-02-24 20:55:35 -08001288 }
Jeff Browna032cc02011-03-07 16:56:21 -08001289 break;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001290 }
1291 }
Jeff Brown33bbfd22011-02-24 20:55:35 -08001292 }
1293
Jeff Browna032cc02011-03-07 16:56:21 -08001294 // Recycle the copy of the event that we made.
1295 if (eventNoHistory != event) {
1296 eventNoHistory.recycle();
1297 }
1298
1299 // Send hover exit to the view group. If there was a child, we will already have
1300 // sent the hover exit to it.
1301 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1302 handled |= super.dispatchHoverEvent(event);
1303 }
1304
1305 // Done.
1306 return handled;
1307 }
1308
Svetoslav Ganov736c2752011-04-22 18:30:36 -07001309 @Override
1310 public boolean onHoverEvent(MotionEvent event) {
1311 // Handle the event only if leaf. This guarantees that
1312 // the leafs (or any custom class that returns true from
1313 // this method) will get a change to process the hover.
1314 if (getChildCount() == 0) {
1315 return super.onHoverEvent(event);
1316 }
1317 return false;
1318 }
1319
Jeff Browna032cc02011-03-07 16:56:21 -08001320 private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
1321 if (event.getHistorySize() == 0) {
1322 return event;
1323 }
1324 return MotionEvent.obtainNoHistory(event);
1325 }
1326
1327 /** @hide */
1328 @Override
1329 protected boolean dispatchGenericPointerEvent(MotionEvent event) {
1330 // Send the event to the child under the pointer.
1331 final int childrenCount = mChildrenCount;
1332 if (childrenCount != 0) {
1333 final View[] children = mChildren;
1334 final float x = event.getX();
1335 final float y = event.getY();
1336
1337 for (int i = childrenCount - 1; i >= 0; i--) {
1338 final View child = children[i];
1339 if (!canViewReceivePointerEvents(child)
1340 || !isTransformedTouchPointInView(x, y, child, null)) {
1341 continue;
1342 }
1343
1344 if (dispatchTransformedGenericPointerEvent(event, child)) {
1345 return true;
1346 }
1347 }
1348 }
1349
1350 // No child handled the event. Send it to this view group.
1351 return super.dispatchGenericPointerEvent(event);
1352 }
1353
1354 /** @hide */
1355 @Override
1356 protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
Jeff Brown33bbfd22011-02-24 20:55:35 -08001357 // Send the event to the focused child or to this view group if it has focus.
Jeff Browncb1404e2011-01-15 18:14:15 -08001358 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
Jeff Browna032cc02011-03-07 16:56:21 -08001359 return super.dispatchGenericFocusedEvent(event);
Jeff Browncb1404e2011-01-15 18:14:15 -08001360 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1361 return mFocused.dispatchGenericMotionEvent(event);
1362 }
1363 return false;
1364 }
1365
1366 /**
Jeff Browna032cc02011-03-07 16:56:21 -08001367 * Dispatches a generic pointer event to a child, taking into account
1368 * transformations that apply to the child.
1369 *
1370 * @param event The event to send.
1371 * @param child The view to send the event to.
1372 * @return {@code true} if the child handled the event.
1373 */
1374 private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) {
1375 final float offsetX = mScrollX - child.mLeft;
1376 final float offsetY = mScrollY - child.mTop;
1377
1378 boolean handled;
1379 if (!child.hasIdentityMatrix()) {
1380 MotionEvent transformedEvent = MotionEvent.obtain(event);
1381 transformedEvent.offsetLocation(offsetX, offsetY);
1382 transformedEvent.transform(child.getInverseMatrix());
1383 handled = child.dispatchGenericMotionEvent(transformedEvent);
1384 transformedEvent.recycle();
1385 } else {
1386 event.offsetLocation(offsetX, offsetY);
1387 handled = child.dispatchGenericMotionEvent(event);
1388 event.offsetLocation(-offsetX, -offsetY);
1389 }
1390 return handled;
1391 }
1392
1393 /**
Jeff Browncb1404e2011-01-15 18:14:15 -08001394 * {@inheritDoc}
1395 */
1396 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001397 public boolean dispatchTouchEvent(MotionEvent ev) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001398 if (mInputEventConsistencyVerifier != null) {
1399 mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
1400 }
1401
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001402 boolean handled = false;
1403 if (onFilterTouchEventForSecurity(ev)) {
1404 final int action = ev.getAction();
1405 final int actionMasked = action & MotionEvent.ACTION_MASK;
Jeff Brown85a31762010-09-01 17:01:00 -07001406
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001407 // Handle an initial down.
1408 if (actionMasked == MotionEvent.ACTION_DOWN) {
1409 // Throw away all previous state when starting a new touch gesture.
1410 // The framework may have dropped the up or cancel event for the previous gesture
1411 // due to an app switch, ANR, or some other state change.
1412 cancelAndClearTouchTargets(ev);
1413 resetTouchState();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001414 }
Adam Powellb08013c2010-09-16 16:28:11 -07001415
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001416 // Check for interception.
1417 final boolean intercepted;
Jeff Brown20e987b2010-08-23 12:01:02 -07001418 if (actionMasked == MotionEvent.ACTION_DOWN
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001419 || mFirstTouchTarget != null) {
1420 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
1421 if (!disallowIntercept) {
1422 intercepted = onInterceptTouchEvent(ev);
1423 ev.setAction(action); // restore action in case it was changed
1424 } else {
1425 intercepted = false;
1426 }
1427 } else {
1428 // There are no touch targets and this action is not an initial down
1429 // so this view group continues to intercept touches.
1430 intercepted = true;
1431 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001432
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001433 // Check for cancelation.
1434 final boolean canceled = resetCancelNextUpFlag(this)
1435 || actionMasked == MotionEvent.ACTION_CANCEL;
Jeff Brown20e987b2010-08-23 12:01:02 -07001436
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001437 // Update list of touch targets for pointer down, if needed.
1438 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
1439 TouchTarget newTouchTarget = null;
1440 boolean alreadyDispatchedToNewTouchTarget = false;
1441 if (!canceled && !intercepted) {
1442 if (actionMasked == MotionEvent.ACTION_DOWN
1443 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
1444 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
1445 final int actionIndex = ev.getActionIndex(); // always 0 for down
1446 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
1447 : TouchTarget.ALL_POINTER_IDS;
Jeff Brown20e987b2010-08-23 12:01:02 -07001448
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001449 // Clean up earlier touch targets for this pointer id in case they
1450 // have become out of sync.
1451 removePointersFromTouchTargets(idBitsToAssign);
1452
1453 final int childrenCount = mChildrenCount;
1454 if (childrenCount != 0) {
1455 // Find a child that can receive the event.
1456 // Scan children from front to back.
1457 final View[] children = mChildren;
1458 final float x = ev.getX(actionIndex);
1459 final float y = ev.getY(actionIndex);
1460
1461 for (int i = childrenCount - 1; i >= 0; i--) {
1462 final View child = children[i];
1463 if (!canViewReceivePointerEvents(child)
1464 || !isTransformedTouchPointInView(x, y, child, null)) {
1465 continue;
1466 }
1467
1468 newTouchTarget = getTouchTarget(child);
1469 if (newTouchTarget != null) {
1470 // Child is already receiving touch within its bounds.
1471 // Give it the new pointer in addition to the ones it is handling.
1472 newTouchTarget.pointerIdBits |= idBitsToAssign;
1473 break;
1474 }
1475
1476 resetCancelNextUpFlag(child);
1477 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
1478 // Child wants to receive touch within its bounds.
1479 mLastTouchDownTime = ev.getDownTime();
1480 mLastTouchDownIndex = i;
1481 mLastTouchDownX = ev.getX();
1482 mLastTouchDownY = ev.getY();
1483 newTouchTarget = addTouchTarget(child, idBitsToAssign);
1484 alreadyDispatchedToNewTouchTarget = true;
1485 break;
1486 }
1487 }
1488 }
1489
1490 if (newTouchTarget == null && mFirstTouchTarget != null) {
1491 // Did not find a child to receive the event.
1492 // Assign the pointer to the least recently added target.
1493 newTouchTarget = mFirstTouchTarget;
1494 while (newTouchTarget.next != null) {
1495 newTouchTarget = newTouchTarget.next;
1496 }
1497 newTouchTarget.pointerIdBits |= idBitsToAssign;
1498 }
1499 }
1500 }
1501
1502 // Dispatch to touch targets.
1503 if (mFirstTouchTarget == null) {
1504 // No touch targets so treat this as an ordinary view.
1505 handled = dispatchTransformedTouchEvent(ev, canceled, null,
1506 TouchTarget.ALL_POINTER_IDS);
1507 } else {
1508 // Dispatch to touch targets, excluding the new touch target if we already
1509 // dispatched to it. Cancel touch targets if necessary.
1510 TouchTarget predecessor = null;
1511 TouchTarget target = mFirstTouchTarget;
1512 while (target != null) {
1513 final TouchTarget next = target.next;
1514 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
1515 handled = true;
1516 } else {
1517 final boolean cancelChild = resetCancelNextUpFlag(target.child)
1518 || intercepted;
1519 if (dispatchTransformedTouchEvent(ev, cancelChild,
1520 target.child, target.pointerIdBits)) {
1521 handled = true;
1522 }
1523 if (cancelChild) {
1524 if (predecessor == null) {
1525 mFirstTouchTarget = next;
1526 } else {
1527 predecessor.next = next;
1528 }
1529 target.recycle();
1530 target = next;
Jeff Brown20e987b2010-08-23 12:01:02 -07001531 continue;
1532 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001533 }
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001534 predecessor = target;
1535 target = next;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001536 }
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001537 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001538
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001539 // Update list of touch targets for pointer up or cancel, if needed.
1540 if (canceled
1541 || actionMasked == MotionEvent.ACTION_UP
1542 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
1543 resetTouchState();
1544 } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
1545 final int actionIndex = ev.getActionIndex();
1546 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
1547 removePointersFromTouchTargets(idBitsToRemove);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001548 }
1549 }
Romain Guy8506ab42009-06-11 17:35:47 -07001550
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001551 if (!handled && mInputEventConsistencyVerifier != null) {
1552 mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
Jeff Brown20e987b2010-08-23 12:01:02 -07001553 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001554 return handled;
1555 }
1556
Romain Guy469b1db2010-10-05 11:49:57 -07001557 /**
1558 * Resets all touch state in preparation for a new cycle.
1559 */
1560 private void resetTouchState() {
Jeff Brown20e987b2010-08-23 12:01:02 -07001561 clearTouchTargets();
1562 resetCancelNextUpFlag(this);
1563 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
1564 }
1565
Romain Guy469b1db2010-10-05 11:49:57 -07001566 /**
1567 * Resets the cancel next up flag.
1568 * Returns true if the flag was previously set.
1569 */
1570 private boolean resetCancelNextUpFlag(View view) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001571 if ((view.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
1572 view.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
1573 return true;
Adam Cohen9b073942010-08-19 16:49:52 -07001574 }
1575 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001576 }
1577
Romain Guy469b1db2010-10-05 11:49:57 -07001578 /**
1579 * Clears all touch targets.
1580 */
1581 private void clearTouchTargets() {
Jeff Brown20e987b2010-08-23 12:01:02 -07001582 TouchTarget target = mFirstTouchTarget;
1583 if (target != null) {
1584 do {
1585 TouchTarget next = target.next;
1586 target.recycle();
1587 target = next;
1588 } while (target != null);
1589 mFirstTouchTarget = null;
1590 }
1591 }
1592
Romain Guy469b1db2010-10-05 11:49:57 -07001593 /**
1594 * Cancels and clears all touch targets.
1595 */
1596 private void cancelAndClearTouchTargets(MotionEvent event) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001597 if (mFirstTouchTarget != null) {
1598 boolean syntheticEvent = false;
1599 if (event == null) {
1600 final long now = SystemClock.uptimeMillis();
1601 event = MotionEvent.obtain(now, now,
1602 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1603 syntheticEvent = true;
1604 }
1605
1606 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
1607 resetCancelNextUpFlag(target.child);
1608 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
1609 }
1610 clearTouchTargets();
1611
1612 if (syntheticEvent) {
1613 event.recycle();
1614 }
1615 }
1616 }
1617
Romain Guy469b1db2010-10-05 11:49:57 -07001618 /**
1619 * Gets the touch target for specified child view.
1620 * Returns null if not found.
1621 */
1622 private TouchTarget getTouchTarget(View child) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001623 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
1624 if (target.child == child) {
1625 return target;
1626 }
1627 }
1628 return null;
1629 }
1630
Romain Guy469b1db2010-10-05 11:49:57 -07001631 /**
1632 * Adds a touch target for specified child to the beginning of the list.
1633 * Assumes the target child is not already present.
1634 */
1635 private TouchTarget addTouchTarget(View child, int pointerIdBits) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001636 TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
1637 target.next = mFirstTouchTarget;
1638 mFirstTouchTarget = target;
1639 return target;
1640 }
1641
Romain Guy469b1db2010-10-05 11:49:57 -07001642 /**
1643 * Removes the pointer ids from consideration.
1644 */
1645 private void removePointersFromTouchTargets(int pointerIdBits) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001646 TouchTarget predecessor = null;
1647 TouchTarget target = mFirstTouchTarget;
1648 while (target != null) {
1649 final TouchTarget next = target.next;
1650 if ((target.pointerIdBits & pointerIdBits) != 0) {
1651 target.pointerIdBits &= ~pointerIdBits;
1652 if (target.pointerIdBits == 0) {
1653 if (predecessor == null) {
1654 mFirstTouchTarget = next;
1655 } else {
1656 predecessor.next = next;
1657 }
1658 target.recycle();
1659 target = next;
1660 continue;
1661 }
1662 }
1663 predecessor = target;
1664 target = next;
1665 }
1666 }
1667
Romain Guy469b1db2010-10-05 11:49:57 -07001668 /**
Jeff Browna032cc02011-03-07 16:56:21 -08001669 * Returns true if a child view can receive pointer events.
1670 * @hide
1671 */
1672 private static boolean canViewReceivePointerEvents(View child) {
1673 return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
1674 || child.getAnimation() != null;
1675 }
1676
1677 /**
Romain Guy469b1db2010-10-05 11:49:57 -07001678 * Returns true if a child view contains the specified point when transformed
Jeff Brown20e987b2010-08-23 12:01:02 -07001679 * into its coordinate space.
Romain Guy469b1db2010-10-05 11:49:57 -07001680 * Child must not be null.
Adam Cohena32edd42010-10-26 10:35:01 -07001681 * @hide
Romain Guy469b1db2010-10-05 11:49:57 -07001682 */
Adam Cohena32edd42010-10-26 10:35:01 -07001683 protected boolean isTransformedTouchPointInView(float x, float y, View child,
Christopher Tate2c095f32010-10-04 14:13:40 -07001684 PointF outLocalPoint) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001685 float localX = x + mScrollX - child.mLeft;
1686 float localY = y + mScrollY - child.mTop;
1687 if (! child.hasIdentityMatrix() && mAttachInfo != null) {
Adam Powell2b342f02010-08-18 18:14:13 -07001688 final float[] localXY = mAttachInfo.mTmpTransformLocation;
1689 localXY[0] = localX;
1690 localXY[1] = localY;
1691 child.getInverseMatrix().mapPoints(localXY);
1692 localX = localXY[0];
1693 localY = localXY[1];
1694 }
Christopher Tate2c095f32010-10-04 14:13:40 -07001695 final boolean isInView = child.pointInView(localX, localY);
1696 if (isInView && outLocalPoint != null) {
1697 outLocalPoint.set(localX, localY);
1698 }
1699 return isInView;
Adam Powell2b342f02010-08-18 18:14:13 -07001700 }
1701
Romain Guy469b1db2010-10-05 11:49:57 -07001702 /**
1703 * Transforms a motion event into the coordinate space of a particular child view,
Jeff Brown20e987b2010-08-23 12:01:02 -07001704 * filters out irrelevant pointer ids, and overrides its action if necessary.
Romain Guy469b1db2010-10-05 11:49:57 -07001705 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
1706 */
1707 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
Jeff Brown20e987b2010-08-23 12:01:02 -07001708 View child, int desiredPointerIdBits) {
1709 final boolean handled;
Adam Powell2b342f02010-08-18 18:14:13 -07001710
Jeff Brown20e987b2010-08-23 12:01:02 -07001711 // Canceling motions is a special case. We don't need to perform any transformations
1712 // or filtering. The important part is the action, not the contents.
1713 final int oldAction = event.getAction();
1714 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
1715 event.setAction(MotionEvent.ACTION_CANCEL);
1716 if (child == null) {
1717 handled = super.dispatchTouchEvent(event);
1718 } else {
1719 handled = child.dispatchTouchEvent(event);
1720 }
1721 event.setAction(oldAction);
1722 return handled;
1723 }
Adam Powell2b342f02010-08-18 18:14:13 -07001724
Jeff Brown20e987b2010-08-23 12:01:02 -07001725 // Calculate the number of pointers to deliver.
1726 final int oldPointerCount = event.getPointerCount();
1727 int newPointerCount = 0;
1728 if (desiredPointerIdBits == TouchTarget.ALL_POINTER_IDS) {
1729 newPointerCount = oldPointerCount;
1730 } else {
1731 for (int i = 0; i < oldPointerCount; i++) {
1732 final int pointerId = event.getPointerId(i);
1733 final int pointerIdBit = 1 << pointerId;
1734 if ((pointerIdBit & desiredPointerIdBits) != 0) {
1735 newPointerCount += 1;
1736 }
1737 }
1738 }
Adam Powell2b342f02010-08-18 18:14:13 -07001739
Jeff Brown20e987b2010-08-23 12:01:02 -07001740 // If for some reason we ended up in an inconsistent state where it looks like we
1741 // might produce a motion event with no pointers in it, then drop the event.
1742 if (newPointerCount == 0) {
1743 return false;
1744 }
Adam Powell2b342f02010-08-18 18:14:13 -07001745
Jeff Brown20e987b2010-08-23 12:01:02 -07001746 // If the number of pointers is the same and we don't need to perform any fancy
1747 // irreversible transformations, then we can reuse the motion event for this
1748 // dispatch as long as we are careful to revert any changes we make.
1749 final boolean reuse = newPointerCount == oldPointerCount
1750 && (child == null || child.hasIdentityMatrix());
1751 if (reuse) {
1752 if (child == null) {
1753 handled = super.dispatchTouchEvent(event);
1754 } else {
1755 final float offsetX = mScrollX - child.mLeft;
1756 final float offsetY = mScrollY - child.mTop;
1757 event.offsetLocation(offsetX, offsetY);
Adam Powell2b342f02010-08-18 18:14:13 -07001758
Jeff Brown20e987b2010-08-23 12:01:02 -07001759 handled = child.dispatchTouchEvent(event);
1760
1761 event.offsetLocation(-offsetX, -offsetY);
1762 }
1763 return handled;
1764 }
1765
1766 // Make a copy of the event.
1767 // If the number of pointers is different, then we need to filter out irrelevant pointers
1768 // as we make a copy of the motion event.
1769 MotionEvent transformedEvent;
1770 if (newPointerCount == oldPointerCount) {
1771 transformedEvent = MotionEvent.obtain(event);
1772 } else {
1773 growTmpPointerArrays(newPointerCount);
1774 final int[] newPointerIndexMap = mTmpPointerIndexMap;
1775 final int[] newPointerIds = mTmpPointerIds;
1776 final MotionEvent.PointerCoords[] newPointerCoords = mTmpPointerCoords;
1777
1778 int newPointerIndex = 0;
1779 int oldPointerIndex = 0;
1780 while (newPointerIndex < newPointerCount) {
1781 final int pointerId = event.getPointerId(oldPointerIndex);
1782 final int pointerIdBits = 1 << pointerId;
1783 if ((pointerIdBits & desiredPointerIdBits) != 0) {
1784 newPointerIndexMap[newPointerIndex] = oldPointerIndex;
1785 newPointerIds[newPointerIndex] = pointerId;
1786 if (newPointerCoords[newPointerIndex] == null) {
1787 newPointerCoords[newPointerIndex] = new MotionEvent.PointerCoords();
1788 }
1789
1790 newPointerIndex += 1;
1791 }
1792 oldPointerIndex += 1;
1793 }
1794
1795 final int newAction;
1796 if (cancel) {
1797 newAction = MotionEvent.ACTION_CANCEL;
1798 } else {
1799 final int oldMaskedAction = oldAction & MotionEvent.ACTION_MASK;
1800 if (oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN
1801 || oldMaskedAction == MotionEvent.ACTION_POINTER_UP) {
1802 final int changedPointerId = event.getPointerId(
1803 (oldAction & MotionEvent.ACTION_POINTER_INDEX_MASK)
1804 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT);
1805 final int changedPointerIdBits = 1 << changedPointerId;
1806 if ((changedPointerIdBits & desiredPointerIdBits) != 0) {
1807 if (newPointerCount == 1) {
1808 // The first/last pointer went down/up.
1809 newAction = oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN
1810 ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP;
1811 } else {
1812 // A secondary pointer went down/up.
1813 int newChangedPointerIndex = 0;
1814 while (newPointerIds[newChangedPointerIndex] != changedPointerId) {
1815 newChangedPointerIndex += 1;
Adam Powell2b342f02010-08-18 18:14:13 -07001816 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001817 newAction = oldMaskedAction | (newChangedPointerIndex
1818 << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
Adam Powell2b342f02010-08-18 18:14:13 -07001819 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001820 } else {
1821 // An unrelated pointer changed.
1822 newAction = MotionEvent.ACTION_MOVE;
1823 }
1824 } else {
1825 // Simple up/down/cancel/move motion action.
1826 newAction = oldMaskedAction;
1827 }
1828 }
1829
1830 transformedEvent = null;
1831 final int historySize = event.getHistorySize();
1832 for (int historyIndex = 0; historyIndex <= historySize; historyIndex++) {
1833 for (newPointerIndex = 0; newPointerIndex < newPointerCount; newPointerIndex++) {
1834 final MotionEvent.PointerCoords c = newPointerCoords[newPointerIndex];
1835 oldPointerIndex = newPointerIndexMap[newPointerIndex];
1836 if (historyIndex != historySize) {
1837 event.getHistoricalPointerCoords(oldPointerIndex, historyIndex, c);
1838 } else {
1839 event.getPointerCoords(oldPointerIndex, c);
Adam Powell2b342f02010-08-18 18:14:13 -07001840 }
1841 }
1842
Jeff Brown20e987b2010-08-23 12:01:02 -07001843 final long eventTime;
1844 if (historyIndex != historySize) {
1845 eventTime = event.getHistoricalEventTime(historyIndex);
1846 } else {
1847 eventTime = event.getEventTime();
1848 }
1849
1850 if (transformedEvent == null) {
1851 transformedEvent = MotionEvent.obtain(
1852 event.getDownTime(), eventTime, newAction,
1853 newPointerCount, newPointerIds, newPointerCoords,
1854 event.getMetaState(), event.getXPrecision(), event.getYPrecision(),
1855 event.getDeviceId(), event.getEdgeFlags(), event.getSource(),
1856 event.getFlags());
1857 } else {
1858 transformedEvent.addBatch(eventTime, newPointerCoords, 0);
Adam Powell2b342f02010-08-18 18:14:13 -07001859 }
1860 }
1861 }
1862
Jeff Brown20e987b2010-08-23 12:01:02 -07001863 // Perform any necessary transformations and dispatch.
1864 if (child == null) {
1865 handled = super.dispatchTouchEvent(transformedEvent);
1866 } else {
1867 final float offsetX = mScrollX - child.mLeft;
1868 final float offsetY = mScrollY - child.mTop;
1869 transformedEvent.offsetLocation(offsetX, offsetY);
1870 if (! child.hasIdentityMatrix()) {
1871 transformedEvent.transform(child.getInverseMatrix());
Adam Powell2b342f02010-08-18 18:14:13 -07001872 }
1873
Jeff Brown20e987b2010-08-23 12:01:02 -07001874 handled = child.dispatchTouchEvent(transformedEvent);
Adam Powell2b342f02010-08-18 18:14:13 -07001875 }
1876
Jeff Brown20e987b2010-08-23 12:01:02 -07001877 // Done.
1878 transformedEvent.recycle();
Adam Powell2b342f02010-08-18 18:14:13 -07001879 return handled;
1880 }
1881
Romain Guy469b1db2010-10-05 11:49:57 -07001882 /**
1883 * Enlarge the temporary pointer arrays for splitting pointers.
1884 * May discard contents (but keeps PointerCoords objects to avoid reallocating them).
1885 */
1886 private void growTmpPointerArrays(int desiredCapacity) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001887 final MotionEvent.PointerCoords[] oldTmpPointerCoords = mTmpPointerCoords;
1888 int capacity;
1889 if (oldTmpPointerCoords != null) {
1890 capacity = oldTmpPointerCoords.length;
1891 if (desiredCapacity <= capacity) {
1892 return;
1893 }
1894 } else {
1895 capacity = 4;
1896 }
1897
1898 while (capacity < desiredCapacity) {
1899 capacity *= 2;
1900 }
1901
1902 mTmpPointerIndexMap = new int[capacity];
1903 mTmpPointerIds = new int[capacity];
1904 mTmpPointerCoords = new MotionEvent.PointerCoords[capacity];
1905
1906 if (oldTmpPointerCoords != null) {
1907 System.arraycopy(oldTmpPointerCoords, 0, mTmpPointerCoords, 0,
1908 oldTmpPointerCoords.length);
1909 }
1910 }
1911
Adam Powell2b342f02010-08-18 18:14:13 -07001912 /**
1913 * Enable or disable the splitting of MotionEvents to multiple children during touch event
Jeff Brown995e7742010-12-22 16:59:36 -08001914 * dispatch. This behavior is enabled by default for applications that target an
1915 * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
Adam Powell2b342f02010-08-18 18:14:13 -07001916 *
1917 * <p>When this option is enabled MotionEvents may be split and dispatched to different child
1918 * views depending on where each pointer initially went down. This allows for user interactions
1919 * such as scrolling two panes of content independently, chording of buttons, and performing
1920 * independent gestures on different pieces of content.
1921 *
1922 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
1923 * child views. <code>false</code> to only allow one child view to be the target of
1924 * any MotionEvent received by this ViewGroup.
1925 */
1926 public void setMotionEventSplittingEnabled(boolean split) {
1927 // TODO Applications really shouldn't change this setting mid-touch event,
1928 // but perhaps this should handle that case and send ACTION_CANCELs to any child views
1929 // with gestures in progress when this is changed.
1930 if (split) {
Adam Powell2b342f02010-08-18 18:14:13 -07001931 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
1932 } else {
1933 mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
Adam Powell2b342f02010-08-18 18:14:13 -07001934 }
1935 }
1936
1937 /**
Jeff Brown995e7742010-12-22 16:59:36 -08001938 * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
Adam Powell2b342f02010-08-18 18:14:13 -07001939 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
1940 */
1941 public boolean isMotionEventSplittingEnabled() {
1942 return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
1943 }
1944
1945 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001946 * {@inheritDoc}
1947 */
1948 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
Romain Guy8506ab42009-06-11 17:35:47 -07001949
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001950 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
1951 // We're already in this state, assume our ancestors are too
1952 return;
1953 }
Romain Guy8506ab42009-06-11 17:35:47 -07001954
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001955 if (disallowIntercept) {
1956 mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
1957 } else {
1958 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
1959 }
Romain Guy8506ab42009-06-11 17:35:47 -07001960
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001961 // Pass it up to our parent
1962 if (mParent != null) {
1963 mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
1964 }
1965 }
1966
1967 /**
1968 * Implement this method to intercept all touch screen motion events. This
1969 * allows you to watch events as they are dispatched to your children, and
1970 * take ownership of the current gesture at any point.
1971 *
1972 * <p>Using this function takes some care, as it has a fairly complicated
1973 * interaction with {@link View#onTouchEvent(MotionEvent)
1974 * View.onTouchEvent(MotionEvent)}, and using it requires implementing
1975 * that method as well as this one in the correct way. Events will be
1976 * received in the following order:
1977 *
1978 * <ol>
1979 * <li> You will receive the down event here.
1980 * <li> The down event will be handled either by a child of this view
1981 * group, or given to your own onTouchEvent() method to handle; this means
1982 * you should implement onTouchEvent() to return true, so you will
1983 * continue to see the rest of the gesture (instead of looking for
1984 * a parent view to handle it). Also, by returning true from
1985 * onTouchEvent(), you will not receive any following
1986 * events in onInterceptTouchEvent() and all touch processing must
1987 * happen in onTouchEvent() like normal.
1988 * <li> For as long as you return false from this function, each following
1989 * event (up to and including the final up) will be delivered first here
1990 * and then to the target's onTouchEvent().
1991 * <li> If you return true from here, you will not receive any
1992 * following events: the target view will receive the same event but
1993 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
1994 * events will be delivered to your onTouchEvent() method and no longer
1995 * appear here.
1996 * </ol>
1997 *
1998 * @param ev The motion event being dispatched down the hierarchy.
1999 * @return Return true to steal motion events from the children and have
2000 * them dispatched to this ViewGroup through onTouchEvent().
2001 * The current target will receive an ACTION_CANCEL event, and no further
2002 * messages will be delivered here.
2003 */
2004 public boolean onInterceptTouchEvent(MotionEvent ev) {
2005 return false;
2006 }
2007
2008 /**
2009 * {@inheritDoc}
2010 *
2011 * Looks for a view to give focus to respecting the setting specified by
2012 * {@link #getDescendantFocusability()}.
2013 *
2014 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
2015 * find focus within the children of this group when appropriate.
2016 *
2017 * @see #FOCUS_BEFORE_DESCENDANTS
2018 * @see #FOCUS_AFTER_DESCENDANTS
2019 * @see #FOCUS_BLOCK_DESCENDANTS
2020 * @see #onRequestFocusInDescendants
2021 */
2022 @Override
2023 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
2024 if (DBG) {
2025 System.out.println(this + " ViewGroup.requestFocus direction="
2026 + direction);
2027 }
2028 int descendantFocusability = getDescendantFocusability();
2029
2030 switch (descendantFocusability) {
2031 case FOCUS_BLOCK_DESCENDANTS:
2032 return super.requestFocus(direction, previouslyFocusedRect);
2033 case FOCUS_BEFORE_DESCENDANTS: {
2034 final boolean took = super.requestFocus(direction, previouslyFocusedRect);
2035 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
2036 }
2037 case FOCUS_AFTER_DESCENDANTS: {
2038 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
2039 return took ? took : super.requestFocus(direction, previouslyFocusedRect);
2040 }
2041 default:
2042 throw new IllegalStateException("descendant focusability must be "
2043 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
2044 + "but is " + descendantFocusability);
2045 }
2046 }
2047
2048 /**
2049 * Look for a descendant to call {@link View#requestFocus} on.
2050 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
2051 * when it wants to request focus within its children. Override this to
2052 * customize how your {@link ViewGroup} requests focus within its children.
2053 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
2054 * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
2055 * to give a finer grained hint about where focus is coming from. May be null
2056 * if there is no hint.
2057 * @return Whether focus was taken.
2058 */
2059 @SuppressWarnings({"ConstantConditions"})
2060 protected boolean onRequestFocusInDescendants(int direction,
2061 Rect previouslyFocusedRect) {
2062 int index;
2063 int increment;
2064 int end;
2065 int count = mChildrenCount;
2066 if ((direction & FOCUS_FORWARD) != 0) {
2067 index = 0;
2068 increment = 1;
2069 end = count;
2070 } else {
2071 index = count - 1;
2072 increment = -1;
2073 end = -1;
2074 }
2075 final View[] children = mChildren;
2076 for (int i = index; i != end; i += increment) {
2077 View child = children[i];
2078 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2079 if (child.requestFocus(direction, previouslyFocusedRect)) {
2080 return true;
2081 }
2082 }
2083 }
2084 return false;
2085 }
Chet Haase5c13d892010-10-08 08:37:55 -07002086
Romain Guya440b002010-02-24 15:57:54 -08002087 /**
2088 * {@inheritDoc}
Chet Haase5c13d892010-10-08 08:37:55 -07002089 *
Romain Guydcc490f2010-02-24 17:59:35 -08002090 * @hide
Romain Guya440b002010-02-24 15:57:54 -08002091 */
2092 @Override
2093 public void dispatchStartTemporaryDetach() {
2094 super.dispatchStartTemporaryDetach();
2095 final int count = mChildrenCount;
2096 final View[] children = mChildren;
2097 for (int i = 0; i < count; i++) {
2098 children[i].dispatchStartTemporaryDetach();
2099 }
2100 }
Chet Haase5c13d892010-10-08 08:37:55 -07002101
Romain Guya440b002010-02-24 15:57:54 -08002102 /**
2103 * {@inheritDoc}
Chet Haase5c13d892010-10-08 08:37:55 -07002104 *
Romain Guydcc490f2010-02-24 17:59:35 -08002105 * @hide
Romain Guya440b002010-02-24 15:57:54 -08002106 */
2107 @Override
2108 public void dispatchFinishTemporaryDetach() {
2109 super.dispatchFinishTemporaryDetach();
2110 final int count = mChildrenCount;
2111 final View[] children = mChildren;
2112 for (int i = 0; i < count; i++) {
2113 children[i].dispatchFinishTemporaryDetach();
2114 }
2115 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002116
2117 /**
2118 * {@inheritDoc}
2119 */
2120 @Override
2121 void dispatchAttachedToWindow(AttachInfo info, int visibility) {
2122 super.dispatchAttachedToWindow(info, visibility);
2123 visibility |= mViewFlags & VISIBILITY_MASK;
2124 final int count = mChildrenCount;
2125 final View[] children = mChildren;
2126 for (int i = 0; i < count; i++) {
2127 children[i].dispatchAttachedToWindow(info, visibility);
2128 }
2129 }
2130
svetoslavganov75986cf2009-05-14 22:28:01 -07002131 @Override
2132 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002133 // We first get a chance to populate the event.
2134 onPopulateAccessibilityEvent(event);
2135 // Let our children have a shot in populating the event.
svetoslavganov75986cf2009-05-14 22:28:01 -07002136 for (int i = 0, count = getChildCount(); i < count; i++) {
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002137 boolean handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event);
2138 if (handled) {
2139 return handled;
2140 }
svetoslavganov75986cf2009-05-14 22:28:01 -07002141 }
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002142 return false;
svetoslavganov75986cf2009-05-14 22:28:01 -07002143 }
2144
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002145 /**
2146 * {@inheritDoc}
2147 */
2148 @Override
2149 void dispatchDetachedFromWindow() {
Jeff Brown20e987b2010-08-23 12:01:02 -07002150 // If we still have a touch target, we are still in the process of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002151 // dispatching motion events to a child; we need to get rid of that
2152 // child to avoid dispatching events to it after the window is torn
2153 // down. To make sure we keep the child in a consistent state, we
2154 // first send it an ACTION_CANCEL motion event.
Jeff Brown20e987b2010-08-23 12:01:02 -07002155 cancelAndClearTouchTargets(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002156
Chet Haase9c087442011-01-12 16:20:16 -08002157 // In case view is detached while transition is running
2158 mLayoutSuppressed = false;
2159
Christopher Tate86cab1b2011-01-13 20:28:55 -08002160 // Tear down our drag tracking
2161 mDragNotifiedChildren = null;
2162 if (mCurrentDrag != null) {
2163 mCurrentDrag.recycle();
2164 mCurrentDrag = null;
2165 }
2166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002167 final int count = mChildrenCount;
2168 final View[] children = mChildren;
2169 for (int i = 0; i < count; i++) {
2170 children[i].dispatchDetachedFromWindow();
2171 }
2172 super.dispatchDetachedFromWindow();
2173 }
2174
2175 /**
2176 * {@inheritDoc}
2177 */
2178 @Override
2179 public void setPadding(int left, int top, int right, int bottom) {
2180 super.setPadding(left, top, right, bottom);
2181
Romain Guy13f35f32011-03-24 12:03:17 -07002182 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002183 mGroupFlags |= FLAG_PADDING_NOT_NULL;
2184 } else {
2185 mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
2186 }
2187 }
2188
2189 /**
2190 * {@inheritDoc}
2191 */
2192 @Override
2193 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
2194 super.dispatchSaveInstanceState(container);
2195 final int count = mChildrenCount;
2196 final View[] children = mChildren;
2197 for (int i = 0; i < count; i++) {
Dianne Hackbornb4bc78b2010-05-12 18:59:50 -07002198 View c = children[i];
2199 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2200 c.dispatchSaveInstanceState(container);
2201 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002202 }
2203 }
2204
2205 /**
Romain Guy9fc27812011-04-27 14:21:41 -07002206 * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()}
2207 * to only this view, not to its children. For use when overriding
2208 * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow
2209 * subclasses to freeze their own state but not the state of their children.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002210 *
2211 * @param container the container
2212 */
2213 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
2214 super.dispatchSaveInstanceState(container);
2215 }
2216
2217 /**
2218 * {@inheritDoc}
2219 */
2220 @Override
2221 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
2222 super.dispatchRestoreInstanceState(container);
2223 final int count = mChildrenCount;
2224 final View[] children = mChildren;
2225 for (int i = 0; i < count; i++) {
Dianne Hackbornb4bc78b2010-05-12 18:59:50 -07002226 View c = children[i];
2227 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2228 c.dispatchRestoreInstanceState(container);
2229 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002230 }
2231 }
2232
2233 /**
2234 * Perform dispatching of a {@link #restoreHierarchyState thaw()} to only this view,
2235 * not to its children. For use when overriding
2236 * {@link #dispatchRestoreInstanceState dispatchThaw()} to allow subclasses to thaw
2237 * their own state but not the state of their children.
2238 *
2239 * @param container the container
2240 */
2241 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
2242 super.dispatchRestoreInstanceState(container);
2243 }
2244
2245 /**
2246 * Enables or disables the drawing cache for each child of this view group.
2247 *
2248 * @param enabled true to enable the cache, false to dispose of it
2249 */
2250 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
2251 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
2252 final View[] children = mChildren;
2253 final int count = mChildrenCount;
2254 for (int i = 0; i < count; i++) {
2255 children[i].setDrawingCacheEnabled(enabled);
2256 }
2257 }
2258 }
2259
2260 @Override
2261 protected void onAnimationStart() {
2262 super.onAnimationStart();
2263
2264 // When this ViewGroup's animation starts, build the cache for the children
2265 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2266 final int count = mChildrenCount;
2267 final View[] children = mChildren;
Romain Guy0d9275e2010-10-26 14:22:30 -07002268 final boolean buildCache = !isHardwareAccelerated();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002269
2270 for (int i = 0; i < count; i++) {
2271 final View child = children[i];
2272 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2273 child.setDrawingCacheEnabled(true);
Romain Guy0d9275e2010-10-26 14:22:30 -07002274 if (buildCache) {
2275 child.buildDrawingCache(true);
2276 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002277 }
2278 }
2279
2280 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2281 }
2282 }
2283
2284 @Override
2285 protected void onAnimationEnd() {
2286 super.onAnimationEnd();
2287
2288 // When this ViewGroup's animation ends, destroy the cache of the children
2289 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2290 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2291
2292 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2293 setChildrenDrawingCacheEnabled(false);
2294 }
2295 }
2296 }
2297
Romain Guy223ff5c2010-03-02 17:07:47 -08002298 @Override
2299 Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
Romain Guy65554f22010-03-22 18:58:21 -07002300 int count = mChildrenCount;
2301 int[] visibilities = null;
2302
Romain Guy223ff5c2010-03-02 17:07:47 -08002303 if (skipChildren) {
Romain Guy65554f22010-03-22 18:58:21 -07002304 visibilities = new int[count];
2305 for (int i = 0; i < count; i++) {
2306 View child = getChildAt(i);
2307 visibilities[i] = child.getVisibility();
2308 if (visibilities[i] == View.VISIBLE) {
2309 child.setVisibility(INVISIBLE);
2310 }
2311 }
Romain Guy223ff5c2010-03-02 17:07:47 -08002312 }
2313
2314 Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
Romain Guy65554f22010-03-22 18:58:21 -07002315
2316 if (skipChildren) {
2317 for (int i = 0; i < count; i++) {
2318 getChildAt(i).setVisibility(visibilities[i]);
Chet Haase5c13d892010-10-08 08:37:55 -07002319 }
Romain Guy65554f22010-03-22 18:58:21 -07002320 }
Romain Guy223ff5c2010-03-02 17:07:47 -08002321
2322 return b;
2323 }
2324
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002325 /**
2326 * {@inheritDoc}
2327 */
2328 @Override
2329 protected void dispatchDraw(Canvas canvas) {
2330 final int count = mChildrenCount;
2331 final View[] children = mChildren;
2332 int flags = mGroupFlags;
2333
2334 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
2335 final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
2336
Romain Guy0d9275e2010-10-26 14:22:30 -07002337 final boolean buildCache = !isHardwareAccelerated();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002338 for (int i = 0; i < count; i++) {
2339 final View child = children[i];
2340 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2341 final LayoutParams params = child.getLayoutParams();
2342 attachLayoutAnimationParameters(child, params, i, count);
2343 bindLayoutAnimation(child);
2344 if (cache) {
2345 child.setDrawingCacheEnabled(true);
Romain Guy0d9275e2010-10-26 14:22:30 -07002346 if (buildCache) {
2347 child.buildDrawingCache(true);
2348 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002349 }
2350 }
2351 }
2352
2353 final LayoutAnimationController controller = mLayoutAnimationController;
2354 if (controller.willOverlap()) {
2355 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
2356 }
2357
2358 controller.start();
2359
2360 mGroupFlags &= ~FLAG_RUN_ANIMATION;
2361 mGroupFlags &= ~FLAG_ANIMATION_DONE;
2362
2363 if (cache) {
2364 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2365 }
2366
2367 if (mAnimationListener != null) {
2368 mAnimationListener.onAnimationStart(controller.getAnimation());
2369 }
2370 }
2371
2372 int saveCount = 0;
2373 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
2374 if (clipToPadding) {
2375 saveCount = canvas.save();
Romain Guy8f2d94f2009-03-25 18:04:42 -07002376 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
2377 mScrollX + mRight - mLeft - mPaddingRight,
2378 mScrollY + mBottom - mTop - mPaddingBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002379
2380 }
2381
2382 // We will draw our child's animation, let's reset the flag
2383 mPrivateFlags &= ~DRAW_ANIMATION;
2384 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
2385
2386 boolean more = false;
2387 final long drawingTime = getDrawingTime();
2388
2389 if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
2390 for (int i = 0; i < count; i++) {
2391 final View child = children[i];
2392 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2393 more |= drawChild(canvas, child, drawingTime);
2394 }
2395 }
2396 } else {
2397 for (int i = 0; i < count; i++) {
2398 final View child = children[getChildDrawingOrder(count, i)];
2399 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2400 more |= drawChild(canvas, child, drawingTime);
2401 }
2402 }
2403 }
2404
2405 // Draw any disappearing views that have animations
2406 if (mDisappearingChildren != null) {
2407 final ArrayList<View> disappearingChildren = mDisappearingChildren;
2408 final int disappearingCount = disappearingChildren.size() - 1;
2409 // Go backwards -- we may delete as animations finish
2410 for (int i = disappearingCount; i >= 0; i--) {
2411 final View child = disappearingChildren.get(i);
2412 more |= drawChild(canvas, child, drawingTime);
2413 }
2414 }
2415
2416 if (clipToPadding) {
2417 canvas.restoreToCount(saveCount);
2418 }
2419
2420 // mGroupFlags might have been updated by drawChild()
2421 flags = mGroupFlags;
2422
2423 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
Romain Guy849d0a32011-02-01 17:20:48 -08002424 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002425 }
2426
2427 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
2428 mLayoutAnimationController.isDone() && !more) {
2429 // We want to erase the drawing cache and notify the listener after the
2430 // next frame is drawn because one extra invalidate() is caused by
2431 // drawChild() after the animation is over
2432 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
2433 final Runnable end = new Runnable() {
2434 public void run() {
2435 notifyAnimationListener();
2436 }
2437 };
2438 post(end);
2439 }
2440 }
Romain Guy8506ab42009-06-11 17:35:47 -07002441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002442 /**
2443 * Returns the index of the child to draw for this iteration. Override this
2444 * if you want to change the drawing order of children. By default, it
2445 * returns i.
2446 * <p>
Romain Guy293451e2009-11-04 13:59:48 -08002447 * NOTE: In order for this method to be called, you must enable child ordering
2448 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
Romain Guy8506ab42009-06-11 17:35:47 -07002449 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002450 * @param i The current iteration.
2451 * @return The index of the child to draw this iteration.
Chet Haase5c13d892010-10-08 08:37:55 -07002452 *
Romain Guy293451e2009-11-04 13:59:48 -08002453 * @see #setChildrenDrawingOrderEnabled(boolean)
2454 * @see #isChildrenDrawingOrderEnabled()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002455 */
2456 protected int getChildDrawingOrder(int childCount, int i) {
2457 return i;
2458 }
Romain Guy8506ab42009-06-11 17:35:47 -07002459
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002460 private void notifyAnimationListener() {
2461 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
2462 mGroupFlags |= FLAG_ANIMATION_DONE;
2463
2464 if (mAnimationListener != null) {
2465 final Runnable end = new Runnable() {
2466 public void run() {
2467 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
2468 }
2469 };
2470 post(end);
2471 }
2472
2473 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2474 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2475 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2476 setChildrenDrawingCacheEnabled(false);
2477 }
2478 }
2479
Romain Guy849d0a32011-02-01 17:20:48 -08002480 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002481 }
2482
2483 /**
Chet Haasedaf98e92011-01-10 14:10:36 -08002484 * This method is used to cause children of this ViewGroup to restore or recreate their
2485 * display lists. It is called by getDisplayList() when the parent ViewGroup does not need
2486 * to recreate its own display list, which would happen if it went through the normal
2487 * draw/dispatchDraw mechanisms.
2488 *
2489 * @hide
2490 */
2491 @Override
2492 protected void dispatchGetDisplayList() {
2493 final int count = mChildrenCount;
2494 final View[] children = mChildren;
2495 for (int i = 0; i < count; i++) {
2496 final View child = children[i];
Romain Guy2f57ba52011-02-03 18:03:29 -08002497 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2498 child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED;
2499 child.mPrivateFlags &= ~INVALIDATED;
2500 child.getDisplayList();
2501 child.mRecreateDisplayList = false;
2502 }
Chet Haasedaf98e92011-01-10 14:10:36 -08002503 }
2504 }
2505
2506 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002507 * Draw one child of this View Group. This method is responsible for getting
2508 * the canvas in the right state. This includes clipping, translating so
2509 * that the child's scrolled origin is at 0, 0, and applying any animation
2510 * transformations.
2511 *
2512 * @param canvas The canvas on which to draw the child
2513 * @param child Who to draw
2514 * @param drawingTime The time at which draw is occuring
2515 * @return True if an invalidate() was issued
2516 */
2517 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
2518 boolean more = false;
2519
2520 final int cl = child.mLeft;
2521 final int ct = child.mTop;
2522 final int cr = child.mRight;
2523 final int cb = child.mBottom;
2524
Chet Haase70d4ba12010-10-06 09:46:45 -07002525 final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
2526
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002527 final int flags = mGroupFlags;
2528
2529 if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
Chet Haase48460322010-06-11 14:22:25 -07002530 mChildTransformation.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002531 mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION;
2532 }
2533
2534 Transformation transformToApply = null;
Chet Haase48460322010-06-11 14:22:25 -07002535 Transformation invalidationTransform;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002536 final Animation a = child.getAnimation();
2537 boolean concatMatrix = false;
2538
Chet Haase48460322010-06-11 14:22:25 -07002539 boolean scalingRequired = false;
Romain Guy171c5922011-01-06 10:04:23 -08002540 boolean caching;
Romain Guy849d0a32011-02-01 17:20:48 -08002541 int layerType = mDrawLayers ? child.getLayerType() : LAYER_TYPE_NONE;
Chet Haasee38ba4a2011-01-27 01:10:35 -08002542
2543 final boolean hardwareAccelerated = canvas.isHardwareAccelerated();
Romain Guyb051e892010-09-28 19:09:36 -07002544 if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
Chet Haase48460322010-06-11 14:22:25 -07002545 (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
2546 caching = true;
2547 if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
Romain Guy171c5922011-01-06 10:04:23 -08002548 } else {
Chet Haasee38ba4a2011-01-27 01:10:35 -08002549 caching = (layerType != LAYER_TYPE_NONE) || hardwareAccelerated;
Chet Haase48460322010-06-11 14:22:25 -07002550 }
2551
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002552 if (a != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002553 final boolean initialized = a.isInitialized();
2554 if (!initialized) {
Romain Guy8f2d94f2009-03-25 18:04:42 -07002555 a.initialize(cr - cl, cb - ct, getWidth(), getHeight());
2556 a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002557 child.onAnimationStart();
2558 }
2559
Chet Haase48460322010-06-11 14:22:25 -07002560 more = a.getTransformation(drawingTime, mChildTransformation,
2561 scalingRequired ? mAttachInfo.mApplicationScale : 1f);
2562 if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
2563 if (mInvalidationTransformation == null) {
2564 mInvalidationTransformation = new Transformation();
2565 }
2566 invalidationTransform = mInvalidationTransformation;
2567 a.getTransformation(drawingTime, invalidationTransform, 1f);
2568 } else {
2569 invalidationTransform = mChildTransformation;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002570 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002571 transformToApply = mChildTransformation;
2572
2573 concatMatrix = a.willChangeTransformationMatrix();
2574
2575 if (more) {
2576 if (!a.willChangeBounds()) {
2577 if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) ==
2578 FLAG_OPTIMIZE_INVALIDATE) {
2579 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
2580 } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) {
2581 // The child need to draw an animation, potentially offscreen, so
2582 // make sure we do not cancel invalidate requests
2583 mPrivateFlags |= DRAW_ANIMATION;
2584 invalidate(cl, ct, cr, cb);
2585 }
2586 } else {
Chet Haase48460322010-06-11 14:22:25 -07002587 if (mInvalidateRegion == null) {
2588 mInvalidateRegion = new RectF();
2589 }
2590 final RectF region = mInvalidateRegion;
2591 a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, invalidationTransform);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002592
2593 // The child need to draw an animation, potentially offscreen, so
2594 // make sure we do not cancel invalidate requests
2595 mPrivateFlags |= DRAW_ANIMATION;
The Android Open Source Project4df24232009-03-05 14:34:35 -08002596
2597 final int left = cl + (int) region.left;
2598 final int top = ct + (int) region.top;
2599 invalidate(left, top, left + (int) region.width(), top + (int) region.height());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002600 }
2601 }
2602 } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
2603 FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002604 final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation);
2605 if (hasTransform) {
2606 final int transformType = mChildTransformation.getTransformationType();
2607 transformToApply = transformType != Transformation.TYPE_IDENTITY ?
2608 mChildTransformation : null;
2609 concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
2610 }
2611 }
2612
Chet Haase70d4ba12010-10-06 09:46:45 -07002613 concatMatrix |= !childHasIdentityMatrix;
Chet Haasedf030d22010-07-30 17:22:38 -07002614
Romain Guy5bcdff42009-05-14 21:27:18 -07002615 // Sets the flag as early as possible to allow draw() implementations
Romain Guy986003d2009-03-25 17:42:35 -07002616 // to call invalidate() successfully when doing animations
Romain Guy5bcdff42009-05-14 21:27:18 -07002617 child.mPrivateFlags |= DRAWN;
Romain Guy986003d2009-03-25 17:42:35 -07002618
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002619 if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
2620 (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
2621 return more;
2622 }
Chet Haase5c13d892010-10-08 08:37:55 -07002623
Romain Guydbc26d22010-10-11 17:58:29 -07002624 float alpha = child.getAlpha();
2625 // Bail out early if the view does not need to be drawn
Romain Guy67a2f7b52010-10-13 15:35:22 -07002626 if (alpha <= ViewConfiguration.ALPHA_THRESHOLD && (child.mPrivateFlags & ALPHA_SET) == 0 &&
2627 !(child instanceof SurfaceView)) {
Romain Guya3496a92010-10-12 11:53:24 -07002628 return more;
2629 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002630
Chet Haasee38ba4a2011-01-27 01:10:35 -08002631 if (hardwareAccelerated) {
Chet Haasedaf98e92011-01-10 14:10:36 -08002632 // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
2633 // retain the flag's value temporarily in the mRecreateDisplayList flag
2634 child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED;
2635 child.mPrivateFlags &= ~INVALIDATED;
2636 }
2637
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002638 child.computeScroll();
2639
2640 final int sx = child.mScrollX;
2641 final int sy = child.mScrollY;
2642
Romain Guyb051e892010-09-28 19:09:36 -07002643 DisplayList displayList = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002644 Bitmap cache = null;
Chet Haase678e0ad2011-01-25 09:37:18 -08002645 boolean hasDisplayList = false;
Chet Haase48460322010-06-11 14:22:25 -07002646 if (caching) {
Chet Haasee38ba4a2011-01-27 01:10:35 -08002647 if (!hardwareAccelerated) {
Romain Guy171c5922011-01-06 10:04:23 -08002648 if (layerType != LAYER_TYPE_NONE) {
Romain Guy6c319ca2011-01-11 14:29:25 -08002649 layerType = LAYER_TYPE_SOFTWARE;
Romain Guy171c5922011-01-06 10:04:23 -08002650 child.buildDrawingCache(true);
2651 }
Romain Guyb051e892010-09-28 19:09:36 -07002652 cache = child.getDrawingCache(true);
2653 } else {
Romain Guyd643bb52011-03-01 14:55:21 -08002654 switch (layerType) {
2655 case LAYER_TYPE_SOFTWARE:
2656 child.buildDrawingCache(true);
2657 cache = child.getDrawingCache(true);
2658 break;
2659 case LAYER_TYPE_NONE:
2660 // Delay getting the display list until animation-driven alpha values are
2661 // set up and possibly passed on to the view
2662 hasDisplayList = child.canHaveDisplayList();
2663 break;
Romain Guy171c5922011-01-06 10:04:23 -08002664 }
Romain Guyb051e892010-09-28 19:09:36 -07002665 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002666 }
2667
Romain Guyb051e892010-09-28 19:09:36 -07002668 final boolean hasNoCache = cache == null || hasDisplayList;
Romain Guyd643bb52011-03-01 14:55:21 -08002669 final boolean offsetForScroll = cache == null && !hasDisplayList &&
2670 layerType != LAYER_TYPE_HARDWARE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002671
2672 final int restoreTo = canvas.save();
Romain Guyd643bb52011-03-01 14:55:21 -08002673 if (offsetForScroll) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002674 canvas.translate(cl - sx, ct - sy);
2675 } else {
2676 canvas.translate(cl, ct);
Romain Guy8506ab42009-06-11 17:35:47 -07002677 if (scalingRequired) {
Romain Guycafdea62009-06-12 10:51:36 -07002678 // mAttachInfo cannot be null, otherwise scalingRequired == false
Romain Guy8506ab42009-06-11 17:35:47 -07002679 final float scale = 1.0f / mAttachInfo.mApplicationScale;
2680 canvas.scale(scale, scale);
2681 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002682 }
2683
Romain Guy33e72ae2010-07-17 12:40:29 -07002684 if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
Chet Haase70d4ba12010-10-06 09:46:45 -07002685 if (transformToApply != null || !childHasIdentityMatrix) {
2686 int transX = 0;
2687 int transY = 0;
Romain Guy33e72ae2010-07-17 12:40:29 -07002688
Romain Guyd643bb52011-03-01 14:55:21 -08002689 if (offsetForScroll) {
Chet Haase70d4ba12010-10-06 09:46:45 -07002690 transX = -sx;
2691 transY = -sy;
2692 }
Romain Guy33e72ae2010-07-17 12:40:29 -07002693
Chet Haase70d4ba12010-10-06 09:46:45 -07002694 if (transformToApply != null) {
2695 if (concatMatrix) {
2696 // Undo the scroll translation, apply the transformation matrix,
2697 // then redo the scroll translate to get the correct result.
2698 canvas.translate(-transX, -transY);
2699 canvas.concat(transformToApply.getMatrix());
2700 canvas.translate(transX, transY);
2701 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
2702 }
2703
2704 float transformAlpha = transformToApply.getAlpha();
2705 if (transformAlpha < 1.0f) {
2706 alpha *= transformToApply.getAlpha();
2707 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
2708 }
2709 }
2710
2711 if (!childHasIdentityMatrix) {
Chet Haasec3aa3612010-06-17 08:50:37 -07002712 canvas.translate(-transX, -transY);
Chet Haase70d4ba12010-10-06 09:46:45 -07002713 canvas.concat(child.getMatrix());
Chet Haasec3aa3612010-06-17 08:50:37 -07002714 canvas.translate(transX, transY);
Chet Haasec3aa3612010-06-17 08:50:37 -07002715 }
Chet Haasec3aa3612010-06-17 08:50:37 -07002716 }
Romain Guy33e72ae2010-07-17 12:40:29 -07002717
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002718 if (alpha < 1.0f) {
2719 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
Romain Guy33e72ae2010-07-17 12:40:29 -07002720 if (hasNoCache) {
2721 final int multipliedAlpha = (int) (255 * alpha);
2722 if (!child.onSetAlpha(multipliedAlpha)) {
Romain Guyfd880422010-09-23 16:16:04 -07002723 int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
Romain Guy171c5922011-01-06 10:04:23 -08002724 if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN ||
2725 layerType != LAYER_TYPE_NONE) {
Romain Guyfd880422010-09-23 16:16:04 -07002726 layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
2727 }
Romain Guy54229ee2011-02-01 13:05:16 -08002728 if (layerType == LAYER_TYPE_NONE) {
Romain Guya7dabcd2011-03-01 15:44:21 -08002729 final int scrollX = hasDisplayList ? 0 : sx;
2730 final int scrollY = hasDisplayList ? 0 : sy;
2731 canvas.saveLayerAlpha(scrollX, scrollY, scrollX + cr - cl,
2732 scrollY + cb - ct, multipliedAlpha, layerFlags);
Romain Guy171c5922011-01-06 10:04:23 -08002733 }
Romain Guy33e72ae2010-07-17 12:40:29 -07002734 } else {
Romain Guy171c5922011-01-06 10:04:23 -08002735 // Alpha is handled by the child directly, clobber the layer's alpha
Romain Guy33e72ae2010-07-17 12:40:29 -07002736 child.mPrivateFlags |= ALPHA_SET;
2737 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002738 }
2739 }
2740 } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
2741 child.onSetAlpha(255);
Romain Guy9b34d452010-09-02 11:45:04 -07002742 child.mPrivateFlags &= ~ALPHA_SET;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002743 }
2744
2745 if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
Romain Guyd643bb52011-03-01 14:55:21 -08002746 if (offsetForScroll) {
Romain Guy8f2d94f2009-03-25 18:04:42 -07002747 canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002748 } else {
Chet Haasedaf98e92011-01-10 14:10:36 -08002749 if (!scalingRequired || cache == null) {
Romain Guy8506ab42009-06-11 17:35:47 -07002750 canvas.clipRect(0, 0, cr - cl, cb - ct);
2751 } else {
2752 canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
2753 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002754 }
2755 }
2756
Chet Haase678e0ad2011-01-25 09:37:18 -08002757 if (hasDisplayList) {
2758 displayList = child.getDisplayList();
2759 }
2760
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002761 if (hasNoCache) {
Romain Guy6c319ca2011-01-11 14:29:25 -08002762 boolean layerRendered = false;
Romain Guyd6cd5722011-01-17 14:42:41 -08002763 if (layerType == LAYER_TYPE_HARDWARE) {
Romain Guy5e7f7662011-01-24 22:35:56 -08002764 final HardwareLayer layer = child.getHardwareLayer();
Romain Guy6c319ca2011-01-11 14:29:25 -08002765 if (layer != null && layer.isValid()) {
Romain Guy54229ee2011-02-01 13:05:16 -08002766 child.mLayerPaint.setAlpha((int) (alpha * 255));
Romain Guyada830f2011-01-13 12:13:20 -08002767 ((HardwareCanvas) canvas).drawHardwareLayer(layer, 0, 0, child.mLayerPaint);
Romain Guy6c319ca2011-01-11 14:29:25 -08002768 layerRendered = true;
Romain Guyb051e892010-09-28 19:09:36 -07002769 } else {
Romain Guya7dabcd2011-03-01 15:44:21 -08002770 final int scrollX = hasDisplayList ? 0 : sx;
2771 final int scrollY = hasDisplayList ? 0 : sy;
2772 canvas.saveLayer(scrollX, scrollY,
2773 scrollX + cr - cl, scrollY + cb - ct, child.mLayerPaint,
Romain Guy6c319ca2011-01-11 14:29:25 -08002774 Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002775 }
Romain Guy6c319ca2011-01-11 14:29:25 -08002776 }
2777
2778 if (!layerRendered) {
2779 if (!hasDisplayList) {
2780 // Fast path for layouts with no backgrounds
2781 if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
2782 if (ViewDebug.TRACE_HIERARCHY) {
2783 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
2784 }
2785 child.mPrivateFlags &= ~DIRTY_MASK;
2786 child.dispatchDraw(canvas);
2787 } else {
2788 child.draw(canvas);
2789 }
2790 } else {
2791 child.mPrivateFlags &= ~DIRTY_MASK;
Romain Guy7b5b6ab2011-03-14 18:05:08 -07002792 ((HardwareCanvas) canvas).drawDisplayList(displayList, cr - cl, cb - ct, null);
Romain Guy6c319ca2011-01-11 14:29:25 -08002793 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002794 }
Romain Guyb051e892010-09-28 19:09:36 -07002795 } else if (cache != null) {
Chet Haasef2f7d8f2010-12-03 14:08:14 -08002796 child.mPrivateFlags &= ~DIRTY_MASK;
Romain Guy171c5922011-01-06 10:04:23 -08002797 Paint cachePaint;
2798
Romain Guyd6cd5722011-01-17 14:42:41 -08002799 if (layerType == LAYER_TYPE_NONE) {
Romain Guy171c5922011-01-06 10:04:23 -08002800 cachePaint = mCachePaint;
2801 if (alpha < 1.0f) {
2802 cachePaint.setAlpha((int) (alpha * 255));
2803 mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
2804 } else if ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
2805 cachePaint.setAlpha(255);
2806 mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
2807 }
2808 } else {
2809 cachePaint = child.mLayerPaint;
Romain Guyd6cd5722011-01-17 14:42:41 -08002810 cachePaint.setAlpha((int) (alpha * 255));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002811 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002812 canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
2813 }
2814
2815 canvas.restoreToCount(restoreTo);
2816
2817 if (a != null && !more) {
Chet Haasee38ba4a2011-01-27 01:10:35 -08002818 if (!hardwareAccelerated && !a.getFillAfter()) {
Chet Haase678e0ad2011-01-25 09:37:18 -08002819 child.onSetAlpha(255);
2820 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002821 finishAnimatingView(child, a);
2822 }
2823
Chet Haasee38ba4a2011-01-27 01:10:35 -08002824 if (more && hardwareAccelerated) {
Chet Haasedaf98e92011-01-10 14:10:36 -08002825 // invalidation is the trigger to recreate display lists, so if we're using
2826 // display lists to render, force an invalidate to allow the animation to
2827 // continue drawing another frame
Romain Guy849d0a32011-02-01 17:20:48 -08002828 invalidate(true);
Chet Haase678e0ad2011-01-25 09:37:18 -08002829 if (a instanceof AlphaAnimation) {
2830 // alpha animations should cause the child to recreate its display list
Romain Guy849d0a32011-02-01 17:20:48 -08002831 child.invalidate(true);
Chet Haase678e0ad2011-01-25 09:37:18 -08002832 }
Chet Haasedaf98e92011-01-10 14:10:36 -08002833 }
2834
2835 child.mRecreateDisplayList = false;
2836
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002837 return more;
2838 }
2839
2840 /**
Romain Guy849d0a32011-02-01 17:20:48 -08002841 *
2842 * @param enabled True if children should be drawn with layers, false otherwise.
2843 *
2844 * @hide
2845 */
2846 public void setChildrenLayersEnabled(boolean enabled) {
Romain Guy9d18f2d2011-02-03 11:25:51 -08002847 if (enabled != mDrawLayers) {
2848 mDrawLayers = enabled;
2849 invalidate(true);
2850
2851 // We need to invalidate any child with a layer. For instance,
2852 // if a child is backed by a hardware layer and we disable layers
2853 // the child is marked as not dirty (flags cleared the last time
2854 // the child was drawn inside its layer.) However, that child might
2855 // never have created its own display list or have an obsolete
2856 // display list. By invalidating the child we ensure the display
2857 // list is in sync with the content of the hardware layer.
2858 for (int i = 0; i < mChildrenCount; i++) {
2859 View child = mChildren[i];
2860 if (child.mLayerType != LAYER_TYPE_NONE) {
2861 child.invalidate(true);
2862 }
2863 }
2864 }
Romain Guy849d0a32011-02-01 17:20:48 -08002865 }
2866
2867 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002868 * By default, children are clipped to their bounds before drawing. This
2869 * allows view groups to override this behavior for animations, etc.
2870 *
2871 * @param clipChildren true to clip children to their bounds,
2872 * false otherwise
2873 * @attr ref android.R.styleable#ViewGroup_clipChildren
2874 */
2875 public void setClipChildren(boolean clipChildren) {
2876 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
2877 }
2878
2879 /**
2880 * By default, children are clipped to the padding of the ViewGroup. This
2881 * allows view groups to override this behavior
2882 *
2883 * @param clipToPadding true to clip children to the padding of the
2884 * group, false otherwise
2885 * @attr ref android.R.styleable#ViewGroup_clipToPadding
2886 */
2887 public void setClipToPadding(boolean clipToPadding) {
2888 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
2889 }
2890
2891 /**
2892 * {@inheritDoc}
2893 */
2894 @Override
2895 public void dispatchSetSelected(boolean selected) {
2896 final View[] children = mChildren;
2897 final int count = mChildrenCount;
2898 for (int i = 0; i < count; i++) {
2899 children[i].setSelected(selected);
2900 }
2901 }
Romain Guy8506ab42009-06-11 17:35:47 -07002902
Dianne Hackbornd0fa3712010-09-14 18:57:14 -07002903 /**
2904 * {@inheritDoc}
2905 */
2906 @Override
2907 public void dispatchSetActivated(boolean activated) {
2908 final View[] children = mChildren;
2909 final int count = mChildrenCount;
2910 for (int i = 0; i < count; i++) {
Dianne Hackbornd0fa3712010-09-14 18:57:14 -07002911 children[i].setActivated(activated);
2912 }
2913 }
2914
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002915 @Override
2916 protected void dispatchSetPressed(boolean pressed) {
2917 final View[] children = mChildren;
2918 final int count = mChildrenCount;
2919 for (int i = 0; i < count; i++) {
2920 children[i].setPressed(pressed);
2921 }
2922 }
2923
2924 /**
2925 * When this property is set to true, this ViewGroup supports static transformations on
2926 * children; this causes
2927 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
2928 * invoked when a child is drawn.
2929 *
2930 * Any subclass overriding
2931 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
2932 * set this property to true.
2933 *
2934 * @param enabled True to enable static transformations on children, false otherwise.
2935 *
2936 * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS
2937 */
2938 protected void setStaticTransformationsEnabled(boolean enabled) {
2939 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
2940 }
2941
2942 /**
2943 * {@inheritDoc}
2944 *
Romain Guy8506ab42009-06-11 17:35:47 -07002945 * @see #setStaticTransformationsEnabled(boolean)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002946 */
2947 protected boolean getChildStaticTransformation(View child, Transformation t) {
2948 return false;
2949 }
2950
2951 /**
2952 * {@hide}
2953 */
2954 @Override
2955 protected View findViewTraversal(int id) {
2956 if (id == mID) {
2957 return this;
2958 }
2959
2960 final View[] where = mChildren;
2961 final int len = mChildrenCount;
2962
2963 for (int i = 0; i < len; i++) {
2964 View v = where[i];
2965
2966 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
2967 v = v.findViewById(id);
2968
2969 if (v != null) {
2970 return v;
2971 }
2972 }
2973 }
2974
2975 return null;
2976 }
2977
2978 /**
2979 * {@hide}
2980 */
2981 @Override
2982 protected View findViewWithTagTraversal(Object tag) {
2983 if (tag != null && tag.equals(mTag)) {
2984 return this;
2985 }
2986
2987 final View[] where = mChildren;
2988 final int len = mChildrenCount;
2989
2990 for (int i = 0; i < len; i++) {
2991 View v = where[i];
2992
2993 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
2994 v = v.findViewWithTag(tag);
2995
2996 if (v != null) {
2997 return v;
2998 }
2999 }
3000 }
3001
3002 return null;
3003 }
3004
3005 /**
Jeff Brown4e6319b2010-12-13 10:36:51 -08003006 * {@hide}
3007 */
3008 @Override
3009 protected View findViewByPredicateTraversal(Predicate<View> predicate) {
3010 if (predicate.apply(this)) {
3011 return this;
3012 }
3013
3014 final View[] where = mChildren;
3015 final int len = mChildrenCount;
3016
3017 for (int i = 0; i < len; i++) {
3018 View v = where[i];
3019
3020 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
3021 v = v.findViewByPredicate(predicate);
3022
3023 if (v != null) {
3024 return v;
3025 }
3026 }
3027 }
3028
3029 return null;
3030 }
3031
3032 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003033 * Adds a child view. If no layout parameters are already set on the child, the
3034 * default parameters for this ViewGroup are set on the child.
3035 *
3036 * @param child the child view to add
3037 *
3038 * @see #generateDefaultLayoutParams()
3039 */
3040 public void addView(View child) {
3041 addView(child, -1);
3042 }
3043
3044 /**
3045 * Adds a child view. If no layout parameters are already set on the child, the
3046 * default parameters for this ViewGroup are set on the child.
3047 *
3048 * @param child the child view to add
3049 * @param index the position at which to add the child
3050 *
3051 * @see #generateDefaultLayoutParams()
3052 */
3053 public void addView(View child, int index) {
3054 LayoutParams params = child.getLayoutParams();
3055 if (params == null) {
3056 params = generateDefaultLayoutParams();
3057 if (params == null) {
3058 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
3059 }
3060 }
3061 addView(child, index, params);
3062 }
3063
3064 /**
3065 * Adds a child view with this ViewGroup's default layout parameters and the
3066 * specified width and height.
3067 *
3068 * @param child the child view to add
3069 */
3070 public void addView(View child, int width, int height) {
3071 final LayoutParams params = generateDefaultLayoutParams();
3072 params.width = width;
3073 params.height = height;
3074 addView(child, -1, params);
3075 }
3076
3077 /**
3078 * Adds a child view with the specified layout parameters.
3079 *
3080 * @param child the child view to add
3081 * @param params the layout parameters to set on the child
3082 */
3083 public void addView(View child, LayoutParams params) {
3084 addView(child, -1, params);
3085 }
3086
3087 /**
3088 * Adds a child view with the specified layout parameters.
3089 *
3090 * @param child the child view to add
3091 * @param index the position at which to add the child
3092 * @param params the layout parameters to set on the child
3093 */
3094 public void addView(View child, int index, LayoutParams params) {
3095 if (DBG) {
3096 System.out.println(this + " addView");
3097 }
3098
3099 // addViewInner() will call child.requestLayout() when setting the new LayoutParams
3100 // therefore, we call requestLayout() on ourselves before, so that the child's request
3101 // will be blocked at our level
3102 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003103 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003104 addViewInner(child, index, params, false);
3105 }
3106
3107 /**
3108 * {@inheritDoc}
3109 */
3110 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
3111 if (!checkLayoutParams(params)) {
3112 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
3113 }
3114 if (view.mParent != this) {
3115 throw new IllegalArgumentException("Given view not a child of " + this);
3116 }
3117 view.setLayoutParams(params);
3118 }
3119
3120 /**
3121 * {@inheritDoc}
3122 */
3123 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3124 return p != null;
3125 }
3126
3127 /**
3128 * Interface definition for a callback to be invoked when the hierarchy
3129 * within this view changed. The hierarchy changes whenever a child is added
3130 * to or removed from this view.
3131 */
3132 public interface OnHierarchyChangeListener {
3133 /**
3134 * Called when a new child is added to a parent view.
3135 *
3136 * @param parent the view in which a child was added
3137 * @param child the new child view added in the hierarchy
3138 */
3139 void onChildViewAdded(View parent, View child);
3140
3141 /**
3142 * Called when a child is removed from a parent view.
3143 *
3144 * @param parent the view from which the child was removed
3145 * @param child the child removed from the hierarchy
3146 */
3147 void onChildViewRemoved(View parent, View child);
3148 }
3149
3150 /**
3151 * Register a callback to be invoked when a child is added to or removed
3152 * from this view.
3153 *
3154 * @param listener the callback to invoke on hierarchy change
3155 */
3156 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
3157 mOnHierarchyChangeListener = listener;
3158 }
3159
3160 /**
3161 * Adds a view during layout. This is useful if in your onLayout() method,
3162 * you need to add more views (as does the list view for example).
3163 *
3164 * If index is negative, it means put it at the end of the list.
3165 *
3166 * @param child the view to add to the group
3167 * @param index the index at which the child must be added
3168 * @param params the layout parameters to associate with the child
3169 * @return true if the child was added, false otherwise
3170 */
3171 protected boolean addViewInLayout(View child, int index, LayoutParams params) {
3172 return addViewInLayout(child, index, params, false);
3173 }
3174
3175 /**
3176 * Adds a view during layout. This is useful if in your onLayout() method,
3177 * you need to add more views (as does the list view for example).
3178 *
3179 * If index is negative, it means put it at the end of the list.
3180 *
3181 * @param child the view to add to the group
3182 * @param index the index at which the child must be added
3183 * @param params the layout parameters to associate with the child
3184 * @param preventRequestLayout if true, calling this method will not trigger a
3185 * layout request on child
3186 * @return true if the child was added, false otherwise
3187 */
3188 protected boolean addViewInLayout(View child, int index, LayoutParams params,
3189 boolean preventRequestLayout) {
3190 child.mParent = null;
3191 addViewInner(child, index, params, preventRequestLayout);
Romain Guy24443ea2009-05-11 11:56:30 -07003192 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003193 return true;
3194 }
3195
3196 /**
3197 * Prevents the specified child to be laid out during the next layout pass.
3198 *
3199 * @param child the child on which to perform the cleanup
3200 */
3201 protected void cleanupLayoutState(View child) {
3202 child.mPrivateFlags &= ~View.FORCE_LAYOUT;
3203 }
3204
3205 private void addViewInner(View child, int index, LayoutParams params,
3206 boolean preventRequestLayout) {
3207
Chet Haasee8e45d32011-03-02 17:07:35 -08003208 if (mTransition != null) {
3209 // Don't prevent other add transitions from completing, but cancel remove
3210 // transitions to let them complete the process before we add to the container
3211 mTransition.cancel(LayoutTransition.DISAPPEARING);
Chet Haaseadd65772011-02-09 16:47:29 -08003212 }
3213
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003214 if (child.getParent() != null) {
3215 throw new IllegalStateException("The specified child already has a parent. " +
3216 "You must call removeView() on the child's parent first.");
3217 }
3218
Chet Haase21cd1382010-09-01 17:42:29 -07003219 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003220 mTransition.addChild(this, child);
Chet Haase21cd1382010-09-01 17:42:29 -07003221 }
3222
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003223 if (!checkLayoutParams(params)) {
3224 params = generateLayoutParams(params);
3225 }
3226
3227 if (preventRequestLayout) {
3228 child.mLayoutParams = params;
3229 } else {
3230 child.setLayoutParams(params);
3231 }
3232
3233 if (index < 0) {
3234 index = mChildrenCount;
3235 }
3236
3237 addInArray(child, index);
3238
3239 // tell our children
3240 if (preventRequestLayout) {
3241 child.assignParent(this);
3242 } else {
3243 child.mParent = this;
3244 }
3245
3246 if (child.hasFocus()) {
3247 requestChildFocus(child, child.findFocus());
3248 }
Romain Guy8506ab42009-06-11 17:35:47 -07003249
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003250 AttachInfo ai = mAttachInfo;
3251 if (ai != null) {
Romain Guy8506ab42009-06-11 17:35:47 -07003252 boolean lastKeepOn = ai.mKeepScreenOn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003253 ai.mKeepScreenOn = false;
3254 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
3255 if (ai.mKeepScreenOn) {
3256 needGlobalAttributesUpdate(true);
3257 }
3258 ai.mKeepScreenOn = lastKeepOn;
3259 }
3260
3261 if (mOnHierarchyChangeListener != null) {
3262 mOnHierarchyChangeListener.onChildViewAdded(this, child);
3263 }
3264
3265 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
3266 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
3267 }
3268 }
3269
3270 private void addInArray(View child, int index) {
3271 View[] children = mChildren;
3272 final int count = mChildrenCount;
3273 final int size = children.length;
3274 if (index == count) {
3275 if (size == count) {
3276 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3277 System.arraycopy(children, 0, mChildren, 0, size);
3278 children = mChildren;
3279 }
3280 children[mChildrenCount++] = child;
3281 } else if (index < count) {
3282 if (size == count) {
3283 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3284 System.arraycopy(children, 0, mChildren, 0, index);
3285 System.arraycopy(children, index, mChildren, index + 1, count - index);
3286 children = mChildren;
3287 } else {
3288 System.arraycopy(children, index, children, index + 1, count - index);
3289 }
3290 children[index] = child;
3291 mChildrenCount++;
Joe Onorato03ab0c72011-01-06 15:46:27 -08003292 if (mLastTouchDownIndex >= index) {
3293 mLastTouchDownIndex++;
3294 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003295 } else {
3296 throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
3297 }
3298 }
3299
3300 // This method also sets the child's mParent to null
3301 private void removeFromArray(int index) {
3302 final View[] children = mChildren;
Chet Haase21cd1382010-09-01 17:42:29 -07003303 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
3304 children[index].mParent = null;
3305 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003306 final int count = mChildrenCount;
3307 if (index == count - 1) {
3308 children[--mChildrenCount] = null;
3309 } else if (index >= 0 && index < count) {
3310 System.arraycopy(children, index + 1, children, index, count - index - 1);
3311 children[--mChildrenCount] = null;
3312 } else {
3313 throw new IndexOutOfBoundsException();
3314 }
Joe Onorato03ab0c72011-01-06 15:46:27 -08003315 if (mLastTouchDownIndex == index) {
3316 mLastTouchDownTime = 0;
3317 mLastTouchDownIndex = -1;
3318 } else if (mLastTouchDownIndex > index) {
3319 mLastTouchDownIndex--;
3320 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003321 }
3322
3323 // This method also sets the children's mParent to null
3324 private void removeFromArray(int start, int count) {
3325 final View[] children = mChildren;
3326 final int childrenCount = mChildrenCount;
3327
3328 start = Math.max(0, start);
3329 final int end = Math.min(childrenCount, start + count);
3330
3331 if (start == end) {
3332 return;
3333 }
3334
3335 if (end == childrenCount) {
3336 for (int i = start; i < end; i++) {
3337 children[i].mParent = null;
3338 children[i] = null;
3339 }
3340 } else {
3341 for (int i = start; i < end; i++) {
3342 children[i].mParent = null;
3343 }
3344
3345 // Since we're looping above, we might as well do the copy, but is arraycopy()
3346 // faster than the extra 2 bounds checks we would do in the loop?
3347 System.arraycopy(children, end, children, start, childrenCount - end);
3348
3349 for (int i = childrenCount - (end - start); i < childrenCount; i++) {
3350 children[i] = null;
3351 }
3352 }
3353
3354 mChildrenCount -= (end - start);
3355 }
3356
3357 private void bindLayoutAnimation(View child) {
3358 Animation a = mLayoutAnimationController.getAnimationForView(child);
3359 child.setAnimation(a);
3360 }
3361
3362 /**
3363 * Subclasses should override this method to set layout animation
3364 * parameters on the supplied child.
3365 *
3366 * @param child the child to associate with animation parameters
3367 * @param params the child's layout parameters which hold the animation
3368 * parameters
3369 * @param index the index of the child in the view group
3370 * @param count the number of children in the view group
3371 */
3372 protected void attachLayoutAnimationParameters(View child,
3373 LayoutParams params, int index, int count) {
3374 LayoutAnimationController.AnimationParameters animationParams =
3375 params.layoutAnimationParameters;
3376 if (animationParams == null) {
3377 animationParams = new LayoutAnimationController.AnimationParameters();
3378 params.layoutAnimationParameters = animationParams;
3379 }
3380
3381 animationParams.count = count;
3382 animationParams.index = index;
3383 }
3384
3385 /**
3386 * {@inheritDoc}
3387 */
3388 public void removeView(View view) {
3389 removeViewInternal(view);
3390 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003391 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003392 }
3393
3394 /**
3395 * Removes a view during layout. This is useful if in your onLayout() method,
3396 * you need to remove more views.
3397 *
3398 * @param view the view to remove from the group
3399 */
3400 public void removeViewInLayout(View view) {
3401 removeViewInternal(view);
3402 }
3403
3404 /**
3405 * Removes a range of views during layout. This is useful if in your onLayout() method,
3406 * you need to remove more views.
3407 *
3408 * @param start the index of the first view to remove from the group
3409 * @param count the number of views to remove from the group
3410 */
3411 public void removeViewsInLayout(int start, int count) {
3412 removeViewsInternal(start, count);
3413 }
3414
3415 /**
3416 * Removes the view at the specified position in the group.
3417 *
3418 * @param index the position in the group of the view to remove
3419 */
3420 public void removeViewAt(int index) {
3421 removeViewInternal(index, getChildAt(index));
3422 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003423 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003424 }
3425
3426 /**
3427 * Removes the specified range of views from the group.
3428 *
3429 * @param start the first position in the group of the range of views to remove
3430 * @param count the number of views to remove
3431 */
3432 public void removeViews(int start, int count) {
3433 removeViewsInternal(start, count);
3434 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003435 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003436 }
3437
3438 private void removeViewInternal(View view) {
3439 final int index = indexOfChild(view);
3440 if (index >= 0) {
3441 removeViewInternal(index, view);
3442 }
3443 }
3444
3445 private void removeViewInternal(int index, View view) {
Chet Haase21cd1382010-09-01 17:42:29 -07003446
3447 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003448 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003449 }
3450
Jeff Browna032cc02011-03-07 16:56:21 -08003451 if (view == mHoveredChild) {
3452 mHoveredChild = null;
3453 }
3454
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003455 boolean clearChildFocus = false;
3456 if (view == mFocused) {
3457 view.clearFocusForRemoval();
3458 clearChildFocus = true;
3459 }
3460
Chet Haase21cd1382010-09-01 17:42:29 -07003461 if (view.getAnimation() != null ||
3462 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003463 addDisappearingView(view);
3464 } else if (view.mAttachInfo != null) {
3465 view.dispatchDetachedFromWindow();
3466 }
3467
3468 if (mOnHierarchyChangeListener != null) {
3469 mOnHierarchyChangeListener.onChildViewRemoved(this, view);
3470 }
3471
3472 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003473
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003474 removeFromArray(index);
3475
3476 if (clearChildFocus) {
3477 clearChildFocus(view);
3478 }
3479 }
3480
Chet Haase21cd1382010-09-01 17:42:29 -07003481 /**
3482 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
3483 * not null, changes in layout which occur because of children being added to or removed from
3484 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
3485 * object. By default, the transition object is null (so layout changes are not animated).
3486 *
3487 * @param transition The LayoutTransition object that will animated changes in layout. A value
3488 * of <code>null</code> means no transition will run on layout changes.
Chet Haase13cc1202010-09-03 15:39:20 -07003489 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
Chet Haase21cd1382010-09-01 17:42:29 -07003490 */
3491 public void setLayoutTransition(LayoutTransition transition) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07003492 if (mTransition != null) {
3493 mTransition.removeTransitionListener(mLayoutTransitionListener);
3494 }
Chet Haase21cd1382010-09-01 17:42:29 -07003495 mTransition = transition;
Chet Haase13cc1202010-09-03 15:39:20 -07003496 if (mTransition != null) {
3497 mTransition.addTransitionListener(mLayoutTransitionListener);
3498 }
3499 }
3500
3501 /**
3502 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
3503 * not null, changes in layout which occur because of children being added to or removed from
3504 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
3505 * object. By default, the transition object is null (so layout changes are not animated).
3506 *
3507 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
3508 * A value of <code>null</code> means no transition will run on layout changes.
3509 */
3510 public LayoutTransition getLayoutTransition() {
3511 return mTransition;
Chet Haase21cd1382010-09-01 17:42:29 -07003512 }
3513
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003514 private void removeViewsInternal(int start, int count) {
3515 final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
3516 final boolean notifyListener = onHierarchyChangeListener != null;
3517 final View focused = mFocused;
Jeff Browna032cc02011-03-07 16:56:21 -08003518 final View hoveredChild = mHoveredChild;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003519 final boolean detach = mAttachInfo != null;
3520 View clearChildFocus = null;
3521
3522 final View[] children = mChildren;
3523 final int end = start + count;
3524
3525 for (int i = start; i < end; i++) {
3526 final View view = children[i];
3527
Chet Haase21cd1382010-09-01 17:42:29 -07003528 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003529 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003530 }
3531
Jeff Browna032cc02011-03-07 16:56:21 -08003532 if (view == hoveredChild) {
3533 mHoveredChild = null;
3534 }
3535
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003536 if (view == focused) {
3537 view.clearFocusForRemoval();
3538 clearChildFocus = view;
3539 }
3540
Chet Haase21cd1382010-09-01 17:42:29 -07003541 if (view.getAnimation() != null ||
3542 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003543 addDisappearingView(view);
3544 } else if (detach) {
3545 view.dispatchDetachedFromWindow();
3546 }
3547
3548 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003549
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003550 if (notifyListener) {
3551 onHierarchyChangeListener.onChildViewRemoved(this, view);
3552 }
3553 }
3554
3555 removeFromArray(start, count);
3556
3557 if (clearChildFocus != null) {
3558 clearChildFocus(clearChildFocus);
3559 }
3560 }
3561
3562 /**
3563 * Call this method to remove all child views from the
3564 * ViewGroup.
3565 */
3566 public void removeAllViews() {
3567 removeAllViewsInLayout();
3568 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003569 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003570 }
3571
3572 /**
3573 * Called by a ViewGroup subclass to remove child views from itself,
3574 * when it must first know its size on screen before it can calculate how many
3575 * child views it will render. An example is a Gallery or a ListView, which
3576 * may "have" 50 children, but actually only render the number of children
3577 * that can currently fit inside the object on screen. Do not call
3578 * this method unless you are extending ViewGroup and understand the
3579 * view measuring and layout pipeline.
3580 */
3581 public void removeAllViewsInLayout() {
3582 final int count = mChildrenCount;
3583 if (count <= 0) {
3584 return;
3585 }
3586
3587 final View[] children = mChildren;
3588 mChildrenCount = 0;
3589
3590 final OnHierarchyChangeListener listener = mOnHierarchyChangeListener;
3591 final boolean notify = listener != null;
3592 final View focused = mFocused;
Jeff Browna032cc02011-03-07 16:56:21 -08003593 final View hoveredChild = mHoveredChild;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003594 final boolean detach = mAttachInfo != null;
3595 View clearChildFocus = null;
3596
3597 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003598
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003599 for (int i = count - 1; i >= 0; i--) {
3600 final View view = children[i];
3601
Chet Haase21cd1382010-09-01 17:42:29 -07003602 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003603 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003604 }
3605
Jeff Browna032cc02011-03-07 16:56:21 -08003606 if (view == hoveredChild) {
3607 mHoveredChild = null;
3608 }
3609
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003610 if (view == focused) {
3611 view.clearFocusForRemoval();
3612 clearChildFocus = view;
3613 }
3614
Chet Haase21cd1382010-09-01 17:42:29 -07003615 if (view.getAnimation() != null ||
3616 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003617 addDisappearingView(view);
3618 } else if (detach) {
3619 view.dispatchDetachedFromWindow();
3620 }
3621
3622 if (notify) {
3623 listener.onChildViewRemoved(this, view);
3624 }
3625
3626 view.mParent = null;
3627 children[i] = null;
3628 }
3629
3630 if (clearChildFocus != null) {
3631 clearChildFocus(clearChildFocus);
3632 }
3633 }
3634
3635 /**
3636 * Finishes the removal of a detached view. This method will dispatch the detached from
3637 * window event and notify the hierarchy change listener.
3638 *
3639 * @param child the child to be definitely removed from the view hierarchy
3640 * @param animate if true and the view has an animation, the view is placed in the
3641 * disappearing views list, otherwise, it is detached from the window
3642 *
3643 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3644 * @see #detachAllViewsFromParent()
3645 * @see #detachViewFromParent(View)
3646 * @see #detachViewFromParent(int)
3647 */
3648 protected void removeDetachedView(View child, boolean animate) {
Chet Haase21cd1382010-09-01 17:42:29 -07003649 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003650 mTransition.removeChild(this, child);
Chet Haase21cd1382010-09-01 17:42:29 -07003651 }
3652
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003653 if (child == mFocused) {
3654 child.clearFocus();
3655 }
Romain Guy8506ab42009-06-11 17:35:47 -07003656
Chet Haase21cd1382010-09-01 17:42:29 -07003657 if ((animate && child.getAnimation() != null) ||
3658 (mTransitioningViews != null && mTransitioningViews.contains(child))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003659 addDisappearingView(child);
3660 } else if (child.mAttachInfo != null) {
3661 child.dispatchDetachedFromWindow();
3662 }
3663
3664 if (mOnHierarchyChangeListener != null) {
3665 mOnHierarchyChangeListener.onChildViewRemoved(this, child);
3666 }
3667 }
3668
3669 /**
3670 * Attaches a view to this view group. Attaching a view assigns this group as the parent,
3671 * sets the layout parameters and puts the view in the list of children so it can be retrieved
3672 * by calling {@link #getChildAt(int)}.
3673 *
3674 * This method should be called only for view which were detached from their parent.
3675 *
3676 * @param child the child to attach
3677 * @param index the index at which the child should be attached
3678 * @param params the layout parameters of the child
3679 *
3680 * @see #removeDetachedView(View, boolean)
3681 * @see #detachAllViewsFromParent()
3682 * @see #detachViewFromParent(View)
3683 * @see #detachViewFromParent(int)
3684 */
3685 protected void attachViewToParent(View child, int index, LayoutParams params) {
3686 child.mLayoutParams = params;
3687
3688 if (index < 0) {
3689 index = mChildrenCount;
3690 }
3691
3692 addInArray(child, index);
3693
3694 child.mParent = this;
Chet Haase3b2b0fc2011-01-24 17:53:52 -08003695 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK & ~DRAWING_CACHE_VALID) |
3696 DRAWN | INVALIDATED;
3697 this.mPrivateFlags |= INVALIDATED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003698
3699 if (child.hasFocus()) {
3700 requestChildFocus(child, child.findFocus());
3701 }
3702 }
3703
3704 /**
3705 * Detaches a view from its parent. Detaching a view should be temporary and followed
3706 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3707 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3708 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3709 *
3710 * @param child the child to detach
3711 *
3712 * @see #detachViewFromParent(int)
3713 * @see #detachViewsFromParent(int, int)
3714 * @see #detachAllViewsFromParent()
3715 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3716 * @see #removeDetachedView(View, boolean)
3717 */
3718 protected void detachViewFromParent(View child) {
3719 removeFromArray(indexOfChild(child));
3720 }
3721
3722 /**
3723 * Detaches a view from its parent. Detaching a view should be temporary and followed
3724 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3725 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3726 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3727 *
3728 * @param index the index of the child to detach
3729 *
3730 * @see #detachViewFromParent(View)
3731 * @see #detachAllViewsFromParent()
3732 * @see #detachViewsFromParent(int, int)
3733 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3734 * @see #removeDetachedView(View, boolean)
3735 */
3736 protected void detachViewFromParent(int index) {
3737 removeFromArray(index);
3738 }
3739
3740 /**
3741 * Detaches a range of view from their parent. Detaching a view should be temporary and followed
3742 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3743 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its
3744 * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3745 *
3746 * @param start the first index of the childrend range to detach
3747 * @param count the number of children to detach
3748 *
3749 * @see #detachViewFromParent(View)
3750 * @see #detachViewFromParent(int)
3751 * @see #detachAllViewsFromParent()
3752 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3753 * @see #removeDetachedView(View, boolean)
3754 */
3755 protected void detachViewsFromParent(int start, int count) {
3756 removeFromArray(start, count);
3757 }
3758
3759 /**
3760 * Detaches all views from the parent. Detaching a view should be temporary and followed
3761 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3762 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3763 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3764 *
3765 * @see #detachViewFromParent(View)
3766 * @see #detachViewFromParent(int)
3767 * @see #detachViewsFromParent(int, int)
3768 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3769 * @see #removeDetachedView(View, boolean)
3770 */
3771 protected void detachAllViewsFromParent() {
3772 final int count = mChildrenCount;
3773 if (count <= 0) {
3774 return;
3775 }
3776
3777 final View[] children = mChildren;
3778 mChildrenCount = 0;
3779
3780 for (int i = count - 1; i >= 0; i--) {
3781 children[i].mParent = null;
3782 children[i] = null;
3783 }
3784 }
3785
3786 /**
3787 * Don't call or override this method. It is used for the implementation of
3788 * the view hierarchy.
3789 */
3790 public final void invalidateChild(View child, final Rect dirty) {
3791 if (ViewDebug.TRACE_HIERARCHY) {
3792 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
3793 }
3794
3795 ViewParent parent = this;
3796
3797 final AttachInfo attachInfo = mAttachInfo;
3798 if (attachInfo != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003799 // If the child is drawing an animation, we want to copy this flag onto
3800 // ourselves and the parent to make sure the invalidate request goes
3801 // through
3802 final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
Romain Guy24443ea2009-05-11 11:56:30 -07003803
Chet Haase70d4ba12010-10-06 09:46:45 -07003804 if (dirty == null) {
Chet Haasedaf98e92011-01-10 14:10:36 -08003805 if (child.mLayerType != LAYER_TYPE_NONE) {
3806 mPrivateFlags |= INVALIDATED;
3807 mPrivateFlags &= ~DRAWING_CACHE_VALID;
Romain Guy3a3133d2011-02-01 22:59:58 -08003808 child.mLocalDirtyRect.setEmpty();
Chet Haasedaf98e92011-01-10 14:10:36 -08003809 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003810 do {
3811 View view = null;
3812 if (parent instanceof View) {
3813 view = (View) parent;
Romain Guy3a3133d2011-02-01 22:59:58 -08003814 if (view.mLayerType != LAYER_TYPE_NONE) {
3815 view.mLocalDirtyRect.setEmpty();
3816 if (view.getParent() instanceof View) {
3817 final View grandParent = (View) view.getParent();
3818 grandParent.mPrivateFlags |= INVALIDATED;
3819 grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3820 }
Chet Haasedaf98e92011-01-10 14:10:36 -08003821 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003822 if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
3823 // already marked dirty - we're done
3824 break;
3825 }
3826 }
3827
3828 if (drawAnimation) {
3829 if (view != null) {
3830 view.mPrivateFlags |= DRAW_ANIMATION;
Joe Onoratoc6cc0f82011-04-12 11:53:13 -07003831 } else if (parent instanceof ViewAncestor) {
3832 ((ViewAncestor) parent).mIsAnimating = true;
Chet Haase70d4ba12010-10-06 09:46:45 -07003833 }
3834 }
3835
Joe Onoratoc6cc0f82011-04-12 11:53:13 -07003836 if (parent instanceof ViewAncestor) {
3837 ((ViewAncestor) parent).invalidate();
Chet Haase70d4ba12010-10-06 09:46:45 -07003838 parent = null;
3839 } else if (view != null) {
Chet Haase77785f92011-01-25 23:22:09 -08003840 if ((view.mPrivateFlags & DRAWN) == DRAWN ||
3841 (view.mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
Chet Haase70d4ba12010-10-06 09:46:45 -07003842 view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
Chet Haase0d200832010-11-05 15:36:16 -07003843 view.mPrivateFlags |= DIRTY;
Chet Haase70d4ba12010-10-06 09:46:45 -07003844 parent = view.mParent;
3845 } else {
3846 parent = null;
3847 }
3848 }
3849 } while (parent != null);
3850 } else {
Chet Haase0d200832010-11-05 15:36:16 -07003851 // Check whether the child that requests the invalidate is fully opaque
3852 final boolean isOpaque = child.isOpaque() && !drawAnimation &&
3853 child.getAnimation() == null;
3854 // Mark the child as dirty, using the appropriate flag
3855 // Make sure we do not set both flags at the same time
Romain Guy7e68efb2011-01-07 14:50:27 -08003856 int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
Chet Haase0d200832010-11-05 15:36:16 -07003857
Romain Guybeff8d82011-02-01 23:53:34 -08003858 if (child.mLayerType != LAYER_TYPE_NONE) {
3859 mPrivateFlags |= INVALIDATED;
3860 mPrivateFlags &= ~DRAWING_CACHE_VALID;
3861 child.mLocalDirtyRect.union(dirty);
3862 }
3863
Chet Haase70d4ba12010-10-06 09:46:45 -07003864 final int[] location = attachInfo.mInvalidateChildLocation;
3865 location[CHILD_LEFT_INDEX] = child.mLeft;
3866 location[CHILD_TOP_INDEX] = child.mTop;
3867 Matrix childMatrix = child.getMatrix();
3868 if (!childMatrix.isIdentity()) {
3869 RectF boundingRect = attachInfo.mTmpTransformRect;
3870 boundingRect.set(dirty);
3871 childMatrix.mapRect(boundingRect);
3872 dirty.set((int) boundingRect.left, (int) boundingRect.top,
3873 (int) (boundingRect.right + 0.5f),
3874 (int) (boundingRect.bottom + 0.5f));
Romain Guy24443ea2009-05-11 11:56:30 -07003875 }
3876
Chet Haase70d4ba12010-10-06 09:46:45 -07003877 do {
3878 View view = null;
3879 if (parent instanceof View) {
3880 view = (View) parent;
Romain Guy7d7b5492011-01-24 16:33:45 -08003881 if (view.mLayerType != LAYER_TYPE_NONE &&
3882 view.getParent() instanceof View) {
3883 final View grandParent = (View) view.getParent();
3884 grandParent.mPrivateFlags |= INVALIDATED;
3885 grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3886 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003887 }
3888
3889 if (drawAnimation) {
3890 if (view != null) {
3891 view.mPrivateFlags |= DRAW_ANIMATION;
Joe Onoratoc6cc0f82011-04-12 11:53:13 -07003892 } else if (parent instanceof ViewAncestor) {
3893 ((ViewAncestor) parent).mIsAnimating = true;
Chet Haase70d4ba12010-10-06 09:46:45 -07003894 }
3895 }
3896
3897 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
3898 // flag coming from the child that initiated the invalidate
Romain Guy7e68efb2011-01-07 14:50:27 -08003899 if (view != null) {
3900 if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
Romain Guy2243e552011-03-08 11:46:28 -08003901 view.getSolidColor() == 0) {
Romain Guy7e68efb2011-01-07 14:50:27 -08003902 opaqueFlag = DIRTY;
3903 }
3904 if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
3905 view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
3906 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003907 }
3908
3909 parent = parent.invalidateChildInParent(location, dirty);
Romain Guy24443ea2009-05-11 11:56:30 -07003910 if (view != null) {
Chet Haase70d4ba12010-10-06 09:46:45 -07003911 // Account for transform on current parent
3912 Matrix m = view.getMatrix();
3913 if (!m.isIdentity()) {
3914 RectF boundingRect = attachInfo.mTmpTransformRect;
3915 boundingRect.set(dirty);
3916 m.mapRect(boundingRect);
3917 dirty.set((int) boundingRect.left, (int) boundingRect.top,
3918 (int) (boundingRect.right + 0.5f),
3919 (int) (boundingRect.bottom + 0.5f));
3920 }
Romain Guybb93d552009-03-24 21:04:15 -07003921 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003922 } while (parent != null);
3923 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003924 }
3925 }
3926
3927 /**
3928 * Don't call or override this method. It is used for the implementation of
3929 * the view hierarchy.
3930 *
3931 * This implementation returns null if this ViewGroup does not have a parent,
3932 * if this ViewGroup is already fully invalidated or if the dirty rectangle
3933 * does not intersect with this ViewGroup's bounds.
3934 */
3935 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
3936 if (ViewDebug.TRACE_HIERARCHY) {
3937 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
3938 }
3939
Chet Haase77785f92011-01-25 23:22:09 -08003940 if ((mPrivateFlags & DRAWN) == DRAWN ||
3941 (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003942 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
3943 FLAG_OPTIMIZE_INVALIDATE) {
3944 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
3945 location[CHILD_TOP_INDEX] - mScrollY);
3946
3947 final int left = mLeft;
3948 final int top = mTop;
3949
Adam Powell879fb6b2010-09-20 11:23:56 -07003950 if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003951 (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
3952 mPrivateFlags &= ~DRAWING_CACHE_VALID;
3953
3954 location[CHILD_LEFT_INDEX] = left;
3955 location[CHILD_TOP_INDEX] = top;
3956
Romain Guy3a3133d2011-02-01 22:59:58 -08003957 if (mLayerType != LAYER_TYPE_NONE) {
3958 mLocalDirtyRect.union(dirty);
3959 }
Romain Guybeff8d82011-02-01 23:53:34 -08003960
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003961 return mParent;
3962 }
3963 } else {
3964 mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
3965
3966 location[CHILD_LEFT_INDEX] = mLeft;
3967 location[CHILD_TOP_INDEX] = mTop;
3968
Romain Guy3a3133d2011-02-01 22:59:58 -08003969 dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
3970
3971 if (mLayerType != LAYER_TYPE_NONE) {
3972 mLocalDirtyRect.union(dirty);
3973 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003974
3975 return mParent;
3976 }
3977 }
3978
3979 return null;
3980 }
3981
3982 /**
3983 * Offset a rectangle that is in a descendant's coordinate
3984 * space into our coordinate space.
3985 * @param descendant A descendant of this view
3986 * @param rect A rectangle defined in descendant's coordinate space.
3987 */
3988 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
3989 offsetRectBetweenParentAndChild(descendant, rect, true, false);
3990 }
3991
3992 /**
3993 * Offset a rectangle that is in our coordinate space into an ancestor's
3994 * coordinate space.
3995 * @param descendant A descendant of this view
3996 * @param rect A rectangle defined in descendant's coordinate space.
3997 */
3998 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
3999 offsetRectBetweenParentAndChild(descendant, rect, false, false);
4000 }
4001
4002 /**
4003 * Helper method that offsets a rect either from parent to descendant or
4004 * descendant to parent.
4005 */
4006 void offsetRectBetweenParentAndChild(View descendant, Rect rect,
4007 boolean offsetFromChildToParent, boolean clipToBounds) {
4008
4009 // already in the same coord system :)
4010 if (descendant == this) {
4011 return;
4012 }
4013
4014 ViewParent theParent = descendant.mParent;
4015
4016 // search and offset up to the parent
4017 while ((theParent != null)
4018 && (theParent instanceof View)
4019 && (theParent != this)) {
4020
4021 if (offsetFromChildToParent) {
4022 rect.offset(descendant.mLeft - descendant.mScrollX,
4023 descendant.mTop - descendant.mScrollY);
4024 if (clipToBounds) {
4025 View p = (View) theParent;
4026 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4027 }
4028 } else {
4029 if (clipToBounds) {
4030 View p = (View) theParent;
4031 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4032 }
4033 rect.offset(descendant.mScrollX - descendant.mLeft,
4034 descendant.mScrollY - descendant.mTop);
4035 }
4036
4037 descendant = (View) theParent;
4038 theParent = descendant.mParent;
4039 }
4040
4041 // now that we are up to this view, need to offset one more time
4042 // to get into our coordinate space
4043 if (theParent == this) {
4044 if (offsetFromChildToParent) {
4045 rect.offset(descendant.mLeft - descendant.mScrollX,
4046 descendant.mTop - descendant.mScrollY);
4047 } else {
4048 rect.offset(descendant.mScrollX - descendant.mLeft,
4049 descendant.mScrollY - descendant.mTop);
4050 }
4051 } else {
4052 throw new IllegalArgumentException("parameter must be a descendant of this view");
4053 }
4054 }
4055
4056 /**
4057 * Offset the vertical location of all children of this view by the specified number of pixels.
4058 *
4059 * @param offset the number of pixels to offset
4060 *
4061 * @hide
4062 */
4063 public void offsetChildrenTopAndBottom(int offset) {
4064 final int count = mChildrenCount;
4065 final View[] children = mChildren;
4066
4067 for (int i = 0; i < count; i++) {
4068 final View v = children[i];
4069 v.mTop += offset;
4070 v.mBottom += offset;
4071 }
4072 }
4073
4074 /**
4075 * {@inheritDoc}
4076 */
4077 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
4078 int dx = child.mLeft - mScrollX;
4079 int dy = child.mTop - mScrollY;
4080 if (offset != null) {
4081 offset.x += dx;
4082 offset.y += dy;
4083 }
4084 r.offset(dx, dy);
4085 return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) &&
4086 (mParent == null || mParent.getChildVisibleRect(this, r, offset));
4087 }
4088
4089 /**
4090 * {@inheritDoc}
4091 */
4092 @Override
Chet Haase9c087442011-01-12 16:20:16 -08004093 public final void layout(int l, int t, int r, int b) {
4094 if (mTransition == null || !mTransition.isChangingLayout()) {
4095 super.layout(l, t, r, b);
4096 } else {
4097 // record the fact that we noop'd it; request layout when transition finishes
4098 mLayoutSuppressed = true;
4099 }
4100 }
4101
4102 /**
4103 * {@inheritDoc}
4104 */
4105 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004106 protected abstract void onLayout(boolean changed,
4107 int l, int t, int r, int b);
4108
4109 /**
4110 * Indicates whether the view group has the ability to animate its children
4111 * after the first layout.
4112 *
4113 * @return true if the children can be animated, false otherwise
4114 */
4115 protected boolean canAnimate() {
4116 return mLayoutAnimationController != null;
4117 }
4118
4119 /**
4120 * Runs the layout animation. Calling this method triggers a relayout of
4121 * this view group.
4122 */
4123 public void startLayoutAnimation() {
4124 if (mLayoutAnimationController != null) {
4125 mGroupFlags |= FLAG_RUN_ANIMATION;
4126 requestLayout();
4127 }
4128 }
4129
4130 /**
4131 * Schedules the layout animation to be played after the next layout pass
4132 * of this view group. This can be used to restart the layout animation
4133 * when the content of the view group changes or when the activity is
4134 * paused and resumed.
4135 */
4136 public void scheduleLayoutAnimation() {
4137 mGroupFlags |= FLAG_RUN_ANIMATION;
4138 }
4139
4140 /**
4141 * Sets the layout animation controller used to animate the group's
4142 * children after the first layout.
4143 *
4144 * @param controller the animation controller
4145 */
4146 public void setLayoutAnimation(LayoutAnimationController controller) {
4147 mLayoutAnimationController = controller;
4148 if (mLayoutAnimationController != null) {
4149 mGroupFlags |= FLAG_RUN_ANIMATION;
4150 }
4151 }
4152
4153 /**
4154 * Returns the layout animation controller used to animate the group's
4155 * children.
4156 *
4157 * @return the current animation controller
4158 */
4159 public LayoutAnimationController getLayoutAnimation() {
4160 return mLayoutAnimationController;
4161 }
4162
4163 /**
4164 * Indicates whether the children's drawing cache is used during a layout
4165 * animation. By default, the drawing cache is enabled but this will prevent
4166 * nested layout animations from working. To nest animations, you must disable
4167 * the cache.
4168 *
4169 * @return true if the animation cache is enabled, false otherwise
4170 *
4171 * @see #setAnimationCacheEnabled(boolean)
4172 * @see View#setDrawingCacheEnabled(boolean)
4173 */
4174 @ViewDebug.ExportedProperty
4175 public boolean isAnimationCacheEnabled() {
4176 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
4177 }
4178
4179 /**
4180 * Enables or disables the children's drawing cache during a layout animation.
4181 * By default, the drawing cache is enabled but this will prevent nested
4182 * layout animations from working. To nest animations, you must disable the
4183 * cache.
4184 *
4185 * @param enabled true to enable the animation cache, false otherwise
4186 *
4187 * @see #isAnimationCacheEnabled()
4188 * @see View#setDrawingCacheEnabled(boolean)
4189 */
4190 public void setAnimationCacheEnabled(boolean enabled) {
4191 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
4192 }
4193
4194 /**
4195 * Indicates whether this ViewGroup will always try to draw its children using their
4196 * drawing cache. By default this property is enabled.
4197 *
4198 * @return true if the animation cache is enabled, false otherwise
4199 *
4200 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4201 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4202 * @see View#setDrawingCacheEnabled(boolean)
4203 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004204 @ViewDebug.ExportedProperty(category = "drawing")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004205 public boolean isAlwaysDrawnWithCacheEnabled() {
4206 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
4207 }
4208
4209 /**
4210 * Indicates whether this ViewGroup will always try to draw its children using their
4211 * drawing cache. This property can be set to true when the cache rendering is
4212 * slightly different from the children's normal rendering. Renderings can be different,
4213 * for instance, when the cache's quality is set to low.
4214 *
4215 * When this property is disabled, the ViewGroup will use the drawing cache of its
4216 * children only when asked to. It's usually the task of subclasses to tell ViewGroup
4217 * when to start using the drawing cache and when to stop using it.
4218 *
4219 * @param always true to always draw with the drawing cache, false otherwise
4220 *
4221 * @see #isAlwaysDrawnWithCacheEnabled()
4222 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4223 * @see View#setDrawingCacheEnabled(boolean)
4224 * @see View#setDrawingCacheQuality(int)
4225 */
4226 public void setAlwaysDrawnWithCacheEnabled(boolean always) {
4227 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
4228 }
4229
4230 /**
4231 * Indicates whether the ViewGroup is currently drawing its children using
4232 * their drawing cache.
4233 *
4234 * @return true if children should be drawn with their cache, false otherwise
4235 *
4236 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4237 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4238 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004239 @ViewDebug.ExportedProperty(category = "drawing")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004240 protected boolean isChildrenDrawnWithCacheEnabled() {
4241 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
4242 }
4243
4244 /**
4245 * Tells the ViewGroup to draw its children using their drawing cache. This property
4246 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
4247 * will be used only if it has been enabled.
4248 *
4249 * Subclasses should call this method to start and stop using the drawing cache when
4250 * they perform performance sensitive operations, like scrolling or animating.
4251 *
4252 * @param enabled true if children should be drawn with their cache, false otherwise
4253 *
4254 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4255 * @see #isChildrenDrawnWithCacheEnabled()
4256 */
4257 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
4258 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
4259 }
4260
Romain Guy293451e2009-11-04 13:59:48 -08004261 /**
4262 * Indicates whether the ViewGroup is drawing its children in the order defined by
4263 * {@link #getChildDrawingOrder(int, int)}.
4264 *
4265 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
4266 * false otherwise
4267 *
4268 * @see #setChildrenDrawingOrderEnabled(boolean)
4269 * @see #getChildDrawingOrder(int, int)
4270 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004271 @ViewDebug.ExportedProperty(category = "drawing")
Romain Guy293451e2009-11-04 13:59:48 -08004272 protected boolean isChildrenDrawingOrderEnabled() {
4273 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
4274 }
4275
4276 /**
4277 * Tells the ViewGroup whether to draw its children in the order defined by the method
4278 * {@link #getChildDrawingOrder(int, int)}.
4279 *
4280 * @param enabled true if the order of the children when drawing is determined by
4281 * {@link #getChildDrawingOrder(int, int)}, false otherwise
4282 *
4283 * @see #isChildrenDrawingOrderEnabled()
4284 * @see #getChildDrawingOrder(int, int)
4285 */
4286 protected void setChildrenDrawingOrderEnabled(boolean enabled) {
4287 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
4288 }
4289
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004290 private void setBooleanFlag(int flag, boolean value) {
4291 if (value) {
4292 mGroupFlags |= flag;
4293 } else {
4294 mGroupFlags &= ~flag;
4295 }
4296 }
4297
4298 /**
4299 * Returns an integer indicating what types of drawing caches are kept in memory.
4300 *
4301 * @see #setPersistentDrawingCache(int)
4302 * @see #setAnimationCacheEnabled(boolean)
4303 *
4304 * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
4305 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
4306 * and {@link #PERSISTENT_ALL_CACHES}
4307 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004308 @ViewDebug.ExportedProperty(category = "drawing", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004309 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
Romain Guy203688c2010-05-12 15:41:32 -07004310 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004311 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
4312 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL")
4313 })
4314 public int getPersistentDrawingCache() {
4315 return mPersistentDrawingCache;
4316 }
4317
4318 /**
4319 * Indicates what types of drawing caches should be kept in memory after
4320 * they have been created.
4321 *
4322 * @see #getPersistentDrawingCache()
4323 * @see #setAnimationCacheEnabled(boolean)
4324 *
4325 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
4326 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
4327 * and {@link #PERSISTENT_ALL_CACHES}
4328 */
4329 public void setPersistentDrawingCache(int drawingCacheToKeep) {
4330 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
4331 }
4332
4333 /**
4334 * Returns a new set of layout parameters based on the supplied attributes set.
4335 *
4336 * @param attrs the attributes to build the layout parameters from
4337 *
4338 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
4339 * of its descendants
4340 */
4341 public LayoutParams generateLayoutParams(AttributeSet attrs) {
4342 return new LayoutParams(getContext(), attrs);
4343 }
4344
4345 /**
4346 * Returns a safe set of layout parameters based on the supplied layout params.
4347 * When a ViewGroup is passed a View whose layout params do not pass the test of
4348 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
4349 * is invoked. This method should return a new set of layout params suitable for
4350 * this ViewGroup, possibly by copying the appropriate attributes from the
4351 * specified set of layout params.
4352 *
4353 * @param p The layout parameters to convert into a suitable set of layout parameters
4354 * for this ViewGroup.
4355 *
4356 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
4357 * of its descendants
4358 */
4359 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4360 return p;
4361 }
4362
4363 /**
4364 * Returns a set of default layout parameters. These parameters are requested
4365 * when the View passed to {@link #addView(View)} has no layout parameters
4366 * already set. If null is returned, an exception is thrown from addView.
4367 *
4368 * @return a set of default layout parameters or null
4369 */
4370 protected LayoutParams generateDefaultLayoutParams() {
4371 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
4372 }
4373
4374 /**
Romain Guy13922e02009-05-12 17:56:14 -07004375 * @hide
4376 */
4377 @Override
4378 protected boolean dispatchConsistencyCheck(int consistency) {
4379 boolean result = super.dispatchConsistencyCheck(consistency);
4380
4381 final int count = mChildrenCount;
4382 final View[] children = mChildren;
4383 for (int i = 0; i < count; i++) {
4384 if (!children[i].dispatchConsistencyCheck(consistency)) result = false;
4385 }
4386
4387 return result;
4388 }
4389
4390 /**
4391 * @hide
4392 */
4393 @Override
4394 protected boolean onConsistencyCheck(int consistency) {
4395 boolean result = super.onConsistencyCheck(consistency);
4396
4397 final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
4398 final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0;
4399
4400 if (checkLayout) {
4401 final int count = mChildrenCount;
4402 final View[] children = mChildren;
4403 for (int i = 0; i < count; i++) {
4404 if (children[i].getParent() != this) {
4405 result = false;
4406 android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
4407 "View " + children[i] + " has no parent/a parent that is not " + this);
4408 }
4409 }
4410 }
4411
4412 if (checkDrawing) {
4413 // If this group is dirty, check that the parent is dirty as well
4414 if ((mPrivateFlags & DIRTY_MASK) != 0) {
4415 final ViewParent parent = getParent();
Joe Onoratoc6cc0f82011-04-12 11:53:13 -07004416 if (parent != null && !(parent instanceof ViewAncestor)) {
Romain Guy13922e02009-05-12 17:56:14 -07004417 if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) {
4418 result = false;
4419 android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
4420 "ViewGroup " + this + " is dirty but its parent is not: " + this);
4421 }
4422 }
4423 }
4424 }
4425
4426 return result;
4427 }
4428
4429 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004430 * {@inheritDoc}
4431 */
4432 @Override
4433 protected void debug(int depth) {
4434 super.debug(depth);
4435 String output;
4436
4437 if (mFocused != null) {
4438 output = debugIndent(depth);
4439 output += "mFocused";
4440 Log.d(VIEW_LOG_TAG, output);
4441 }
4442 if (mChildrenCount != 0) {
4443 output = debugIndent(depth);
4444 output += "{";
4445 Log.d(VIEW_LOG_TAG, output);
4446 }
4447 int count = mChildrenCount;
4448 for (int i = 0; i < count; i++) {
4449 View child = mChildren[i];
4450 child.debug(depth + 1);
4451 }
4452
4453 if (mChildrenCount != 0) {
4454 output = debugIndent(depth);
4455 output += "}";
4456 Log.d(VIEW_LOG_TAG, output);
4457 }
4458 }
4459
4460 /**
4461 * Returns the position in the group of the specified child view.
4462 *
4463 * @param child the view for which to get the position
4464 * @return a positive integer representing the position of the view in the
4465 * group, or -1 if the view does not exist in the group
4466 */
4467 public int indexOfChild(View child) {
4468 final int count = mChildrenCount;
4469 final View[] children = mChildren;
4470 for (int i = 0; i < count; i++) {
4471 if (children[i] == child) {
4472 return i;
4473 }
4474 }
4475 return -1;
4476 }
4477
4478 /**
4479 * Returns the number of children in the group.
4480 *
4481 * @return a positive integer representing the number of children in
4482 * the group
4483 */
4484 public int getChildCount() {
4485 return mChildrenCount;
4486 }
4487
4488 /**
4489 * Returns the view at the specified position in the group.
4490 *
4491 * @param index the position at which to get the view from
4492 * @return the view at the specified position or null if the position
4493 * does not exist within the group
4494 */
4495 public View getChildAt(int index) {
Adam Powell3ba8f5d62011-03-07 15:36:33 -08004496 if (index < 0 || index >= mChildrenCount) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004497 return null;
4498 }
Adam Powell3ba8f5d62011-03-07 15:36:33 -08004499 return mChildren[index];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004500 }
4501
4502 /**
4503 * Ask all of the children of this view to measure themselves, taking into
4504 * account both the MeasureSpec requirements for this view and its padding.
4505 * We skip children that are in the GONE state The heavy lifting is done in
4506 * getChildMeasureSpec.
4507 *
4508 * @param widthMeasureSpec The width requirements for this view
4509 * @param heightMeasureSpec The height requirements for this view
4510 */
4511 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
4512 final int size = mChildrenCount;
4513 final View[] children = mChildren;
4514 for (int i = 0; i < size; ++i) {
4515 final View child = children[i];
4516 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
4517 measureChild(child, widthMeasureSpec, heightMeasureSpec);
4518 }
4519 }
4520 }
4521
4522 /**
4523 * Ask one of the children of this view to measure itself, taking into
4524 * account both the MeasureSpec requirements for this view and its padding.
4525 * The heavy lifting is done in getChildMeasureSpec.
4526 *
4527 * @param child The child to measure
4528 * @param parentWidthMeasureSpec The width requirements for this view
4529 * @param parentHeightMeasureSpec The height requirements for this view
4530 */
4531 protected void measureChild(View child, int parentWidthMeasureSpec,
4532 int parentHeightMeasureSpec) {
4533 final LayoutParams lp = child.getLayoutParams();
4534
4535 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
4536 mPaddingLeft + mPaddingRight, lp.width);
4537 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
4538 mPaddingTop + mPaddingBottom, lp.height);
4539
4540 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4541 }
4542
4543 /**
4544 * Ask one of the children of this view to measure itself, taking into
4545 * account both the MeasureSpec requirements for this view and its padding
4546 * and margins. The child must have MarginLayoutParams The heavy lifting is
4547 * done in getChildMeasureSpec.
4548 *
4549 * @param child The child to measure
4550 * @param parentWidthMeasureSpec The width requirements for this view
4551 * @param widthUsed Extra space that has been used up by the parent
4552 * horizontally (possibly by other children of the parent)
4553 * @param parentHeightMeasureSpec The height requirements for this view
4554 * @param heightUsed Extra space that has been used up by the parent
4555 * vertically (possibly by other children of the parent)
4556 */
4557 protected void measureChildWithMargins(View child,
4558 int parentWidthMeasureSpec, int widthUsed,
4559 int parentHeightMeasureSpec, int heightUsed) {
4560 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
4561
4562 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
4563 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
4564 + widthUsed, lp.width);
4565 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
4566 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
4567 + heightUsed, lp.height);
4568
4569 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4570 }
4571
4572 /**
4573 * Does the hard part of measureChildren: figuring out the MeasureSpec to
4574 * pass to a particular child. This method figures out the right MeasureSpec
4575 * for one dimension (height or width) of one child view.
4576 *
4577 * The goal is to combine information from our MeasureSpec with the
4578 * LayoutParams of the child to get the best possible results. For example,
4579 * if the this view knows its size (because its MeasureSpec has a mode of
4580 * EXACTLY), and the child has indicated in its LayoutParams that it wants
4581 * to be the same size as the parent, the parent should ask the child to
4582 * layout given an exact size.
4583 *
4584 * @param spec The requirements for this view
4585 * @param padding The padding of this view for the current dimension and
4586 * margins, if applicable
4587 * @param childDimension How big the child wants to be in the current
4588 * dimension
4589 * @return a MeasureSpec integer for the child
4590 */
4591 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
4592 int specMode = MeasureSpec.getMode(spec);
4593 int specSize = MeasureSpec.getSize(spec);
4594
4595 int size = Math.max(0, specSize - padding);
4596
4597 int resultSize = 0;
4598 int resultMode = 0;
4599
4600 switch (specMode) {
4601 // Parent has imposed an exact size on us
4602 case MeasureSpec.EXACTLY:
4603 if (childDimension >= 0) {
4604 resultSize = childDimension;
4605 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08004606 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004607 // Child wants to be our size. So be it.
4608 resultSize = size;
4609 resultMode = MeasureSpec.EXACTLY;
4610 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4611 // Child wants to determine its own size. It can't be
4612 // bigger than us.
4613 resultSize = size;
4614 resultMode = MeasureSpec.AT_MOST;
4615 }
4616 break;
4617
4618 // Parent has imposed a maximum size on us
4619 case MeasureSpec.AT_MOST:
4620 if (childDimension >= 0) {
4621 // Child wants a specific size... so be it
4622 resultSize = childDimension;
4623 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08004624 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004625 // Child wants to be our size, but our size is not fixed.
4626 // Constrain child to not be bigger than us.
4627 resultSize = size;
4628 resultMode = MeasureSpec.AT_MOST;
4629 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4630 // Child wants to determine its own size. It can't be
4631 // bigger than us.
4632 resultSize = size;
4633 resultMode = MeasureSpec.AT_MOST;
4634 }
4635 break;
4636
4637 // Parent asked to see how big we want to be
4638 case MeasureSpec.UNSPECIFIED:
4639 if (childDimension >= 0) {
4640 // Child wants a specific size... let him have it
4641 resultSize = childDimension;
4642 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08004643 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004644 // Child wants to be our size... find out how big it should
4645 // be
4646 resultSize = 0;
4647 resultMode = MeasureSpec.UNSPECIFIED;
4648 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4649 // Child wants to determine its own size.... find out how
4650 // big it should be
4651 resultSize = 0;
4652 resultMode = MeasureSpec.UNSPECIFIED;
4653 }
4654 break;
4655 }
4656 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
4657 }
4658
4659
4660 /**
4661 * Removes any pending animations for views that have been removed. Call
4662 * this if you don't want animations for exiting views to stack up.
4663 */
4664 public void clearDisappearingChildren() {
4665 if (mDisappearingChildren != null) {
4666 mDisappearingChildren.clear();
4667 }
4668 }
4669
4670 /**
4671 * Add a view which is removed from mChildren but still needs animation
4672 *
4673 * @param v View to add
4674 */
4675 private void addDisappearingView(View v) {
4676 ArrayList<View> disappearingChildren = mDisappearingChildren;
4677
4678 if (disappearingChildren == null) {
4679 disappearingChildren = mDisappearingChildren = new ArrayList<View>();
4680 }
4681
4682 disappearingChildren.add(v);
4683 }
4684
4685 /**
4686 * Cleanup a view when its animation is done. This may mean removing it from
4687 * the list of disappearing views.
4688 *
4689 * @param view The view whose animation has finished
4690 * @param animation The animation, cannot be null
4691 */
4692 private void finishAnimatingView(final View view, Animation animation) {
4693 final ArrayList<View> disappearingChildren = mDisappearingChildren;
4694 if (disappearingChildren != null) {
4695 if (disappearingChildren.contains(view)) {
4696 disappearingChildren.remove(view);
4697
4698 if (view.mAttachInfo != null) {
4699 view.dispatchDetachedFromWindow();
4700 }
4701
4702 view.clearAnimation();
4703 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4704 }
4705 }
4706
4707 if (animation != null && !animation.getFillAfter()) {
4708 view.clearAnimation();
4709 }
4710
4711 if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) {
4712 view.onAnimationEnd();
4713 // Should be performed by onAnimationEnd() but this avoid an infinite loop,
4714 // so we'd rather be safe than sorry
4715 view.mPrivateFlags &= ~ANIMATION_STARTED;
4716 // Draw one more frame after the animation is done
4717 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4718 }
4719 }
4720
Chet Haaseb20db3e2010-09-10 13:07:30 -07004721 /**
4722 * This method tells the ViewGroup that the given View object, which should have this
4723 * ViewGroup as its parent,
4724 * should be kept around (re-displayed when the ViewGroup draws its children) even if it
4725 * is removed from its parent. This allows animations, such as those used by
4726 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
4727 * the removal of views. A call to this method should always be accompanied by a later call
4728 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
4729 * so that the View finally gets removed.
4730 *
4731 * @param view The View object to be kept visible even if it gets removed from its parent.
4732 */
4733 public void startViewTransition(View view) {
4734 if (view.mParent == this) {
4735 if (mTransitioningViews == null) {
4736 mTransitioningViews = new ArrayList<View>();
4737 }
4738 mTransitioningViews.add(view);
4739 }
4740 }
4741
4742 /**
4743 * This method should always be called following an earlier call to
4744 * {@link #startViewTransition(View)}. The given View is finally removed from its parent
4745 * and will no longer be displayed. Note that this method does not perform the functionality
4746 * of removing a view from its parent; it just discontinues the display of a View that
4747 * has previously been removed.
4748 *
4749 * @return view The View object that has been removed but is being kept around in the visible
4750 * hierarchy by an earlier call to {@link #startViewTransition(View)}.
4751 */
4752 public void endViewTransition(View view) {
4753 if (mTransitioningViews != null) {
4754 mTransitioningViews.remove(view);
4755 final ArrayList<View> disappearingChildren = mDisappearingChildren;
4756 if (disappearingChildren != null && disappearingChildren.contains(view)) {
4757 disappearingChildren.remove(view);
Chet Haase5e25c2c2010-09-16 11:15:56 -07004758 if (mVisibilityChangingChildren != null &&
4759 mVisibilityChangingChildren.contains(view)) {
4760 mVisibilityChangingChildren.remove(view);
4761 } else {
4762 if (view.mAttachInfo != null) {
4763 view.dispatchDetachedFromWindow();
4764 }
4765 if (view.mParent != null) {
4766 view.mParent = null;
4767 }
Chet Haaseb20db3e2010-09-10 13:07:30 -07004768 }
4769 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4770 }
4771 }
4772 }
4773
Chet Haase21cd1382010-09-01 17:42:29 -07004774 private LayoutTransition.TransitionListener mLayoutTransitionListener =
4775 new LayoutTransition.TransitionListener() {
4776 @Override
4777 public void startTransition(LayoutTransition transition, ViewGroup container,
4778 View view, int transitionType) {
4779 // We only care about disappearing items, since we need special logic to keep
4780 // those items visible after they've been 'removed'
4781 if (transitionType == LayoutTransition.DISAPPEARING) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07004782 startViewTransition(view);
Chet Haase21cd1382010-09-01 17:42:29 -07004783 }
4784 }
4785
4786 @Override
4787 public void endTransition(LayoutTransition transition, ViewGroup container,
4788 View view, int transitionType) {
Chet Haase9c087442011-01-12 16:20:16 -08004789 if (mLayoutSuppressed && !transition.isChangingLayout()) {
4790 requestLayout();
4791 mLayoutSuppressed = false;
4792 }
Chet Haase21cd1382010-09-01 17:42:29 -07004793 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07004794 endViewTransition(view);
Chet Haase21cd1382010-09-01 17:42:29 -07004795 }
4796 }
4797 };
4798
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004799 /**
4800 * {@inheritDoc}
4801 */
4802 @Override
4803 public boolean gatherTransparentRegion(Region region) {
4804 // If no transparent regions requested, we are always opaque.
4805 final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0;
4806 if (meOpaque && region == null) {
4807 // The caller doesn't care about the region, so stop now.
4808 return true;
4809 }
4810 super.gatherTransparentRegion(region);
4811 final View[] children = mChildren;
4812 final int count = mChildrenCount;
4813 boolean noneOfTheChildrenAreTransparent = true;
4814 for (int i = 0; i < count; i++) {
4815 final View child = children[i];
Mathias Agopiane3381152010-12-02 15:19:36 -08004816 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004817 if (!child.gatherTransparentRegion(region)) {
4818 noneOfTheChildrenAreTransparent = false;
4819 }
4820 }
4821 }
4822 return meOpaque || noneOfTheChildrenAreTransparent;
4823 }
4824
4825 /**
4826 * {@inheritDoc}
4827 */
4828 public void requestTransparentRegion(View child) {
4829 if (child != null) {
4830 child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
4831 if (mParent != null) {
4832 mParent.requestTransparentRegion(this);
4833 }
4834 }
4835 }
Romain Guy8506ab42009-06-11 17:35:47 -07004836
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004837
4838 @Override
4839 protected boolean fitSystemWindows(Rect insets) {
4840 boolean done = super.fitSystemWindows(insets);
4841 if (!done) {
4842 final int count = mChildrenCount;
4843 final View[] children = mChildren;
4844 for (int i = 0; i < count; i++) {
4845 done = children[i].fitSystemWindows(insets);
4846 if (done) {
4847 break;
4848 }
4849 }
4850 }
4851 return done;
4852 }
4853
4854 /**
4855 * Returns the animation listener to which layout animation events are
4856 * sent.
4857 *
4858 * @return an {@link android.view.animation.Animation.AnimationListener}
4859 */
4860 public Animation.AnimationListener getLayoutAnimationListener() {
4861 return mAnimationListener;
4862 }
4863
4864 @Override
4865 protected void drawableStateChanged() {
4866 super.drawableStateChanged();
4867
4868 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
4869 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
4870 throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
4871 + " child has duplicateParentState set to true");
4872 }
4873
4874 final View[] children = mChildren;
4875 final int count = mChildrenCount;
4876
4877 for (int i = 0; i < count; i++) {
4878 final View child = children[i];
4879 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
4880 child.refreshDrawableState();
4881 }
4882 }
4883 }
4884 }
4885
4886 @Override
Dianne Hackborne2136772010-11-04 15:08:59 -07004887 public void jumpDrawablesToCurrentState() {
4888 super.jumpDrawablesToCurrentState();
4889 final View[] children = mChildren;
4890 final int count = mChildrenCount;
4891 for (int i = 0; i < count; i++) {
4892 children[i].jumpDrawablesToCurrentState();
4893 }
4894 }
4895
4896 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004897 protected int[] onCreateDrawableState(int extraSpace) {
4898 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
4899 return super.onCreateDrawableState(extraSpace);
4900 }
4901
4902 int need = 0;
4903 int n = getChildCount();
4904 for (int i = 0; i < n; i++) {
4905 int[] childState = getChildAt(i).getDrawableState();
4906
4907 if (childState != null) {
4908 need += childState.length;
4909 }
4910 }
4911
4912 int[] state = super.onCreateDrawableState(extraSpace + need);
4913
4914 for (int i = 0; i < n; i++) {
4915 int[] childState = getChildAt(i).getDrawableState();
4916
4917 if (childState != null) {
4918 state = mergeDrawableStates(state, childState);
4919 }
4920 }
4921
4922 return state;
4923 }
4924
4925 /**
4926 * Sets whether this ViewGroup's drawable states also include
4927 * its children's drawable states. This is used, for example, to
4928 * make a group appear to be focused when its child EditText or button
4929 * is focused.
4930 */
4931 public void setAddStatesFromChildren(boolean addsStates) {
4932 if (addsStates) {
4933 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
4934 } else {
4935 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
4936 }
4937
4938 refreshDrawableState();
4939 }
4940
4941 /**
4942 * Returns whether this ViewGroup's drawable states also include
4943 * its children's drawable states. This is used, for example, to
4944 * make a group appear to be focused when its child EditText or button
4945 * is focused.
4946 */
4947 public boolean addStatesFromChildren() {
4948 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
4949 }
4950
4951 /**
4952 * If {link #addStatesFromChildren} is true, refreshes this group's
4953 * drawable state (to include the states from its children).
4954 */
4955 public void childDrawableStateChanged(View child) {
4956 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
4957 refreshDrawableState();
4958 }
4959 }
4960
4961 /**
4962 * Specifies the animation listener to which layout animation events must
4963 * be sent. Only
4964 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
4965 * and
4966 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
4967 * are invoked.
4968 *
4969 * @param animationListener the layout animation listener
4970 */
4971 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
4972 mAnimationListener = animationListener;
4973 }
4974
4975 /**
Patrick Dubroye0a799a2011-05-04 16:19:22 -07004976 * Return true if the pressed state should be delayed for children or descendants of this
4977 * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
4978 * This prevents the pressed state from appearing when the user is actually trying to scroll
4979 * the content.
4980 *
4981 * The default implementation returns true for compatibility reasons. Subclasses that do
4982 * not scroll should generally override this method and return false.
4983 */
4984 public boolean shouldDelayChildPressedState() {
4985 return true;
4986 }
4987
4988 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004989 * LayoutParams are used by views to tell their parents how they want to be
4990 * laid out. See
4991 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
4992 * for a list of all child view attributes that this class supports.
Romain Guy8506ab42009-06-11 17:35:47 -07004993 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004994 * <p>
4995 * The base LayoutParams class just describes how big the view wants to be
4996 * for both width and height. For each dimension, it can specify one of:
4997 * <ul>
Dirk Dougherty75c66da2010-03-25 16:33:33 -07004998 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
4999 * means that the view wants to be as big as its parent (minus padding)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005000 * <li> WRAP_CONTENT, which means that the view wants to be just big enough
5001 * to enclose its content (plus padding)
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005002 * <li> an exact number
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005003 * </ul>
5004 * There are subclasses of LayoutParams for different subclasses of
5005 * ViewGroup. For example, AbsoluteLayout has its own subclass of
5006 * LayoutParams which adds an X and Y value.
5007 *
5008 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
5009 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
5010 */
5011 public static class LayoutParams {
5012 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005013 * Special value for the height or width requested by a View.
5014 * FILL_PARENT means that the view wants to be as big as its parent,
5015 * minus the parent's padding, if any. This value is deprecated
5016 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005017 */
Romain Guy980a9382010-01-08 15:06:28 -08005018 @SuppressWarnings({"UnusedDeclaration"})
5019 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005020 public static final int FILL_PARENT = -1;
5021
5022 /**
5023 * Special value for the height or width requested by a View.
Gilles Debunnef5c6eff2010-02-09 19:08:36 -08005024 * MATCH_PARENT means that the view wants to be as big as its parent,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005025 * minus the parent's padding, if any. Introduced in API Level 8.
Romain Guy980a9382010-01-08 15:06:28 -08005026 */
5027 public static final int MATCH_PARENT = -1;
5028
5029 /**
5030 * Special value for the height or width requested by a View.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005031 * WRAP_CONTENT means that the view wants to be just large enough to fit
5032 * its own internal content, taking its own padding into account.
5033 */
5034 public static final int WRAP_CONTENT = -2;
5035
5036 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005037 * Information about how wide the view wants to be. Can be one of the
5038 * constants FILL_PARENT (replaced by MATCH_PARENT ,
5039 * in API Level 8) or WRAP_CONTENT. or an exact size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005040 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005041 @ViewDebug.ExportedProperty(category = "layout", mapping = {
Romain Guy980a9382010-01-08 15:06:28 -08005042 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005043 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
5044 })
5045 public int width;
5046
5047 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005048 * Information about how tall the view wants to be. Can be one of the
5049 * constants FILL_PARENT (replaced by MATCH_PARENT ,
5050 * in API Level 8) or WRAP_CONTENT. or an exact size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005051 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005052 @ViewDebug.ExportedProperty(category = "layout", mapping = {
Romain Guy980a9382010-01-08 15:06:28 -08005053 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005054 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
5055 })
5056 public int height;
5057
5058 /**
5059 * Used to animate layouts.
5060 */
5061 public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
5062
5063 /**
5064 * Creates a new set of layout parameters. The values are extracted from
5065 * the supplied attributes set and context. The XML attributes mapped
5066 * to this set of layout parameters are:
5067 *
5068 * <ul>
5069 * <li><code>layout_width</code>: the width, either an exact value,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005070 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
5071 * {@link #MATCH_PARENT} in API Level 8)</li>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005072 * <li><code>layout_height</code>: the height, either an exact value,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005073 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
5074 * {@link #MATCH_PARENT} in API Level 8)</li>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005075 * </ul>
5076 *
5077 * @param c the application environment
5078 * @param attrs the set of attributes from which to extract the layout
5079 * parameters' values
5080 */
5081 public LayoutParams(Context c, AttributeSet attrs) {
5082 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
5083 setBaseAttributes(a,
5084 R.styleable.ViewGroup_Layout_layout_width,
5085 R.styleable.ViewGroup_Layout_layout_height);
5086 a.recycle();
5087 }
5088
5089 /**
5090 * Creates a new set of layout parameters with the specified width
5091 * and height.
5092 *
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005093 * @param width the width, either {@link #WRAP_CONTENT},
5094 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
5095 * API Level 8), or a fixed size in pixels
5096 * @param height the height, either {@link #WRAP_CONTENT},
5097 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
5098 * API Level 8), or a fixed size in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005099 */
5100 public LayoutParams(int width, int height) {
5101 this.width = width;
5102 this.height = height;
5103 }
5104
5105 /**
5106 * Copy constructor. Clones the width and height values of the source.
5107 *
5108 * @param source The layout params to copy from.
5109 */
5110 public LayoutParams(LayoutParams source) {
5111 this.width = source.width;
5112 this.height = source.height;
5113 }
5114
5115 /**
5116 * Used internally by MarginLayoutParams.
5117 * @hide
5118 */
5119 LayoutParams() {
5120 }
5121
5122 /**
5123 * Extracts the layout parameters from the supplied attributes.
5124 *
5125 * @param a the style attributes to extract the parameters from
5126 * @param widthAttr the identifier of the width attribute
5127 * @param heightAttr the identifier of the height attribute
5128 */
5129 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
5130 width = a.getLayoutDimension(widthAttr, "layout_width");
5131 height = a.getLayoutDimension(heightAttr, "layout_height");
5132 }
5133
5134 /**
5135 * Returns a String representation of this set of layout parameters.
5136 *
5137 * @param output the String to prepend to the internal representation
5138 * @return a String with the following format: output +
5139 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
Romain Guy8506ab42009-06-11 17:35:47 -07005140 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005141 * @hide
5142 */
5143 public String debug(String output) {
5144 return output + "ViewGroup.LayoutParams={ width="
5145 + sizeToString(width) + ", height=" + sizeToString(height) + " }";
5146 }
5147
5148 /**
5149 * Converts the specified size to a readable String.
5150 *
5151 * @param size the size to convert
5152 * @return a String instance representing the supplied size
Romain Guy8506ab42009-06-11 17:35:47 -07005153 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005154 * @hide
5155 */
5156 protected static String sizeToString(int size) {
5157 if (size == WRAP_CONTENT) {
5158 return "wrap-content";
5159 }
Romain Guy980a9382010-01-08 15:06:28 -08005160 if (size == MATCH_PARENT) {
5161 return "match-parent";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005162 }
5163 return String.valueOf(size);
5164 }
5165 }
5166
5167 /**
5168 * Per-child layout information for layouts that support margins.
5169 * See
5170 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
5171 * for a list of all child view attributes that this class supports.
5172 */
5173 public static class MarginLayoutParams extends ViewGroup.LayoutParams {
5174 /**
5175 * The left margin in pixels of the child.
5176 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005177 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005178 public int leftMargin;
5179
5180 /**
5181 * The top margin in pixels of the child.
5182 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005183 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005184 public int topMargin;
5185
5186 /**
5187 * The right margin in pixels of the child.
5188 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005189 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005190 public int rightMargin;
5191
5192 /**
5193 * The bottom margin in pixels of the child.
5194 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005195 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005196 public int bottomMargin;
5197
5198 /**
5199 * Creates a new set of layout parameters. The values are extracted from
5200 * the supplied attributes set and context.
5201 *
5202 * @param c the application environment
5203 * @param attrs the set of attributes from which to extract the layout
5204 * parameters' values
5205 */
5206 public MarginLayoutParams(Context c, AttributeSet attrs) {
5207 super();
5208
5209 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
5210 setBaseAttributes(a,
5211 R.styleable.ViewGroup_MarginLayout_layout_width,
5212 R.styleable.ViewGroup_MarginLayout_layout_height);
5213
5214 int margin = a.getDimensionPixelSize(
5215 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
5216 if (margin >= 0) {
5217 leftMargin = margin;
5218 topMargin = margin;
5219 rightMargin= margin;
5220 bottomMargin = margin;
5221 } else {
5222 leftMargin = a.getDimensionPixelSize(
5223 R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
5224 topMargin = a.getDimensionPixelSize(
5225 R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
5226 rightMargin = a.getDimensionPixelSize(
5227 R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
5228 bottomMargin = a.getDimensionPixelSize(
5229 R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
5230 }
5231
5232 a.recycle();
5233 }
5234
5235 /**
5236 * {@inheritDoc}
5237 */
5238 public MarginLayoutParams(int width, int height) {
5239 super(width, height);
5240 }
5241
5242 /**
5243 * Copy constructor. Clones the width, height and margin values of the source.
5244 *
5245 * @param source The layout params to copy from.
5246 */
5247 public MarginLayoutParams(MarginLayoutParams source) {
5248 this.width = source.width;
5249 this.height = source.height;
5250
5251 this.leftMargin = source.leftMargin;
5252 this.topMargin = source.topMargin;
5253 this.rightMargin = source.rightMargin;
5254 this.bottomMargin = source.bottomMargin;
5255 }
5256
5257 /**
5258 * {@inheritDoc}
5259 */
5260 public MarginLayoutParams(LayoutParams source) {
5261 super(source);
5262 }
5263
5264 /**
5265 * Sets the margins, in pixels.
5266 *
5267 * @param left the left margin size
5268 * @param top the top margin size
5269 * @param right the right margin size
5270 * @param bottom the bottom margin size
5271 *
5272 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
5273 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
5274 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
5275 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
5276 */
5277 public void setMargins(int left, int top, int right, int bottom) {
5278 leftMargin = left;
5279 topMargin = top;
5280 rightMargin = right;
5281 bottomMargin = bottom;
5282 }
5283 }
Adam Powell2b342f02010-08-18 18:14:13 -07005284
Jeff Brown20e987b2010-08-23 12:01:02 -07005285 /* Describes a touched view and the ids of the pointers that it has captured.
5286 *
5287 * This code assumes that pointer ids are always in the range 0..31 such that
5288 * it can use a bitfield to track which pointer ids are present.
5289 * As it happens, the lower layers of the input dispatch pipeline also use the
5290 * same trick so the assumption should be safe here...
5291 */
5292 private static final class TouchTarget {
5293 private static final int MAX_RECYCLED = 32;
5294 private static final Object sRecycleLock = new Object();
5295 private static TouchTarget sRecycleBin;
5296 private static int sRecycledCount;
Adam Powell2b342f02010-08-18 18:14:13 -07005297
Jeff Brown20e987b2010-08-23 12:01:02 -07005298 public static final int ALL_POINTER_IDS = -1; // all ones
Adam Powell2b342f02010-08-18 18:14:13 -07005299
Jeff Brown20e987b2010-08-23 12:01:02 -07005300 // The touched child view.
5301 public View child;
5302
5303 // The combined bit mask of pointer ids for all pointers captured by the target.
5304 public int pointerIdBits;
5305
5306 // The next target in the target list.
5307 public TouchTarget next;
5308
5309 private TouchTarget() {
Adam Powell2b342f02010-08-18 18:14:13 -07005310 }
5311
Jeff Brown20e987b2010-08-23 12:01:02 -07005312 public static TouchTarget obtain(View child, int pointerIdBits) {
5313 final TouchTarget target;
5314 synchronized (sRecycleLock) {
Adam Powell816c3be2010-08-23 18:00:05 -07005315 if (sRecycleBin == null) {
Jeff Brown20e987b2010-08-23 12:01:02 -07005316 target = new TouchTarget();
Adam Powell816c3be2010-08-23 18:00:05 -07005317 } else {
Jeff Brown20e987b2010-08-23 12:01:02 -07005318 target = sRecycleBin;
5319 sRecycleBin = target.next;
5320 sRecycledCount--;
5321 target.next = null;
Adam Powell816c3be2010-08-23 18:00:05 -07005322 }
Adam Powell816c3be2010-08-23 18:00:05 -07005323 }
Jeff Brown20e987b2010-08-23 12:01:02 -07005324 target.child = child;
5325 target.pointerIdBits = pointerIdBits;
5326 return target;
5327 }
Adam Powell816c3be2010-08-23 18:00:05 -07005328
Jeff Brown20e987b2010-08-23 12:01:02 -07005329 public void recycle() {
5330 synchronized (sRecycleLock) {
5331 if (sRecycledCount < MAX_RECYCLED) {
5332 next = sRecycleBin;
5333 sRecycleBin = this;
5334 sRecycledCount += 1;
Patrick Dubroyfb0547d22010-10-19 17:36:18 -07005335 } else {
5336 next = null;
Adam Powell816c3be2010-08-23 18:00:05 -07005337 }
Patrick Dubroyfb0547d22010-10-19 17:36:18 -07005338 child = null;
Adam Powell816c3be2010-08-23 18:00:05 -07005339 }
5340 }
Adam Powell2b342f02010-08-18 18:14:13 -07005341 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005342}