blob: 7c826ca9523378f1e11323abf742d2b33774101e [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
Chet Haase21cd1382010-09-01 17:42:29 -070019import android.animation.LayoutTransition;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.content.Context;
Dianne Hackborne36d6e22010-02-17 19:46:25 -080021import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.res.TypedArray;
23import android.graphics.Bitmap;
24import android.graphics.Canvas;
Adam Powell6e346362010-07-23 10:18:23 -070025import android.graphics.Matrix;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.graphics.Paint;
Christopher Tatea53146c2010-09-07 11:57:52 -070027import android.graphics.PointF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.graphics.Rect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.graphics.RectF;
svetoslavganov75986cf2009-05-14 22:28:01 -070030import android.graphics.Region;
Jeff Brown995e7742010-12-22 16:59:36 -080031import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.os.Parcelable;
33import android.os.SystemClock;
34import android.util.AttributeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.util.Log;
36import android.util.SparseArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070037import android.view.accessibility.AccessibilityEvent;
Svetoslav Ganov8643aa02011-04-20 12:12:33 -070038import android.view.accessibility.AccessibilityNodeInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.view.animation.Animation;
40import android.view.animation.AnimationUtils;
41import android.view.animation.LayoutAnimationController;
42import android.view.animation.Transformation;
Doug Feltcb379122011-07-07 11:57:48 -070043
Romain Guy0211a0a2011-02-14 16:34:59 -080044import com.android.internal.R;
45import com.android.internal.util.Predicate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046
47import java.util.ArrayList;
Christopher Tate86cab1b2011-01-13 20:28:55 -080048import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
50/**
51 * <p>
52 * A <code>ViewGroup</code> is a special view that can contain other views
53 * (called children.) The view group is the base class for layouts and views
54 * containers. This class also defines the
55 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
56 * class for layouts parameters.
57 * </p>
58 *
59 * <p>
60 * Also see {@link LayoutParams} for layout attributes.
61 * </p>
Romain Guyd6a463a2009-05-21 23:10:10 -070062 *
63 * @attr ref android.R.styleable#ViewGroup_clipChildren
64 * @attr ref android.R.styleable#ViewGroup_clipToPadding
65 * @attr ref android.R.styleable#ViewGroup_layoutAnimation
66 * @attr ref android.R.styleable#ViewGroup_animationCache
67 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
68 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
69 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
70 * @attr ref android.R.styleable#ViewGroup_descendantFocusability
Chet Haase13cc1202010-09-03 15:39:20 -070071 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 */
73public abstract class ViewGroup extends View implements ViewParent, ViewManager {
Chet Haase21cd1382010-09-01 17:42:29 -070074
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 private static final boolean DBG = false;
Romain Guydbc26d22010-10-11 17:58:29 -070076
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 /**
78 * Views which have been hidden or removed which need to be animated on
79 * their way out.
80 * This field should be made private, so it is hidden from the SDK.
81 * {@hide}
82 */
83 protected ArrayList<View> mDisappearingChildren;
84
85 /**
86 * Listener used to propagate events indicating when children are added
87 * and/or removed from a view group.
88 * This field should be made private, so it is hidden from the SDK.
89 * {@hide}
90 */
91 protected OnHierarchyChangeListener mOnHierarchyChangeListener;
92
93 // The view contained within this ViewGroup that has or contains focus.
94 private View mFocused;
95
Chet Haase48460322010-06-11 14:22:25 -070096 /**
97 * A Transformation used when drawing children, to
98 * apply on the child being drawn.
99 */
100 private final Transformation mChildTransformation = new Transformation();
101
102 /**
103 * Used to track the current invalidation region.
104 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 private RectF mInvalidateRegion;
106
Chet Haase48460322010-06-11 14:22:25 -0700107 /**
108 * A Transformation used to calculate a correct
109 * invalidation area when the application is autoscaled.
110 */
111 private Transformation mInvalidationTransformation;
112
Christopher Tatea53146c2010-09-07 11:57:52 -0700113 // View currently under an ongoing drag
114 private View mCurrentDragView;
115
Christopher Tate86cab1b2011-01-13 20:28:55 -0800116 // Metadata about the ongoing drag
117 private DragEvent mCurrentDrag;
118 private HashSet<View> mDragNotifiedChildren;
119
Christopher Tatea53146c2010-09-07 11:57:52 -0700120 // Does this group have a child that can accept the current drag payload?
121 private boolean mChildAcceptsDrag;
122
123 // Used during drag dispatch
124 private final PointF mLocalPoint = new PointF();
125
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 // Layout animation
127 private LayoutAnimationController mLayoutAnimationController;
128 private Animation.AnimationListener mAnimationListener;
129
Jeff Brown20e987b2010-08-23 12:01:02 -0700130 // First touch target in the linked list of touch targets.
131 private TouchTarget mFirstTouchTarget;
132
Joe Onorato03ab0c72011-01-06 15:46:27 -0800133 // For debugging only. You can see these in hierarchyviewer.
Romain Guye95003e2011-01-09 13:53:06 -0800134 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800135 @ViewDebug.ExportedProperty(category = "events")
136 private long mLastTouchDownTime;
137 @ViewDebug.ExportedProperty(category = "events")
138 private int mLastTouchDownIndex = -1;
Romain Guye95003e2011-01-09 13:53:06 -0800139 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800140 @ViewDebug.ExportedProperty(category = "events")
141 private float mLastTouchDownX;
Romain Guye95003e2011-01-09 13:53:06 -0800142 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800143 @ViewDebug.ExportedProperty(category = "events")
144 private float mLastTouchDownY;
145
Jeff Brown87b7f802011-06-21 18:35:45 -0700146 // First hover target in the linked list of hover targets.
147 // The hover targets are children which have received ACTION_HOVER_ENTER.
148 // They might not have actually handled the hover event, but we will
149 // continue sending hover events to them as long as the pointer remains over
150 // their bounds and the view group does not intercept hover.
151 private HoverTarget mFirstHoverTarget;
Jeff Browna032cc02011-03-07 16:56:21 -0800152
Jeff Brown10b62902011-06-20 16:40:37 -0700153 // True if the view group itself received a hover event.
154 // It might not have actually handled the hover event.
155 private boolean mHoveredSelf;
156
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 /**
158 * Internal flags.
Romain Guy8506ab42009-06-11 17:35:47 -0700159 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 * This field should be made private, so it is hidden from the SDK.
161 * {@hide}
162 */
163 protected int mGroupFlags;
164
165 // When set, ViewGroup invalidates only the child's rectangle
166 // Set by default
167 private static final int FLAG_CLIP_CHILDREN = 0x1;
168
169 // When set, ViewGroup excludes the padding area from the invalidate rectangle
170 // Set by default
171 private static final int FLAG_CLIP_TO_PADDING = 0x2;
172
173 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
174 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
175 private static final int FLAG_INVALIDATE_REQUIRED = 0x4;
176
177 // When set, dispatchDraw() will run the layout animation and unset the flag
178 private static final int FLAG_RUN_ANIMATION = 0x8;
179
180 // When set, there is either no layout animation on the ViewGroup or the layout
181 // animation is over
182 // Set by default
183 private static final int FLAG_ANIMATION_DONE = 0x10;
184
185 // If set, this ViewGroup has padding; if unset there is no padding and we don't need
186 // to clip it, even if FLAG_CLIP_TO_PADDING is set
187 private static final int FLAG_PADDING_NOT_NULL = 0x20;
188
189 // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation
190 // Set by default
191 private static final int FLAG_ANIMATION_CACHE = 0x40;
192
193 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
194 // layout animation; this avoid clobbering the hierarchy
195 // Automatically set when the layout animation starts, depending on the animation's
196 // characteristics
197 private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
198
199 // When set, the next call to drawChild() will clear mChildTransformation's matrix
200 private static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
201
202 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
203 // the children's Bitmap caches if necessary
204 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
205 private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
206
207 /**
208 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
209 * to get the index of the child to draw for that iteration.
Romain Guy293451e2009-11-04 13:59:48 -0800210 *
211 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 */
213 protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
Romain Guy8506ab42009-06-11 17:35:47 -0700214
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 /**
216 * When set, this ViewGroup supports static transformations on children; this causes
217 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
218 * invoked when a child is drawn.
219 *
220 * Any subclass overriding
221 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
222 * set this flags in {@link #mGroupFlags}.
Romain Guy8506ab42009-06-11 17:35:47 -0700223 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 * {@hide}
225 */
226 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
227
228 // When the previous drawChild() invocation used an alpha value that was lower than
229 // 1.0 and set it in mCachePaint
230 private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000;
231
232 /**
233 * When set, this ViewGroup's drawable states also include those
234 * of its children.
235 */
236 private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
237
238 /**
239 * When set, this ViewGroup tries to always draw its children using their drawing cache.
240 */
241 private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
242
243 /**
244 * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to
245 * draw its children with their drawing cache.
246 */
247 private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
248
249 /**
250 * When set, this group will go through its list of children to notify them of
251 * any drawable state change.
252 */
253 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
254
255 private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
256
257 /**
258 * This view will get focus before any of its descendants.
259 */
260 public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
261
262 /**
263 * This view will get focus only if none of its descendants want it.
264 */
265 public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
266
267 /**
268 * This view will block any of its descendants from getting focus, even
269 * if they are focusable.
270 */
271 public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
272
273 /**
274 * Used to map between enum in attrubutes and flag values.
275 */
276 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
277 {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
278 FOCUS_BLOCK_DESCENDANTS};
279
280 /**
281 * When set, this ViewGroup should not intercept touch events.
Adam Powell110486f2010-06-22 17:14:44 -0700282 * {@hide}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 */
Adam Powell110486f2010-06-22 17:14:44 -0700284 protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
Romain Guy8506ab42009-06-11 17:35:47 -0700285
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 /**
Adam Powell2b342f02010-08-18 18:14:13 -0700287 * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
288 */
Adam Powellf37df072010-09-17 16:22:49 -0700289 private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
Adam Powell2b342f02010-08-18 18:14:13 -0700290
291 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 * Indicates which types of drawing caches are to be kept in memory.
293 * This field should be made private, so it is hidden from the SDK.
294 * {@hide}
295 */
296 protected int mPersistentDrawingCache;
297
298 /**
299 * Used to indicate that no drawing cache should be kept in memory.
300 */
301 public static final int PERSISTENT_NO_CACHE = 0x0;
302
303 /**
304 * Used to indicate that the animation drawing cache should be kept in memory.
305 */
306 public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
307
308 /**
309 * Used to indicate that the scrolling drawing cache should be kept in memory.
310 */
311 public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
312
313 /**
314 * Used to indicate that all drawing caches should be kept in memory.
315 */
316 public static final int PERSISTENT_ALL_CACHES = 0x3;
317
318 /**
319 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
320 * are set at the same time.
321 */
322 protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
323
324 // Index of the child's left position in the mLocation array
325 private static final int CHILD_LEFT_INDEX = 0;
326 // Index of the child's top position in the mLocation array
327 private static final int CHILD_TOP_INDEX = 1;
328
329 // Child views of this ViewGroup
330 private View[] mChildren;
331 // Number of valid children in the mChildren array, the rest should be null or not
332 // considered as children
Chet Haase9c087442011-01-12 16:20:16 -0800333
334 private boolean mLayoutSuppressed = false;
335
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 private int mChildrenCount;
337
338 private static final int ARRAY_INITIAL_CAPACITY = 12;
339 private static final int ARRAY_CAPACITY_INCREMENT = 12;
340
341 // Used to draw cached views
342 private final Paint mCachePaint = new Paint();
343
Chet Haase21cd1382010-09-01 17:42:29 -0700344 // Used to animate add/remove changes in layout
345 private LayoutTransition mTransition;
346
347 // The set of views that are currently being transitioned. This list is used to track views
348 // being removed that should not actually be removed from the parent yet because they are
349 // being animated.
350 private ArrayList<View> mTransitioningViews;
351
Chet Haase5e25c2c2010-09-16 11:15:56 -0700352 // List of children changing visibility. This is used to potentially keep rendering
353 // views during a transition when they otherwise would have become gone/invisible
354 private ArrayList<View> mVisibilityChangingChildren;
355
Romain Guy849d0a32011-02-01 17:20:48 -0800356 // Indicates whether this container will use its children layers to draw
357 @ViewDebug.ExportedProperty(category = "drawing")
358 private boolean mDrawLayers = true;
359
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 public ViewGroup(Context context) {
361 super(context);
362 initViewGroup();
363 }
364
365 public ViewGroup(Context context, AttributeSet attrs) {
366 super(context, attrs);
367 initViewGroup();
368 initFromAttributes(context, attrs);
369 }
370
371 public ViewGroup(Context context, AttributeSet attrs, int defStyle) {
372 super(context, attrs, defStyle);
373 initViewGroup();
374 initFromAttributes(context, attrs);
375 }
376
377 private void initViewGroup() {
378 // ViewGroup doesn't draw by default
379 setFlags(WILL_NOT_DRAW, DRAW_MASK);
380 mGroupFlags |= FLAG_CLIP_CHILDREN;
381 mGroupFlags |= FLAG_CLIP_TO_PADDING;
382 mGroupFlags |= FLAG_ANIMATION_DONE;
383 mGroupFlags |= FLAG_ANIMATION_CACHE;
384 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
385
Jeff Brown995e7742010-12-22 16:59:36 -0800386 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
387 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
388 }
389
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
391
392 mChildren = new View[ARRAY_INITIAL_CAPACITY];
393 mChildrenCount = 0;
394
395 mCachePaint.setDither(false);
396
397 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
398 }
399
400 private void initFromAttributes(Context context, AttributeSet attrs) {
401 TypedArray a = context.obtainStyledAttributes(attrs,
402 R.styleable.ViewGroup);
403
404 final int N = a.getIndexCount();
405 for (int i = 0; i < N; i++) {
406 int attr = a.getIndex(i);
407 switch (attr) {
408 case R.styleable.ViewGroup_clipChildren:
409 setClipChildren(a.getBoolean(attr, true));
410 break;
411 case R.styleable.ViewGroup_clipToPadding:
412 setClipToPadding(a.getBoolean(attr, true));
413 break;
414 case R.styleable.ViewGroup_animationCache:
415 setAnimationCacheEnabled(a.getBoolean(attr, true));
416 break;
417 case R.styleable.ViewGroup_persistentDrawingCache:
418 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
419 break;
420 case R.styleable.ViewGroup_addStatesFromChildren:
421 setAddStatesFromChildren(a.getBoolean(attr, false));
422 break;
423 case R.styleable.ViewGroup_alwaysDrawnWithCache:
424 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
425 break;
426 case R.styleable.ViewGroup_layoutAnimation:
427 int id = a.getResourceId(attr, -1);
428 if (id > 0) {
429 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
430 }
431 break;
432 case R.styleable.ViewGroup_descendantFocusability:
433 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
434 break;
Adam Powell2b342f02010-08-18 18:14:13 -0700435 case R.styleable.ViewGroup_splitMotionEvents:
436 setMotionEventSplittingEnabled(a.getBoolean(attr, false));
437 break;
Chet Haase13cc1202010-09-03 15:39:20 -0700438 case R.styleable.ViewGroup_animateLayoutChanges:
439 boolean animateLayoutChanges = a.getBoolean(attr, false);
440 if (animateLayoutChanges) {
441 setLayoutTransition(new LayoutTransition());
442 }
443 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 }
445 }
446
447 a.recycle();
448 }
449
450 /**
451 * Gets the descendant focusability of this view group. The descendant
452 * focusability defines the relationship between this view group and its
453 * descendants when looking for a view to take focus in
454 * {@link #requestFocus(int, android.graphics.Rect)}.
455 *
456 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
457 * {@link #FOCUS_BLOCK_DESCENDANTS}.
458 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700459 @ViewDebug.ExportedProperty(category = "focus", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
461 @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
462 @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
463 })
464 public int getDescendantFocusability() {
465 return mGroupFlags & FLAG_MASK_FOCUSABILITY;
466 }
467
468 /**
469 * Set the descendant focusability of this view group. This defines the relationship
470 * between this view group and its descendants when looking for a view to
471 * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
472 *
473 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
474 * {@link #FOCUS_BLOCK_DESCENDANTS}.
475 */
476 public void setDescendantFocusability(int focusability) {
477 switch (focusability) {
478 case FOCUS_BEFORE_DESCENDANTS:
479 case FOCUS_AFTER_DESCENDANTS:
480 case FOCUS_BLOCK_DESCENDANTS:
481 break;
482 default:
483 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
484 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
485 }
486 mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
487 mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
488 }
489
490 /**
491 * {@inheritDoc}
492 */
493 @Override
494 void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
495 if (mFocused != null) {
496 mFocused.unFocus();
497 mFocused = null;
498 }
499 super.handleFocusGainInternal(direction, previouslyFocusedRect);
500 }
501
502 /**
503 * {@inheritDoc}
504 */
505 public void requestChildFocus(View child, View focused) {
506 if (DBG) {
507 System.out.println(this + " requestChildFocus()");
508 }
509 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
510 return;
511 }
512
513 // Unfocus us, if necessary
514 super.unFocus();
515
516 // We had a previous notion of who had focus. Clear it.
517 if (mFocused != child) {
518 if (mFocused != null) {
519 mFocused.unFocus();
520 }
521
522 mFocused = child;
523 }
524 if (mParent != null) {
525 mParent.requestChildFocus(this, focused);
526 }
527 }
528
529 /**
530 * {@inheritDoc}
531 */
532 public void focusableViewAvailable(View v) {
533 if (mParent != null
534 // shortcut: don't report a new focusable view if we block our descendants from
535 // getting focus
536 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
537 // shortcut: don't report a new focusable view if we already are focused
538 // (and we don't prefer our descendants)
539 //
540 // note: knowing that mFocused is non-null is not a good enough reason
541 // to break the traversal since in that case we'd actually have to find
542 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
Joe Onoratoc6cc0f82011-04-12 11:53:13 -0700543 // an ancestor of v; this will get checked for at ViewAncestor
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
545 mParent.focusableViewAvailable(v);
546 }
547 }
548
549 /**
550 * {@inheritDoc}
551 */
552 public boolean showContextMenuForChild(View originalView) {
553 return mParent != null && mParent.showContextMenuForChild(originalView);
554 }
555
556 /**
Adam Powell6e346362010-07-23 10:18:23 -0700557 * {@inheritDoc}
558 */
559 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
560 return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null;
561 }
562
563 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 * Find the nearest view in the specified direction that wants to take
565 * focus.
566 *
567 * @param focused The view that currently has focus
568 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
569 * FOCUS_RIGHT, or 0 for not applicable.
570 */
571 public View focusSearch(View focused, int direction) {
572 if (isRootNamespace()) {
573 // root namespace means we should consider ourselves the top of the
574 // tree for focus searching; otherwise we could be focus searching
575 // into other tabs. see LocalActivityManager and TabHost for more info
576 return FocusFinder.getInstance().findNextFocus(this, focused, direction);
577 } else if (mParent != null) {
578 return mParent.focusSearch(focused, direction);
579 }
580 return null;
581 }
582
583 /**
584 * {@inheritDoc}
585 */
586 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
587 return false;
588 }
589
590 /**
591 * {@inheritDoc}
592 */
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700593 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
594 ViewParent parent = getParent();
595 if (parent == null) {
596 return false;
597 }
598 final boolean propagate = onRequestSendAccessibilityEvent(child, event);
Romain Guy02739a82011-05-16 11:43:18 -0700599 //noinspection SimplifiableIfStatement
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700600 if (!propagate) {
601 return false;
602 }
603 return parent.requestSendAccessibilityEvent(this, event);
604 }
605
606 /**
607 * Called when a child has requested sending an {@link AccessibilityEvent} and
608 * gives an opportunity to its parent to augment the event.
609 *
610 * @param child The child which requests sending the event.
611 * @param event The event to be sent.
612 * @return True if the event should be sent.
613 *
614 * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
615 */
616 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
617 return true;
618 }
619
620 /**
621 * {@inheritDoc}
622 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 @Override
624 public boolean dispatchUnhandledMove(View focused, int direction) {
625 return mFocused != null &&
626 mFocused.dispatchUnhandledMove(focused, direction);
627 }
628
629 /**
630 * {@inheritDoc}
631 */
632 public void clearChildFocus(View child) {
633 if (DBG) {
634 System.out.println(this + " clearChildFocus()");
635 }
636
637 mFocused = null;
638 if (mParent != null) {
639 mParent.clearChildFocus(this);
640 }
641 }
642
643 /**
644 * {@inheritDoc}
645 */
646 @Override
647 public void clearFocus() {
648 super.clearFocus();
649
650 // clear any child focus if it exists
651 if (mFocused != null) {
652 mFocused.clearFocus();
653 }
654 }
655
656 /**
657 * {@inheritDoc}
658 */
659 @Override
660 void unFocus() {
661 if (DBG) {
662 System.out.println(this + " unFocus()");
663 }
664
665 super.unFocus();
666 if (mFocused != null) {
667 mFocused.unFocus();
668 }
669 mFocused = null;
670 }
671
672 /**
673 * Returns the focused child of this view, if any. The child may have focus
674 * or contain focus.
675 *
676 * @return the focused child or null.
677 */
678 public View getFocusedChild() {
679 return mFocused;
680 }
681
682 /**
683 * Returns true if this view has or contains focus
684 *
685 * @return true if this view has or contains focus
686 */
687 @Override
688 public boolean hasFocus() {
689 return (mPrivateFlags & FOCUSED) != 0 || mFocused != null;
690 }
691
692 /*
693 * (non-Javadoc)
694 *
695 * @see android.view.View#findFocus()
696 */
697 @Override
698 public View findFocus() {
699 if (DBG) {
700 System.out.println("Find focus in " + this + ": flags="
701 + isFocused() + ", child=" + mFocused);
702 }
703
704 if (isFocused()) {
705 return this;
706 }
707
708 if (mFocused != null) {
709 return mFocused.findFocus();
710 }
711 return null;
712 }
713
714 /**
715 * {@inheritDoc}
716 */
717 @Override
718 public boolean hasFocusable() {
719 if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
720 return false;
721 }
722
723 if (isFocusable()) {
724 return true;
725 }
726
727 final int descendantFocusability = getDescendantFocusability();
728 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
729 final int count = mChildrenCount;
730 final View[] children = mChildren;
731
732 for (int i = 0; i < count; i++) {
733 final View child = children[i];
734 if (child.hasFocusable()) {
735 return true;
736 }
737 }
738 }
739
740 return false;
741 }
742
743 /**
744 * {@inheritDoc}
745 */
746 @Override
747 public void addFocusables(ArrayList<View> views, int direction) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700748 addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
749 }
750
751 /**
752 * {@inheritDoc}
753 */
754 @Override
755 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 final int focusableCount = views.size();
757
758 final int descendantFocusability = getDescendantFocusability();
759
760 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
761 final int count = mChildrenCount;
762 final View[] children = mChildren;
763
764 for (int i = 0; i < count; i++) {
765 final View child = children[i];
766 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700767 child.addFocusables(views, direction, focusableMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 }
769 }
770 }
771
772 // we add ourselves (if focusable) in all cases except for when we are
773 // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
774 // to avoid the focus search finding layouts when a more precise search
775 // among the focusable children would be more interesting.
776 if (
777 descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
778 // No focusable descendants
779 (focusableCount == views.size())) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700780 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 }
782 }
783
Svetoslav Ganov8643aa02011-04-20 12:12:33 -0700784 @Override
785 public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
786 final int childrenCount = mChildrenCount;
787 final View[] children = mChildren;
788 for (int i = 0; i < childrenCount; i++) {
789 View child = children[i];
Svetoslav Ganoveffe10f2011-07-26 18:09:37 -0700790 if ((child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
Svetoslav Ganov8643aa02011-04-20 12:12:33 -0700791 child.findViewsWithText(outViews, text);
792 }
793 }
794 }
795
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 /**
797 * {@inheritDoc}
798 */
799 @Override
800 public void dispatchWindowFocusChanged(boolean hasFocus) {
801 super.dispatchWindowFocusChanged(hasFocus);
802 final int count = mChildrenCount;
803 final View[] children = mChildren;
804 for (int i = 0; i < count; i++) {
805 children[i].dispatchWindowFocusChanged(hasFocus);
806 }
807 }
808
809 /**
810 * {@inheritDoc}
811 */
812 @Override
813 public void addTouchables(ArrayList<View> views) {
814 super.addTouchables(views);
815
816 final int count = mChildrenCount;
817 final View[] children = mChildren;
818
819 for (int i = 0; i < count; i++) {
820 final View child = children[i];
821 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
822 child.addTouchables(views);
823 }
824 }
825 }
Romain Guy43c9cdf2010-01-27 13:53:55 -0800826
827 /**
828 * {@inheritDoc}
829 */
830 @Override
831 public void dispatchDisplayHint(int hint) {
832 super.dispatchDisplayHint(hint);
833 final int count = mChildrenCount;
834 final View[] children = mChildren;
835 for (int i = 0; i < count; i++) {
836 children[i].dispatchDisplayHint(hint);
837 }
838 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839
840 /**
Chet Haase5e25c2c2010-09-16 11:15:56 -0700841 * @hide
842 * @param child
843 * @param visibility
844 */
Philip Milne350f0a62011-07-19 14:00:56 -0700845 protected void onChildVisibilityChanged(View child, int visibility) {
Chet Haase5e25c2c2010-09-16 11:15:56 -0700846 if (mTransition != null) {
847 if (visibility == VISIBLE) {
848 mTransition.showChild(this, child);
849 } else {
850 mTransition.hideChild(this, child);
851 }
852 if (visibility != VISIBLE) {
853 // Only track this on disappearing views - appearing views are already visible
854 // and don't need special handling during drawChild()
855 if (mVisibilityChangingChildren == null) {
856 mVisibilityChangingChildren = new ArrayList<View>();
857 }
858 mVisibilityChangingChildren.add(child);
859 if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
860 addDisappearingView(child);
861 }
862 }
863 }
Christopher Tate86cab1b2011-01-13 20:28:55 -0800864
865 // in all cases, for drags
866 if (mCurrentDrag != null) {
867 if (visibility == VISIBLE) {
868 notifyChildOfDrag(child);
869 }
870 }
Chet Haase5e25c2c2010-09-16 11:15:56 -0700871 }
872
873 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 * {@inheritDoc}
875 */
876 @Override
Adam Powell326d8082009-12-09 15:10:07 -0800877 protected void dispatchVisibilityChanged(View changedView, int visibility) {
878 super.dispatchVisibilityChanged(changedView, visibility);
879 final int count = mChildrenCount;
880 final View[] children = mChildren;
881 for (int i = 0; i < count; i++) {
882 children[i].dispatchVisibilityChanged(changedView, visibility);
883 }
884 }
885
886 /**
887 * {@inheritDoc}
888 */
889 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 public void dispatchWindowVisibilityChanged(int visibility) {
891 super.dispatchWindowVisibilityChanged(visibility);
892 final int count = mChildrenCount;
893 final View[] children = mChildren;
894 for (int i = 0; i < count; i++) {
895 children[i].dispatchWindowVisibilityChanged(visibility);
896 }
897 }
898
899 /**
900 * {@inheritDoc}
901 */
Dianne Hackborne36d6e22010-02-17 19:46:25 -0800902 @Override
903 public void dispatchConfigurationChanged(Configuration newConfig) {
904 super.dispatchConfigurationChanged(newConfig);
905 final int count = mChildrenCount;
906 final View[] children = mChildren;
907 for (int i = 0; i < count; i++) {
908 children[i].dispatchConfigurationChanged(newConfig);
909 }
910 }
911
912 /**
913 * {@inheritDoc}
914 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915 public void recomputeViewAttributes(View child) {
Joe Onorato664644d2011-01-23 17:53:23 -0800916 if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
917 ViewParent parent = mParent;
918 if (parent != null) parent.recomputeViewAttributes(this);
919 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920 }
Romain Guy8506ab42009-06-11 17:35:47 -0700921
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 @Override
923 void dispatchCollectViewAttributes(int visibility) {
924 visibility |= mViewFlags&VISIBILITY_MASK;
925 super.dispatchCollectViewAttributes(visibility);
926 final int count = mChildrenCount;
927 final View[] children = mChildren;
928 for (int i = 0; i < count; i++) {
929 children[i].dispatchCollectViewAttributes(visibility);
930 }
931 }
932
933 /**
934 * {@inheritDoc}
935 */
936 public void bringChildToFront(View child) {
937 int index = indexOfChild(child);
938 if (index >= 0) {
939 removeFromArray(index);
940 addInArray(child, mChildrenCount);
941 child.mParent = this;
942 }
943 }
944
945 /**
946 * {@inheritDoc}
Christopher Tatea53146c2010-09-07 11:57:52 -0700947 *
948 * !!! TODO: write real docs
949 */
950 @Override
951 public boolean dispatchDragEvent(DragEvent event) {
952 boolean retval = false;
953 final float tx = event.mX;
954 final float ty = event.mY;
955
Dianne Hackborn6dd005b2011-07-18 13:22:50 -0700956 ViewRootImpl root = getViewRootImpl();
Christopher Tatea53146c2010-09-07 11:57:52 -0700957
958 // Dispatch down the view hierarchy
959 switch (event.mAction) {
960 case DragEvent.ACTION_DRAG_STARTED: {
961 // clear state to recalculate which views we drag over
Chris Tate9d1ab882010-11-02 15:55:39 -0700962 mCurrentDragView = null;
Christopher Tatea53146c2010-09-07 11:57:52 -0700963
Christopher Tate86cab1b2011-01-13 20:28:55 -0800964 // Set up our tracking of drag-started notifications
965 mCurrentDrag = DragEvent.obtain(event);
966 if (mDragNotifiedChildren == null) {
967 mDragNotifiedChildren = new HashSet<View>();
968 } else {
969 mDragNotifiedChildren.clear();
970 }
971
Christopher Tatea53146c2010-09-07 11:57:52 -0700972 // Now dispatch down to our children, caching the responses
973 mChildAcceptsDrag = false;
974 final int count = mChildrenCount;
975 final View[] children = mChildren;
976 for (int i = 0; i < count; i++) {
Christopher Tate2c095f32010-10-04 14:13:40 -0700977 final View child = children[i];
Christopher Tate3d4bf172011-03-28 16:16:46 -0700978 child.mPrivateFlags2 &= ~View.DRAG_MASK;
Christopher Tate2c095f32010-10-04 14:13:40 -0700979 if (child.getVisibility() == VISIBLE) {
Christopher Tate86cab1b2011-01-13 20:28:55 -0800980 final boolean handled = notifyChildOfDrag(children[i]);
Christopher Tate2c095f32010-10-04 14:13:40 -0700981 if (handled) {
982 mChildAcceptsDrag = true;
983 }
Christopher Tatea53146c2010-09-07 11:57:52 -0700984 }
985 }
986
987 // Return HANDLED if one of our children can accept the drag
988 if (mChildAcceptsDrag) {
989 retval = true;
990 }
991 } break;
992
993 case DragEvent.ACTION_DRAG_ENDED: {
Christopher Tate86cab1b2011-01-13 20:28:55 -0800994 // Release the bookkeeping now that the drag lifecycle has ended
Christopher Tate1fc014f2011-01-19 12:56:26 -0800995 if (mDragNotifiedChildren != null) {
996 for (View child : mDragNotifiedChildren) {
997 // If a child was notified about an ongoing drag, it's told that it's over
998 child.dispatchDragEvent(event);
Christopher Tate3d4bf172011-03-28 16:16:46 -0700999 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1000 child.refreshDrawableState();
Christopher Tate1fc014f2011-01-19 12:56:26 -08001001 }
1002
1003 mDragNotifiedChildren.clear();
1004 mCurrentDrag.recycle();
1005 mCurrentDrag = null;
1006 }
Christopher Tate86cab1b2011-01-13 20:28:55 -08001007
Christopher Tatea53146c2010-09-07 11:57:52 -07001008 // We consider drag-ended to have been handled if one of our children
1009 // had offered to handle the drag.
1010 if (mChildAcceptsDrag) {
1011 retval = true;
1012 }
1013 } break;
1014
1015 case DragEvent.ACTION_DRAG_LOCATION: {
1016 // Find the [possibly new] drag target
1017 final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
1018
1019 // If we've changed apparent drag target, tell the view root which view
Chris Tate9d1ab882010-11-02 15:55:39 -07001020 // we're over now [for purposes of the eventual drag-recipient-changed
1021 // notifications to the framework] and tell the new target that the drag
1022 // has entered its bounds. The root will see setDragFocus() calls all
1023 // the way down to the final leaf view that is handling the LOCATION event
1024 // before reporting the new potential recipient to the framework.
Christopher Tatea53146c2010-09-07 11:57:52 -07001025 if (mCurrentDragView != target) {
Chris Tate9d1ab882010-11-02 15:55:39 -07001026 root.setDragFocus(target);
1027
1028 final int action = event.mAction;
1029 // If we've dragged off of a child view, send it the EXITED message
1030 if (mCurrentDragView != null) {
Christopher Tate3d4bf172011-03-28 16:16:46 -07001031 final View view = mCurrentDragView;
Chris Tate9d1ab882010-11-02 15:55:39 -07001032 event.mAction = DragEvent.ACTION_DRAG_EXITED;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001033 view.dispatchDragEvent(event);
1034 view.mPrivateFlags2 &= ~View.DRAG_HOVERED;
1035 view.refreshDrawableState();
Chris Tate9d1ab882010-11-02 15:55:39 -07001036 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001037 mCurrentDragView = target;
Chris Tate9d1ab882010-11-02 15:55:39 -07001038
1039 // If we've dragged over a new child view, send it the ENTERED message
1040 if (target != null) {
1041 event.mAction = DragEvent.ACTION_DRAG_ENTERED;
1042 target.dispatchDragEvent(event);
Christopher Tate3d4bf172011-03-28 16:16:46 -07001043 target.mPrivateFlags2 |= View.DRAG_HOVERED;
1044 target.refreshDrawableState();
Chris Tate9d1ab882010-11-02 15:55:39 -07001045 }
1046 event.mAction = action; // restore the event's original state
Christopher Tatea53146c2010-09-07 11:57:52 -07001047 }
Christopher Tate2c095f32010-10-04 14:13:40 -07001048
Christopher Tatea53146c2010-09-07 11:57:52 -07001049 // Dispatch the actual drag location notice, localized into its coordinates
1050 if (target != null) {
1051 event.mX = mLocalPoint.x;
1052 event.mY = mLocalPoint.y;
1053
1054 retval = target.dispatchDragEvent(event);
1055
1056 event.mX = tx;
1057 event.mY = ty;
1058 }
1059 } break;
1060
Chris Tate9d1ab882010-11-02 15:55:39 -07001061 /* Entered / exited dispatch
1062 *
1063 * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is
1064 * that we're about to get the corresponding LOCATION event, which we will use to
1065 * determine which of our children is the new target; at that point we will
1066 * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
1067 *
1068 * DRAG_EXITED *is* dispatched all the way down immediately: once we know the
1069 * drag has left this ViewGroup, we know by definition that every contained subview
1070 * is also no longer under the drag point.
1071 */
1072
1073 case DragEvent.ACTION_DRAG_EXITED: {
1074 if (mCurrentDragView != null) {
Christopher Tate3d4bf172011-03-28 16:16:46 -07001075 final View view = mCurrentDragView;
1076 view.dispatchDragEvent(event);
1077 view.mPrivateFlags2 &= ~View.DRAG_HOVERED;
1078 view.refreshDrawableState();
1079
Chris Tate9d1ab882010-11-02 15:55:39 -07001080 mCurrentDragView = null;
1081 }
1082 } break;
1083
Christopher Tatea53146c2010-09-07 11:57:52 -07001084 case DragEvent.ACTION_DROP: {
Christopher Tate2c095f32010-10-04 14:13:40 -07001085 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
Christopher Tatea53146c2010-09-07 11:57:52 -07001086 View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
1087 if (target != null) {
Christopher Tate5ada6cb2010-10-05 14:15:29 -07001088 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target);
Christopher Tatea53146c2010-09-07 11:57:52 -07001089 event.mX = mLocalPoint.x;
1090 event.mY = mLocalPoint.y;
1091 retval = target.dispatchDragEvent(event);
1092 event.mX = tx;
1093 event.mY = ty;
Christopher Tate5ada6cb2010-10-05 14:15:29 -07001094 } else {
1095 if (ViewDebug.DEBUG_DRAG) {
1096 Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view");
1097 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001098 }
1099 } break;
1100 }
1101
1102 // If none of our children could handle the event, try here
1103 if (!retval) {
Chris Tate32affef2010-10-18 15:29:21 -07001104 // Call up to the View implementation that dispatches to installed listeners
1105 retval = super.dispatchDragEvent(event);
Christopher Tatea53146c2010-09-07 11:57:52 -07001106 }
1107 return retval;
1108 }
1109
1110 // Find the frontmost child view that lies under the given point, and calculate
1111 // the position within its own local coordinate system.
1112 View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001113 final int count = mChildrenCount;
1114 final View[] children = mChildren;
1115 for (int i = count - 1; i >= 0; i--) {
1116 final View child = children[i];
Christopher Tate3d4bf172011-03-28 16:16:46 -07001117 if (!child.canAcceptDrag()) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001118 continue;
1119 }
1120
Christopher Tate2c095f32010-10-04 14:13:40 -07001121 if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001122 return child;
1123 }
1124 }
1125 return null;
1126 }
1127
Christopher Tate86cab1b2011-01-13 20:28:55 -08001128 boolean notifyChildOfDrag(View child) {
1129 if (ViewDebug.DEBUG_DRAG) {
1130 Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
1131 }
1132
Christopher Tate3d4bf172011-03-28 16:16:46 -07001133 boolean canAccept = false;
Christopher Tate86cab1b2011-01-13 20:28:55 -08001134 if (! mDragNotifiedChildren.contains(child)) {
1135 mDragNotifiedChildren.add(child);
Christopher Tate3d4bf172011-03-28 16:16:46 -07001136 canAccept = child.dispatchDragEvent(mCurrentDrag);
1137 if (canAccept && !child.canAcceptDrag()) {
1138 child.mPrivateFlags2 |= View.DRAG_CAN_ACCEPT;
1139 child.refreshDrawableState();
1140 }
Christopher Tate86cab1b2011-01-13 20:28:55 -08001141 }
Christopher Tate3d4bf172011-03-28 16:16:46 -07001142 return canAccept;
Christopher Tate86cab1b2011-01-13 20:28:55 -08001143 }
1144
Joe Onorato664644d2011-01-23 17:53:23 -08001145 @Override
1146 public void dispatchSystemUiVisibilityChanged(int visible) {
1147 super.dispatchSystemUiVisibilityChanged(visible);
1148
1149 final int count = mChildrenCount;
1150 final View[] children = mChildren;
1151 for (int i=0; i <count; i++) {
1152 final View child = children[i];
1153 child.dispatchSystemUiVisibilityChanged(visible);
1154 }
1155 }
1156
Christopher Tatea53146c2010-09-07 11:57:52 -07001157 /**
1158 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001159 */
1160 @Override
1161 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1162 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1163 return super.dispatchKeyEventPreIme(event);
1164 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1165 return mFocused.dispatchKeyEventPreIme(event);
1166 }
1167 return false;
1168 }
1169
1170 /**
1171 * {@inheritDoc}
1172 */
1173 @Override
1174 public boolean dispatchKeyEvent(KeyEvent event) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001175 if (mInputEventConsistencyVerifier != null) {
1176 mInputEventConsistencyVerifier.onKeyEvent(event, 1);
1177 }
1178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001179 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001180 if (super.dispatchKeyEvent(event)) {
1181 return true;
1182 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001183 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001184 if (mFocused.dispatchKeyEvent(event)) {
1185 return true;
1186 }
1187 }
1188
1189 if (mInputEventConsistencyVerifier != null) {
1190 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001191 }
1192 return false;
1193 }
1194
1195 /**
1196 * {@inheritDoc}
1197 */
1198 @Override
1199 public boolean dispatchKeyShortcutEvent(KeyEvent event) {
1200 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1201 return super.dispatchKeyShortcutEvent(event);
1202 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1203 return mFocused.dispatchKeyShortcutEvent(event);
1204 }
1205 return false;
1206 }
1207
1208 /**
1209 * {@inheritDoc}
1210 */
1211 @Override
1212 public boolean dispatchTrackballEvent(MotionEvent event) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001213 if (mInputEventConsistencyVerifier != null) {
1214 mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
1215 }
1216
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001217 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001218 if (super.dispatchTrackballEvent(event)) {
1219 return true;
1220 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001221 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001222 if (mFocused.dispatchTrackballEvent(event)) {
1223 return true;
1224 }
1225 }
1226
1227 if (mInputEventConsistencyVerifier != null) {
1228 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229 }
1230 return false;
1231 }
1232
Jeff Brown10b62902011-06-20 16:40:37 -07001233 /**
1234 * {@inheritDoc}
1235 */
Romain Guya9489272011-06-22 20:58:11 -07001236 @SuppressWarnings({"ConstantConditions"})
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001237 @Override
Jeff Browna032cc02011-03-07 16:56:21 -08001238 protected boolean dispatchHoverEvent(MotionEvent event) {
Jeff Browna032cc02011-03-07 16:56:21 -08001239 final int action = event.getAction();
Jeff Browna032cc02011-03-07 16:56:21 -08001240
Jeff Brown10b62902011-06-20 16:40:37 -07001241 // First check whether the view group wants to intercept the hover event.
1242 final boolean interceptHover = onInterceptHoverEvent(event);
1243 event.setAction(action); // restore action in case it was changed
1244
Jeff Brown87b7f802011-06-21 18:35:45 -07001245 MotionEvent eventNoHistory = event;
1246 boolean handled = false;
1247
1248 // Send events to the hovered children and build a new list of hover targets until
1249 // one is found that handles the event.
1250 HoverTarget firstOldHoverTarget = mFirstHoverTarget;
1251 mFirstHoverTarget = null;
Jeff Brown10b62902011-06-20 16:40:37 -07001252 if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) {
Jeff Browna032cc02011-03-07 16:56:21 -08001253 final float x = event.getX();
1254 final float y = event.getY();
Jeff Brown33bbfd22011-02-24 20:55:35 -08001255 final int childrenCount = mChildrenCount;
1256 if (childrenCount != 0) {
1257 final View[] children = mChildren;
Jeff Brown87b7f802011-06-21 18:35:45 -07001258 HoverTarget lastHoverTarget = null;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001259 for (int i = childrenCount - 1; i >= 0; i--) {
1260 final View child = children[i];
Jeff Brown87b7f802011-06-21 18:35:45 -07001261 if (!canViewReceivePointerEvents(child)
1262 || !isTransformedTouchPointInView(x, y, child, null)) {
1263 continue;
1264 }
1265
1266 // Obtain a hover target for this child. Dequeue it from the
1267 // old hover target list if the child was previously hovered.
1268 HoverTarget hoverTarget = firstOldHoverTarget;
1269 final boolean wasHovered;
1270 for (HoverTarget predecessor = null; ;) {
1271 if (hoverTarget == null) {
1272 hoverTarget = HoverTarget.obtain(child);
1273 wasHovered = false;
1274 break;
1275 }
1276
1277 if (hoverTarget.child == child) {
1278 if (predecessor != null) {
1279 predecessor.next = hoverTarget.next;
1280 } else {
1281 firstOldHoverTarget = hoverTarget.next;
1282 }
1283 hoverTarget.next = null;
1284 wasHovered = true;
1285 break;
1286 }
1287
1288 predecessor = hoverTarget;
1289 hoverTarget = hoverTarget.next;
1290 }
1291
1292 // Enqueue the hover target onto the new hover target list.
1293 if (lastHoverTarget != null) {
1294 lastHoverTarget.next = hoverTarget;
1295 } else {
1296 lastHoverTarget = hoverTarget;
1297 mFirstHoverTarget = hoverTarget;
1298 }
1299
1300 // Dispatch the event to the child.
1301 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1302 if (!wasHovered) {
1303 // Send the enter as is.
1304 handled |= dispatchTransformedGenericPointerEvent(
1305 event, child); // enter
1306 }
1307 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1308 if (!wasHovered) {
1309 // Synthesize an enter from a move.
1310 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1311 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1312 handled |= dispatchTransformedGenericPointerEvent(
1313 eventNoHistory, child); // enter
1314 eventNoHistory.setAction(action);
1315
1316 handled |= dispatchTransformedGenericPointerEvent(
1317 eventNoHistory, child); // move
1318 } else {
1319 // Send the move as is.
1320 handled |= dispatchTransformedGenericPointerEvent(event, child);
1321 }
1322 }
1323 if (handled) {
Jeff Brown10b62902011-06-20 16:40:37 -07001324 break;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001325 }
Jeff Brown10b62902011-06-20 16:40:37 -07001326 }
1327 }
1328 }
Jeff Brown33bbfd22011-02-24 20:55:35 -08001329
Jeff Brown87b7f802011-06-21 18:35:45 -07001330 // Send exit events to all previously hovered children that are no longer hovered.
1331 while (firstOldHoverTarget != null) {
1332 final View child = firstOldHoverTarget.child;
Jeff Brown10b62902011-06-20 16:40:37 -07001333
Jeff Brown87b7f802011-06-21 18:35:45 -07001334 // Exit the old hovered child.
1335 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1336 // Send the exit as is.
1337 handled |= dispatchTransformedGenericPointerEvent(
1338 event, child); // exit
1339 } else {
1340 // Synthesize an exit from a move or enter.
1341 // Ignore the result because hover focus has moved to a different view.
1342 if (action == MotionEvent.ACTION_HOVER_MOVE) {
Jeff Brown10b62902011-06-20 16:40:37 -07001343 dispatchTransformedGenericPointerEvent(
Jeff Brown87b7f802011-06-21 18:35:45 -07001344 event, child); // move
Jeff Brown10b62902011-06-20 16:40:37 -07001345 }
Jeff Brown87b7f802011-06-21 18:35:45 -07001346 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1347 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1348 dispatchTransformedGenericPointerEvent(
1349 eventNoHistory, child); // exit
1350 eventNoHistory.setAction(action);
Jeff Brown10b62902011-06-20 16:40:37 -07001351 }
1352
Jeff Brown87b7f802011-06-21 18:35:45 -07001353 final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next;
1354 firstOldHoverTarget.recycle();
1355 firstOldHoverTarget = nextOldHoverTarget;
Jeff Brown10b62902011-06-20 16:40:37 -07001356 }
1357
Jeff Brown87b7f802011-06-21 18:35:45 -07001358 // Send events to the view group itself if no children have handled it.
Jeff Brown10b62902011-06-20 16:40:37 -07001359 boolean newHoveredSelf = !handled;
1360 if (newHoveredSelf == mHoveredSelf) {
1361 if (newHoveredSelf) {
1362 // Send event to the view group as before.
1363 handled |= super.dispatchHoverEvent(event);
1364 }
1365 } else {
1366 if (mHoveredSelf) {
1367 // Exit the view group.
1368 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1369 // Send the exit as is.
1370 handled |= super.dispatchHoverEvent(event); // exit
1371 } else {
1372 // Synthesize an exit from a move or enter.
1373 // Ignore the result because hover focus is moving to a different view.
1374 if (action == MotionEvent.ACTION_HOVER_MOVE) {
1375 super.dispatchHoverEvent(event); // move
1376 }
1377 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1378 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1379 super.dispatchHoverEvent(eventNoHistory); // exit
1380 eventNoHistory.setAction(action);
1381 }
1382 mHoveredSelf = false;
1383 }
1384
1385 if (newHoveredSelf) {
1386 // Enter the view group.
1387 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1388 // Send the enter as is.
1389 handled |= super.dispatchHoverEvent(event); // enter
1390 mHoveredSelf = true;
1391 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1392 // Synthesize an enter from a move.
1393 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1394 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1395 handled |= super.dispatchHoverEvent(eventNoHistory); // enter
1396 eventNoHistory.setAction(action);
1397
1398 handled |= super.dispatchHoverEvent(eventNoHistory); // move
1399 mHoveredSelf = true;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001400 }
1401 }
Jeff Brown33bbfd22011-02-24 20:55:35 -08001402 }
1403
Jeff Browna032cc02011-03-07 16:56:21 -08001404 // Recycle the copy of the event that we made.
1405 if (eventNoHistory != event) {
1406 eventNoHistory.recycle();
1407 }
1408
Jeff Browna032cc02011-03-07 16:56:21 -08001409 // Done.
1410 return handled;
1411 }
1412
Jeff Brown87b7f802011-06-21 18:35:45 -07001413 /** @hide */
1414 @Override
1415 protected boolean hasHoveredChild() {
1416 return mFirstHoverTarget != null;
1417 }
1418
Jeff Brown10b62902011-06-20 16:40:37 -07001419 /**
1420 * Implement this method to intercept hover events before they are handled
1421 * by child views.
1422 * <p>
1423 * This method is called before dispatching a hover event to a child of
1424 * the view group or to the view group's own {@link #onHoverEvent} to allow
1425 * the view group a chance to intercept the hover event.
1426 * This method can also be used to watch all pointer motions that occur within
1427 * the bounds of the view group even when the pointer is hovering over
1428 * a child of the view group rather than over the view group itself.
1429 * </p><p>
1430 * The view group can prevent its children from receiving hover events by
1431 * implementing this method and returning <code>true</code> to indicate
1432 * that it would like to intercept hover events. The view group must
1433 * continuously return <code>true</code> from {@link #onInterceptHoverEvent}
1434 * for as long as it wishes to continue intercepting hover events from
1435 * its children.
1436 * </p><p>
1437 * Interception preserves the invariant that at most one view can be
1438 * hovered at a time by transferring hover focus from the currently hovered
1439 * child to the view group or vice-versa as needed.
1440 * </p><p>
1441 * If this method returns <code>true</code> and a child is already hovered, then the
1442 * child view will first receive a hover exit event and then the view group
1443 * itself will receive a hover enter event in {@link #onHoverEvent}.
1444 * Likewise, if this method had previously returned <code>true</code> to intercept hover
1445 * events and instead returns <code>false</code> while the pointer is hovering
1446 * within the bounds of one of a child, then the view group will first receive a
1447 * hover exit event in {@link #onHoverEvent} and then the hovered child will
1448 * receive a hover enter event.
1449 * </p><p>
1450 * The default implementation always returns false.
1451 * </p>
1452 *
1453 * @param event The motion event that describes the hover.
1454 * @return True if the view group would like to intercept the hover event
1455 * and prevent its children from receiving it.
1456 */
1457 public boolean onInterceptHoverEvent(MotionEvent event) {
Svetoslav Ganov736c2752011-04-22 18:30:36 -07001458 return false;
1459 }
1460
Jeff Browna032cc02011-03-07 16:56:21 -08001461 private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
1462 if (event.getHistorySize() == 0) {
1463 return event;
1464 }
1465 return MotionEvent.obtainNoHistory(event);
1466 }
1467
Jeff Brown10b62902011-06-20 16:40:37 -07001468 /**
1469 * {@inheritDoc}
1470 */
Jeff Browna032cc02011-03-07 16:56:21 -08001471 @Override
1472 protected boolean dispatchGenericPointerEvent(MotionEvent event) {
1473 // Send the event to the child under the pointer.
1474 final int childrenCount = mChildrenCount;
1475 if (childrenCount != 0) {
1476 final View[] children = mChildren;
1477 final float x = event.getX();
1478 final float y = event.getY();
1479
1480 for (int i = childrenCount - 1; i >= 0; i--) {
1481 final View child = children[i];
1482 if (!canViewReceivePointerEvents(child)
1483 || !isTransformedTouchPointInView(x, y, child, null)) {
1484 continue;
1485 }
1486
1487 if (dispatchTransformedGenericPointerEvent(event, child)) {
1488 return true;
1489 }
1490 }
1491 }
1492
1493 // No child handled the event. Send it to this view group.
1494 return super.dispatchGenericPointerEvent(event);
1495 }
1496
Jeff Brown10b62902011-06-20 16:40:37 -07001497 /**
1498 * {@inheritDoc}
1499 */
Jeff Browna032cc02011-03-07 16:56:21 -08001500 @Override
1501 protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
Jeff Brown33bbfd22011-02-24 20:55:35 -08001502 // Send the event to the focused child or to this view group if it has focus.
Jeff Browncb1404e2011-01-15 18:14:15 -08001503 if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
Jeff Browna032cc02011-03-07 16:56:21 -08001504 return super.dispatchGenericFocusedEvent(event);
Jeff Browncb1404e2011-01-15 18:14:15 -08001505 } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1506 return mFocused.dispatchGenericMotionEvent(event);
1507 }
1508 return false;
1509 }
1510
1511 /**
Jeff Browna032cc02011-03-07 16:56:21 -08001512 * Dispatches a generic pointer event to a child, taking into account
1513 * transformations that apply to the child.
1514 *
1515 * @param event The event to send.
1516 * @param child The view to send the event to.
1517 * @return {@code true} if the child handled the event.
1518 */
1519 private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) {
1520 final float offsetX = mScrollX - child.mLeft;
1521 final float offsetY = mScrollY - child.mTop;
1522
1523 boolean handled;
1524 if (!child.hasIdentityMatrix()) {
1525 MotionEvent transformedEvent = MotionEvent.obtain(event);
1526 transformedEvent.offsetLocation(offsetX, offsetY);
1527 transformedEvent.transform(child.getInverseMatrix());
1528 handled = child.dispatchGenericMotionEvent(transformedEvent);
1529 transformedEvent.recycle();
1530 } else {
1531 event.offsetLocation(offsetX, offsetY);
1532 handled = child.dispatchGenericMotionEvent(event);
1533 event.offsetLocation(-offsetX, -offsetY);
1534 }
1535 return handled;
1536 }
1537
1538 /**
Jeff Browncb1404e2011-01-15 18:14:15 -08001539 * {@inheritDoc}
1540 */
1541 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001542 public boolean dispatchTouchEvent(MotionEvent ev) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001543 if (mInputEventConsistencyVerifier != null) {
1544 mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
1545 }
1546
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001547 boolean handled = false;
1548 if (onFilterTouchEventForSecurity(ev)) {
1549 final int action = ev.getAction();
1550 final int actionMasked = action & MotionEvent.ACTION_MASK;
Jeff Brown85a31762010-09-01 17:01:00 -07001551
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001552 // Handle an initial down.
1553 if (actionMasked == MotionEvent.ACTION_DOWN) {
1554 // Throw away all previous state when starting a new touch gesture.
1555 // The framework may have dropped the up or cancel event for the previous gesture
1556 // due to an app switch, ANR, or some other state change.
1557 cancelAndClearTouchTargets(ev);
1558 resetTouchState();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001559 }
Adam Powellb08013c2010-09-16 16:28:11 -07001560
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001561 // Check for interception.
1562 final boolean intercepted;
Jeff Brown20e987b2010-08-23 12:01:02 -07001563 if (actionMasked == MotionEvent.ACTION_DOWN
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001564 || mFirstTouchTarget != null) {
1565 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
1566 if (!disallowIntercept) {
1567 intercepted = onInterceptTouchEvent(ev);
1568 ev.setAction(action); // restore action in case it was changed
1569 } else {
1570 intercepted = false;
1571 }
1572 } else {
1573 // There are no touch targets and this action is not an initial down
1574 // so this view group continues to intercept touches.
1575 intercepted = true;
1576 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001577
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001578 // Check for cancelation.
1579 final boolean canceled = resetCancelNextUpFlag(this)
1580 || actionMasked == MotionEvent.ACTION_CANCEL;
Jeff Brown20e987b2010-08-23 12:01:02 -07001581
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001582 // Update list of touch targets for pointer down, if needed.
1583 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
1584 TouchTarget newTouchTarget = null;
1585 boolean alreadyDispatchedToNewTouchTarget = false;
1586 if (!canceled && !intercepted) {
1587 if (actionMasked == MotionEvent.ACTION_DOWN
1588 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
1589 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
1590 final int actionIndex = ev.getActionIndex(); // always 0 for down
1591 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
1592 : TouchTarget.ALL_POINTER_IDS;
Jeff Brown20e987b2010-08-23 12:01:02 -07001593
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001594 // Clean up earlier touch targets for this pointer id in case they
1595 // have become out of sync.
1596 removePointersFromTouchTargets(idBitsToAssign);
1597
1598 final int childrenCount = mChildrenCount;
1599 if (childrenCount != 0) {
1600 // Find a child that can receive the event.
1601 // Scan children from front to back.
1602 final View[] children = mChildren;
1603 final float x = ev.getX(actionIndex);
1604 final float y = ev.getY(actionIndex);
1605
1606 for (int i = childrenCount - 1; i >= 0; i--) {
1607 final View child = children[i];
1608 if (!canViewReceivePointerEvents(child)
1609 || !isTransformedTouchPointInView(x, y, child, null)) {
1610 continue;
1611 }
1612
1613 newTouchTarget = getTouchTarget(child);
1614 if (newTouchTarget != null) {
1615 // Child is already receiving touch within its bounds.
1616 // Give it the new pointer in addition to the ones it is handling.
1617 newTouchTarget.pointerIdBits |= idBitsToAssign;
1618 break;
1619 }
1620
1621 resetCancelNextUpFlag(child);
1622 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
1623 // Child wants to receive touch within its bounds.
1624 mLastTouchDownTime = ev.getDownTime();
1625 mLastTouchDownIndex = i;
1626 mLastTouchDownX = ev.getX();
1627 mLastTouchDownY = ev.getY();
1628 newTouchTarget = addTouchTarget(child, idBitsToAssign);
1629 alreadyDispatchedToNewTouchTarget = true;
1630 break;
1631 }
1632 }
1633 }
1634
1635 if (newTouchTarget == null && mFirstTouchTarget != null) {
1636 // Did not find a child to receive the event.
1637 // Assign the pointer to the least recently added target.
1638 newTouchTarget = mFirstTouchTarget;
1639 while (newTouchTarget.next != null) {
1640 newTouchTarget = newTouchTarget.next;
1641 }
1642 newTouchTarget.pointerIdBits |= idBitsToAssign;
1643 }
1644 }
1645 }
1646
1647 // Dispatch to touch targets.
1648 if (mFirstTouchTarget == null) {
1649 // No touch targets so treat this as an ordinary view.
1650 handled = dispatchTransformedTouchEvent(ev, canceled, null,
1651 TouchTarget.ALL_POINTER_IDS);
1652 } else {
1653 // Dispatch to touch targets, excluding the new touch target if we already
1654 // dispatched to it. Cancel touch targets if necessary.
1655 TouchTarget predecessor = null;
1656 TouchTarget target = mFirstTouchTarget;
1657 while (target != null) {
1658 final TouchTarget next = target.next;
1659 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
1660 handled = true;
1661 } else {
1662 final boolean cancelChild = resetCancelNextUpFlag(target.child)
1663 || intercepted;
1664 if (dispatchTransformedTouchEvent(ev, cancelChild,
1665 target.child, target.pointerIdBits)) {
1666 handled = true;
1667 }
1668 if (cancelChild) {
1669 if (predecessor == null) {
1670 mFirstTouchTarget = next;
1671 } else {
1672 predecessor.next = next;
1673 }
1674 target.recycle();
1675 target = next;
Jeff Brown20e987b2010-08-23 12:01:02 -07001676 continue;
1677 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001678 }
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001679 predecessor = target;
1680 target = next;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001681 }
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001682 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001683
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001684 // Update list of touch targets for pointer up or cancel, if needed.
1685 if (canceled
1686 || actionMasked == MotionEvent.ACTION_UP
1687 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
1688 resetTouchState();
1689 } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
1690 final int actionIndex = ev.getActionIndex();
1691 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
1692 removePointersFromTouchTargets(idBitsToRemove);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001693 }
1694 }
Romain Guy8506ab42009-06-11 17:35:47 -07001695
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001696 if (!handled && mInputEventConsistencyVerifier != null) {
1697 mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
Jeff Brown20e987b2010-08-23 12:01:02 -07001698 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001699 return handled;
1700 }
1701
Romain Guy469b1db2010-10-05 11:49:57 -07001702 /**
1703 * Resets all touch state in preparation for a new cycle.
1704 */
1705 private void resetTouchState() {
Jeff Brown20e987b2010-08-23 12:01:02 -07001706 clearTouchTargets();
1707 resetCancelNextUpFlag(this);
1708 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
1709 }
1710
Romain Guy469b1db2010-10-05 11:49:57 -07001711 /**
1712 * Resets the cancel next up flag.
1713 * Returns true if the flag was previously set.
1714 */
1715 private boolean resetCancelNextUpFlag(View view) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001716 if ((view.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
1717 view.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
1718 return true;
Adam Cohen9b073942010-08-19 16:49:52 -07001719 }
1720 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001721 }
1722
Romain Guy469b1db2010-10-05 11:49:57 -07001723 /**
1724 * Clears all touch targets.
1725 */
1726 private void clearTouchTargets() {
Jeff Brown20e987b2010-08-23 12:01:02 -07001727 TouchTarget target = mFirstTouchTarget;
1728 if (target != null) {
1729 do {
1730 TouchTarget next = target.next;
1731 target.recycle();
1732 target = next;
1733 } while (target != null);
1734 mFirstTouchTarget = null;
1735 }
1736 }
1737
Romain Guy469b1db2010-10-05 11:49:57 -07001738 /**
1739 * Cancels and clears all touch targets.
1740 */
1741 private void cancelAndClearTouchTargets(MotionEvent event) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001742 if (mFirstTouchTarget != null) {
1743 boolean syntheticEvent = false;
1744 if (event == null) {
1745 final long now = SystemClock.uptimeMillis();
1746 event = MotionEvent.obtain(now, now,
1747 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
Jeff Brown2fdbc5a2011-06-30 12:25:54 -07001748 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
Jeff Brown20e987b2010-08-23 12:01:02 -07001749 syntheticEvent = true;
1750 }
1751
1752 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
1753 resetCancelNextUpFlag(target.child);
1754 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
1755 }
1756 clearTouchTargets();
1757
1758 if (syntheticEvent) {
1759 event.recycle();
1760 }
1761 }
1762 }
1763
Romain Guy469b1db2010-10-05 11:49:57 -07001764 /**
1765 * Gets the touch target for specified child view.
1766 * Returns null if not found.
1767 */
1768 private TouchTarget getTouchTarget(View child) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001769 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
1770 if (target.child == child) {
1771 return target;
1772 }
1773 }
1774 return null;
1775 }
1776
Romain Guy469b1db2010-10-05 11:49:57 -07001777 /**
1778 * Adds a touch target for specified child to the beginning of the list.
1779 * Assumes the target child is not already present.
1780 */
1781 private TouchTarget addTouchTarget(View child, int pointerIdBits) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001782 TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
1783 target.next = mFirstTouchTarget;
1784 mFirstTouchTarget = target;
1785 return target;
1786 }
1787
Romain Guy469b1db2010-10-05 11:49:57 -07001788 /**
1789 * Removes the pointer ids from consideration.
1790 */
1791 private void removePointersFromTouchTargets(int pointerIdBits) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001792 TouchTarget predecessor = null;
1793 TouchTarget target = mFirstTouchTarget;
1794 while (target != null) {
1795 final TouchTarget next = target.next;
1796 if ((target.pointerIdBits & pointerIdBits) != 0) {
1797 target.pointerIdBits &= ~pointerIdBits;
1798 if (target.pointerIdBits == 0) {
1799 if (predecessor == null) {
1800 mFirstTouchTarget = next;
1801 } else {
1802 predecessor.next = next;
1803 }
1804 target.recycle();
1805 target = next;
1806 continue;
1807 }
1808 }
1809 predecessor = target;
1810 target = next;
1811 }
1812 }
1813
Romain Guy469b1db2010-10-05 11:49:57 -07001814 /**
Jeff Browna032cc02011-03-07 16:56:21 -08001815 * Returns true if a child view can receive pointer events.
1816 * @hide
1817 */
1818 private static boolean canViewReceivePointerEvents(View child) {
1819 return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
1820 || child.getAnimation() != null;
1821 }
1822
1823 /**
Romain Guy469b1db2010-10-05 11:49:57 -07001824 * Returns true if a child view contains the specified point when transformed
Jeff Brown20e987b2010-08-23 12:01:02 -07001825 * into its coordinate space.
Romain Guy469b1db2010-10-05 11:49:57 -07001826 * Child must not be null.
Adam Cohena32edd42010-10-26 10:35:01 -07001827 * @hide
Romain Guy469b1db2010-10-05 11:49:57 -07001828 */
Adam Cohena32edd42010-10-26 10:35:01 -07001829 protected boolean isTransformedTouchPointInView(float x, float y, View child,
Christopher Tate2c095f32010-10-04 14:13:40 -07001830 PointF outLocalPoint) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001831 float localX = x + mScrollX - child.mLeft;
1832 float localY = y + mScrollY - child.mTop;
1833 if (! child.hasIdentityMatrix() && mAttachInfo != null) {
Adam Powell2b342f02010-08-18 18:14:13 -07001834 final float[] localXY = mAttachInfo.mTmpTransformLocation;
1835 localXY[0] = localX;
1836 localXY[1] = localY;
1837 child.getInverseMatrix().mapPoints(localXY);
1838 localX = localXY[0];
1839 localY = localXY[1];
1840 }
Christopher Tate2c095f32010-10-04 14:13:40 -07001841 final boolean isInView = child.pointInView(localX, localY);
1842 if (isInView && outLocalPoint != null) {
1843 outLocalPoint.set(localX, localY);
1844 }
1845 return isInView;
Adam Powell2b342f02010-08-18 18:14:13 -07001846 }
1847
Romain Guy469b1db2010-10-05 11:49:57 -07001848 /**
1849 * Transforms a motion event into the coordinate space of a particular child view,
Jeff Brown20e987b2010-08-23 12:01:02 -07001850 * filters out irrelevant pointer ids, and overrides its action if necessary.
Romain Guy469b1db2010-10-05 11:49:57 -07001851 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
1852 */
1853 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
Jeff Brown20e987b2010-08-23 12:01:02 -07001854 View child, int desiredPointerIdBits) {
1855 final boolean handled;
Adam Powell2b342f02010-08-18 18:14:13 -07001856
Jeff Brown20e987b2010-08-23 12:01:02 -07001857 // Canceling motions is a special case. We don't need to perform any transformations
1858 // or filtering. The important part is the action, not the contents.
1859 final int oldAction = event.getAction();
1860 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
1861 event.setAction(MotionEvent.ACTION_CANCEL);
1862 if (child == null) {
1863 handled = super.dispatchTouchEvent(event);
1864 } else {
1865 handled = child.dispatchTouchEvent(event);
1866 }
1867 event.setAction(oldAction);
1868 return handled;
1869 }
Adam Powell2b342f02010-08-18 18:14:13 -07001870
Jeff Brown20e987b2010-08-23 12:01:02 -07001871 // Calculate the number of pointers to deliver.
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07001872 final int oldPointerIdBits = event.getPointerIdBits();
1873 final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
Adam Powell2b342f02010-08-18 18:14:13 -07001874
Jeff Brown20e987b2010-08-23 12:01:02 -07001875 // If for some reason we ended up in an inconsistent state where it looks like we
1876 // might produce a motion event with no pointers in it, then drop the event.
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07001877 if (newPointerIdBits == 0) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001878 return false;
1879 }
Adam Powell2b342f02010-08-18 18:14:13 -07001880
Jeff Brown20e987b2010-08-23 12:01:02 -07001881 // If the number of pointers is the same and we don't need to perform any fancy
1882 // irreversible transformations, then we can reuse the motion event for this
1883 // dispatch as long as we are careful to revert any changes we make.
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07001884 // Otherwise we need to make a copy.
1885 final MotionEvent transformedEvent;
1886 if (newPointerIdBits == oldPointerIdBits) {
1887 if (child == null || child.hasIdentityMatrix()) {
1888 if (child == null) {
1889 handled = super.dispatchTouchEvent(event);
1890 } else {
1891 final float offsetX = mScrollX - child.mLeft;
1892 final float offsetY = mScrollY - child.mTop;
1893 event.offsetLocation(offsetX, offsetY);
Adam Powell2b342f02010-08-18 18:14:13 -07001894
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07001895 handled = child.dispatchTouchEvent(event);
Jeff Brown20e987b2010-08-23 12:01:02 -07001896
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07001897 event.offsetLocation(-offsetX, -offsetY);
1898 }
1899 return handled;
Jeff Brown20e987b2010-08-23 12:01:02 -07001900 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001901 transformedEvent = MotionEvent.obtain(event);
1902 } else {
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07001903 transformedEvent = event.split(newPointerIdBits);
Adam Powell2b342f02010-08-18 18:14:13 -07001904 }
1905
Jeff Brown20e987b2010-08-23 12:01:02 -07001906 // Perform any necessary transformations and dispatch.
1907 if (child == null) {
1908 handled = super.dispatchTouchEvent(transformedEvent);
1909 } else {
1910 final float offsetX = mScrollX - child.mLeft;
1911 final float offsetY = mScrollY - child.mTop;
1912 transformedEvent.offsetLocation(offsetX, offsetY);
1913 if (! child.hasIdentityMatrix()) {
1914 transformedEvent.transform(child.getInverseMatrix());
Adam Powell2b342f02010-08-18 18:14:13 -07001915 }
1916
Jeff Brown20e987b2010-08-23 12:01:02 -07001917 handled = child.dispatchTouchEvent(transformedEvent);
Adam Powell2b342f02010-08-18 18:14:13 -07001918 }
1919
Jeff Brown20e987b2010-08-23 12:01:02 -07001920 // Done.
1921 transformedEvent.recycle();
Adam Powell2b342f02010-08-18 18:14:13 -07001922 return handled;
1923 }
1924
Romain Guy469b1db2010-10-05 11:49:57 -07001925 /**
Adam Powell2b342f02010-08-18 18:14:13 -07001926 * Enable or disable the splitting of MotionEvents to multiple children during touch event
Jeff Brown995e7742010-12-22 16:59:36 -08001927 * dispatch. This behavior is enabled by default for applications that target an
1928 * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
Adam Powell2b342f02010-08-18 18:14:13 -07001929 *
1930 * <p>When this option is enabled MotionEvents may be split and dispatched to different child
1931 * views depending on where each pointer initially went down. This allows for user interactions
1932 * such as scrolling two panes of content independently, chording of buttons, and performing
1933 * independent gestures on different pieces of content.
1934 *
1935 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
1936 * child views. <code>false</code> to only allow one child view to be the target of
1937 * any MotionEvent received by this ViewGroup.
1938 */
1939 public void setMotionEventSplittingEnabled(boolean split) {
1940 // TODO Applications really shouldn't change this setting mid-touch event,
1941 // but perhaps this should handle that case and send ACTION_CANCELs to any child views
1942 // with gestures in progress when this is changed.
1943 if (split) {
Adam Powell2b342f02010-08-18 18:14:13 -07001944 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
1945 } else {
1946 mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
Adam Powell2b342f02010-08-18 18:14:13 -07001947 }
1948 }
1949
1950 /**
Jeff Brown995e7742010-12-22 16:59:36 -08001951 * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
Adam Powell2b342f02010-08-18 18:14:13 -07001952 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
1953 */
1954 public boolean isMotionEventSplittingEnabled() {
1955 return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
1956 }
1957
1958 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001959 * {@inheritDoc}
1960 */
1961 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
Romain Guy8506ab42009-06-11 17:35:47 -07001962
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001963 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
1964 // We're already in this state, assume our ancestors are too
1965 return;
1966 }
Romain Guy8506ab42009-06-11 17:35:47 -07001967
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001968 if (disallowIntercept) {
1969 mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
1970 } else {
1971 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
1972 }
Romain Guy8506ab42009-06-11 17:35:47 -07001973
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001974 // Pass it up to our parent
1975 if (mParent != null) {
1976 mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
1977 }
1978 }
1979
1980 /**
1981 * Implement this method to intercept all touch screen motion events. This
1982 * allows you to watch events as they are dispatched to your children, and
1983 * take ownership of the current gesture at any point.
1984 *
1985 * <p>Using this function takes some care, as it has a fairly complicated
1986 * interaction with {@link View#onTouchEvent(MotionEvent)
1987 * View.onTouchEvent(MotionEvent)}, and using it requires implementing
1988 * that method as well as this one in the correct way. Events will be
1989 * received in the following order:
1990 *
1991 * <ol>
1992 * <li> You will receive the down event here.
1993 * <li> The down event will be handled either by a child of this view
1994 * group, or given to your own onTouchEvent() method to handle; this means
1995 * you should implement onTouchEvent() to return true, so you will
1996 * continue to see the rest of the gesture (instead of looking for
1997 * a parent view to handle it). Also, by returning true from
1998 * onTouchEvent(), you will not receive any following
1999 * events in onInterceptTouchEvent() and all touch processing must
2000 * happen in onTouchEvent() like normal.
2001 * <li> For as long as you return false from this function, each following
2002 * event (up to and including the final up) will be delivered first here
2003 * and then to the target's onTouchEvent().
2004 * <li> If you return true from here, you will not receive any
2005 * following events: the target view will receive the same event but
2006 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
2007 * events will be delivered to your onTouchEvent() method and no longer
2008 * appear here.
2009 * </ol>
2010 *
2011 * @param ev The motion event being dispatched down the hierarchy.
2012 * @return Return true to steal motion events from the children and have
2013 * them dispatched to this ViewGroup through onTouchEvent().
2014 * The current target will receive an ACTION_CANCEL event, and no further
2015 * messages will be delivered here.
2016 */
2017 public boolean onInterceptTouchEvent(MotionEvent ev) {
2018 return false;
2019 }
2020
2021 /**
2022 * {@inheritDoc}
2023 *
2024 * Looks for a view to give focus to respecting the setting specified by
2025 * {@link #getDescendantFocusability()}.
2026 *
2027 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
2028 * find focus within the children of this group when appropriate.
2029 *
2030 * @see #FOCUS_BEFORE_DESCENDANTS
2031 * @see #FOCUS_AFTER_DESCENDANTS
2032 * @see #FOCUS_BLOCK_DESCENDANTS
Romain Guy02739a82011-05-16 11:43:18 -07002033 * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002034 */
2035 @Override
2036 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
2037 if (DBG) {
2038 System.out.println(this + " ViewGroup.requestFocus direction="
2039 + direction);
2040 }
2041 int descendantFocusability = getDescendantFocusability();
2042
2043 switch (descendantFocusability) {
2044 case FOCUS_BLOCK_DESCENDANTS:
2045 return super.requestFocus(direction, previouslyFocusedRect);
2046 case FOCUS_BEFORE_DESCENDANTS: {
2047 final boolean took = super.requestFocus(direction, previouslyFocusedRect);
2048 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
2049 }
2050 case FOCUS_AFTER_DESCENDANTS: {
2051 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
2052 return took ? took : super.requestFocus(direction, previouslyFocusedRect);
2053 }
2054 default:
2055 throw new IllegalStateException("descendant focusability must be "
2056 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
2057 + "but is " + descendantFocusability);
2058 }
2059 }
2060
2061 /**
2062 * Look for a descendant to call {@link View#requestFocus} on.
2063 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
2064 * when it wants to request focus within its children. Override this to
2065 * customize how your {@link ViewGroup} requests focus within its children.
2066 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
2067 * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
2068 * to give a finer grained hint about where focus is coming from. May be null
2069 * if there is no hint.
2070 * @return Whether focus was taken.
2071 */
2072 @SuppressWarnings({"ConstantConditions"})
2073 protected boolean onRequestFocusInDescendants(int direction,
2074 Rect previouslyFocusedRect) {
2075 int index;
2076 int increment;
2077 int end;
2078 int count = mChildrenCount;
2079 if ((direction & FOCUS_FORWARD) != 0) {
2080 index = 0;
2081 increment = 1;
2082 end = count;
2083 } else {
2084 index = count - 1;
2085 increment = -1;
2086 end = -1;
2087 }
2088 final View[] children = mChildren;
2089 for (int i = index; i != end; i += increment) {
2090 View child = children[i];
2091 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2092 if (child.requestFocus(direction, previouslyFocusedRect)) {
2093 return true;
2094 }
2095 }
2096 }
2097 return false;
2098 }
Chet Haase5c13d892010-10-08 08:37:55 -07002099
Romain Guya440b002010-02-24 15:57:54 -08002100 /**
2101 * {@inheritDoc}
Chet Haase5c13d892010-10-08 08:37:55 -07002102 *
Romain Guydcc490f2010-02-24 17:59:35 -08002103 * @hide
Romain Guya440b002010-02-24 15:57:54 -08002104 */
2105 @Override
2106 public void dispatchStartTemporaryDetach() {
2107 super.dispatchStartTemporaryDetach();
2108 final int count = mChildrenCount;
2109 final View[] children = mChildren;
2110 for (int i = 0; i < count; i++) {
2111 children[i].dispatchStartTemporaryDetach();
2112 }
2113 }
Chet Haase5c13d892010-10-08 08:37:55 -07002114
Romain Guya440b002010-02-24 15:57:54 -08002115 /**
2116 * {@inheritDoc}
Chet Haase5c13d892010-10-08 08:37:55 -07002117 *
Romain Guydcc490f2010-02-24 17:59:35 -08002118 * @hide
Romain Guya440b002010-02-24 15:57:54 -08002119 */
2120 @Override
2121 public void dispatchFinishTemporaryDetach() {
2122 super.dispatchFinishTemporaryDetach();
2123 final int count = mChildrenCount;
2124 final View[] children = mChildren;
2125 for (int i = 0; i < count; i++) {
2126 children[i].dispatchFinishTemporaryDetach();
2127 }
2128 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002129
2130 /**
2131 * {@inheritDoc}
2132 */
2133 @Override
2134 void dispatchAttachedToWindow(AttachInfo info, int visibility) {
2135 super.dispatchAttachedToWindow(info, visibility);
2136 visibility |= mViewFlags & VISIBILITY_MASK;
2137 final int count = mChildrenCount;
2138 final View[] children = mChildren;
2139 for (int i = 0; i < count; i++) {
2140 children[i].dispatchAttachedToWindow(info, visibility);
2141 }
2142 }
2143
svetoslavganov75986cf2009-05-14 22:28:01 -07002144 @Override
2145 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002146 // We first get a chance to populate the event.
2147 onPopulateAccessibilityEvent(event);
2148 // Let our children have a shot in populating the event.
svetoslavganov75986cf2009-05-14 22:28:01 -07002149 for (int i = 0, count = getChildCount(); i < count; i++) {
Svetoslav Ganov6179ea32011-06-28 01:12:41 -07002150 View child = getChildAt(i);
2151 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2152 boolean handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event);
2153 if (handled) {
2154 return handled;
2155 }
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002156 }
svetoslavganov75986cf2009-05-14 22:28:01 -07002157 }
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002158 return false;
svetoslavganov75986cf2009-05-14 22:28:01 -07002159 }
2160
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07002161 @Override
2162 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2163 super.onInitializeAccessibilityNodeInfo(info);
Svetoslav Ganov93b21822011-07-27 15:42:53 -07002164 // If the view is not the topmost one in the view hierarchy and it is
2165 // marked as the logical root of a view hierarchy, do not go any deeper.
2166 if ((!(getParent() instanceof ViewRootImpl)) && (mPrivateFlags & IS_ROOT_NAMESPACE) != 0) {
Svetoslav Ganoveffe10f2011-07-26 18:09:37 -07002167 return;
2168 }
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07002169 for (int i = 0, count = mChildrenCount; i < count; i++) {
2170 View child = mChildren[i];
Svetoslav Ganovea1da3d2011-06-15 17:16:02 -07002171 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2172 info.addChild(child);
2173 }
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07002174 }
2175 }
2176
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002177 /**
2178 * {@inheritDoc}
2179 */
2180 @Override
2181 void dispatchDetachedFromWindow() {
Jeff Brown20e987b2010-08-23 12:01:02 -07002182 // If we still have a touch target, we are still in the process of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002183 // dispatching motion events to a child; we need to get rid of that
2184 // child to avoid dispatching events to it after the window is torn
2185 // down. To make sure we keep the child in a consistent state, we
2186 // first send it an ACTION_CANCEL motion event.
Jeff Brown20e987b2010-08-23 12:01:02 -07002187 cancelAndClearTouchTargets(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002188
Chet Haase9c087442011-01-12 16:20:16 -08002189 // In case view is detached while transition is running
2190 mLayoutSuppressed = false;
2191
Christopher Tate86cab1b2011-01-13 20:28:55 -08002192 // Tear down our drag tracking
2193 mDragNotifiedChildren = null;
2194 if (mCurrentDrag != null) {
2195 mCurrentDrag.recycle();
2196 mCurrentDrag = null;
2197 }
2198
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002199 final int count = mChildrenCount;
2200 final View[] children = mChildren;
2201 for (int i = 0; i < count; i++) {
2202 children[i].dispatchDetachedFromWindow();
2203 }
2204 super.dispatchDetachedFromWindow();
2205 }
2206
2207 /**
2208 * {@inheritDoc}
2209 */
2210 @Override
2211 public void setPadding(int left, int top, int right, int bottom) {
2212 super.setPadding(left, top, right, bottom);
2213
Romain Guy13f35f32011-03-24 12:03:17 -07002214 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002215 mGroupFlags |= FLAG_PADDING_NOT_NULL;
2216 } else {
2217 mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
2218 }
2219 }
2220
2221 /**
2222 * {@inheritDoc}
2223 */
2224 @Override
2225 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
2226 super.dispatchSaveInstanceState(container);
2227 final int count = mChildrenCount;
2228 final View[] children = mChildren;
2229 for (int i = 0; i < count; i++) {
Dianne Hackbornb4bc78b2010-05-12 18:59:50 -07002230 View c = children[i];
2231 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2232 c.dispatchSaveInstanceState(container);
2233 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002234 }
2235 }
2236
2237 /**
Romain Guy9fc27812011-04-27 14:21:41 -07002238 * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()}
2239 * to only this view, not to its children. For use when overriding
2240 * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow
2241 * subclasses to freeze their own state but not the state of their children.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002242 *
2243 * @param container the container
2244 */
2245 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
2246 super.dispatchSaveInstanceState(container);
2247 }
2248
2249 /**
2250 * {@inheritDoc}
2251 */
2252 @Override
2253 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
2254 super.dispatchRestoreInstanceState(container);
2255 final int count = mChildrenCount;
2256 final View[] children = mChildren;
2257 for (int i = 0; i < count; i++) {
Dianne Hackbornb4bc78b2010-05-12 18:59:50 -07002258 View c = children[i];
2259 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2260 c.dispatchRestoreInstanceState(container);
2261 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002262 }
2263 }
2264
2265 /**
Romain Guy02739a82011-05-16 11:43:18 -07002266 * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)}
2267 * to only this view, not to its children. For use when overriding
2268 * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow
2269 * subclasses to thaw their own state but not the state of their children.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002270 *
2271 * @param container the container
2272 */
2273 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
2274 super.dispatchRestoreInstanceState(container);
2275 }
2276
2277 /**
2278 * Enables or disables the drawing cache for each child of this view group.
2279 *
2280 * @param enabled true to enable the cache, false to dispose of it
2281 */
2282 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
2283 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
2284 final View[] children = mChildren;
2285 final int count = mChildrenCount;
2286 for (int i = 0; i < count; i++) {
2287 children[i].setDrawingCacheEnabled(enabled);
2288 }
2289 }
2290 }
2291
2292 @Override
2293 protected void onAnimationStart() {
2294 super.onAnimationStart();
2295
2296 // When this ViewGroup's animation starts, build the cache for the children
2297 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2298 final int count = mChildrenCount;
2299 final View[] children = mChildren;
Romain Guy0d9275e2010-10-26 14:22:30 -07002300 final boolean buildCache = !isHardwareAccelerated();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002301
2302 for (int i = 0; i < count; i++) {
2303 final View child = children[i];
2304 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2305 child.setDrawingCacheEnabled(true);
Romain Guy0d9275e2010-10-26 14:22:30 -07002306 if (buildCache) {
2307 child.buildDrawingCache(true);
2308 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002309 }
2310 }
2311
2312 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2313 }
2314 }
2315
2316 @Override
2317 protected void onAnimationEnd() {
2318 super.onAnimationEnd();
2319
2320 // When this ViewGroup's animation ends, destroy the cache of the children
2321 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2322 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2323
2324 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2325 setChildrenDrawingCacheEnabled(false);
2326 }
2327 }
2328 }
2329
Romain Guy223ff5c2010-03-02 17:07:47 -08002330 @Override
2331 Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
Romain Guy65554f22010-03-22 18:58:21 -07002332 int count = mChildrenCount;
2333 int[] visibilities = null;
2334
Romain Guy223ff5c2010-03-02 17:07:47 -08002335 if (skipChildren) {
Romain Guy65554f22010-03-22 18:58:21 -07002336 visibilities = new int[count];
2337 for (int i = 0; i < count; i++) {
2338 View child = getChildAt(i);
2339 visibilities[i] = child.getVisibility();
2340 if (visibilities[i] == View.VISIBLE) {
2341 child.setVisibility(INVISIBLE);
2342 }
2343 }
Romain Guy223ff5c2010-03-02 17:07:47 -08002344 }
2345
2346 Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
Romain Guy65554f22010-03-22 18:58:21 -07002347
2348 if (skipChildren) {
2349 for (int i = 0; i < count; i++) {
2350 getChildAt(i).setVisibility(visibilities[i]);
Chet Haase5c13d892010-10-08 08:37:55 -07002351 }
Romain Guy65554f22010-03-22 18:58:21 -07002352 }
Romain Guy223ff5c2010-03-02 17:07:47 -08002353
2354 return b;
2355 }
2356
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002357 /**
2358 * {@inheritDoc}
2359 */
2360 @Override
2361 protected void dispatchDraw(Canvas canvas) {
2362 final int count = mChildrenCount;
2363 final View[] children = mChildren;
2364 int flags = mGroupFlags;
2365
2366 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
2367 final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
2368
Romain Guy0d9275e2010-10-26 14:22:30 -07002369 final boolean buildCache = !isHardwareAccelerated();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002370 for (int i = 0; i < count; i++) {
2371 final View child = children[i];
2372 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2373 final LayoutParams params = child.getLayoutParams();
2374 attachLayoutAnimationParameters(child, params, i, count);
2375 bindLayoutAnimation(child);
2376 if (cache) {
2377 child.setDrawingCacheEnabled(true);
Romain Guy0d9275e2010-10-26 14:22:30 -07002378 if (buildCache) {
2379 child.buildDrawingCache(true);
2380 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002381 }
2382 }
2383 }
2384
2385 final LayoutAnimationController controller = mLayoutAnimationController;
2386 if (controller.willOverlap()) {
2387 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
2388 }
2389
2390 controller.start();
2391
2392 mGroupFlags &= ~FLAG_RUN_ANIMATION;
2393 mGroupFlags &= ~FLAG_ANIMATION_DONE;
2394
2395 if (cache) {
2396 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2397 }
2398
2399 if (mAnimationListener != null) {
2400 mAnimationListener.onAnimationStart(controller.getAnimation());
2401 }
2402 }
2403
2404 int saveCount = 0;
2405 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
2406 if (clipToPadding) {
2407 saveCount = canvas.save();
Romain Guy8f2d94f2009-03-25 18:04:42 -07002408 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
2409 mScrollX + mRight - mLeft - mPaddingRight,
2410 mScrollY + mBottom - mTop - mPaddingBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002411
2412 }
2413
2414 // We will draw our child's animation, let's reset the flag
2415 mPrivateFlags &= ~DRAW_ANIMATION;
2416 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
2417
2418 boolean more = false;
2419 final long drawingTime = getDrawingTime();
2420
2421 if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
2422 for (int i = 0; i < count; i++) {
2423 final View child = children[i];
2424 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2425 more |= drawChild(canvas, child, drawingTime);
2426 }
2427 }
2428 } else {
2429 for (int i = 0; i < count; i++) {
2430 final View child = children[getChildDrawingOrder(count, i)];
2431 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2432 more |= drawChild(canvas, child, drawingTime);
2433 }
2434 }
2435 }
2436
2437 // Draw any disappearing views that have animations
2438 if (mDisappearingChildren != null) {
2439 final ArrayList<View> disappearingChildren = mDisappearingChildren;
2440 final int disappearingCount = disappearingChildren.size() - 1;
2441 // Go backwards -- we may delete as animations finish
2442 for (int i = disappearingCount; i >= 0; i--) {
2443 final View child = disappearingChildren.get(i);
2444 more |= drawChild(canvas, child, drawingTime);
2445 }
2446 }
2447
2448 if (clipToPadding) {
2449 canvas.restoreToCount(saveCount);
2450 }
2451
2452 // mGroupFlags might have been updated by drawChild()
2453 flags = mGroupFlags;
2454
2455 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
Romain Guy849d0a32011-02-01 17:20:48 -08002456 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002457 }
2458
2459 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
2460 mLayoutAnimationController.isDone() && !more) {
2461 // We want to erase the drawing cache and notify the listener after the
2462 // next frame is drawn because one extra invalidate() is caused by
2463 // drawChild() after the animation is over
2464 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
2465 final Runnable end = new Runnable() {
2466 public void run() {
2467 notifyAnimationListener();
2468 }
2469 };
2470 post(end);
2471 }
2472 }
Romain Guy8506ab42009-06-11 17:35:47 -07002473
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002474 /**
2475 * Returns the index of the child to draw for this iteration. Override this
2476 * if you want to change the drawing order of children. By default, it
2477 * returns i.
2478 * <p>
Romain Guy293451e2009-11-04 13:59:48 -08002479 * NOTE: In order for this method to be called, you must enable child ordering
2480 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
Romain Guy8506ab42009-06-11 17:35:47 -07002481 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002482 * @param i The current iteration.
2483 * @return The index of the child to draw this iteration.
Chet Haase5c13d892010-10-08 08:37:55 -07002484 *
Romain Guy293451e2009-11-04 13:59:48 -08002485 * @see #setChildrenDrawingOrderEnabled(boolean)
2486 * @see #isChildrenDrawingOrderEnabled()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002487 */
2488 protected int getChildDrawingOrder(int childCount, int i) {
2489 return i;
2490 }
Romain Guy8506ab42009-06-11 17:35:47 -07002491
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002492 private void notifyAnimationListener() {
2493 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
2494 mGroupFlags |= FLAG_ANIMATION_DONE;
2495
2496 if (mAnimationListener != null) {
2497 final Runnable end = new Runnable() {
2498 public void run() {
2499 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
2500 }
2501 };
2502 post(end);
2503 }
2504
2505 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2506 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2507 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2508 setChildrenDrawingCacheEnabled(false);
2509 }
2510 }
2511
Romain Guy849d0a32011-02-01 17:20:48 -08002512 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002513 }
2514
2515 /**
Chet Haasedaf98e92011-01-10 14:10:36 -08002516 * This method is used to cause children of this ViewGroup to restore or recreate their
2517 * display lists. It is called by getDisplayList() when the parent ViewGroup does not need
2518 * to recreate its own display list, which would happen if it went through the normal
2519 * draw/dispatchDraw mechanisms.
2520 *
2521 * @hide
2522 */
2523 @Override
2524 protected void dispatchGetDisplayList() {
2525 final int count = mChildrenCount;
2526 final View[] children = mChildren;
2527 for (int i = 0; i < count; i++) {
2528 final View child = children[i];
Romain Guy2f57ba52011-02-03 18:03:29 -08002529 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2530 child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED;
2531 child.mPrivateFlags &= ~INVALIDATED;
2532 child.getDisplayList();
2533 child.mRecreateDisplayList = false;
2534 }
Chet Haasedaf98e92011-01-10 14:10:36 -08002535 }
2536 }
2537
2538 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002539 * Draw one child of this View Group. This method is responsible for getting
2540 * the canvas in the right state. This includes clipping, translating so
2541 * that the child's scrolled origin is at 0, 0, and applying any animation
2542 * transformations.
2543 *
2544 * @param canvas The canvas on which to draw the child
2545 * @param child Who to draw
2546 * @param drawingTime The time at which draw is occuring
2547 * @return True if an invalidate() was issued
2548 */
2549 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
2550 boolean more = false;
2551
2552 final int cl = child.mLeft;
2553 final int ct = child.mTop;
2554 final int cr = child.mRight;
2555 final int cb = child.mBottom;
2556
Chet Haase70d4ba12010-10-06 09:46:45 -07002557 final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
2558
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002559 final int flags = mGroupFlags;
2560
2561 if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
Chet Haase48460322010-06-11 14:22:25 -07002562 mChildTransformation.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002563 mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION;
2564 }
2565
2566 Transformation transformToApply = null;
Chet Haase48460322010-06-11 14:22:25 -07002567 Transformation invalidationTransform;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002568 final Animation a = child.getAnimation();
2569 boolean concatMatrix = false;
2570
Chet Haase48460322010-06-11 14:22:25 -07002571 boolean scalingRequired = false;
Romain Guy171c5922011-01-06 10:04:23 -08002572 boolean caching;
Romain Guy849d0a32011-02-01 17:20:48 -08002573 int layerType = mDrawLayers ? child.getLayerType() : LAYER_TYPE_NONE;
Chet Haasee38ba4a2011-01-27 01:10:35 -08002574
2575 final boolean hardwareAccelerated = canvas.isHardwareAccelerated();
Romain Guyb051e892010-09-28 19:09:36 -07002576 if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
Chet Haase48460322010-06-11 14:22:25 -07002577 (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
2578 caching = true;
2579 if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
Romain Guy171c5922011-01-06 10:04:23 -08002580 } else {
Chet Haasee38ba4a2011-01-27 01:10:35 -08002581 caching = (layerType != LAYER_TYPE_NONE) || hardwareAccelerated;
Chet Haase48460322010-06-11 14:22:25 -07002582 }
2583
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002584 if (a != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002585 final boolean initialized = a.isInitialized();
2586 if (!initialized) {
Romain Guy8f2d94f2009-03-25 18:04:42 -07002587 a.initialize(cr - cl, cb - ct, getWidth(), getHeight());
2588 a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002589 child.onAnimationStart();
2590 }
2591
Chet Haase48460322010-06-11 14:22:25 -07002592 more = a.getTransformation(drawingTime, mChildTransformation,
2593 scalingRequired ? mAttachInfo.mApplicationScale : 1f);
2594 if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
2595 if (mInvalidationTransformation == null) {
2596 mInvalidationTransformation = new Transformation();
2597 }
2598 invalidationTransform = mInvalidationTransformation;
2599 a.getTransformation(drawingTime, invalidationTransform, 1f);
2600 } else {
2601 invalidationTransform = mChildTransformation;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002602 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002603 transformToApply = mChildTransformation;
2604
2605 concatMatrix = a.willChangeTransformationMatrix();
2606
2607 if (more) {
2608 if (!a.willChangeBounds()) {
2609 if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) ==
2610 FLAG_OPTIMIZE_INVALIDATE) {
2611 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
2612 } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) {
2613 // The child need to draw an animation, potentially offscreen, so
2614 // make sure we do not cancel invalidate requests
2615 mPrivateFlags |= DRAW_ANIMATION;
2616 invalidate(cl, ct, cr, cb);
2617 }
2618 } else {
Chet Haase48460322010-06-11 14:22:25 -07002619 if (mInvalidateRegion == null) {
2620 mInvalidateRegion = new RectF();
2621 }
2622 final RectF region = mInvalidateRegion;
2623 a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, invalidationTransform);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002624
2625 // The child need to draw an animation, potentially offscreen, so
2626 // make sure we do not cancel invalidate requests
2627 mPrivateFlags |= DRAW_ANIMATION;
The Android Open Source Project4df24232009-03-05 14:34:35 -08002628
2629 final int left = cl + (int) region.left;
2630 final int top = ct + (int) region.top;
Chet Haase530b22a2011-08-23 14:36:55 -07002631 invalidate(left, top, left + (int) (region.width() + .5f),
2632 top + (int) (region.height() + .5f));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002633 }
2634 }
2635 } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
2636 FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002637 final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation);
2638 if (hasTransform) {
2639 final int transformType = mChildTransformation.getTransformationType();
2640 transformToApply = transformType != Transformation.TYPE_IDENTITY ?
2641 mChildTransformation : null;
2642 concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
2643 }
2644 }
2645
Chet Haase70d4ba12010-10-06 09:46:45 -07002646 concatMatrix |= !childHasIdentityMatrix;
Chet Haasedf030d22010-07-30 17:22:38 -07002647
Romain Guy5bcdff42009-05-14 21:27:18 -07002648 // Sets the flag as early as possible to allow draw() implementations
Romain Guy986003d2009-03-25 17:42:35 -07002649 // to call invalidate() successfully when doing animations
Romain Guy5bcdff42009-05-14 21:27:18 -07002650 child.mPrivateFlags |= DRAWN;
Romain Guy986003d2009-03-25 17:42:35 -07002651
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002652 if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
2653 (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
2654 return more;
2655 }
Chet Haase5c13d892010-10-08 08:37:55 -07002656
Romain Guydbc26d22010-10-11 17:58:29 -07002657 float alpha = child.getAlpha();
2658 // Bail out early if the view does not need to be drawn
Romain Guy67a2f7b2010-10-13 15:35:22 -07002659 if (alpha <= ViewConfiguration.ALPHA_THRESHOLD && (child.mPrivateFlags & ALPHA_SET) == 0 &&
2660 !(child instanceof SurfaceView)) {
Romain Guya3496a92010-10-12 11:53:24 -07002661 return more;
2662 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002663
Chet Haasee38ba4a2011-01-27 01:10:35 -08002664 if (hardwareAccelerated) {
Chet Haasedaf98e92011-01-10 14:10:36 -08002665 // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
2666 // retain the flag's value temporarily in the mRecreateDisplayList flag
2667 child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED;
2668 child.mPrivateFlags &= ~INVALIDATED;
2669 }
2670
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002671 child.computeScroll();
2672
2673 final int sx = child.mScrollX;
2674 final int sy = child.mScrollY;
2675
Romain Guyb051e892010-09-28 19:09:36 -07002676 DisplayList displayList = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002677 Bitmap cache = null;
Chet Haase678e0ad2011-01-25 09:37:18 -08002678 boolean hasDisplayList = false;
Chet Haase48460322010-06-11 14:22:25 -07002679 if (caching) {
Chet Haasee38ba4a2011-01-27 01:10:35 -08002680 if (!hardwareAccelerated) {
Romain Guy171c5922011-01-06 10:04:23 -08002681 if (layerType != LAYER_TYPE_NONE) {
Romain Guy6c319ca2011-01-11 14:29:25 -08002682 layerType = LAYER_TYPE_SOFTWARE;
Romain Guy171c5922011-01-06 10:04:23 -08002683 child.buildDrawingCache(true);
2684 }
Romain Guyb051e892010-09-28 19:09:36 -07002685 cache = child.getDrawingCache(true);
2686 } else {
Romain Guyd643bb52011-03-01 14:55:21 -08002687 switch (layerType) {
2688 case LAYER_TYPE_SOFTWARE:
2689 child.buildDrawingCache(true);
2690 cache = child.getDrawingCache(true);
2691 break;
2692 case LAYER_TYPE_NONE:
2693 // Delay getting the display list until animation-driven alpha values are
2694 // set up and possibly passed on to the view
2695 hasDisplayList = child.canHaveDisplayList();
2696 break;
Romain Guy171c5922011-01-06 10:04:23 -08002697 }
Romain Guyb051e892010-09-28 19:09:36 -07002698 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002699 }
2700
Romain Guyb051e892010-09-28 19:09:36 -07002701 final boolean hasNoCache = cache == null || hasDisplayList;
Romain Guyd643bb52011-03-01 14:55:21 -08002702 final boolean offsetForScroll = cache == null && !hasDisplayList &&
2703 layerType != LAYER_TYPE_HARDWARE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002704
2705 final int restoreTo = canvas.save();
Romain Guyd643bb52011-03-01 14:55:21 -08002706 if (offsetForScroll) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002707 canvas.translate(cl - sx, ct - sy);
2708 } else {
2709 canvas.translate(cl, ct);
Romain Guy8506ab42009-06-11 17:35:47 -07002710 if (scalingRequired) {
Romain Guycafdea62009-06-12 10:51:36 -07002711 // mAttachInfo cannot be null, otherwise scalingRequired == false
Romain Guy8506ab42009-06-11 17:35:47 -07002712 final float scale = 1.0f / mAttachInfo.mApplicationScale;
2713 canvas.scale(scale, scale);
2714 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002715 }
2716
Romain Guy33e72ae2010-07-17 12:40:29 -07002717 if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
Chet Haase70d4ba12010-10-06 09:46:45 -07002718 if (transformToApply != null || !childHasIdentityMatrix) {
2719 int transX = 0;
2720 int transY = 0;
Romain Guy33e72ae2010-07-17 12:40:29 -07002721
Romain Guyd643bb52011-03-01 14:55:21 -08002722 if (offsetForScroll) {
Chet Haase70d4ba12010-10-06 09:46:45 -07002723 transX = -sx;
2724 transY = -sy;
2725 }
Romain Guy33e72ae2010-07-17 12:40:29 -07002726
Chet Haase70d4ba12010-10-06 09:46:45 -07002727 if (transformToApply != null) {
2728 if (concatMatrix) {
2729 // Undo the scroll translation, apply the transformation matrix,
2730 // then redo the scroll translate to get the correct result.
2731 canvas.translate(-transX, -transY);
2732 canvas.concat(transformToApply.getMatrix());
2733 canvas.translate(transX, transY);
2734 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
2735 }
2736
2737 float transformAlpha = transformToApply.getAlpha();
2738 if (transformAlpha < 1.0f) {
2739 alpha *= transformToApply.getAlpha();
2740 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
2741 }
2742 }
2743
2744 if (!childHasIdentityMatrix) {
Chet Haasec3aa3612010-06-17 08:50:37 -07002745 canvas.translate(-transX, -transY);
Chet Haase70d4ba12010-10-06 09:46:45 -07002746 canvas.concat(child.getMatrix());
Chet Haasec3aa3612010-06-17 08:50:37 -07002747 canvas.translate(transX, transY);
Chet Haasec3aa3612010-06-17 08:50:37 -07002748 }
Chet Haasec3aa3612010-06-17 08:50:37 -07002749 }
Romain Guy33e72ae2010-07-17 12:40:29 -07002750
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002751 if (alpha < 1.0f) {
2752 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
Romain Guy33e72ae2010-07-17 12:40:29 -07002753 if (hasNoCache) {
2754 final int multipliedAlpha = (int) (255 * alpha);
2755 if (!child.onSetAlpha(multipliedAlpha)) {
Romain Guyfd880422010-09-23 16:16:04 -07002756 int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
Romain Guy171c5922011-01-06 10:04:23 -08002757 if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN ||
2758 layerType != LAYER_TYPE_NONE) {
Romain Guyfd880422010-09-23 16:16:04 -07002759 layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
2760 }
Romain Guy54229ee2011-02-01 13:05:16 -08002761 if (layerType == LAYER_TYPE_NONE) {
Romain Guya7dabcd2011-03-01 15:44:21 -08002762 final int scrollX = hasDisplayList ? 0 : sx;
2763 final int scrollY = hasDisplayList ? 0 : sy;
2764 canvas.saveLayerAlpha(scrollX, scrollY, scrollX + cr - cl,
2765 scrollY + cb - ct, multipliedAlpha, layerFlags);
Romain Guy171c5922011-01-06 10:04:23 -08002766 }
Romain Guy33e72ae2010-07-17 12:40:29 -07002767 } else {
Romain Guy171c5922011-01-06 10:04:23 -08002768 // Alpha is handled by the child directly, clobber the layer's alpha
Romain Guy33e72ae2010-07-17 12:40:29 -07002769 child.mPrivateFlags |= ALPHA_SET;
2770 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002771 }
2772 }
2773 } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
2774 child.onSetAlpha(255);
Romain Guy9b34d452010-09-02 11:45:04 -07002775 child.mPrivateFlags &= ~ALPHA_SET;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002776 }
2777
2778 if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
Romain Guyd643bb52011-03-01 14:55:21 -08002779 if (offsetForScroll) {
Romain Guy8f2d94f2009-03-25 18:04:42 -07002780 canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002781 } else {
Chet Haasedaf98e92011-01-10 14:10:36 -08002782 if (!scalingRequired || cache == null) {
Romain Guy8506ab42009-06-11 17:35:47 -07002783 canvas.clipRect(0, 0, cr - cl, cb - ct);
2784 } else {
2785 canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
2786 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002787 }
2788 }
2789
Chet Haase678e0ad2011-01-25 09:37:18 -08002790 if (hasDisplayList) {
2791 displayList = child.getDisplayList();
2792 }
2793
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002794 if (hasNoCache) {
Romain Guy6c319ca2011-01-11 14:29:25 -08002795 boolean layerRendered = false;
Romain Guyd6cd5722011-01-17 14:42:41 -08002796 if (layerType == LAYER_TYPE_HARDWARE) {
Romain Guy5e7f7662011-01-24 22:35:56 -08002797 final HardwareLayer layer = child.getHardwareLayer();
Romain Guy6c319ca2011-01-11 14:29:25 -08002798 if (layer != null && layer.isValid()) {
Romain Guy54229ee2011-02-01 13:05:16 -08002799 child.mLayerPaint.setAlpha((int) (alpha * 255));
Romain Guyada830f2011-01-13 12:13:20 -08002800 ((HardwareCanvas) canvas).drawHardwareLayer(layer, 0, 0, child.mLayerPaint);
Romain Guy6c319ca2011-01-11 14:29:25 -08002801 layerRendered = true;
Romain Guyb051e892010-09-28 19:09:36 -07002802 } else {
Romain Guya7dabcd2011-03-01 15:44:21 -08002803 final int scrollX = hasDisplayList ? 0 : sx;
2804 final int scrollY = hasDisplayList ? 0 : sy;
2805 canvas.saveLayer(scrollX, scrollY,
2806 scrollX + cr - cl, scrollY + cb - ct, child.mLayerPaint,
Romain Guy6c319ca2011-01-11 14:29:25 -08002807 Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002808 }
Romain Guy6c319ca2011-01-11 14:29:25 -08002809 }
2810
2811 if (!layerRendered) {
2812 if (!hasDisplayList) {
2813 // Fast path for layouts with no backgrounds
2814 if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
2815 if (ViewDebug.TRACE_HIERARCHY) {
2816 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
2817 }
2818 child.mPrivateFlags &= ~DIRTY_MASK;
2819 child.dispatchDraw(canvas);
2820 } else {
2821 child.draw(canvas);
2822 }
2823 } else {
2824 child.mPrivateFlags &= ~DIRTY_MASK;
Romain Guy7b5b6ab2011-03-14 18:05:08 -07002825 ((HardwareCanvas) canvas).drawDisplayList(displayList, cr - cl, cb - ct, null);
Romain Guy6c319ca2011-01-11 14:29:25 -08002826 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002827 }
Romain Guyb051e892010-09-28 19:09:36 -07002828 } else if (cache != null) {
Chet Haasef2f7d8f2010-12-03 14:08:14 -08002829 child.mPrivateFlags &= ~DIRTY_MASK;
Romain Guy171c5922011-01-06 10:04:23 -08002830 Paint cachePaint;
2831
Romain Guyd6cd5722011-01-17 14:42:41 -08002832 if (layerType == LAYER_TYPE_NONE) {
Romain Guy171c5922011-01-06 10:04:23 -08002833 cachePaint = mCachePaint;
2834 if (alpha < 1.0f) {
2835 cachePaint.setAlpha((int) (alpha * 255));
2836 mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
2837 } else if ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
2838 cachePaint.setAlpha(255);
2839 mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
2840 }
2841 } else {
2842 cachePaint = child.mLayerPaint;
Romain Guyd6cd5722011-01-17 14:42:41 -08002843 cachePaint.setAlpha((int) (alpha * 255));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002844 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002845 canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
2846 }
2847
2848 canvas.restoreToCount(restoreTo);
2849
2850 if (a != null && !more) {
Chet Haasee38ba4a2011-01-27 01:10:35 -08002851 if (!hardwareAccelerated && !a.getFillAfter()) {
Chet Haase678e0ad2011-01-25 09:37:18 -08002852 child.onSetAlpha(255);
2853 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002854 finishAnimatingView(child, a);
2855 }
2856
Chet Haasee38ba4a2011-01-27 01:10:35 -08002857 if (more && hardwareAccelerated) {
Chet Haasedaf98e92011-01-10 14:10:36 -08002858 // invalidation is the trigger to recreate display lists, so if we're using
2859 // display lists to render, force an invalidate to allow the animation to
2860 // continue drawing another frame
Romain Guy849d0a32011-02-01 17:20:48 -08002861 invalidate(true);
Romain Guy29d23ec2011-07-25 14:42:24 -07002862 if (a.hasAlpha() && (child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
Chet Haase678e0ad2011-01-25 09:37:18 -08002863 // alpha animations should cause the child to recreate its display list
Romain Guy849d0a32011-02-01 17:20:48 -08002864 child.invalidate(true);
Chet Haase678e0ad2011-01-25 09:37:18 -08002865 }
Chet Haasedaf98e92011-01-10 14:10:36 -08002866 }
2867
2868 child.mRecreateDisplayList = false;
2869
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002870 return more;
2871 }
2872
2873 /**
Romain Guy849d0a32011-02-01 17:20:48 -08002874 *
2875 * @param enabled True if children should be drawn with layers, false otherwise.
2876 *
2877 * @hide
2878 */
2879 public void setChildrenLayersEnabled(boolean enabled) {
Romain Guy9d18f2d2011-02-03 11:25:51 -08002880 if (enabled != mDrawLayers) {
2881 mDrawLayers = enabled;
2882 invalidate(true);
2883
2884 // We need to invalidate any child with a layer. For instance,
2885 // if a child is backed by a hardware layer and we disable layers
2886 // the child is marked as not dirty (flags cleared the last time
2887 // the child was drawn inside its layer.) However, that child might
2888 // never have created its own display list or have an obsolete
2889 // display list. By invalidating the child we ensure the display
2890 // list is in sync with the content of the hardware layer.
2891 for (int i = 0; i < mChildrenCount; i++) {
2892 View child = mChildren[i];
2893 if (child.mLayerType != LAYER_TYPE_NONE) {
2894 child.invalidate(true);
2895 }
2896 }
2897 }
Romain Guy849d0a32011-02-01 17:20:48 -08002898 }
2899
2900 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002901 * By default, children are clipped to their bounds before drawing. This
2902 * allows view groups to override this behavior for animations, etc.
2903 *
2904 * @param clipChildren true to clip children to their bounds,
2905 * false otherwise
2906 * @attr ref android.R.styleable#ViewGroup_clipChildren
2907 */
2908 public void setClipChildren(boolean clipChildren) {
2909 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
2910 }
2911
2912 /**
2913 * By default, children are clipped to the padding of the ViewGroup. This
2914 * allows view groups to override this behavior
2915 *
2916 * @param clipToPadding true to clip children to the padding of the
2917 * group, false otherwise
2918 * @attr ref android.R.styleable#ViewGroup_clipToPadding
2919 */
2920 public void setClipToPadding(boolean clipToPadding) {
2921 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
2922 }
2923
2924 /**
2925 * {@inheritDoc}
2926 */
2927 @Override
2928 public void dispatchSetSelected(boolean selected) {
2929 final View[] children = mChildren;
2930 final int count = mChildrenCount;
2931 for (int i = 0; i < count; i++) {
2932 children[i].setSelected(selected);
2933 }
2934 }
Romain Guy8506ab42009-06-11 17:35:47 -07002935
Dianne Hackbornd0fa3712010-09-14 18:57:14 -07002936 /**
2937 * {@inheritDoc}
2938 */
2939 @Override
2940 public void dispatchSetActivated(boolean activated) {
2941 final View[] children = mChildren;
2942 final int count = mChildrenCount;
2943 for (int i = 0; i < count; i++) {
Dianne Hackbornd0fa3712010-09-14 18:57:14 -07002944 children[i].setActivated(activated);
2945 }
2946 }
2947
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002948 @Override
2949 protected void dispatchSetPressed(boolean pressed) {
2950 final View[] children = mChildren;
2951 final int count = mChildrenCount;
2952 for (int i = 0; i < count; i++) {
2953 children[i].setPressed(pressed);
2954 }
2955 }
2956
2957 /**
2958 * When this property is set to true, this ViewGroup supports static transformations on
2959 * children; this causes
2960 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
2961 * invoked when a child is drawn.
2962 *
2963 * Any subclass overriding
2964 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
2965 * set this property to true.
2966 *
2967 * @param enabled True to enable static transformations on children, false otherwise.
2968 *
2969 * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS
2970 */
2971 protected void setStaticTransformationsEnabled(boolean enabled) {
2972 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
2973 }
2974
2975 /**
2976 * {@inheritDoc}
2977 *
Romain Guy8506ab42009-06-11 17:35:47 -07002978 * @see #setStaticTransformationsEnabled(boolean)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002979 */
2980 protected boolean getChildStaticTransformation(View child, Transformation t) {
2981 return false;
2982 }
2983
2984 /**
2985 * {@hide}
2986 */
2987 @Override
2988 protected View findViewTraversal(int id) {
2989 if (id == mID) {
2990 return this;
2991 }
2992
2993 final View[] where = mChildren;
2994 final int len = mChildrenCount;
2995
2996 for (int i = 0; i < len; i++) {
2997 View v = where[i];
2998
2999 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
3000 v = v.findViewById(id);
3001
3002 if (v != null) {
3003 return v;
3004 }
3005 }
3006 }
3007
3008 return null;
3009 }
3010
3011 /**
3012 * {@hide}
3013 */
3014 @Override
3015 protected View findViewWithTagTraversal(Object tag) {
3016 if (tag != null && tag.equals(mTag)) {
3017 return this;
3018 }
3019
3020 final View[] where = mChildren;
3021 final int len = mChildrenCount;
3022
3023 for (int i = 0; i < len; i++) {
3024 View v = where[i];
3025
3026 if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
3027 v = v.findViewWithTag(tag);
3028
3029 if (v != null) {
3030 return v;
3031 }
3032 }
3033 }
3034
3035 return null;
3036 }
3037
3038 /**
Jeff Brown4e6319b2010-12-13 10:36:51 -08003039 * {@hide}
3040 */
3041 @Override
Jeff Brown4dfbec22011-08-15 14:55:37 -07003042 protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
Jeff Brown4e6319b2010-12-13 10:36:51 -08003043 if (predicate.apply(this)) {
3044 return this;
3045 }
3046
3047 final View[] where = mChildren;
3048 final int len = mChildrenCount;
3049
3050 for (int i = 0; i < len; i++) {
3051 View v = where[i];
3052
Jeff Brown4dfbec22011-08-15 14:55:37 -07003053 if (v != childToSkip && (v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
Jeff Brown4e6319b2010-12-13 10:36:51 -08003054 v = v.findViewByPredicate(predicate);
3055
3056 if (v != null) {
3057 return v;
3058 }
3059 }
3060 }
3061
3062 return null;
3063 }
3064
3065 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003066 * Adds a child view. If no layout parameters are already set on the child, the
3067 * default parameters for this ViewGroup are set on the child.
3068 *
3069 * @param child the child view to add
3070 *
3071 * @see #generateDefaultLayoutParams()
3072 */
3073 public void addView(View child) {
3074 addView(child, -1);
3075 }
3076
3077 /**
3078 * Adds a child view. If no layout parameters are already set on the child, the
3079 * default parameters for this ViewGroup are set on the child.
3080 *
3081 * @param child the child view to add
3082 * @param index the position at which to add the child
3083 *
3084 * @see #generateDefaultLayoutParams()
3085 */
3086 public void addView(View child, int index) {
3087 LayoutParams params = child.getLayoutParams();
3088 if (params == null) {
3089 params = generateDefaultLayoutParams();
3090 if (params == null) {
3091 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
3092 }
3093 }
3094 addView(child, index, params);
3095 }
3096
3097 /**
3098 * Adds a child view with this ViewGroup's default layout parameters and the
3099 * specified width and height.
3100 *
3101 * @param child the child view to add
3102 */
3103 public void addView(View child, int width, int height) {
3104 final LayoutParams params = generateDefaultLayoutParams();
3105 params.width = width;
3106 params.height = height;
3107 addView(child, -1, params);
3108 }
3109
3110 /**
3111 * Adds a child view with the specified layout parameters.
3112 *
3113 * @param child the child view to add
3114 * @param params the layout parameters to set on the child
3115 */
3116 public void addView(View child, LayoutParams params) {
3117 addView(child, -1, params);
3118 }
3119
3120 /**
3121 * Adds a child view with the specified layout parameters.
3122 *
3123 * @param child the child view to add
3124 * @param index the position at which to add the child
3125 * @param params the layout parameters to set on the child
3126 */
3127 public void addView(View child, int index, LayoutParams params) {
3128 if (DBG) {
3129 System.out.println(this + " addView");
3130 }
3131
3132 // addViewInner() will call child.requestLayout() when setting the new LayoutParams
3133 // therefore, we call requestLayout() on ourselves before, so that the child's request
3134 // will be blocked at our level
3135 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003136 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003137 addViewInner(child, index, params, false);
3138 }
3139
3140 /**
3141 * {@inheritDoc}
3142 */
3143 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
3144 if (!checkLayoutParams(params)) {
3145 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
3146 }
3147 if (view.mParent != this) {
3148 throw new IllegalArgumentException("Given view not a child of " + this);
3149 }
3150 view.setLayoutParams(params);
3151 }
3152
3153 /**
3154 * {@inheritDoc}
3155 */
3156 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3157 return p != null;
3158 }
3159
3160 /**
3161 * Interface definition for a callback to be invoked when the hierarchy
3162 * within this view changed. The hierarchy changes whenever a child is added
3163 * to or removed from this view.
3164 */
3165 public interface OnHierarchyChangeListener {
3166 /**
3167 * Called when a new child is added to a parent view.
3168 *
3169 * @param parent the view in which a child was added
3170 * @param child the new child view added in the hierarchy
3171 */
3172 void onChildViewAdded(View parent, View child);
3173
3174 /**
3175 * Called when a child is removed from a parent view.
3176 *
3177 * @param parent the view from which the child was removed
3178 * @param child the child removed from the hierarchy
3179 */
3180 void onChildViewRemoved(View parent, View child);
3181 }
3182
3183 /**
3184 * Register a callback to be invoked when a child is added to or removed
3185 * from this view.
3186 *
3187 * @param listener the callback to invoke on hierarchy change
3188 */
3189 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
3190 mOnHierarchyChangeListener = listener;
3191 }
3192
3193 /**
Philip Milnef51d91c2011-07-18 16:12:19 -07003194 * @hide
3195 */
3196 protected void onViewAdded(View child) {
3197 if (mOnHierarchyChangeListener != null) {
3198 mOnHierarchyChangeListener.onChildViewAdded(this, child);
3199 }
3200 }
3201
3202 /**
3203 * @hide
3204 */
3205 protected void onViewRemoved(View child) {
3206 if (mOnHierarchyChangeListener != null) {
3207 mOnHierarchyChangeListener.onChildViewRemoved(this, child);
3208 }
3209 }
3210
3211 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003212 * Adds a view during layout. This is useful if in your onLayout() method,
3213 * you need to add more views (as does the list view for example).
3214 *
3215 * If index is negative, it means put it at the end of the list.
3216 *
3217 * @param child the view to add to the group
3218 * @param index the index at which the child must be added
3219 * @param params the layout parameters to associate with the child
3220 * @return true if the child was added, false otherwise
3221 */
3222 protected boolean addViewInLayout(View child, int index, LayoutParams params) {
3223 return addViewInLayout(child, index, params, false);
3224 }
3225
3226 /**
3227 * Adds a view during layout. This is useful if in your onLayout() method,
3228 * you need to add more views (as does the list view for example).
3229 *
3230 * If index is negative, it means put it at the end of the list.
3231 *
3232 * @param child the view to add to the group
3233 * @param index the index at which the child must be added
3234 * @param params the layout parameters to associate with the child
3235 * @param preventRequestLayout if true, calling this method will not trigger a
3236 * layout request on child
3237 * @return true if the child was added, false otherwise
3238 */
3239 protected boolean addViewInLayout(View child, int index, LayoutParams params,
3240 boolean preventRequestLayout) {
3241 child.mParent = null;
3242 addViewInner(child, index, params, preventRequestLayout);
Romain Guy24443ea2009-05-11 11:56:30 -07003243 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003244 return true;
3245 }
3246
3247 /**
3248 * Prevents the specified child to be laid out during the next layout pass.
3249 *
3250 * @param child the child on which to perform the cleanup
3251 */
3252 protected void cleanupLayoutState(View child) {
3253 child.mPrivateFlags &= ~View.FORCE_LAYOUT;
3254 }
3255
3256 private void addViewInner(View child, int index, LayoutParams params,
3257 boolean preventRequestLayout) {
3258
Chet Haasee8e45d32011-03-02 17:07:35 -08003259 if (mTransition != null) {
3260 // Don't prevent other add transitions from completing, but cancel remove
3261 // transitions to let them complete the process before we add to the container
3262 mTransition.cancel(LayoutTransition.DISAPPEARING);
Chet Haaseadd65772011-02-09 16:47:29 -08003263 }
3264
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003265 if (child.getParent() != null) {
3266 throw new IllegalStateException("The specified child already has a parent. " +
3267 "You must call removeView() on the child's parent first.");
3268 }
3269
Chet Haase21cd1382010-09-01 17:42:29 -07003270 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003271 mTransition.addChild(this, child);
Chet Haase21cd1382010-09-01 17:42:29 -07003272 }
3273
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003274 if (!checkLayoutParams(params)) {
3275 params = generateLayoutParams(params);
3276 }
3277
3278 if (preventRequestLayout) {
3279 child.mLayoutParams = params;
3280 } else {
3281 child.setLayoutParams(params);
3282 }
3283
3284 if (index < 0) {
3285 index = mChildrenCount;
3286 }
3287
3288 addInArray(child, index);
3289
3290 // tell our children
3291 if (preventRequestLayout) {
3292 child.assignParent(this);
3293 } else {
3294 child.mParent = this;
3295 }
3296
3297 if (child.hasFocus()) {
3298 requestChildFocus(child, child.findFocus());
3299 }
Romain Guy8506ab42009-06-11 17:35:47 -07003300
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003301 AttachInfo ai = mAttachInfo;
3302 if (ai != null) {
Romain Guy8506ab42009-06-11 17:35:47 -07003303 boolean lastKeepOn = ai.mKeepScreenOn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003304 ai.mKeepScreenOn = false;
3305 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
3306 if (ai.mKeepScreenOn) {
3307 needGlobalAttributesUpdate(true);
3308 }
3309 ai.mKeepScreenOn = lastKeepOn;
3310 }
3311
Philip Milnef51d91c2011-07-18 16:12:19 -07003312 onViewAdded(child);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003313
3314 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
3315 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
3316 }
3317 }
3318
3319 private void addInArray(View child, int index) {
3320 View[] children = mChildren;
3321 final int count = mChildrenCount;
3322 final int size = children.length;
3323 if (index == count) {
3324 if (size == count) {
3325 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3326 System.arraycopy(children, 0, mChildren, 0, size);
3327 children = mChildren;
3328 }
3329 children[mChildrenCount++] = child;
3330 } else if (index < count) {
3331 if (size == count) {
3332 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3333 System.arraycopy(children, 0, mChildren, 0, index);
3334 System.arraycopy(children, index, mChildren, index + 1, count - index);
3335 children = mChildren;
3336 } else {
3337 System.arraycopy(children, index, children, index + 1, count - index);
3338 }
3339 children[index] = child;
3340 mChildrenCount++;
Joe Onorato03ab0c72011-01-06 15:46:27 -08003341 if (mLastTouchDownIndex >= index) {
3342 mLastTouchDownIndex++;
3343 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003344 } else {
3345 throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
3346 }
3347 }
3348
3349 // This method also sets the child's mParent to null
3350 private void removeFromArray(int index) {
3351 final View[] children = mChildren;
Chet Haase21cd1382010-09-01 17:42:29 -07003352 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
3353 children[index].mParent = null;
3354 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003355 final int count = mChildrenCount;
3356 if (index == count - 1) {
3357 children[--mChildrenCount] = null;
3358 } else if (index >= 0 && index < count) {
3359 System.arraycopy(children, index + 1, children, index, count - index - 1);
3360 children[--mChildrenCount] = null;
3361 } else {
3362 throw new IndexOutOfBoundsException();
3363 }
Joe Onorato03ab0c72011-01-06 15:46:27 -08003364 if (mLastTouchDownIndex == index) {
3365 mLastTouchDownTime = 0;
3366 mLastTouchDownIndex = -1;
3367 } else if (mLastTouchDownIndex > index) {
3368 mLastTouchDownIndex--;
3369 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003370 }
3371
3372 // This method also sets the children's mParent to null
3373 private void removeFromArray(int start, int count) {
3374 final View[] children = mChildren;
3375 final int childrenCount = mChildrenCount;
3376
3377 start = Math.max(0, start);
3378 final int end = Math.min(childrenCount, start + count);
3379
3380 if (start == end) {
3381 return;
3382 }
3383
3384 if (end == childrenCount) {
3385 for (int i = start; i < end; i++) {
3386 children[i].mParent = null;
3387 children[i] = null;
3388 }
3389 } else {
3390 for (int i = start; i < end; i++) {
3391 children[i].mParent = null;
3392 }
3393
3394 // Since we're looping above, we might as well do the copy, but is arraycopy()
3395 // faster than the extra 2 bounds checks we would do in the loop?
3396 System.arraycopy(children, end, children, start, childrenCount - end);
3397
3398 for (int i = childrenCount - (end - start); i < childrenCount; i++) {
3399 children[i] = null;
3400 }
3401 }
3402
3403 mChildrenCount -= (end - start);
3404 }
3405
3406 private void bindLayoutAnimation(View child) {
3407 Animation a = mLayoutAnimationController.getAnimationForView(child);
3408 child.setAnimation(a);
3409 }
3410
3411 /**
3412 * Subclasses should override this method to set layout animation
3413 * parameters on the supplied child.
3414 *
3415 * @param child the child to associate with animation parameters
3416 * @param params the child's layout parameters which hold the animation
3417 * parameters
3418 * @param index the index of the child in the view group
3419 * @param count the number of children in the view group
3420 */
3421 protected void attachLayoutAnimationParameters(View child,
3422 LayoutParams params, int index, int count) {
3423 LayoutAnimationController.AnimationParameters animationParams =
3424 params.layoutAnimationParameters;
3425 if (animationParams == null) {
3426 animationParams = new LayoutAnimationController.AnimationParameters();
3427 params.layoutAnimationParameters = animationParams;
3428 }
3429
3430 animationParams.count = count;
3431 animationParams.index = index;
3432 }
3433
3434 /**
3435 * {@inheritDoc}
3436 */
3437 public void removeView(View view) {
3438 removeViewInternal(view);
3439 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003440 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003441 }
3442
3443 /**
3444 * Removes a view during layout. This is useful if in your onLayout() method,
3445 * you need to remove more views.
3446 *
3447 * @param view the view to remove from the group
3448 */
3449 public void removeViewInLayout(View view) {
3450 removeViewInternal(view);
3451 }
3452
3453 /**
3454 * Removes a range of views during layout. This is useful if in your onLayout() method,
3455 * you need to remove more views.
3456 *
3457 * @param start the index of the first view to remove from the group
3458 * @param count the number of views to remove from the group
3459 */
3460 public void removeViewsInLayout(int start, int count) {
3461 removeViewsInternal(start, count);
3462 }
3463
3464 /**
3465 * Removes the view at the specified position in the group.
3466 *
3467 * @param index the position in the group of the view to remove
3468 */
3469 public void removeViewAt(int index) {
3470 removeViewInternal(index, getChildAt(index));
3471 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003472 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003473 }
3474
3475 /**
3476 * Removes the specified range of views from the group.
3477 *
3478 * @param start the first position in the group of the range of views to remove
3479 * @param count the number of views to remove
3480 */
3481 public void removeViews(int start, int count) {
3482 removeViewsInternal(start, count);
3483 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003484 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003485 }
3486
3487 private void removeViewInternal(View view) {
3488 final int index = indexOfChild(view);
3489 if (index >= 0) {
3490 removeViewInternal(index, view);
3491 }
3492 }
3493
3494 private void removeViewInternal(int index, View view) {
Chet Haase21cd1382010-09-01 17:42:29 -07003495
3496 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003497 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003498 }
3499
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003500 boolean clearChildFocus = false;
3501 if (view == mFocused) {
3502 view.clearFocusForRemoval();
3503 clearChildFocus = true;
3504 }
3505
Chet Haase21cd1382010-09-01 17:42:29 -07003506 if (view.getAnimation() != null ||
3507 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003508 addDisappearingView(view);
3509 } else if (view.mAttachInfo != null) {
3510 view.dispatchDetachedFromWindow();
3511 }
3512
Philip Milnef51d91c2011-07-18 16:12:19 -07003513 onViewRemoved(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003514
3515 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003517 removeFromArray(index);
3518
3519 if (clearChildFocus) {
3520 clearChildFocus(view);
3521 }
3522 }
3523
Chet Haase21cd1382010-09-01 17:42:29 -07003524 /**
3525 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
3526 * not null, changes in layout which occur because of children being added to or removed from
3527 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
3528 * object. By default, the transition object is null (so layout changes are not animated).
3529 *
3530 * @param transition The LayoutTransition object that will animated changes in layout. A value
3531 * of <code>null</code> means no transition will run on layout changes.
Chet Haase13cc1202010-09-03 15:39:20 -07003532 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
Chet Haase21cd1382010-09-01 17:42:29 -07003533 */
3534 public void setLayoutTransition(LayoutTransition transition) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07003535 if (mTransition != null) {
3536 mTransition.removeTransitionListener(mLayoutTransitionListener);
3537 }
Chet Haase21cd1382010-09-01 17:42:29 -07003538 mTransition = transition;
Chet Haase13cc1202010-09-03 15:39:20 -07003539 if (mTransition != null) {
3540 mTransition.addTransitionListener(mLayoutTransitionListener);
3541 }
3542 }
3543
3544 /**
3545 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
3546 * not null, changes in layout which occur because of children being added to or removed from
3547 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
3548 * object. By default, the transition object is null (so layout changes are not animated).
3549 *
3550 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
3551 * A value of <code>null</code> means no transition will run on layout changes.
3552 */
3553 public LayoutTransition getLayoutTransition() {
3554 return mTransition;
Chet Haase21cd1382010-09-01 17:42:29 -07003555 }
3556
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003557 private void removeViewsInternal(int start, int count) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003558 final View focused = mFocused;
3559 final boolean detach = mAttachInfo != null;
3560 View clearChildFocus = null;
3561
3562 final View[] children = mChildren;
3563 final int end = start + count;
3564
3565 for (int i = start; i < end; i++) {
3566 final View view = children[i];
3567
Chet Haase21cd1382010-09-01 17:42:29 -07003568 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003569 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003570 }
3571
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003572 if (view == focused) {
3573 view.clearFocusForRemoval();
3574 clearChildFocus = view;
3575 }
3576
Chet Haase21cd1382010-09-01 17:42:29 -07003577 if (view.getAnimation() != null ||
3578 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003579 addDisappearingView(view);
3580 } else if (detach) {
3581 view.dispatchDetachedFromWindow();
3582 }
3583
3584 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003585
Philip Milnef51d91c2011-07-18 16:12:19 -07003586 onViewRemoved(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003587 }
3588
3589 removeFromArray(start, count);
3590
3591 if (clearChildFocus != null) {
3592 clearChildFocus(clearChildFocus);
3593 }
3594 }
3595
3596 /**
3597 * Call this method to remove all child views from the
3598 * ViewGroup.
3599 */
3600 public void removeAllViews() {
3601 removeAllViewsInLayout();
3602 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003603 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003604 }
3605
3606 /**
3607 * Called by a ViewGroup subclass to remove child views from itself,
3608 * when it must first know its size on screen before it can calculate how many
3609 * child views it will render. An example is a Gallery or a ListView, which
3610 * may "have" 50 children, but actually only render the number of children
3611 * that can currently fit inside the object on screen. Do not call
3612 * this method unless you are extending ViewGroup and understand the
3613 * view measuring and layout pipeline.
3614 */
3615 public void removeAllViewsInLayout() {
3616 final int count = mChildrenCount;
3617 if (count <= 0) {
3618 return;
3619 }
3620
3621 final View[] children = mChildren;
3622 mChildrenCount = 0;
3623
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003624 final View focused = mFocused;
3625 final boolean detach = mAttachInfo != null;
3626 View clearChildFocus = null;
3627
3628 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003629
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003630 for (int i = count - 1; i >= 0; i--) {
3631 final View view = children[i];
3632
Chet Haase21cd1382010-09-01 17:42:29 -07003633 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003634 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003635 }
3636
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003637 if (view == focused) {
3638 view.clearFocusForRemoval();
3639 clearChildFocus = view;
3640 }
3641
Chet Haase21cd1382010-09-01 17:42:29 -07003642 if (view.getAnimation() != null ||
3643 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003644 addDisappearingView(view);
3645 } else if (detach) {
3646 view.dispatchDetachedFromWindow();
3647 }
3648
Philip Milnef51d91c2011-07-18 16:12:19 -07003649 onViewRemoved(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003650
3651 view.mParent = null;
3652 children[i] = null;
3653 }
3654
3655 if (clearChildFocus != null) {
3656 clearChildFocus(clearChildFocus);
3657 }
3658 }
3659
3660 /**
3661 * Finishes the removal of a detached view. This method will dispatch the detached from
3662 * window event and notify the hierarchy change listener.
3663 *
3664 * @param child the child to be definitely removed from the view hierarchy
3665 * @param animate if true and the view has an animation, the view is placed in the
3666 * disappearing views list, otherwise, it is detached from the window
3667 *
3668 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3669 * @see #detachAllViewsFromParent()
3670 * @see #detachViewFromParent(View)
3671 * @see #detachViewFromParent(int)
3672 */
3673 protected void removeDetachedView(View child, boolean animate) {
Chet Haase21cd1382010-09-01 17:42:29 -07003674 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003675 mTransition.removeChild(this, child);
Chet Haase21cd1382010-09-01 17:42:29 -07003676 }
3677
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003678 if (child == mFocused) {
3679 child.clearFocus();
3680 }
Romain Guy8506ab42009-06-11 17:35:47 -07003681
Chet Haase21cd1382010-09-01 17:42:29 -07003682 if ((animate && child.getAnimation() != null) ||
3683 (mTransitioningViews != null && mTransitioningViews.contains(child))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003684 addDisappearingView(child);
3685 } else if (child.mAttachInfo != null) {
3686 child.dispatchDetachedFromWindow();
3687 }
3688
Philip Milnef51d91c2011-07-18 16:12:19 -07003689 onViewRemoved(child);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003690 }
3691
3692 /**
3693 * Attaches a view to this view group. Attaching a view assigns this group as the parent,
3694 * sets the layout parameters and puts the view in the list of children so it can be retrieved
3695 * by calling {@link #getChildAt(int)}.
3696 *
3697 * This method should be called only for view which were detached from their parent.
3698 *
3699 * @param child the child to attach
3700 * @param index the index at which the child should be attached
3701 * @param params the layout parameters of the child
3702 *
3703 * @see #removeDetachedView(View, boolean)
3704 * @see #detachAllViewsFromParent()
3705 * @see #detachViewFromParent(View)
3706 * @see #detachViewFromParent(int)
3707 */
3708 protected void attachViewToParent(View child, int index, LayoutParams params) {
3709 child.mLayoutParams = params;
3710
3711 if (index < 0) {
3712 index = mChildrenCount;
3713 }
3714
3715 addInArray(child, index);
3716
3717 child.mParent = this;
Chet Haase3b2b0fc2011-01-24 17:53:52 -08003718 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK & ~DRAWING_CACHE_VALID) |
3719 DRAWN | INVALIDATED;
3720 this.mPrivateFlags |= INVALIDATED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003721
3722 if (child.hasFocus()) {
3723 requestChildFocus(child, child.findFocus());
3724 }
3725 }
3726
3727 /**
3728 * Detaches a view from its parent. Detaching a view should be temporary and followed
3729 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3730 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3731 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3732 *
3733 * @param child the child to detach
3734 *
3735 * @see #detachViewFromParent(int)
3736 * @see #detachViewsFromParent(int, int)
3737 * @see #detachAllViewsFromParent()
3738 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3739 * @see #removeDetachedView(View, boolean)
3740 */
3741 protected void detachViewFromParent(View child) {
3742 removeFromArray(indexOfChild(child));
3743 }
3744
3745 /**
3746 * Detaches a view from its parent. Detaching a view should be temporary and followed
3747 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3748 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3749 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3750 *
3751 * @param index the index of the child to detach
3752 *
3753 * @see #detachViewFromParent(View)
3754 * @see #detachAllViewsFromParent()
3755 * @see #detachViewsFromParent(int, int)
3756 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3757 * @see #removeDetachedView(View, boolean)
3758 */
3759 protected void detachViewFromParent(int index) {
3760 removeFromArray(index);
3761 }
3762
3763 /**
3764 * Detaches a range of view from their parent. Detaching a view should be temporary and followed
3765 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3766 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its
3767 * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3768 *
3769 * @param start the first index of the childrend range to detach
3770 * @param count the number of children to detach
3771 *
3772 * @see #detachViewFromParent(View)
3773 * @see #detachViewFromParent(int)
3774 * @see #detachAllViewsFromParent()
3775 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3776 * @see #removeDetachedView(View, boolean)
3777 */
3778 protected void detachViewsFromParent(int start, int count) {
3779 removeFromArray(start, count);
3780 }
3781
3782 /**
3783 * Detaches all views from the parent. Detaching a view should be temporary and followed
3784 * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3785 * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3786 * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3787 *
3788 * @see #detachViewFromParent(View)
3789 * @see #detachViewFromParent(int)
3790 * @see #detachViewsFromParent(int, int)
3791 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3792 * @see #removeDetachedView(View, boolean)
3793 */
3794 protected void detachAllViewsFromParent() {
3795 final int count = mChildrenCount;
3796 if (count <= 0) {
3797 return;
3798 }
3799
3800 final View[] children = mChildren;
3801 mChildrenCount = 0;
3802
3803 for (int i = count - 1; i >= 0; i--) {
3804 children[i].mParent = null;
3805 children[i] = null;
3806 }
3807 }
3808
3809 /**
3810 * Don't call or override this method. It is used for the implementation of
3811 * the view hierarchy.
3812 */
3813 public final void invalidateChild(View child, final Rect dirty) {
3814 if (ViewDebug.TRACE_HIERARCHY) {
3815 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
3816 }
3817
3818 ViewParent parent = this;
3819
3820 final AttachInfo attachInfo = mAttachInfo;
3821 if (attachInfo != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003822 // If the child is drawing an animation, we want to copy this flag onto
3823 // ourselves and the parent to make sure the invalidate request goes
3824 // through
3825 final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
Romain Guy24443ea2009-05-11 11:56:30 -07003826
Chet Haase70d4ba12010-10-06 09:46:45 -07003827 if (dirty == null) {
Chet Haasedaf98e92011-01-10 14:10:36 -08003828 if (child.mLayerType != LAYER_TYPE_NONE) {
3829 mPrivateFlags |= INVALIDATED;
3830 mPrivateFlags &= ~DRAWING_CACHE_VALID;
Romain Guy3a3133d2011-02-01 22:59:58 -08003831 child.mLocalDirtyRect.setEmpty();
Chet Haasedaf98e92011-01-10 14:10:36 -08003832 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003833 do {
3834 View view = null;
3835 if (parent instanceof View) {
3836 view = (View) parent;
Romain Guy3a3133d2011-02-01 22:59:58 -08003837 if (view.mLayerType != LAYER_TYPE_NONE) {
3838 view.mLocalDirtyRect.setEmpty();
3839 if (view.getParent() instanceof View) {
3840 final View grandParent = (View) view.getParent();
3841 grandParent.mPrivateFlags |= INVALIDATED;
3842 grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3843 }
Chet Haasedaf98e92011-01-10 14:10:36 -08003844 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003845 if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
3846 // already marked dirty - we're done
3847 break;
3848 }
3849 }
3850
3851 if (drawAnimation) {
3852 if (view != null) {
3853 view.mPrivateFlags |= DRAW_ANIMATION;
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07003854 } else if (parent instanceof ViewRootImpl) {
3855 ((ViewRootImpl) parent).mIsAnimating = true;
Chet Haase70d4ba12010-10-06 09:46:45 -07003856 }
3857 }
3858
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07003859 if (parent instanceof ViewRootImpl) {
3860 ((ViewRootImpl) parent).invalidate();
Chet Haase70d4ba12010-10-06 09:46:45 -07003861 parent = null;
3862 } else if (view != null) {
Chet Haase77785f92011-01-25 23:22:09 -08003863 if ((view.mPrivateFlags & DRAWN) == DRAWN ||
3864 (view.mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
Chet Haase70d4ba12010-10-06 09:46:45 -07003865 view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
Chet Haase0d200832010-11-05 15:36:16 -07003866 view.mPrivateFlags |= DIRTY;
Chet Haase70d4ba12010-10-06 09:46:45 -07003867 parent = view.mParent;
3868 } else {
3869 parent = null;
3870 }
3871 }
3872 } while (parent != null);
3873 } else {
Chet Haase0d200832010-11-05 15:36:16 -07003874 // Check whether the child that requests the invalidate is fully opaque
3875 final boolean isOpaque = child.isOpaque() && !drawAnimation &&
3876 child.getAnimation() == null;
3877 // Mark the child as dirty, using the appropriate flag
3878 // Make sure we do not set both flags at the same time
Romain Guy7e68efb2011-01-07 14:50:27 -08003879 int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
Chet Haase0d200832010-11-05 15:36:16 -07003880
Romain Guybeff8d82011-02-01 23:53:34 -08003881 if (child.mLayerType != LAYER_TYPE_NONE) {
3882 mPrivateFlags |= INVALIDATED;
3883 mPrivateFlags &= ~DRAWING_CACHE_VALID;
3884 child.mLocalDirtyRect.union(dirty);
3885 }
3886
Chet Haase70d4ba12010-10-06 09:46:45 -07003887 final int[] location = attachInfo.mInvalidateChildLocation;
3888 location[CHILD_LEFT_INDEX] = child.mLeft;
3889 location[CHILD_TOP_INDEX] = child.mTop;
3890 Matrix childMatrix = child.getMatrix();
3891 if (!childMatrix.isIdentity()) {
3892 RectF boundingRect = attachInfo.mTmpTransformRect;
3893 boundingRect.set(dirty);
Romain Guya9489272011-06-22 20:58:11 -07003894 //boundingRect.inset(-0.5f, -0.5f);
Chet Haase70d4ba12010-10-06 09:46:45 -07003895 childMatrix.mapRect(boundingRect);
Romain Guya9489272011-06-22 20:58:11 -07003896 dirty.set((int) (boundingRect.left - 0.5f),
3897 (int) (boundingRect.top - 0.5f),
Chet Haase70d4ba12010-10-06 09:46:45 -07003898 (int) (boundingRect.right + 0.5f),
3899 (int) (boundingRect.bottom + 0.5f));
Romain Guy24443ea2009-05-11 11:56:30 -07003900 }
3901
Chet Haase70d4ba12010-10-06 09:46:45 -07003902 do {
3903 View view = null;
3904 if (parent instanceof View) {
3905 view = (View) parent;
Romain Guy7d7b5492011-01-24 16:33:45 -08003906 if (view.mLayerType != LAYER_TYPE_NONE &&
3907 view.getParent() instanceof View) {
3908 final View grandParent = (View) view.getParent();
3909 grandParent.mPrivateFlags |= INVALIDATED;
3910 grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3911 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003912 }
3913
3914 if (drawAnimation) {
3915 if (view != null) {
3916 view.mPrivateFlags |= DRAW_ANIMATION;
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07003917 } else if (parent instanceof ViewRootImpl) {
3918 ((ViewRootImpl) parent).mIsAnimating = true;
Chet Haase70d4ba12010-10-06 09:46:45 -07003919 }
3920 }
3921
3922 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
3923 // flag coming from the child that initiated the invalidate
Romain Guy7e68efb2011-01-07 14:50:27 -08003924 if (view != null) {
3925 if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
Romain Guy2243e552011-03-08 11:46:28 -08003926 view.getSolidColor() == 0) {
Romain Guy7e68efb2011-01-07 14:50:27 -08003927 opaqueFlag = DIRTY;
3928 }
3929 if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
3930 view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
3931 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003932 }
3933
3934 parent = parent.invalidateChildInParent(location, dirty);
Romain Guy24443ea2009-05-11 11:56:30 -07003935 if (view != null) {
Chet Haase70d4ba12010-10-06 09:46:45 -07003936 // Account for transform on current parent
3937 Matrix m = view.getMatrix();
3938 if (!m.isIdentity()) {
3939 RectF boundingRect = attachInfo.mTmpTransformRect;
3940 boundingRect.set(dirty);
3941 m.mapRect(boundingRect);
3942 dirty.set((int) boundingRect.left, (int) boundingRect.top,
3943 (int) (boundingRect.right + 0.5f),
3944 (int) (boundingRect.bottom + 0.5f));
3945 }
Romain Guybb93d552009-03-24 21:04:15 -07003946 }
Chet Haase70d4ba12010-10-06 09:46:45 -07003947 } while (parent != null);
3948 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003949 }
3950 }
3951
3952 /**
3953 * Don't call or override this method. It is used for the implementation of
3954 * the view hierarchy.
3955 *
3956 * This implementation returns null if this ViewGroup does not have a parent,
3957 * if this ViewGroup is already fully invalidated or if the dirty rectangle
3958 * does not intersect with this ViewGroup's bounds.
3959 */
3960 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
3961 if (ViewDebug.TRACE_HIERARCHY) {
3962 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
3963 }
3964
Chet Haase77785f92011-01-25 23:22:09 -08003965 if ((mPrivateFlags & DRAWN) == DRAWN ||
3966 (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003967 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
3968 FLAG_OPTIMIZE_INVALIDATE) {
3969 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
3970 location[CHILD_TOP_INDEX] - mScrollY);
3971
3972 final int left = mLeft;
3973 final int top = mTop;
3974
Chet Haasea3db8662011-07-19 10:36:05 -07003975 if ((mGroupFlags & FLAG_CLIP_CHILDREN) != FLAG_CLIP_CHILDREN ||
3976 dirty.intersect(0, 0, mRight - left, mBottom - top) ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003977 (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
3978 mPrivateFlags &= ~DRAWING_CACHE_VALID;
3979
3980 location[CHILD_LEFT_INDEX] = left;
3981 location[CHILD_TOP_INDEX] = top;
3982
Romain Guy3a3133d2011-02-01 22:59:58 -08003983 if (mLayerType != LAYER_TYPE_NONE) {
3984 mLocalDirtyRect.union(dirty);
3985 }
Romain Guybeff8d82011-02-01 23:53:34 -08003986
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003987 return mParent;
3988 }
3989 } else {
3990 mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
3991
3992 location[CHILD_LEFT_INDEX] = mLeft;
3993 location[CHILD_TOP_INDEX] = mTop;
Chet Haasea3db8662011-07-19 10:36:05 -07003994 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
3995 dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
3996 } else {
3997 // in case the dirty rect extends outside the bounds of this container
3998 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
3999 }
Romain Guy3a3133d2011-02-01 22:59:58 -08004000
4001 if (mLayerType != LAYER_TYPE_NONE) {
4002 mLocalDirtyRect.union(dirty);
4003 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004004
4005 return mParent;
4006 }
4007 }
4008
4009 return null;
4010 }
4011
4012 /**
4013 * Offset a rectangle that is in a descendant's coordinate
4014 * space into our coordinate space.
4015 * @param descendant A descendant of this view
4016 * @param rect A rectangle defined in descendant's coordinate space.
4017 */
4018 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
4019 offsetRectBetweenParentAndChild(descendant, rect, true, false);
4020 }
4021
4022 /**
4023 * Offset a rectangle that is in our coordinate space into an ancestor's
4024 * coordinate space.
4025 * @param descendant A descendant of this view
4026 * @param rect A rectangle defined in descendant's coordinate space.
4027 */
4028 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
4029 offsetRectBetweenParentAndChild(descendant, rect, false, false);
4030 }
4031
4032 /**
4033 * Helper method that offsets a rect either from parent to descendant or
4034 * descendant to parent.
4035 */
4036 void offsetRectBetweenParentAndChild(View descendant, Rect rect,
4037 boolean offsetFromChildToParent, boolean clipToBounds) {
4038
4039 // already in the same coord system :)
4040 if (descendant == this) {
4041 return;
4042 }
4043
4044 ViewParent theParent = descendant.mParent;
4045
4046 // search and offset up to the parent
4047 while ((theParent != null)
4048 && (theParent instanceof View)
4049 && (theParent != this)) {
4050
4051 if (offsetFromChildToParent) {
4052 rect.offset(descendant.mLeft - descendant.mScrollX,
4053 descendant.mTop - descendant.mScrollY);
4054 if (clipToBounds) {
4055 View p = (View) theParent;
4056 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4057 }
4058 } else {
4059 if (clipToBounds) {
4060 View p = (View) theParent;
4061 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4062 }
4063 rect.offset(descendant.mScrollX - descendant.mLeft,
4064 descendant.mScrollY - descendant.mTop);
4065 }
4066
4067 descendant = (View) theParent;
4068 theParent = descendant.mParent;
4069 }
4070
4071 // now that we are up to this view, need to offset one more time
4072 // to get into our coordinate space
4073 if (theParent == this) {
4074 if (offsetFromChildToParent) {
4075 rect.offset(descendant.mLeft - descendant.mScrollX,
4076 descendant.mTop - descendant.mScrollY);
4077 } else {
4078 rect.offset(descendant.mScrollX - descendant.mLeft,
4079 descendant.mScrollY - descendant.mTop);
4080 }
4081 } else {
4082 throw new IllegalArgumentException("parameter must be a descendant of this view");
4083 }
4084 }
4085
4086 /**
4087 * Offset the vertical location of all children of this view by the specified number of pixels.
4088 *
4089 * @param offset the number of pixels to offset
4090 *
4091 * @hide
4092 */
4093 public void offsetChildrenTopAndBottom(int offset) {
4094 final int count = mChildrenCount;
4095 final View[] children = mChildren;
4096
4097 for (int i = 0; i < count; i++) {
4098 final View v = children[i];
4099 v.mTop += offset;
4100 v.mBottom += offset;
4101 }
4102 }
4103
4104 /**
4105 * {@inheritDoc}
4106 */
4107 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
4108 int dx = child.mLeft - mScrollX;
4109 int dy = child.mTop - mScrollY;
4110 if (offset != null) {
4111 offset.x += dx;
4112 offset.y += dy;
4113 }
4114 r.offset(dx, dy);
4115 return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) &&
4116 (mParent == null || mParent.getChildVisibleRect(this, r, offset));
4117 }
4118
4119 /**
4120 * {@inheritDoc}
4121 */
4122 @Override
Chet Haase9c087442011-01-12 16:20:16 -08004123 public final void layout(int l, int t, int r, int b) {
4124 if (mTransition == null || !mTransition.isChangingLayout()) {
4125 super.layout(l, t, r, b);
4126 } else {
4127 // record the fact that we noop'd it; request layout when transition finishes
4128 mLayoutSuppressed = true;
4129 }
4130 }
4131
4132 /**
4133 * {@inheritDoc}
4134 */
4135 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004136 protected abstract void onLayout(boolean changed,
4137 int l, int t, int r, int b);
4138
4139 /**
4140 * Indicates whether the view group has the ability to animate its children
4141 * after the first layout.
4142 *
4143 * @return true if the children can be animated, false otherwise
4144 */
4145 protected boolean canAnimate() {
4146 return mLayoutAnimationController != null;
4147 }
4148
4149 /**
4150 * Runs the layout animation. Calling this method triggers a relayout of
4151 * this view group.
4152 */
4153 public void startLayoutAnimation() {
4154 if (mLayoutAnimationController != null) {
4155 mGroupFlags |= FLAG_RUN_ANIMATION;
4156 requestLayout();
4157 }
4158 }
4159
4160 /**
4161 * Schedules the layout animation to be played after the next layout pass
4162 * of this view group. This can be used to restart the layout animation
4163 * when the content of the view group changes or when the activity is
4164 * paused and resumed.
4165 */
4166 public void scheduleLayoutAnimation() {
4167 mGroupFlags |= FLAG_RUN_ANIMATION;
4168 }
4169
4170 /**
4171 * Sets the layout animation controller used to animate the group's
4172 * children after the first layout.
4173 *
4174 * @param controller the animation controller
4175 */
4176 public void setLayoutAnimation(LayoutAnimationController controller) {
4177 mLayoutAnimationController = controller;
4178 if (mLayoutAnimationController != null) {
4179 mGroupFlags |= FLAG_RUN_ANIMATION;
4180 }
4181 }
4182
4183 /**
4184 * Returns the layout animation controller used to animate the group's
4185 * children.
4186 *
4187 * @return the current animation controller
4188 */
4189 public LayoutAnimationController getLayoutAnimation() {
4190 return mLayoutAnimationController;
4191 }
4192
4193 /**
4194 * Indicates whether the children's drawing cache is used during a layout
4195 * animation. By default, the drawing cache is enabled but this will prevent
4196 * nested layout animations from working. To nest animations, you must disable
4197 * the cache.
4198 *
4199 * @return true if the animation cache is enabled, false otherwise
4200 *
4201 * @see #setAnimationCacheEnabled(boolean)
4202 * @see View#setDrawingCacheEnabled(boolean)
4203 */
4204 @ViewDebug.ExportedProperty
4205 public boolean isAnimationCacheEnabled() {
4206 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
4207 }
4208
4209 /**
4210 * Enables or disables the children's drawing cache during a layout animation.
4211 * By default, the drawing cache is enabled but this will prevent nested
4212 * layout animations from working. To nest animations, you must disable the
4213 * cache.
4214 *
4215 * @param enabled true to enable the animation cache, false otherwise
4216 *
4217 * @see #isAnimationCacheEnabled()
4218 * @see View#setDrawingCacheEnabled(boolean)
4219 */
4220 public void setAnimationCacheEnabled(boolean enabled) {
4221 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
4222 }
4223
4224 /**
4225 * Indicates whether this ViewGroup will always try to draw its children using their
4226 * drawing cache. By default this property is enabled.
4227 *
4228 * @return true if the animation cache is enabled, false otherwise
4229 *
4230 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4231 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4232 * @see View#setDrawingCacheEnabled(boolean)
4233 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004234 @ViewDebug.ExportedProperty(category = "drawing")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004235 public boolean isAlwaysDrawnWithCacheEnabled() {
4236 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
4237 }
4238
4239 /**
4240 * Indicates whether this ViewGroup will always try to draw its children using their
4241 * drawing cache. This property can be set to true when the cache rendering is
4242 * slightly different from the children's normal rendering. Renderings can be different,
4243 * for instance, when the cache's quality is set to low.
4244 *
4245 * When this property is disabled, the ViewGroup will use the drawing cache of its
4246 * children only when asked to. It's usually the task of subclasses to tell ViewGroup
4247 * when to start using the drawing cache and when to stop using it.
4248 *
4249 * @param always true to always draw with the drawing cache, false otherwise
4250 *
4251 * @see #isAlwaysDrawnWithCacheEnabled()
4252 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4253 * @see View#setDrawingCacheEnabled(boolean)
4254 * @see View#setDrawingCacheQuality(int)
4255 */
4256 public void setAlwaysDrawnWithCacheEnabled(boolean always) {
4257 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
4258 }
4259
4260 /**
4261 * Indicates whether the ViewGroup is currently drawing its children using
4262 * their drawing cache.
4263 *
4264 * @return true if children should be drawn with their cache, false otherwise
4265 *
4266 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4267 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4268 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004269 @ViewDebug.ExportedProperty(category = "drawing")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004270 protected boolean isChildrenDrawnWithCacheEnabled() {
4271 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
4272 }
4273
4274 /**
4275 * Tells the ViewGroup to draw its children using their drawing cache. This property
4276 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
4277 * will be used only if it has been enabled.
4278 *
4279 * Subclasses should call this method to start and stop using the drawing cache when
4280 * they perform performance sensitive operations, like scrolling or animating.
4281 *
4282 * @param enabled true if children should be drawn with their cache, false otherwise
4283 *
4284 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4285 * @see #isChildrenDrawnWithCacheEnabled()
4286 */
4287 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
4288 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
4289 }
4290
Romain Guy293451e2009-11-04 13:59:48 -08004291 /**
4292 * Indicates whether the ViewGroup is drawing its children in the order defined by
4293 * {@link #getChildDrawingOrder(int, int)}.
4294 *
4295 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
4296 * false otherwise
4297 *
4298 * @see #setChildrenDrawingOrderEnabled(boolean)
4299 * @see #getChildDrawingOrder(int, int)
4300 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004301 @ViewDebug.ExportedProperty(category = "drawing")
Romain Guy293451e2009-11-04 13:59:48 -08004302 protected boolean isChildrenDrawingOrderEnabled() {
4303 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
4304 }
4305
4306 /**
4307 * Tells the ViewGroup whether to draw its children in the order defined by the method
4308 * {@link #getChildDrawingOrder(int, int)}.
4309 *
4310 * @param enabled true if the order of the children when drawing is determined by
4311 * {@link #getChildDrawingOrder(int, int)}, false otherwise
4312 *
4313 * @see #isChildrenDrawingOrderEnabled()
4314 * @see #getChildDrawingOrder(int, int)
4315 */
4316 protected void setChildrenDrawingOrderEnabled(boolean enabled) {
4317 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
4318 }
4319
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004320 private void setBooleanFlag(int flag, boolean value) {
4321 if (value) {
4322 mGroupFlags |= flag;
4323 } else {
4324 mGroupFlags &= ~flag;
4325 }
4326 }
4327
4328 /**
4329 * Returns an integer indicating what types of drawing caches are kept in memory.
4330 *
4331 * @see #setPersistentDrawingCache(int)
4332 * @see #setAnimationCacheEnabled(boolean)
4333 *
4334 * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
4335 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
4336 * and {@link #PERSISTENT_ALL_CACHES}
4337 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004338 @ViewDebug.ExportedProperty(category = "drawing", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004339 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
Romain Guy203688c2010-05-12 15:41:32 -07004340 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004341 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
4342 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL")
4343 })
4344 public int getPersistentDrawingCache() {
4345 return mPersistentDrawingCache;
4346 }
4347
4348 /**
4349 * Indicates what types of drawing caches should be kept in memory after
4350 * they have been created.
4351 *
4352 * @see #getPersistentDrawingCache()
4353 * @see #setAnimationCacheEnabled(boolean)
4354 *
4355 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
4356 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
4357 * and {@link #PERSISTENT_ALL_CACHES}
4358 */
4359 public void setPersistentDrawingCache(int drawingCacheToKeep) {
4360 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
4361 }
4362
4363 /**
4364 * Returns a new set of layout parameters based on the supplied attributes set.
4365 *
4366 * @param attrs the attributes to build the layout parameters from
4367 *
4368 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
4369 * of its descendants
4370 */
4371 public LayoutParams generateLayoutParams(AttributeSet attrs) {
4372 return new LayoutParams(getContext(), attrs);
4373 }
4374
4375 /**
4376 * Returns a safe set of layout parameters based on the supplied layout params.
4377 * When a ViewGroup is passed a View whose layout params do not pass the test of
4378 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
4379 * is invoked. This method should return a new set of layout params suitable for
4380 * this ViewGroup, possibly by copying the appropriate attributes from the
4381 * specified set of layout params.
4382 *
4383 * @param p The layout parameters to convert into a suitable set of layout parameters
4384 * for this ViewGroup.
4385 *
4386 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
4387 * of its descendants
4388 */
4389 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4390 return p;
4391 }
4392
4393 /**
4394 * Returns a set of default layout parameters. These parameters are requested
4395 * when the View passed to {@link #addView(View)} has no layout parameters
4396 * already set. If null is returned, an exception is thrown from addView.
4397 *
4398 * @return a set of default layout parameters or null
4399 */
4400 protected LayoutParams generateDefaultLayoutParams() {
4401 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
4402 }
4403
4404 /**
Romain Guy13922e02009-05-12 17:56:14 -07004405 * @hide
4406 */
4407 @Override
4408 protected boolean dispatchConsistencyCheck(int consistency) {
4409 boolean result = super.dispatchConsistencyCheck(consistency);
4410
4411 final int count = mChildrenCount;
4412 final View[] children = mChildren;
4413 for (int i = 0; i < count; i++) {
4414 if (!children[i].dispatchConsistencyCheck(consistency)) result = false;
4415 }
4416
4417 return result;
4418 }
4419
4420 /**
4421 * @hide
4422 */
4423 @Override
4424 protected boolean onConsistencyCheck(int consistency) {
4425 boolean result = super.onConsistencyCheck(consistency);
4426
4427 final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
4428 final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0;
4429
4430 if (checkLayout) {
4431 final int count = mChildrenCount;
4432 final View[] children = mChildren;
4433 for (int i = 0; i < count; i++) {
4434 if (children[i].getParent() != this) {
4435 result = false;
4436 android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
4437 "View " + children[i] + " has no parent/a parent that is not " + this);
4438 }
4439 }
4440 }
4441
4442 if (checkDrawing) {
4443 // If this group is dirty, check that the parent is dirty as well
4444 if ((mPrivateFlags & DIRTY_MASK) != 0) {
4445 final ViewParent parent = getParent();
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07004446 if (parent != null && !(parent instanceof ViewRootImpl)) {
Romain Guy13922e02009-05-12 17:56:14 -07004447 if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) {
4448 result = false;
4449 android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
4450 "ViewGroup " + this + " is dirty but its parent is not: " + this);
4451 }
4452 }
4453 }
4454 }
4455
4456 return result;
4457 }
4458
4459 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004460 * {@inheritDoc}
4461 */
4462 @Override
4463 protected void debug(int depth) {
4464 super.debug(depth);
4465 String output;
4466
4467 if (mFocused != null) {
4468 output = debugIndent(depth);
4469 output += "mFocused";
4470 Log.d(VIEW_LOG_TAG, output);
4471 }
4472 if (mChildrenCount != 0) {
4473 output = debugIndent(depth);
4474 output += "{";
4475 Log.d(VIEW_LOG_TAG, output);
4476 }
4477 int count = mChildrenCount;
4478 for (int i = 0; i < count; i++) {
4479 View child = mChildren[i];
4480 child.debug(depth + 1);
4481 }
4482
4483 if (mChildrenCount != 0) {
4484 output = debugIndent(depth);
4485 output += "}";
4486 Log.d(VIEW_LOG_TAG, output);
4487 }
4488 }
4489
4490 /**
4491 * Returns the position in the group of the specified child view.
4492 *
4493 * @param child the view for which to get the position
4494 * @return a positive integer representing the position of the view in the
4495 * group, or -1 if the view does not exist in the group
4496 */
4497 public int indexOfChild(View child) {
4498 final int count = mChildrenCount;
4499 final View[] children = mChildren;
4500 for (int i = 0; i < count; i++) {
4501 if (children[i] == child) {
4502 return i;
4503 }
4504 }
4505 return -1;
4506 }
4507
4508 /**
4509 * Returns the number of children in the group.
4510 *
4511 * @return a positive integer representing the number of children in
4512 * the group
4513 */
4514 public int getChildCount() {
4515 return mChildrenCount;
4516 }
4517
4518 /**
4519 * Returns the view at the specified position in the group.
4520 *
4521 * @param index the position at which to get the view from
4522 * @return the view at the specified position or null if the position
4523 * does not exist within the group
4524 */
4525 public View getChildAt(int index) {
Adam Powell3ba8f5d2011-03-07 15:36:33 -08004526 if (index < 0 || index >= mChildrenCount) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004527 return null;
4528 }
Adam Powell3ba8f5d2011-03-07 15:36:33 -08004529 return mChildren[index];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004530 }
4531
4532 /**
4533 * Ask all of the children of this view to measure themselves, taking into
4534 * account both the MeasureSpec requirements for this view and its padding.
4535 * We skip children that are in the GONE state The heavy lifting is done in
4536 * getChildMeasureSpec.
4537 *
4538 * @param widthMeasureSpec The width requirements for this view
4539 * @param heightMeasureSpec The height requirements for this view
4540 */
4541 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
4542 final int size = mChildrenCount;
4543 final View[] children = mChildren;
4544 for (int i = 0; i < size; ++i) {
4545 final View child = children[i];
4546 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
4547 measureChild(child, widthMeasureSpec, heightMeasureSpec);
4548 }
4549 }
4550 }
4551
4552 /**
4553 * Ask one of the children of this view to measure itself, taking into
4554 * account both the MeasureSpec requirements for this view and its padding.
4555 * The heavy lifting is done in getChildMeasureSpec.
4556 *
4557 * @param child The child to measure
4558 * @param parentWidthMeasureSpec The width requirements for this view
4559 * @param parentHeightMeasureSpec The height requirements for this view
4560 */
4561 protected void measureChild(View child, int parentWidthMeasureSpec,
4562 int parentHeightMeasureSpec) {
4563 final LayoutParams lp = child.getLayoutParams();
4564
4565 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
4566 mPaddingLeft + mPaddingRight, lp.width);
4567 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
4568 mPaddingTop + mPaddingBottom, lp.height);
4569
4570 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4571 }
4572
4573 /**
4574 * Ask one of the children of this view to measure itself, taking into
4575 * account both the MeasureSpec requirements for this view and its padding
4576 * and margins. The child must have MarginLayoutParams The heavy lifting is
4577 * done in getChildMeasureSpec.
4578 *
4579 * @param child The child to measure
4580 * @param parentWidthMeasureSpec The width requirements for this view
4581 * @param widthUsed Extra space that has been used up by the parent
4582 * horizontally (possibly by other children of the parent)
4583 * @param parentHeightMeasureSpec The height requirements for this view
4584 * @param heightUsed Extra space that has been used up by the parent
4585 * vertically (possibly by other children of the parent)
4586 */
4587 protected void measureChildWithMargins(View child,
4588 int parentWidthMeasureSpec, int widthUsed,
4589 int parentHeightMeasureSpec, int heightUsed) {
4590 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
4591
4592 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
4593 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
4594 + widthUsed, lp.width);
4595 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
4596 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
4597 + heightUsed, lp.height);
4598
4599 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4600 }
4601
4602 /**
4603 * Does the hard part of measureChildren: figuring out the MeasureSpec to
4604 * pass to a particular child. This method figures out the right MeasureSpec
4605 * for one dimension (height or width) of one child view.
4606 *
4607 * The goal is to combine information from our MeasureSpec with the
4608 * LayoutParams of the child to get the best possible results. For example,
4609 * if the this view knows its size (because its MeasureSpec has a mode of
4610 * EXACTLY), and the child has indicated in its LayoutParams that it wants
4611 * to be the same size as the parent, the parent should ask the child to
4612 * layout given an exact size.
4613 *
4614 * @param spec The requirements for this view
4615 * @param padding The padding of this view for the current dimension and
4616 * margins, if applicable
4617 * @param childDimension How big the child wants to be in the current
4618 * dimension
4619 * @return a MeasureSpec integer for the child
4620 */
4621 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
4622 int specMode = MeasureSpec.getMode(spec);
4623 int specSize = MeasureSpec.getSize(spec);
4624
4625 int size = Math.max(0, specSize - padding);
4626
4627 int resultSize = 0;
4628 int resultMode = 0;
4629
4630 switch (specMode) {
4631 // Parent has imposed an exact size on us
4632 case MeasureSpec.EXACTLY:
4633 if (childDimension >= 0) {
4634 resultSize = childDimension;
4635 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08004636 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004637 // Child wants to be our size. So be it.
4638 resultSize = size;
4639 resultMode = MeasureSpec.EXACTLY;
4640 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4641 // Child wants to determine its own size. It can't be
4642 // bigger than us.
4643 resultSize = size;
4644 resultMode = MeasureSpec.AT_MOST;
4645 }
4646 break;
4647
4648 // Parent has imposed a maximum size on us
4649 case MeasureSpec.AT_MOST:
4650 if (childDimension >= 0) {
4651 // Child wants a specific size... so be it
4652 resultSize = childDimension;
4653 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08004654 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004655 // Child wants to be our size, but our size is not fixed.
4656 // Constrain child to not be bigger than us.
4657 resultSize = size;
4658 resultMode = MeasureSpec.AT_MOST;
4659 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4660 // Child wants to determine its own size. It can't be
4661 // bigger than us.
4662 resultSize = size;
4663 resultMode = MeasureSpec.AT_MOST;
4664 }
4665 break;
4666
4667 // Parent asked to see how big we want to be
4668 case MeasureSpec.UNSPECIFIED:
4669 if (childDimension >= 0) {
4670 // Child wants a specific size... let him have it
4671 resultSize = childDimension;
4672 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08004673 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004674 // Child wants to be our size... find out how big it should
4675 // be
4676 resultSize = 0;
4677 resultMode = MeasureSpec.UNSPECIFIED;
4678 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4679 // Child wants to determine its own size.... find out how
4680 // big it should be
4681 resultSize = 0;
4682 resultMode = MeasureSpec.UNSPECIFIED;
4683 }
4684 break;
4685 }
4686 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
4687 }
4688
4689
4690 /**
4691 * Removes any pending animations for views that have been removed. Call
4692 * this if you don't want animations for exiting views to stack up.
4693 */
4694 public void clearDisappearingChildren() {
4695 if (mDisappearingChildren != null) {
4696 mDisappearingChildren.clear();
4697 }
4698 }
4699
4700 /**
4701 * Add a view which is removed from mChildren but still needs animation
4702 *
4703 * @param v View to add
4704 */
4705 private void addDisappearingView(View v) {
4706 ArrayList<View> disappearingChildren = mDisappearingChildren;
4707
4708 if (disappearingChildren == null) {
4709 disappearingChildren = mDisappearingChildren = new ArrayList<View>();
4710 }
4711
4712 disappearingChildren.add(v);
4713 }
4714
4715 /**
4716 * Cleanup a view when its animation is done. This may mean removing it from
4717 * the list of disappearing views.
4718 *
4719 * @param view The view whose animation has finished
4720 * @param animation The animation, cannot be null
4721 */
4722 private void finishAnimatingView(final View view, Animation animation) {
4723 final ArrayList<View> disappearingChildren = mDisappearingChildren;
4724 if (disappearingChildren != null) {
4725 if (disappearingChildren.contains(view)) {
4726 disappearingChildren.remove(view);
4727
4728 if (view.mAttachInfo != null) {
4729 view.dispatchDetachedFromWindow();
4730 }
4731
4732 view.clearAnimation();
4733 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4734 }
4735 }
4736
4737 if (animation != null && !animation.getFillAfter()) {
4738 view.clearAnimation();
4739 }
4740
4741 if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) {
4742 view.onAnimationEnd();
4743 // Should be performed by onAnimationEnd() but this avoid an infinite loop,
4744 // so we'd rather be safe than sorry
4745 view.mPrivateFlags &= ~ANIMATION_STARTED;
4746 // Draw one more frame after the animation is done
4747 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4748 }
4749 }
4750
Chet Haaseb20db3e2010-09-10 13:07:30 -07004751 /**
4752 * This method tells the ViewGroup that the given View object, which should have this
4753 * ViewGroup as its parent,
4754 * should be kept around (re-displayed when the ViewGroup draws its children) even if it
4755 * is removed from its parent. This allows animations, such as those used by
4756 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
4757 * the removal of views. A call to this method should always be accompanied by a later call
4758 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
4759 * so that the View finally gets removed.
4760 *
4761 * @param view The View object to be kept visible even if it gets removed from its parent.
4762 */
4763 public void startViewTransition(View view) {
4764 if (view.mParent == this) {
4765 if (mTransitioningViews == null) {
4766 mTransitioningViews = new ArrayList<View>();
4767 }
4768 mTransitioningViews.add(view);
4769 }
4770 }
4771
4772 /**
4773 * This method should always be called following an earlier call to
4774 * {@link #startViewTransition(View)}. The given View is finally removed from its parent
4775 * and will no longer be displayed. Note that this method does not perform the functionality
4776 * of removing a view from its parent; it just discontinues the display of a View that
4777 * has previously been removed.
4778 *
4779 * @return view The View object that has been removed but is being kept around in the visible
4780 * hierarchy by an earlier call to {@link #startViewTransition(View)}.
4781 */
4782 public void endViewTransition(View view) {
4783 if (mTransitioningViews != null) {
4784 mTransitioningViews.remove(view);
4785 final ArrayList<View> disappearingChildren = mDisappearingChildren;
4786 if (disappearingChildren != null && disappearingChildren.contains(view)) {
4787 disappearingChildren.remove(view);
Chet Haase5e25c2c2010-09-16 11:15:56 -07004788 if (mVisibilityChangingChildren != null &&
4789 mVisibilityChangingChildren.contains(view)) {
4790 mVisibilityChangingChildren.remove(view);
4791 } else {
4792 if (view.mAttachInfo != null) {
4793 view.dispatchDetachedFromWindow();
4794 }
4795 if (view.mParent != null) {
4796 view.mParent = null;
4797 }
Chet Haaseb20db3e2010-09-10 13:07:30 -07004798 }
4799 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4800 }
4801 }
4802 }
4803
Chet Haase21cd1382010-09-01 17:42:29 -07004804 private LayoutTransition.TransitionListener mLayoutTransitionListener =
4805 new LayoutTransition.TransitionListener() {
4806 @Override
4807 public void startTransition(LayoutTransition transition, ViewGroup container,
4808 View view, int transitionType) {
4809 // We only care about disappearing items, since we need special logic to keep
4810 // those items visible after they've been 'removed'
4811 if (transitionType == LayoutTransition.DISAPPEARING) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07004812 startViewTransition(view);
Chet Haase21cd1382010-09-01 17:42:29 -07004813 }
4814 }
4815
4816 @Override
4817 public void endTransition(LayoutTransition transition, ViewGroup container,
4818 View view, int transitionType) {
Chet Haase9c087442011-01-12 16:20:16 -08004819 if (mLayoutSuppressed && !transition.isChangingLayout()) {
4820 requestLayout();
4821 mLayoutSuppressed = false;
4822 }
Chet Haase21cd1382010-09-01 17:42:29 -07004823 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07004824 endViewTransition(view);
Chet Haase21cd1382010-09-01 17:42:29 -07004825 }
4826 }
4827 };
4828
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004829 /**
4830 * {@inheritDoc}
4831 */
4832 @Override
4833 public boolean gatherTransparentRegion(Region region) {
4834 // If no transparent regions requested, we are always opaque.
4835 final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0;
4836 if (meOpaque && region == null) {
4837 // The caller doesn't care about the region, so stop now.
4838 return true;
4839 }
4840 super.gatherTransparentRegion(region);
4841 final View[] children = mChildren;
4842 final int count = mChildrenCount;
4843 boolean noneOfTheChildrenAreTransparent = true;
4844 for (int i = 0; i < count; i++) {
4845 final View child = children[i];
Mathias Agopiane3381152010-12-02 15:19:36 -08004846 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004847 if (!child.gatherTransparentRegion(region)) {
4848 noneOfTheChildrenAreTransparent = false;
4849 }
4850 }
4851 }
4852 return meOpaque || noneOfTheChildrenAreTransparent;
4853 }
4854
4855 /**
4856 * {@inheritDoc}
4857 */
4858 public void requestTransparentRegion(View child) {
4859 if (child != null) {
4860 child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
4861 if (mParent != null) {
4862 mParent.requestTransparentRegion(this);
4863 }
4864 }
4865 }
Romain Guy8506ab42009-06-11 17:35:47 -07004866
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004867
4868 @Override
4869 protected boolean fitSystemWindows(Rect insets) {
4870 boolean done = super.fitSystemWindows(insets);
4871 if (!done) {
4872 final int count = mChildrenCount;
4873 final View[] children = mChildren;
4874 for (int i = 0; i < count; i++) {
4875 done = children[i].fitSystemWindows(insets);
4876 if (done) {
4877 break;
4878 }
4879 }
4880 }
4881 return done;
4882 }
4883
4884 /**
4885 * Returns the animation listener to which layout animation events are
4886 * sent.
4887 *
4888 * @return an {@link android.view.animation.Animation.AnimationListener}
4889 */
4890 public Animation.AnimationListener getLayoutAnimationListener() {
4891 return mAnimationListener;
4892 }
4893
4894 @Override
4895 protected void drawableStateChanged() {
4896 super.drawableStateChanged();
4897
4898 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
4899 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
4900 throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
4901 + " child has duplicateParentState set to true");
4902 }
4903
4904 final View[] children = mChildren;
4905 final int count = mChildrenCount;
4906
4907 for (int i = 0; i < count; i++) {
4908 final View child = children[i];
4909 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
4910 child.refreshDrawableState();
4911 }
4912 }
4913 }
4914 }
4915
4916 @Override
Dianne Hackborne2136772010-11-04 15:08:59 -07004917 public void jumpDrawablesToCurrentState() {
4918 super.jumpDrawablesToCurrentState();
4919 final View[] children = mChildren;
4920 final int count = mChildrenCount;
4921 for (int i = 0; i < count; i++) {
4922 children[i].jumpDrawablesToCurrentState();
4923 }
4924 }
4925
4926 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004927 protected int[] onCreateDrawableState(int extraSpace) {
4928 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
4929 return super.onCreateDrawableState(extraSpace);
4930 }
4931
4932 int need = 0;
4933 int n = getChildCount();
4934 for (int i = 0; i < n; i++) {
4935 int[] childState = getChildAt(i).getDrawableState();
4936
4937 if (childState != null) {
4938 need += childState.length;
4939 }
4940 }
4941
4942 int[] state = super.onCreateDrawableState(extraSpace + need);
4943
4944 for (int i = 0; i < n; i++) {
4945 int[] childState = getChildAt(i).getDrawableState();
4946
4947 if (childState != null) {
4948 state = mergeDrawableStates(state, childState);
4949 }
4950 }
4951
4952 return state;
4953 }
4954
4955 /**
4956 * Sets whether this ViewGroup's drawable states also include
4957 * its children's drawable states. This is used, for example, to
4958 * make a group appear to be focused when its child EditText or button
4959 * is focused.
4960 */
4961 public void setAddStatesFromChildren(boolean addsStates) {
4962 if (addsStates) {
4963 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
4964 } else {
4965 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
4966 }
4967
4968 refreshDrawableState();
4969 }
4970
4971 /**
4972 * Returns whether this ViewGroup's drawable states also include
4973 * its children's drawable states. This is used, for example, to
4974 * make a group appear to be focused when its child EditText or button
4975 * is focused.
4976 */
4977 public boolean addStatesFromChildren() {
4978 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
4979 }
4980
4981 /**
4982 * If {link #addStatesFromChildren} is true, refreshes this group's
4983 * drawable state (to include the states from its children).
4984 */
4985 public void childDrawableStateChanged(View child) {
4986 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
4987 refreshDrawableState();
4988 }
4989 }
4990
4991 /**
4992 * Specifies the animation listener to which layout animation events must
4993 * be sent. Only
4994 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
4995 * and
4996 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
4997 * are invoked.
4998 *
4999 * @param animationListener the layout animation listener
5000 */
5001 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
5002 mAnimationListener = animationListener;
5003 }
5004
5005 /**
Chet Haasecca2c982011-05-20 14:34:18 -07005006 * This method is called by LayoutTransition when there are 'changing' animations that need
5007 * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
5008 * starts all pending transitions prior to the drawing phase in the current traversal.
5009 *
5010 * @param transition The LayoutTransition to be started on the next traversal.
5011 *
5012 * @hide
5013 */
5014 public void requestTransitionStart(LayoutTransition transition) {
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07005015 ViewRootImpl viewAncestor = getViewRootImpl();
Chet Haase1abf7fa2011-08-17 18:31:56 -07005016 if (viewAncestor != null) {
5017 viewAncestor.requestTransitionStart(transition);
5018 }
Chet Haasecca2c982011-05-20 14:34:18 -07005019 }
5020
Fabrice Di Meglio80dc53d2011-06-21 18:36:33 -07005021 @Override
Fabrice Di Meglio7f86c802011-07-01 15:09:24 -07005022 protected void resetResolvedLayoutDirection() {
5023 super.resetResolvedLayoutDirection();
Fabrice Di Meglio80dc53d2011-06-21 18:36:33 -07005024
5025 // Take care of resetting the children resolution too
5026 final int count = getChildCount();
5027 for (int i = 0; i < count; i++) {
5028 final View child = getChildAt(i);
5029 if (child.getLayoutDirection() == LAYOUT_DIRECTION_INHERIT) {
Fabrice Di Meglio7f86c802011-07-01 15:09:24 -07005030 child.resetResolvedLayoutDirection();
Fabrice Di Meglio80dc53d2011-06-21 18:36:33 -07005031 }
5032 }
5033 }
5034
Fabrice Di Meglio22268862011-06-27 18:13:18 -07005035 @Override
5036 protected void resetResolvedTextDirection() {
5037 super.resetResolvedTextDirection();
5038
5039 // Take care of resetting the children resolution too
5040 final int count = getChildCount();
5041 for (int i = 0; i < count; i++) {
5042 final View child = getChildAt(i);
5043 if (child.getTextDirection() == TEXT_DIRECTION_INHERIT) {
5044 child.resetResolvedTextDirection();
5045 }
5046 }
5047 }
5048
5049 /**
Patrick Dubroye0a799a2011-05-04 16:19:22 -07005050 * Return true if the pressed state should be delayed for children or descendants of this
5051 * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
5052 * This prevents the pressed state from appearing when the user is actually trying to scroll
5053 * the content.
5054 *
5055 * The default implementation returns true for compatibility reasons. Subclasses that do
5056 * not scroll should generally override this method and return false.
5057 */
5058 public boolean shouldDelayChildPressedState() {
5059 return true;
5060 }
5061
5062 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005063 * LayoutParams are used by views to tell their parents how they want to be
5064 * laid out. See
5065 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
5066 * for a list of all child view attributes that this class supports.
Romain Guy8506ab42009-06-11 17:35:47 -07005067 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005068 * <p>
5069 * The base LayoutParams class just describes how big the view wants to be
5070 * for both width and height. For each dimension, it can specify one of:
5071 * <ul>
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005072 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
5073 * means that the view wants to be as big as its parent (minus padding)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005074 * <li> WRAP_CONTENT, which means that the view wants to be just big enough
5075 * to enclose its content (plus padding)
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005076 * <li> an exact number
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005077 * </ul>
5078 * There are subclasses of LayoutParams for different subclasses of
5079 * ViewGroup. For example, AbsoluteLayout has its own subclass of
5080 * LayoutParams which adds an X and Y value.
5081 *
5082 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
5083 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
5084 */
5085 public static class LayoutParams {
5086 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005087 * Special value for the height or width requested by a View.
5088 * FILL_PARENT means that the view wants to be as big as its parent,
5089 * minus the parent's padding, if any. This value is deprecated
5090 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005091 */
Romain Guy980a9382010-01-08 15:06:28 -08005092 @SuppressWarnings({"UnusedDeclaration"})
5093 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005094 public static final int FILL_PARENT = -1;
5095
5096 /**
5097 * Special value for the height or width requested by a View.
Gilles Debunnef5c6eff2010-02-09 19:08:36 -08005098 * MATCH_PARENT means that the view wants to be as big as its parent,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005099 * minus the parent's padding, if any. Introduced in API Level 8.
Romain Guy980a9382010-01-08 15:06:28 -08005100 */
5101 public static final int MATCH_PARENT = -1;
5102
5103 /**
5104 * Special value for the height or width requested by a View.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005105 * WRAP_CONTENT means that the view wants to be just large enough to fit
5106 * its own internal content, taking its own padding into account.
5107 */
5108 public static final int WRAP_CONTENT = -2;
5109
5110 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005111 * Information about how wide the view wants to be. Can be one of the
5112 * constants FILL_PARENT (replaced by MATCH_PARENT ,
5113 * in API Level 8) or WRAP_CONTENT. or an exact size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005114 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005115 @ViewDebug.ExportedProperty(category = "layout", mapping = {
Romain Guy980a9382010-01-08 15:06:28 -08005116 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005117 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
5118 })
5119 public int width;
5120
5121 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005122 * Information about how tall the view wants to be. Can be one of the
5123 * constants FILL_PARENT (replaced by MATCH_PARENT ,
5124 * in API Level 8) or WRAP_CONTENT. or an exact size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005125 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005126 @ViewDebug.ExportedProperty(category = "layout", mapping = {
Romain Guy980a9382010-01-08 15:06:28 -08005127 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005128 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
5129 })
5130 public int height;
5131
5132 /**
5133 * Used to animate layouts.
5134 */
5135 public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
5136
5137 /**
5138 * Creates a new set of layout parameters. The values are extracted from
5139 * the supplied attributes set and context. The XML attributes mapped
5140 * to this set of layout parameters are:
5141 *
5142 * <ul>
5143 * <li><code>layout_width</code>: the width, either an exact value,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005144 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
5145 * {@link #MATCH_PARENT} in API Level 8)</li>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005146 * <li><code>layout_height</code>: the height, either an exact value,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005147 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
5148 * {@link #MATCH_PARENT} in API Level 8)</li>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005149 * </ul>
5150 *
5151 * @param c the application environment
5152 * @param attrs the set of attributes from which to extract the layout
5153 * parameters' values
5154 */
5155 public LayoutParams(Context c, AttributeSet attrs) {
5156 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
5157 setBaseAttributes(a,
5158 R.styleable.ViewGroup_Layout_layout_width,
5159 R.styleable.ViewGroup_Layout_layout_height);
5160 a.recycle();
5161 }
5162
5163 /**
5164 * Creates a new set of layout parameters with the specified width
5165 * and height.
5166 *
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005167 * @param width the width, either {@link #WRAP_CONTENT},
5168 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
5169 * API Level 8), or a fixed size in pixels
5170 * @param height the height, either {@link #WRAP_CONTENT},
5171 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
5172 * API Level 8), or a fixed size in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005173 */
5174 public LayoutParams(int width, int height) {
5175 this.width = width;
5176 this.height = height;
5177 }
5178
5179 /**
5180 * Copy constructor. Clones the width and height values of the source.
5181 *
5182 * @param source The layout params to copy from.
5183 */
5184 public LayoutParams(LayoutParams source) {
5185 this.width = source.width;
5186 this.height = source.height;
5187 }
5188
5189 /**
5190 * Used internally by MarginLayoutParams.
5191 * @hide
5192 */
5193 LayoutParams() {
5194 }
5195
5196 /**
5197 * Extracts the layout parameters from the supplied attributes.
5198 *
5199 * @param a the style attributes to extract the parameters from
5200 * @param widthAttr the identifier of the width attribute
5201 * @param heightAttr the identifier of the height attribute
5202 */
5203 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
5204 width = a.getLayoutDimension(widthAttr, "layout_width");
5205 height = a.getLayoutDimension(heightAttr, "layout_height");
5206 }
5207
5208 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005209 * Resolve layout parameters depending on the layout direction. Subclasses that care about
5210 * layoutDirection changes should override this method. The default implementation does
5211 * nothing.
5212 *
5213 * @param layoutDirection the direction of the layout
5214 *
5215 * {@link View#LAYOUT_DIRECTION_LTR}
5216 * {@link View#LAYOUT_DIRECTION_RTL}
5217 *
5218 * @hide
5219 */
5220 protected void resolveWithDirection(int layoutDirection) {
5221 }
5222
5223 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005224 * Returns a String representation of this set of layout parameters.
5225 *
5226 * @param output the String to prepend to the internal representation
5227 * @return a String with the following format: output +
5228 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
Romain Guy8506ab42009-06-11 17:35:47 -07005229 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005230 * @hide
5231 */
5232 public String debug(String output) {
5233 return output + "ViewGroup.LayoutParams={ width="
5234 + sizeToString(width) + ", height=" + sizeToString(height) + " }";
5235 }
5236
5237 /**
5238 * Converts the specified size to a readable String.
5239 *
5240 * @param size the size to convert
5241 * @return a String instance representing the supplied size
Romain Guy8506ab42009-06-11 17:35:47 -07005242 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005243 * @hide
5244 */
5245 protected static String sizeToString(int size) {
5246 if (size == WRAP_CONTENT) {
5247 return "wrap-content";
5248 }
Romain Guy980a9382010-01-08 15:06:28 -08005249 if (size == MATCH_PARENT) {
5250 return "match-parent";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005251 }
5252 return String.valueOf(size);
5253 }
5254 }
5255
5256 /**
5257 * Per-child layout information for layouts that support margins.
5258 * See
5259 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
5260 * for a list of all child view attributes that this class supports.
5261 */
5262 public static class MarginLayoutParams extends ViewGroup.LayoutParams {
5263 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005264 * The left margin in pixels of the child. Whenever this value is changed, a call to
5265 * {@link android.view.View#requestLayout()} needs to be done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005266 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005267 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005268 public int leftMargin;
5269
5270 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005271 * The top margin in pixels of the child. Whenever this value is changed, a call to
5272 * {@link android.view.View#requestLayout()} needs to be done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005273 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005274 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005275 public int topMargin;
5276
5277 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005278 * The right margin in pixels of the child. Whenever this value is changed, a call to
5279 * {@link android.view.View#requestLayout()} needs to be done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005280 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005281 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005282 public int rightMargin;
5283
5284 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005285 * The bottom margin in pixels of the child. Whenever this value is changed, a call to
5286 * {@link android.view.View#requestLayout()} needs to be done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005287 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005288 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005289 public int bottomMargin;
5290
5291 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005292 * The start margin in pixels of the child.
5293 *
5294 * @hide
5295 *
5296 */
5297 @ViewDebug.ExportedProperty(category = "layout")
5298 protected int startMargin = DEFAULT_RELATIVE;
5299
5300 /**
5301 * The end margin in pixels of the child.
5302 *
5303 * @hide
5304 */
5305 @ViewDebug.ExportedProperty(category = "layout")
5306 protected int endMargin = DEFAULT_RELATIVE;
5307
5308 /**
5309 * The default start and end margin.
5310 */
5311 static private final int DEFAULT_RELATIVE = Integer.MIN_VALUE;
5312
5313 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005314 * Creates a new set of layout parameters. The values are extracted from
5315 * the supplied attributes set and context.
5316 *
5317 * @param c the application environment
5318 * @param attrs the set of attributes from which to extract the layout
5319 * parameters' values
5320 */
5321 public MarginLayoutParams(Context c, AttributeSet attrs) {
5322 super();
5323
5324 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
5325 setBaseAttributes(a,
5326 R.styleable.ViewGroup_MarginLayout_layout_width,
5327 R.styleable.ViewGroup_MarginLayout_layout_height);
5328
5329 int margin = a.getDimensionPixelSize(
5330 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
5331 if (margin >= 0) {
5332 leftMargin = margin;
5333 topMargin = margin;
5334 rightMargin= margin;
5335 bottomMargin = margin;
5336 } else {
5337 leftMargin = a.getDimensionPixelSize(
5338 R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
5339 topMargin = a.getDimensionPixelSize(
5340 R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
5341 rightMargin = a.getDimensionPixelSize(
5342 R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
5343 bottomMargin = a.getDimensionPixelSize(
5344 R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005345 startMargin = a.getDimensionPixelSize(
5346 R.styleable.ViewGroup_MarginLayout_layout_marginStart, DEFAULT_RELATIVE);
5347 endMargin = a.getDimensionPixelSize(
5348 R.styleable.ViewGroup_MarginLayout_layout_marginEnd, DEFAULT_RELATIVE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005349 }
5350
5351 a.recycle();
5352 }
5353
5354 /**
5355 * {@inheritDoc}
5356 */
5357 public MarginLayoutParams(int width, int height) {
5358 super(width, height);
5359 }
5360
5361 /**
5362 * Copy constructor. Clones the width, height and margin values of the source.
5363 *
5364 * @param source The layout params to copy from.
5365 */
5366 public MarginLayoutParams(MarginLayoutParams source) {
5367 this.width = source.width;
5368 this.height = source.height;
5369
5370 this.leftMargin = source.leftMargin;
5371 this.topMargin = source.topMargin;
5372 this.rightMargin = source.rightMargin;
5373 this.bottomMargin = source.bottomMargin;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005374 this.startMargin = source.startMargin;
5375 this.endMargin = source.endMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005376 }
5377
5378 /**
5379 * {@inheritDoc}
5380 */
5381 public MarginLayoutParams(LayoutParams source) {
5382 super(source);
5383 }
5384
5385 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005386 * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
5387 * to be done so that the new margins are taken into account. Left and right margins may be
5388 * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005389 *
5390 * @param left the left margin size
5391 * @param top the top margin size
5392 * @param right the right margin size
5393 * @param bottom the bottom margin size
5394 *
5395 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
5396 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
5397 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
5398 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
5399 */
5400 public void setMargins(int left, int top, int right, int bottom) {
5401 leftMargin = left;
5402 topMargin = top;
5403 rightMargin = right;
5404 bottomMargin = bottom;
5405 }
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005406
5407 /**
5408 * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
5409 * needs to be done so that the new relative margins are taken into account. Left and right
5410 * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout
5411 * direction.
5412 *
5413 * @param start the start margin size
5414 * @param top the top margin size
5415 * @param end the right margin size
5416 * @param bottom the bottom margin size
5417 *
5418 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5419 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
5420 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5421 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
5422 *
5423 * @hide
5424 */
5425 public void setMarginsRelative(int start, int top, int end, int bottom) {
5426 startMargin = start;
5427 topMargin = top;
5428 endMargin = end;
5429 bottomMargin = bottom;
5430 }
5431
5432 /**
5433 * Returns the start margin in pixels.
5434 *
5435 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5436 *
5437 * @return the start margin in pixels.
5438 *
5439 * @hide
5440 */
5441 public int getMarginStart() {
5442 return startMargin;
5443 }
5444
5445 /**
5446 * Returns the end margin in pixels.
5447 *
5448 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5449 *
5450 * @return the end margin in pixels.
5451 *
5452 * @hide
5453 */
5454 public int getMarginEnd() {
5455 return endMargin;
5456 }
5457
5458 /**
5459 * Check if margins are relative.
5460 *
5461 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5462 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5463 *
5464 * @return true if either marginStart or marginEnd has been set
5465 *
5466 * @hide
5467 */
5468 public boolean isMarginRelative() {
5469 return (startMargin != DEFAULT_RELATIVE) || (endMargin != DEFAULT_RELATIVE);
5470 }
5471
5472 /**
5473 * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
5474 * maybe overriden depending on layout direction.
5475 *
5476 * @hide
5477 */
5478 @Override
5479 protected void resolveWithDirection(int layoutDirection) {
5480 switch(layoutDirection) {
5481 case View.LAYOUT_DIRECTION_RTL:
5482 leftMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : leftMargin;
5483 rightMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : rightMargin;
5484 break;
5485 case View.LAYOUT_DIRECTION_LTR:
5486 default:
5487 leftMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : leftMargin;
5488 rightMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : rightMargin;
5489 break;
5490 }
5491 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005492 }
Adam Powell2b342f02010-08-18 18:14:13 -07005493
Jeff Brown20e987b2010-08-23 12:01:02 -07005494 /* Describes a touched view and the ids of the pointers that it has captured.
5495 *
5496 * This code assumes that pointer ids are always in the range 0..31 such that
5497 * it can use a bitfield to track which pointer ids are present.
5498 * As it happens, the lower layers of the input dispatch pipeline also use the
5499 * same trick so the assumption should be safe here...
5500 */
5501 private static final class TouchTarget {
5502 private static final int MAX_RECYCLED = 32;
5503 private static final Object sRecycleLock = new Object();
5504 private static TouchTarget sRecycleBin;
5505 private static int sRecycledCount;
Adam Powell2b342f02010-08-18 18:14:13 -07005506
Jeff Brown20e987b2010-08-23 12:01:02 -07005507 public static final int ALL_POINTER_IDS = -1; // all ones
Adam Powell2b342f02010-08-18 18:14:13 -07005508
Jeff Brown20e987b2010-08-23 12:01:02 -07005509 // The touched child view.
5510 public View child;
5511
5512 // The combined bit mask of pointer ids for all pointers captured by the target.
5513 public int pointerIdBits;
5514
5515 // The next target in the target list.
5516 public TouchTarget next;
5517
5518 private TouchTarget() {
Adam Powell2b342f02010-08-18 18:14:13 -07005519 }
5520
Jeff Brown20e987b2010-08-23 12:01:02 -07005521 public static TouchTarget obtain(View child, int pointerIdBits) {
5522 final TouchTarget target;
5523 synchronized (sRecycleLock) {
Adam Powell816c3be2010-08-23 18:00:05 -07005524 if (sRecycleBin == null) {
Jeff Brown20e987b2010-08-23 12:01:02 -07005525 target = new TouchTarget();
Adam Powell816c3be2010-08-23 18:00:05 -07005526 } else {
Jeff Brown20e987b2010-08-23 12:01:02 -07005527 target = sRecycleBin;
5528 sRecycleBin = target.next;
5529 sRecycledCount--;
5530 target.next = null;
Adam Powell816c3be2010-08-23 18:00:05 -07005531 }
Adam Powell816c3be2010-08-23 18:00:05 -07005532 }
Jeff Brown20e987b2010-08-23 12:01:02 -07005533 target.child = child;
5534 target.pointerIdBits = pointerIdBits;
5535 return target;
5536 }
Adam Powell816c3be2010-08-23 18:00:05 -07005537
Jeff Brown20e987b2010-08-23 12:01:02 -07005538 public void recycle() {
5539 synchronized (sRecycleLock) {
5540 if (sRecycledCount < MAX_RECYCLED) {
5541 next = sRecycleBin;
5542 sRecycleBin = this;
5543 sRecycledCount += 1;
Patrick Dubroyfb0547d2010-10-19 17:36:18 -07005544 } else {
5545 next = null;
Adam Powell816c3be2010-08-23 18:00:05 -07005546 }
Patrick Dubroyfb0547d2010-10-19 17:36:18 -07005547 child = null;
Adam Powell816c3be2010-08-23 18:00:05 -07005548 }
5549 }
Adam Powell2b342f02010-08-18 18:14:13 -07005550 }
Jeff Brown87b7f802011-06-21 18:35:45 -07005551
5552 /* Describes a hovered view. */
5553 private static final class HoverTarget {
5554 private static final int MAX_RECYCLED = 32;
5555 private static final Object sRecycleLock = new Object();
5556 private static HoverTarget sRecycleBin;
5557 private static int sRecycledCount;
5558
5559 // The hovered child view.
5560 public View child;
5561
5562 // The next target in the target list.
5563 public HoverTarget next;
5564
5565 private HoverTarget() {
5566 }
5567
5568 public static HoverTarget obtain(View child) {
5569 final HoverTarget target;
5570 synchronized (sRecycleLock) {
5571 if (sRecycleBin == null) {
5572 target = new HoverTarget();
5573 } else {
5574 target = sRecycleBin;
5575 sRecycleBin = target.next;
5576 sRecycledCount--;
5577 target.next = null;
5578 }
5579 }
5580 target.child = child;
5581 return target;
5582 }
5583
5584 public void recycle() {
5585 synchronized (sRecycleLock) {
5586 if (sRecycledCount < MAX_RECYCLED) {
5587 next = sRecycleBin;
5588 sRecycleBin = this;
5589 sRecycledCount += 1;
5590 } else {
5591 next = null;
5592 }
5593 child = null;
5594 }
5595 }
5596 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005597}