blob: dbbcde6d0f2b20a05720f15dd5ac540fc2792c97 [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;
Philip Milne10ca24a2012-04-23 15:38:27 -070025import android.graphics.Color;
26import android.graphics.Insets;
Adam Powell6e346362010-07-23 10:18:23 -070027import android.graphics.Matrix;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.graphics.Paint;
Christopher Tatea53146c2010-09-07 11:57:52 -070029import android.graphics.PointF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.graphics.Rect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.graphics.RectF;
svetoslavganov75986cf2009-05-14 22:28:01 -070032import android.graphics.Region;
Jeff Brown995e7742010-12-22 16:59:36 -080033import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.os.Parcelable;
35import android.os.SystemClock;
36import android.util.AttributeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.util.Log;
38import android.util.SparseArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070039import android.view.accessibility.AccessibilityEvent;
Svetoslav Ganov8643aa02011-04-20 12:12:33 -070040import android.view.accessibility.AccessibilityNodeInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.view.animation.Animation;
42import android.view.animation.AnimationUtils;
43import android.view.animation.LayoutAnimationController;
44import android.view.animation.Transformation;
Doug Feltcb3791202011-07-07 11:57:48 -070045
Romain Guy0211a0a2011-02-14 16:34:59 -080046import com.android.internal.R;
47import com.android.internal.util.Predicate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
49import java.util.ArrayList;
Svetoslav Ganov42138042012-03-20 11:51:39 -070050import java.util.Collections;
Christopher Tate86cab1b2011-01-13 20:28:55 -080051import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
53/**
54 * <p>
55 * A <code>ViewGroup</code> is a special view that can contain other views
56 * (called children.) The view group is the base class for layouts and views
57 * containers. This class also defines the
58 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
59 * class for layouts parameters.
60 * </p>
61 *
62 * <p>
63 * Also see {@link LayoutParams} for layout attributes.
64 * </p>
Romain Guyd6a463a2009-05-21 23:10:10 -070065 *
Joe Fernandez558459f2011-10-13 16:47:36 -070066 * <div class="special reference">
67 * <h3>Developer Guides</h3>
68 * <p>For more information about creating user interface layouts, read the
69 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
70 * guide.</p></div>
71 *
Romain Guyd6a463a2009-05-21 23:10:10 -070072 * @attr ref android.R.styleable#ViewGroup_clipChildren
73 * @attr ref android.R.styleable#ViewGroup_clipToPadding
74 * @attr ref android.R.styleable#ViewGroup_layoutAnimation
75 * @attr ref android.R.styleable#ViewGroup_animationCache
76 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
77 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
78 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
79 * @attr ref android.R.styleable#ViewGroup_descendantFocusability
Chet Haase13cc1202010-09-03 15:39:20 -070080 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 */
82public abstract class ViewGroup extends View implements ViewParent, ViewManager {
Adam Powell539ee872012-02-03 19:00:49 -080083 private static final String TAG = "ViewGroup";
Chet Haase21cd1382010-09-01 17:42:29 -070084
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 private static final boolean DBG = false;
Gilles Debunnecea45132011-11-24 02:19:27 +010086
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 /**
88 * Views which have been hidden or removed which need to be animated on
89 * their way out.
90 * This field should be made private, so it is hidden from the SDK.
91 * {@hide}
92 */
93 protected ArrayList<View> mDisappearingChildren;
94
95 /**
96 * Listener used to propagate events indicating when children are added
97 * and/or removed from a view group.
98 * This field should be made private, so it is hidden from the SDK.
99 * {@hide}
100 */
101 protected OnHierarchyChangeListener mOnHierarchyChangeListener;
102
103 // The view contained within this ViewGroup that has or contains focus.
104 private View mFocused;
105
Chet Haase48460322010-06-11 14:22:25 -0700106 /**
107 * A Transformation used when drawing children, to
108 * apply on the child being drawn.
109 */
Chet Haase64a48c12012-02-13 16:33:29 -0800110 final Transformation mChildTransformation = new Transformation();
Chet Haase48460322010-06-11 14:22:25 -0700111
112 /**
113 * Used to track the current invalidation region.
114 */
Chet Haase64a48c12012-02-13 16:33:29 -0800115 RectF mInvalidateRegion;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116
Chet Haase48460322010-06-11 14:22:25 -0700117 /**
118 * A Transformation used to calculate a correct
119 * invalidation area when the application is autoscaled.
120 */
Chet Haase64a48c12012-02-13 16:33:29 -0800121 Transformation mInvalidationTransformation;
Chet Haase48460322010-06-11 14:22:25 -0700122
Christopher Tatea53146c2010-09-07 11:57:52 -0700123 // View currently under an ongoing drag
124 private View mCurrentDragView;
125
Christopher Tate86cab1b2011-01-13 20:28:55 -0800126 // Metadata about the ongoing drag
127 private DragEvent mCurrentDrag;
128 private HashSet<View> mDragNotifiedChildren;
129
Christopher Tatea53146c2010-09-07 11:57:52 -0700130 // Does this group have a child that can accept the current drag payload?
131 private boolean mChildAcceptsDrag;
132
133 // Used during drag dispatch
134 private final PointF mLocalPoint = new PointF();
135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 // Layout animation
137 private LayoutAnimationController mLayoutAnimationController;
138 private Animation.AnimationListener mAnimationListener;
139
Jeff Brown20e987b2010-08-23 12:01:02 -0700140 // First touch target in the linked list of touch targets.
141 private TouchTarget mFirstTouchTarget;
142
Joe Onorato03ab0c72011-01-06 15:46:27 -0800143 // For debugging only. You can see these in hierarchyviewer.
Romain Guye95003e2011-01-09 13:53:06 -0800144 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800145 @ViewDebug.ExportedProperty(category = "events")
146 private long mLastTouchDownTime;
147 @ViewDebug.ExportedProperty(category = "events")
148 private int mLastTouchDownIndex = -1;
Romain Guye95003e2011-01-09 13:53:06 -0800149 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800150 @ViewDebug.ExportedProperty(category = "events")
151 private float mLastTouchDownX;
Romain Guye95003e2011-01-09 13:53:06 -0800152 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800153 @ViewDebug.ExportedProperty(category = "events")
154 private float mLastTouchDownY;
155
Jeff Brown87b7f802011-06-21 18:35:45 -0700156 // First hover target in the linked list of hover targets.
157 // The hover targets are children which have received ACTION_HOVER_ENTER.
158 // They might not have actually handled the hover event, but we will
159 // continue sending hover events to them as long as the pointer remains over
160 // their bounds and the view group does not intercept hover.
161 private HoverTarget mFirstHoverTarget;
Jeff Browna032cc02011-03-07 16:56:21 -0800162
Jeff Brown10b62902011-06-20 16:40:37 -0700163 // True if the view group itself received a hover event.
164 // It might not have actually handled the hover event.
165 private boolean mHoveredSelf;
166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 /**
168 * Internal flags.
Romain Guy8506ab42009-06-11 17:35:47 -0700169 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 * This field should be made private, so it is hidden from the SDK.
171 * {@hide}
172 */
Romain Guy2440e672012-08-07 14:43:43 -0700173 @ViewDebug.ExportedProperty(flagMapping = {
174 @ViewDebug.FlagToString(mask = FLAG_CLIP_CHILDREN, equals = FLAG_CLIP_CHILDREN,
175 name = "CLIP_CHILDREN"),
176 @ViewDebug.FlagToString(mask = FLAG_CLIP_TO_PADDING, equals = FLAG_CLIP_TO_PADDING,
177 name = "CLIP_TO_PADDING"),
178 @ViewDebug.FlagToString(mask = FLAG_PADDING_NOT_NULL, equals = FLAG_PADDING_NOT_NULL,
179 name = "PADDING_NOT_NULL")
180 })
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 protected int mGroupFlags;
182
Philip Milne1557fd72012-04-04 23:41:34 -0700183 /*
Philip Milne7a23b492012-04-24 22:12:36 -0700184 * The layout mode: either {@link #CLIP_BOUNDS} or {@link #OPTICAL_BOUNDS}
Philip Milne1557fd72012-04-04 23:41:34 -0700185 */
Philip Milne7a23b492012-04-24 22:12:36 -0700186 private int mLayoutMode = CLIP_BOUNDS;
Philip Milne1557fd72012-04-04 23:41:34 -0700187
Romain Guy33f6beb2012-02-16 19:24:51 -0800188 /**
189 * NOTE: If you change the flags below make sure to reflect the changes
190 * the DisplayList class
191 */
192
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 // When set, ViewGroup invalidates only the child's rectangle
194 // Set by default
Chet Haase64a48c12012-02-13 16:33:29 -0800195 static final int FLAG_CLIP_CHILDREN = 0x1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196
197 // When set, ViewGroup excludes the padding area from the invalidate rectangle
198 // Set by default
199 private static final int FLAG_CLIP_TO_PADDING = 0x2;
200
201 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
202 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
Chet Haase64a48c12012-02-13 16:33:29 -0800203 static final int FLAG_INVALIDATE_REQUIRED = 0x4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204
205 // When set, dispatchDraw() will run the layout animation and unset the flag
206 private static final int FLAG_RUN_ANIMATION = 0x8;
207
208 // When set, there is either no layout animation on the ViewGroup or the layout
209 // animation is over
210 // Set by default
Chet Haase64a48c12012-02-13 16:33:29 -0800211 static final int FLAG_ANIMATION_DONE = 0x10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212
213 // If set, this ViewGroup has padding; if unset there is no padding and we don't need
214 // to clip it, even if FLAG_CLIP_TO_PADDING is set
215 private static final int FLAG_PADDING_NOT_NULL = 0x20;
216
217 // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation
218 // Set by default
219 private static final int FLAG_ANIMATION_CACHE = 0x40;
220
221 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
222 // layout animation; this avoid clobbering the hierarchy
223 // Automatically set when the layout animation starts, depending on the animation's
224 // characteristics
Chet Haase64a48c12012-02-13 16:33:29 -0800225 static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226
227 // When set, the next call to drawChild() will clear mChildTransformation's matrix
Chet Haase64a48c12012-02-13 16:33:29 -0800228 static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229
230 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
231 // the children's Bitmap caches if necessary
232 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
233 private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
234
235 /**
236 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
237 * to get the index of the child to draw for that iteration.
Romain Guy293451e2009-11-04 13:59:48 -0800238 *
239 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 */
241 protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
Romain Guy8506ab42009-06-11 17:35:47 -0700242
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 /**
244 * When set, this ViewGroup supports static transformations on children; this causes
245 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
246 * invoked when a child is drawn.
247 *
248 * Any subclass overriding
249 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
250 * set this flags in {@link #mGroupFlags}.
Romain Guy8506ab42009-06-11 17:35:47 -0700251 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 * {@hide}
253 */
254 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
255
256 // When the previous drawChild() invocation used an alpha value that was lower than
257 // 1.0 and set it in mCachePaint
Chet Haase64a48c12012-02-13 16:33:29 -0800258 static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259
260 /**
261 * When set, this ViewGroup's drawable states also include those
262 * of its children.
263 */
264 private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
265
266 /**
267 * When set, this ViewGroup tries to always draw its children using their drawing cache.
268 */
Chet Haase64a48c12012-02-13 16:33:29 -0800269 static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270
271 /**
272 * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to
273 * draw its children with their drawing cache.
274 */
Chet Haase64a48c12012-02-13 16:33:29 -0800275 static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276
277 /**
278 * When set, this group will go through its list of children to notify them of
279 * any drawable state change.
280 */
281 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
282
283 private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
284
285 /**
286 * This view will get focus before any of its descendants.
287 */
288 public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
289
290 /**
291 * This view will get focus only if none of its descendants want it.
292 */
293 public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
294
295 /**
296 * This view will block any of its descendants from getting focus, even
297 * if they are focusable.
298 */
299 public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
300
301 /**
302 * Used to map between enum in attrubutes and flag values.
303 */
304 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
305 {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
306 FOCUS_BLOCK_DESCENDANTS};
307
308 /**
309 * When set, this ViewGroup should not intercept touch events.
Adam Powell110486f2010-06-22 17:14:44 -0700310 * {@hide}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 */
Adam Powell110486f2010-06-22 17:14:44 -0700312 protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
Romain Guy8506ab42009-06-11 17:35:47 -0700313
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 /**
Adam Powell2b342f02010-08-18 18:14:13 -0700315 * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
316 */
Adam Powellf37df072010-09-17 16:22:49 -0700317 private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
Adam Powell2b342f02010-08-18 18:14:13 -0700318
319 /**
Adam Powell4b867882011-09-16 12:59:46 -0700320 * When set, this ViewGroup will not dispatch onAttachedToWindow calls
321 * to children when adding new views. This is used to prevent multiple
322 * onAttached calls when a ViewGroup adds children in its own onAttached method.
323 */
324 private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000;
325
326 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 * Indicates which types of drawing caches are to be kept in memory.
328 * This field should be made private, so it is hidden from the SDK.
329 * {@hide}
330 */
331 protected int mPersistentDrawingCache;
332
333 /**
334 * Used to indicate that no drawing cache should be kept in memory.
335 */
336 public static final int PERSISTENT_NO_CACHE = 0x0;
337
338 /**
339 * Used to indicate that the animation drawing cache should be kept in memory.
340 */
341 public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
342
343 /**
344 * Used to indicate that the scrolling drawing cache should be kept in memory.
345 */
346 public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
347
348 /**
349 * Used to indicate that all drawing caches should be kept in memory.
350 */
351 public static final int PERSISTENT_ALL_CACHES = 0x3;
352
Philip Milne1557fd72012-04-04 23:41:34 -0700353 // Layout Modes
354
Philip Milne1557fd72012-04-04 23:41:34 -0700355 /**
356 * This constant is a {@link #setLayoutMode(int) layoutMode}.
Philip Milne7a23b492012-04-24 22:12:36 -0700357 * Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top},
Philip Milne1557fd72012-04-04 23:41:34 -0700358 * {@link #getRight() right} and {@link #getBottom() bottom}.
Philip Milne7bb66c92012-05-07 11:42:57 -0700359 *
360 * @hide
Philip Milne1557fd72012-04-04 23:41:34 -0700361 */
Philip Milne7a23b492012-04-24 22:12:36 -0700362 public static final int CLIP_BOUNDS = 0;
Philip Milne1557fd72012-04-04 23:41:34 -0700363
364 /**
365 * This constant is a {@link #setLayoutMode(int) layoutMode}.
Philip Milne7a23b492012-04-24 22:12:36 -0700366 * Optical bounds describe where a widget appears to be. They sit inside the clip
367 * bounds which need to cover a larger area to allow other effects,
368 * such as shadows and glows, to be drawn.
Philip Milne7bb66c92012-05-07 11:42:57 -0700369 *
370 * @hide
Philip Milne1557fd72012-04-04 23:41:34 -0700371 */
Philip Milne7a23b492012-04-24 22:12:36 -0700372 public static final int OPTICAL_BOUNDS = 1;
Philip Milne1557fd72012-04-04 23:41:34 -0700373
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 /**
375 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
376 * are set at the same time.
377 */
378 protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
379
380 // Index of the child's left position in the mLocation array
381 private static final int CHILD_LEFT_INDEX = 0;
382 // Index of the child's top position in the mLocation array
383 private static final int CHILD_TOP_INDEX = 1;
384
385 // Child views of this ViewGroup
386 private View[] mChildren;
387 // Number of valid children in the mChildren array, the rest should be null or not
388 // considered as children
Chet Haase9c087442011-01-12 16:20:16 -0800389
390 private boolean mLayoutSuppressed = false;
391
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 private int mChildrenCount;
393
394 private static final int ARRAY_INITIAL_CAPACITY = 12;
395 private static final int ARRAY_CAPACITY_INCREMENT = 12;
396
Romain Guycbc67742012-04-27 16:12:57 -0700397 private static Paint sDebugPaint;
398 private static float[] sDebugLines;
Philip Milne604f4402012-04-24 19:27:11 -0700399
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 // Used to draw cached views
Chet Haase64a48c12012-02-13 16:33:29 -0800401 Paint mCachePaint;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402
Chet Haase21cd1382010-09-01 17:42:29 -0700403 // Used to animate add/remove changes in layout
404 private LayoutTransition mTransition;
405
406 // The set of views that are currently being transitioned. This list is used to track views
407 // being removed that should not actually be removed from the parent yet because they are
408 // being animated.
409 private ArrayList<View> mTransitioningViews;
410
Chet Haase5e25c2c2010-09-16 11:15:56 -0700411 // List of children changing visibility. This is used to potentially keep rendering
412 // views during a transition when they otherwise would have become gone/invisible
413 private ArrayList<View> mVisibilityChangingChildren;
414
Adam Powell539ee872012-02-03 19:00:49 -0800415 // Indicates how many of this container's child subtrees contain transient state
416 @ViewDebug.ExportedProperty(category = "layout")
417 private int mChildCountWithTransientState = 0;
418
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 public ViewGroup(Context context) {
420 super(context);
421 initViewGroup();
422 }
423
424 public ViewGroup(Context context, AttributeSet attrs) {
425 super(context, attrs);
426 initViewGroup();
427 initFromAttributes(context, attrs);
428 }
429
430 public ViewGroup(Context context, AttributeSet attrs, int defStyle) {
431 super(context, attrs, defStyle);
432 initViewGroup();
433 initFromAttributes(context, attrs);
434 }
435
Philip Milne10ca24a2012-04-23 15:38:27 -0700436 private boolean debugDraw() {
437 return mAttachInfo != null && mAttachInfo.mDebugLayout;
438 }
439
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 private void initViewGroup() {
441 // ViewGroup doesn't draw by default
Philip Milne10ca24a2012-04-23 15:38:27 -0700442 if (!debugDraw()) {
443 setFlags(WILL_NOT_DRAW, DRAW_MASK);
444 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 mGroupFlags |= FLAG_CLIP_CHILDREN;
446 mGroupFlags |= FLAG_CLIP_TO_PADDING;
447 mGroupFlags |= FLAG_ANIMATION_DONE;
448 mGroupFlags |= FLAG_ANIMATION_CACHE;
449 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
450
Jeff Brown995e7742010-12-22 16:59:36 -0800451 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
452 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
453 }
454
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
456
457 mChildren = new View[ARRAY_INITIAL_CAPACITY];
458 mChildrenCount = 0;
459
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
461 }
462
463 private void initFromAttributes(Context context, AttributeSet attrs) {
464 TypedArray a = context.obtainStyledAttributes(attrs,
465 R.styleable.ViewGroup);
466
467 final int N = a.getIndexCount();
468 for (int i = 0; i < N; i++) {
469 int attr = a.getIndex(i);
470 switch (attr) {
471 case R.styleable.ViewGroup_clipChildren:
472 setClipChildren(a.getBoolean(attr, true));
473 break;
474 case R.styleable.ViewGroup_clipToPadding:
475 setClipToPadding(a.getBoolean(attr, true));
476 break;
477 case R.styleable.ViewGroup_animationCache:
478 setAnimationCacheEnabled(a.getBoolean(attr, true));
479 break;
480 case R.styleable.ViewGroup_persistentDrawingCache:
481 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
482 break;
483 case R.styleable.ViewGroup_addStatesFromChildren:
484 setAddStatesFromChildren(a.getBoolean(attr, false));
485 break;
486 case R.styleable.ViewGroup_alwaysDrawnWithCache:
487 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
488 break;
489 case R.styleable.ViewGroup_layoutAnimation:
490 int id = a.getResourceId(attr, -1);
491 if (id > 0) {
492 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
493 }
494 break;
495 case R.styleable.ViewGroup_descendantFocusability:
496 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
497 break;
Adam Powell2b342f02010-08-18 18:14:13 -0700498 case R.styleable.ViewGroup_splitMotionEvents:
499 setMotionEventSplittingEnabled(a.getBoolean(attr, false));
500 break;
Chet Haase13cc1202010-09-03 15:39:20 -0700501 case R.styleable.ViewGroup_animateLayoutChanges:
502 boolean animateLayoutChanges = a.getBoolean(attr, false);
503 if (animateLayoutChanges) {
504 setLayoutTransition(new LayoutTransition());
505 }
506 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 }
508 }
509
510 a.recycle();
511 }
512
513 /**
514 * Gets the descendant focusability of this view group. The descendant
515 * focusability defines the relationship between this view group and its
516 * descendants when looking for a view to take focus in
517 * {@link #requestFocus(int, android.graphics.Rect)}.
518 *
519 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
520 * {@link #FOCUS_BLOCK_DESCENDANTS}.
521 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700522 @ViewDebug.ExportedProperty(category = "focus", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
524 @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
525 @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
526 })
527 public int getDescendantFocusability() {
528 return mGroupFlags & FLAG_MASK_FOCUSABILITY;
529 }
530
531 /**
532 * Set the descendant focusability of this view group. This defines the relationship
533 * between this view group and its descendants when looking for a view to
534 * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
535 *
536 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
537 * {@link #FOCUS_BLOCK_DESCENDANTS}.
538 */
539 public void setDescendantFocusability(int focusability) {
540 switch (focusability) {
541 case FOCUS_BEFORE_DESCENDANTS:
542 case FOCUS_AFTER_DESCENDANTS:
543 case FOCUS_BLOCK_DESCENDANTS:
544 break;
545 default:
546 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
547 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
548 }
549 mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
550 mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
551 }
552
553 /**
554 * {@inheritDoc}
555 */
556 @Override
557 void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
558 if (mFocused != null) {
559 mFocused.unFocus();
560 mFocused = null;
561 }
562 super.handleFocusGainInternal(direction, previouslyFocusedRect);
563 }
564
565 /**
566 * {@inheritDoc}
567 */
568 public void requestChildFocus(View child, View focused) {
569 if (DBG) {
570 System.out.println(this + " requestChildFocus()");
571 }
572 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
573 return;
574 }
575
576 // Unfocus us, if necessary
577 super.unFocus();
578
579 // We had a previous notion of who had focus. Clear it.
580 if (mFocused != child) {
581 if (mFocused != null) {
582 mFocused.unFocus();
583 }
584
585 mFocused = child;
586 }
587 if (mParent != null) {
588 mParent.requestChildFocus(this, focused);
589 }
590 }
591
592 /**
593 * {@inheritDoc}
594 */
595 public void focusableViewAvailable(View v) {
596 if (mParent != null
597 // shortcut: don't report a new focusable view if we block our descendants from
598 // getting focus
599 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
600 // shortcut: don't report a new focusable view if we already are focused
601 // (and we don't prefer our descendants)
602 //
603 // note: knowing that mFocused is non-null is not a good enough reason
604 // to break the traversal since in that case we'd actually have to find
605 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
Joe Onoratoc6cc0f82011-04-12 11:53:13 -0700606 // an ancestor of v; this will get checked for at ViewAncestor
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
608 mParent.focusableViewAvailable(v);
609 }
610 }
611
612 /**
613 * {@inheritDoc}
614 */
615 public boolean showContextMenuForChild(View originalView) {
616 return mParent != null && mParent.showContextMenuForChild(originalView);
617 }
618
619 /**
Adam Powell6e346362010-07-23 10:18:23 -0700620 * {@inheritDoc}
621 */
622 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
623 return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null;
624 }
625
626 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 * Find the nearest view in the specified direction that wants to take
628 * focus.
629 *
630 * @param focused The view that currently has focus
631 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
632 * FOCUS_RIGHT, or 0 for not applicable.
633 */
634 public View focusSearch(View focused, int direction) {
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700635 if (isRootNamespace()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 // root namespace means we should consider ourselves the top of the
637 // tree for focus searching; otherwise we could be focus searching
638 // into other tabs. see LocalActivityManager and TabHost for more info
639 return FocusFinder.getInstance().findNextFocus(this, focused, direction);
640 } else if (mParent != null) {
641 return mParent.focusSearch(focused, direction);
642 }
643 return null;
644 }
645
646 /**
647 * {@inheritDoc}
648 */
649 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
650 return false;
651 }
652
653 /**
654 * {@inheritDoc}
655 */
Svetoslav Ganov42138042012-03-20 11:51:39 -0700656 @Override
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700657 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700658 ViewParent parent = mParent;
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700659 if (parent == null) {
660 return false;
661 }
662 final boolean propagate = onRequestSendAccessibilityEvent(child, event);
663 if (!propagate) {
664 return false;
665 }
666 return parent.requestSendAccessibilityEvent(this, event);
667 }
668
669 /**
670 * Called when a child has requested sending an {@link AccessibilityEvent} and
671 * gives an opportunity to its parent to augment the event.
Svetoslav Ganov031d9c12011-09-09 16:41:13 -0700672 * <p>
Adam Powell2fcbbd02011-09-28 18:56:43 -0700673 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
674 * {@link android.view.View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
675 * {@link android.view.View.AccessibilityDelegate#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)}
Svetoslav Ganov031d9c12011-09-09 16:41:13 -0700676 * is responsible for handling this call.
677 * </p>
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700678 *
679 * @param child The child which requests sending the event.
680 * @param event The event to be sent.
681 * @return True if the event should be sent.
682 *
683 * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
684 */
685 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
Svetoslav Ganov031d9c12011-09-09 16:41:13 -0700686 if (mAccessibilityDelegate != null) {
687 return mAccessibilityDelegate.onRequestSendAccessibilityEvent(this, child, event);
688 } else {
689 return onRequestSendAccessibilityEventInternal(child, event);
690 }
691 }
692
693 /**
694 * @see #onRequestSendAccessibilityEvent(View, AccessibilityEvent)
695 *
696 * Note: Called from the default {@link View.AccessibilityDelegate}.
697 */
698 boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700699 return true;
700 }
701
702 /**
Adam Powell539ee872012-02-03 19:00:49 -0800703 * Called when a child view has changed whether or not it is tracking transient state.
704 *
705 * @hide
706 */
707 public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
708 final boolean oldHasTransientState = hasTransientState();
709 if (childHasTransientState) {
710 mChildCountWithTransientState++;
711 } else {
712 mChildCountWithTransientState--;
713 }
714
715 final boolean newHasTransientState = hasTransientState();
716 if (mParent != null && oldHasTransientState != newHasTransientState) {
717 try {
718 mParent.childHasTransientStateChanged(this, newHasTransientState);
719 } catch (AbstractMethodError e) {
720 Log.e(TAG, mParent.getClass().getSimpleName() +
721 " does not fully implement ViewParent", e);
722 }
723 }
724 }
725
726 /**
727 * @hide
728 */
729 @Override
730 public boolean hasTransientState() {
731 return mChildCountWithTransientState > 0 || super.hasTransientState();
732 }
733
734 /**
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700735 * {@inheritDoc}
736 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 @Override
738 public boolean dispatchUnhandledMove(View focused, int direction) {
739 return mFocused != null &&
740 mFocused.dispatchUnhandledMove(focused, direction);
741 }
742
743 /**
744 * {@inheritDoc}
745 */
746 public void clearChildFocus(View child) {
747 if (DBG) {
748 System.out.println(this + " clearChildFocus()");
749 }
750
751 mFocused = null;
752 if (mParent != null) {
753 mParent.clearChildFocus(this);
754 }
755 }
756
757 /**
758 * {@inheritDoc}
759 */
760 @Override
761 public void clearFocus() {
Svetoslav Ganovb36a0ac2012-02-14 17:46:47 -0800762 if (DBG) {
763 System.out.println(this + " clearFocus()");
764 }
765 if (mFocused == null) {
766 super.clearFocus();
767 } else {
Svetoslav Ganovb552d892012-06-02 14:35:02 -0700768 View focused = mFocused;
Svetoslav Ganovb36a0ac2012-02-14 17:46:47 -0800769 mFocused = null;
Svetoslav Ganovb552d892012-06-02 14:35:02 -0700770 focused.clearFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 }
772 }
773
774 /**
775 * {@inheritDoc}
776 */
777 @Override
778 void unFocus() {
779 if (DBG) {
780 System.out.println(this + " unFocus()");
781 }
Svetoslav Ganovb36a0ac2012-02-14 17:46:47 -0800782 if (mFocused == null) {
783 super.unFocus();
784 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 mFocused.unFocus();
Svetoslav Ganovb36a0ac2012-02-14 17:46:47 -0800786 mFocused = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 }
789
790 /**
791 * Returns the focused child of this view, if any. The child may have focus
792 * or contain focus.
793 *
794 * @return the focused child or null.
795 */
796 public View getFocusedChild() {
797 return mFocused;
798 }
799
800 /**
801 * Returns true if this view has or contains focus
802 *
803 * @return true if this view has or contains focus
804 */
805 @Override
806 public boolean hasFocus() {
Dianne Hackborn4702a852012-08-17 15:18:29 -0700807 return (mPrivateFlags & PFLAG_FOCUSED) != 0 || mFocused != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 }
809
810 /*
811 * (non-Javadoc)
812 *
813 * @see android.view.View#findFocus()
814 */
815 @Override
816 public View findFocus() {
817 if (DBG) {
818 System.out.println("Find focus in " + this + ": flags="
819 + isFocused() + ", child=" + mFocused);
820 }
821
822 if (isFocused()) {
823 return this;
824 }
825
826 if (mFocused != null) {
827 return mFocused.findFocus();
828 }
829 return null;
830 }
831
832 /**
833 * {@inheritDoc}
834 */
835 @Override
836 public boolean hasFocusable() {
837 if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
838 return false;
839 }
840
841 if (isFocusable()) {
842 return true;
843 }
844
845 final int descendantFocusability = getDescendantFocusability();
846 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
847 final int count = mChildrenCount;
848 final View[] children = mChildren;
849
850 for (int i = 0; i < count; i++) {
851 final View child = children[i];
852 if (child.hasFocusable()) {
853 return true;
854 }
855 }
856 }
857
858 return false;
859 }
860
861 /**
862 * {@inheritDoc}
863 */
864 @Override
svetoslavganov75986cf2009-05-14 22:28:01 -0700865 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 final int focusableCount = views.size();
867
868 final int descendantFocusability = getDescendantFocusability();
869
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700870 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 final int count = mChildrenCount;
872 final View[] children = mChildren;
873
874 for (int i = 0; i < count; i++) {
875 final View child = children[i];
876 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700877 child.addFocusables(views, direction, focusableMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 }
879 }
880 }
881
882 // we add ourselves (if focusable) in all cases except for when we are
883 // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
884 // to avoid the focus search finding layouts when a more precise search
885 // among the focusable children would be more interesting.
Svetoslav Ganove5dfa47d2012-05-08 15:58:32 -0700886 if (descendantFocusability != FOCUS_AFTER_DESCENDANTS
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 // No focusable descendants
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700888 || (focusableCount == views.size())) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700889 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 }
891 }
892
Svetoslav Ganov8643aa02011-04-20 12:12:33 -0700893 @Override
Svetoslav Ganovea515ae2011-09-14 18:15:32 -0700894 public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) {
895 super.findViewsWithText(outViews, text, flags);
Svetoslav Ganov8643aa02011-04-20 12:12:33 -0700896 final int childrenCount = mChildrenCount;
897 final View[] children = mChildren;
898 for (int i = 0; i < childrenCount; i++) {
899 View child = children[i];
Svetoslav Ganovea515ae2011-09-14 18:15:32 -0700900 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
Dianne Hackborn4702a852012-08-17 15:18:29 -0700901 && (child.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
Svetoslav Ganovea515ae2011-09-14 18:15:32 -0700902 child.findViewsWithText(outViews, text, flags);
Svetoslav Ganov8643aa02011-04-20 12:12:33 -0700903 }
904 }
905 }
906
Svetoslav Ganov2cdedff2011-10-03 14:18:42 -0700907 @Override
908 View findViewByAccessibilityIdTraversal(int accessibilityId) {
909 View foundView = super.findViewByAccessibilityIdTraversal(accessibilityId);
910 if (foundView != null) {
911 return foundView;
912 }
913 final int childrenCount = mChildrenCount;
914 final View[] children = mChildren;
915 for (int i = 0; i < childrenCount; i++) {
916 View child = children[i];
917 foundView = child.findViewByAccessibilityIdTraversal(accessibilityId);
918 if (foundView != null) {
919 return foundView;
920 }
921 }
922 return null;
923 }
924
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925 /**
926 * {@inheritDoc}
927 */
928 @Override
929 public void dispatchWindowFocusChanged(boolean hasFocus) {
930 super.dispatchWindowFocusChanged(hasFocus);
931 final int count = mChildrenCount;
932 final View[] children = mChildren;
933 for (int i = 0; i < count; i++) {
934 children[i].dispatchWindowFocusChanged(hasFocus);
935 }
936 }
937
938 /**
939 * {@inheritDoc}
940 */
941 @Override
942 public void addTouchables(ArrayList<View> views) {
943 super.addTouchables(views);
944
945 final int count = mChildrenCount;
946 final View[] children = mChildren;
947
948 for (int i = 0; i < count; i++) {
949 final View child = children[i];
950 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
951 child.addTouchables(views);
952 }
953 }
954 }
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700955
956 /**
957 * @hide
958 */
959 @Override
960 public void makeOptionalFitsSystemWindows() {
961 super.makeOptionalFitsSystemWindows();
962 final int count = mChildrenCount;
963 final View[] children = mChildren;
964 for (int i = 0; i < count; i++) {
965 children[i].makeOptionalFitsSystemWindows();
966 }
967 }
968
Romain Guy43c9cdf2010-01-27 13:53:55 -0800969 /**
970 * {@inheritDoc}
971 */
972 @Override
973 public void dispatchDisplayHint(int hint) {
974 super.dispatchDisplayHint(hint);
975 final int count = mChildrenCount;
976 final View[] children = mChildren;
977 for (int i = 0; i < count; i++) {
978 children[i].dispatchDisplayHint(hint);
979 }
980 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981
982 /**
Chet Haase0d299362012-01-26 10:51:48 -0800983 * Called when a view's visibility has changed. Notify the parent to take any appropriate
984 * action.
985 *
986 * @param child The view whose visibility has changed
987 * @param oldVisibility The previous visibility value (GONE, INVISIBLE, or VISIBLE).
988 * @param newVisibility The new visibility value (GONE, INVISIBLE, or VISIBLE).
Chet Haase5e25c2c2010-09-16 11:15:56 -0700989 * @hide
Chet Haase5e25c2c2010-09-16 11:15:56 -0700990 */
Chet Haase0d299362012-01-26 10:51:48 -0800991 protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
Chet Haase5e25c2c2010-09-16 11:15:56 -0700992 if (mTransition != null) {
Chet Haase0d299362012-01-26 10:51:48 -0800993 if (newVisibility == VISIBLE) {
994 mTransition.showChild(this, child, oldVisibility);
Chet Haase5e25c2c2010-09-16 11:15:56 -0700995 } else {
Chet Haase0d299362012-01-26 10:51:48 -0800996 mTransition.hideChild(this, child, newVisibility);
Chet Haase5e25c2c2010-09-16 11:15:56 -0700997 if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
Chet Haaseddbb3462012-06-19 13:54:29 -0700998 // Only track this on disappearing views - appearing views are already visible
999 // and don't need special handling during drawChild()
1000 if (mVisibilityChangingChildren == null) {
1001 mVisibilityChangingChildren = new ArrayList<View>();
1002 }
1003 mVisibilityChangingChildren.add(child);
Chet Haase5e25c2c2010-09-16 11:15:56 -07001004 addDisappearingView(child);
1005 }
1006 }
1007 }
Christopher Tate86cab1b2011-01-13 20:28:55 -08001008
1009 // in all cases, for drags
1010 if (mCurrentDrag != null) {
Chet Haase0d299362012-01-26 10:51:48 -08001011 if (newVisibility == VISIBLE) {
Christopher Tate86cab1b2011-01-13 20:28:55 -08001012 notifyChildOfDrag(child);
1013 }
1014 }
Chet Haase5e25c2c2010-09-16 11:15:56 -07001015 }
1016
1017 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 * {@inheritDoc}
1019 */
1020 @Override
Adam Powell326d8082009-12-09 15:10:07 -08001021 protected void dispatchVisibilityChanged(View changedView, int visibility) {
1022 super.dispatchVisibilityChanged(changedView, visibility);
1023 final int count = mChildrenCount;
1024 final View[] children = mChildren;
1025 for (int i = 0; i < count; i++) {
1026 children[i].dispatchVisibilityChanged(changedView, visibility);
1027 }
1028 }
1029
1030 /**
1031 * {@inheritDoc}
1032 */
1033 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 public void dispatchWindowVisibilityChanged(int visibility) {
1035 super.dispatchWindowVisibilityChanged(visibility);
1036 final int count = mChildrenCount;
1037 final View[] children = mChildren;
1038 for (int i = 0; i < count; i++) {
1039 children[i].dispatchWindowVisibilityChanged(visibility);
1040 }
1041 }
1042
1043 /**
1044 * {@inheritDoc}
1045 */
Dianne Hackborne36d6e22010-02-17 19:46:25 -08001046 @Override
1047 public void dispatchConfigurationChanged(Configuration newConfig) {
1048 super.dispatchConfigurationChanged(newConfig);
1049 final int count = mChildrenCount;
1050 final View[] children = mChildren;
1051 for (int i = 0; i < count; i++) {
1052 children[i].dispatchConfigurationChanged(newConfig);
1053 }
1054 }
1055
1056 /**
1057 * {@inheritDoc}
1058 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 public void recomputeViewAttributes(View child) {
Joe Onorato664644d2011-01-23 17:53:23 -08001060 if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
1061 ViewParent parent = mParent;
1062 if (parent != null) parent.recomputeViewAttributes(this);
1063 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064 }
Romain Guy8506ab42009-06-11 17:35:47 -07001065
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 @Override
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -07001067 void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {
1068 if ((visibility & VISIBILITY_MASK) == VISIBLE) {
1069 super.dispatchCollectViewAttributes(attachInfo, visibility);
1070 final int count = mChildrenCount;
1071 final View[] children = mChildren;
1072 for (int i = 0; i < count; i++) {
1073 final View child = children[i];
1074 child.dispatchCollectViewAttributes(attachInfo,
1075 visibility | (child.mViewFlags&VISIBILITY_MASK));
1076 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 }
1078 }
1079
1080 /**
1081 * {@inheritDoc}
1082 */
1083 public void bringChildToFront(View child) {
1084 int index = indexOfChild(child);
1085 if (index >= 0) {
1086 removeFromArray(index);
1087 addInArray(child, mChildrenCount);
1088 child.mParent = this;
1089 }
1090 }
1091
1092 /**
1093 * {@inheritDoc}
Christopher Tatea53146c2010-09-07 11:57:52 -07001094 */
Steve Block8a7259b2012-03-01 11:24:41 +00001095 // TODO: Write real docs
Christopher Tatea53146c2010-09-07 11:57:52 -07001096 @Override
1097 public boolean dispatchDragEvent(DragEvent event) {
1098 boolean retval = false;
1099 final float tx = event.mX;
1100 final float ty = event.mY;
1101
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07001102 ViewRootImpl root = getViewRootImpl();
Christopher Tatea53146c2010-09-07 11:57:52 -07001103
1104 // Dispatch down the view hierarchy
1105 switch (event.mAction) {
1106 case DragEvent.ACTION_DRAG_STARTED: {
1107 // clear state to recalculate which views we drag over
Chris Tate9d1ab882010-11-02 15:55:39 -07001108 mCurrentDragView = null;
Christopher Tatea53146c2010-09-07 11:57:52 -07001109
Christopher Tate86cab1b2011-01-13 20:28:55 -08001110 // Set up our tracking of drag-started notifications
1111 mCurrentDrag = DragEvent.obtain(event);
1112 if (mDragNotifiedChildren == null) {
1113 mDragNotifiedChildren = new HashSet<View>();
1114 } else {
1115 mDragNotifiedChildren.clear();
1116 }
1117
Christopher Tatea53146c2010-09-07 11:57:52 -07001118 // Now dispatch down to our children, caching the responses
1119 mChildAcceptsDrag = false;
1120 final int count = mChildrenCount;
1121 final View[] children = mChildren;
1122 for (int i = 0; i < count; i++) {
Christopher Tate2c095f32010-10-04 14:13:40 -07001123 final View child = children[i];
Christopher Tate3d4bf172011-03-28 16:16:46 -07001124 child.mPrivateFlags2 &= ~View.DRAG_MASK;
Christopher Tate2c095f32010-10-04 14:13:40 -07001125 if (child.getVisibility() == VISIBLE) {
Christopher Tate86cab1b2011-01-13 20:28:55 -08001126 final boolean handled = notifyChildOfDrag(children[i]);
Christopher Tate2c095f32010-10-04 14:13:40 -07001127 if (handled) {
1128 mChildAcceptsDrag = true;
1129 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001130 }
1131 }
1132
1133 // Return HANDLED if one of our children can accept the drag
1134 if (mChildAcceptsDrag) {
1135 retval = true;
1136 }
1137 } break;
1138
1139 case DragEvent.ACTION_DRAG_ENDED: {
Christopher Tate86cab1b2011-01-13 20:28:55 -08001140 // Release the bookkeeping now that the drag lifecycle has ended
Christopher Tate1fc014f2011-01-19 12:56:26 -08001141 if (mDragNotifiedChildren != null) {
1142 for (View child : mDragNotifiedChildren) {
1143 // If a child was notified about an ongoing drag, it's told that it's over
1144 child.dispatchDragEvent(event);
Christopher Tate3d4bf172011-03-28 16:16:46 -07001145 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1146 child.refreshDrawableState();
Christopher Tate1fc014f2011-01-19 12:56:26 -08001147 }
1148
1149 mDragNotifiedChildren.clear();
1150 mCurrentDrag.recycle();
1151 mCurrentDrag = null;
1152 }
Christopher Tate86cab1b2011-01-13 20:28:55 -08001153
Christopher Tatea53146c2010-09-07 11:57:52 -07001154 // We consider drag-ended to have been handled if one of our children
1155 // had offered to handle the drag.
1156 if (mChildAcceptsDrag) {
1157 retval = true;
1158 }
1159 } break;
1160
1161 case DragEvent.ACTION_DRAG_LOCATION: {
1162 // Find the [possibly new] drag target
1163 final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
1164
1165 // If we've changed apparent drag target, tell the view root which view
Chris Tate9d1ab882010-11-02 15:55:39 -07001166 // we're over now [for purposes of the eventual drag-recipient-changed
1167 // notifications to the framework] and tell the new target that the drag
1168 // has entered its bounds. The root will see setDragFocus() calls all
1169 // the way down to the final leaf view that is handling the LOCATION event
1170 // before reporting the new potential recipient to the framework.
Christopher Tatea53146c2010-09-07 11:57:52 -07001171 if (mCurrentDragView != target) {
Chris Tate9d1ab882010-11-02 15:55:39 -07001172 root.setDragFocus(target);
1173
1174 final int action = event.mAction;
1175 // If we've dragged off of a child view, send it the EXITED message
1176 if (mCurrentDragView != null) {
Christopher Tate3d4bf172011-03-28 16:16:46 -07001177 final View view = mCurrentDragView;
Chris Tate9d1ab882010-11-02 15:55:39 -07001178 event.mAction = DragEvent.ACTION_DRAG_EXITED;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001179 view.dispatchDragEvent(event);
Dianne Hackborn4702a852012-08-17 15:18:29 -07001180 view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001181 view.refreshDrawableState();
Chris Tate9d1ab882010-11-02 15:55:39 -07001182 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001183 mCurrentDragView = target;
Chris Tate9d1ab882010-11-02 15:55:39 -07001184
1185 // If we've dragged over a new child view, send it the ENTERED message
1186 if (target != null) {
1187 event.mAction = DragEvent.ACTION_DRAG_ENTERED;
1188 target.dispatchDragEvent(event);
Dianne Hackborn4702a852012-08-17 15:18:29 -07001189 target.mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001190 target.refreshDrawableState();
Chris Tate9d1ab882010-11-02 15:55:39 -07001191 }
1192 event.mAction = action; // restore the event's original state
Christopher Tatea53146c2010-09-07 11:57:52 -07001193 }
Christopher Tate2c095f32010-10-04 14:13:40 -07001194
Christopher Tatea53146c2010-09-07 11:57:52 -07001195 // Dispatch the actual drag location notice, localized into its coordinates
1196 if (target != null) {
1197 event.mX = mLocalPoint.x;
1198 event.mY = mLocalPoint.y;
1199
1200 retval = target.dispatchDragEvent(event);
1201
1202 event.mX = tx;
1203 event.mY = ty;
1204 }
1205 } break;
1206
Chris Tate9d1ab882010-11-02 15:55:39 -07001207 /* Entered / exited dispatch
1208 *
1209 * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is
1210 * that we're about to get the corresponding LOCATION event, which we will use to
1211 * determine which of our children is the new target; at that point we will
1212 * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
1213 *
1214 * DRAG_EXITED *is* dispatched all the way down immediately: once we know the
1215 * drag has left this ViewGroup, we know by definition that every contained subview
1216 * is also no longer under the drag point.
1217 */
1218
1219 case DragEvent.ACTION_DRAG_EXITED: {
1220 if (mCurrentDragView != null) {
Christopher Tate3d4bf172011-03-28 16:16:46 -07001221 final View view = mCurrentDragView;
1222 view.dispatchDragEvent(event);
Dianne Hackborn4702a852012-08-17 15:18:29 -07001223 view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001224 view.refreshDrawableState();
1225
Chris Tate9d1ab882010-11-02 15:55:39 -07001226 mCurrentDragView = null;
1227 }
1228 } break;
1229
Christopher Tatea53146c2010-09-07 11:57:52 -07001230 case DragEvent.ACTION_DROP: {
Christopher Tate2c095f32010-10-04 14:13:40 -07001231 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
Christopher Tatea53146c2010-09-07 11:57:52 -07001232 View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
1233 if (target != null) {
Christopher Tate5ada6cb2010-10-05 14:15:29 -07001234 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target);
Christopher Tatea53146c2010-09-07 11:57:52 -07001235 event.mX = mLocalPoint.x;
1236 event.mY = mLocalPoint.y;
1237 retval = target.dispatchDragEvent(event);
1238 event.mX = tx;
1239 event.mY = ty;
Christopher Tate5ada6cb2010-10-05 14:15:29 -07001240 } else {
1241 if (ViewDebug.DEBUG_DRAG) {
1242 Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view");
1243 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001244 }
1245 } break;
1246 }
1247
1248 // If none of our children could handle the event, try here
1249 if (!retval) {
Chris Tate32affef2010-10-18 15:29:21 -07001250 // Call up to the View implementation that dispatches to installed listeners
1251 retval = super.dispatchDragEvent(event);
Christopher Tatea53146c2010-09-07 11:57:52 -07001252 }
1253 return retval;
1254 }
1255
1256 // Find the frontmost child view that lies under the given point, and calculate
1257 // the position within its own local coordinate system.
1258 View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001259 final int count = mChildrenCount;
1260 final View[] children = mChildren;
1261 for (int i = count - 1; i >= 0; i--) {
1262 final View child = children[i];
Christopher Tate3d4bf172011-03-28 16:16:46 -07001263 if (!child.canAcceptDrag()) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001264 continue;
1265 }
1266
Christopher Tate2c095f32010-10-04 14:13:40 -07001267 if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001268 return child;
1269 }
1270 }
1271 return null;
1272 }
1273
Christopher Tate86cab1b2011-01-13 20:28:55 -08001274 boolean notifyChildOfDrag(View child) {
1275 if (ViewDebug.DEBUG_DRAG) {
1276 Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
1277 }
1278
Christopher Tate3d4bf172011-03-28 16:16:46 -07001279 boolean canAccept = false;
Christopher Tate86cab1b2011-01-13 20:28:55 -08001280 if (! mDragNotifiedChildren.contains(child)) {
1281 mDragNotifiedChildren.add(child);
Christopher Tate3d4bf172011-03-28 16:16:46 -07001282 canAccept = child.dispatchDragEvent(mCurrentDrag);
1283 if (canAccept && !child.canAcceptDrag()) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07001284 child.mPrivateFlags2 |= View.PFLAG2_DRAG_CAN_ACCEPT;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001285 child.refreshDrawableState();
1286 }
Christopher Tate86cab1b2011-01-13 20:28:55 -08001287 }
Christopher Tate3d4bf172011-03-28 16:16:46 -07001288 return canAccept;
Christopher Tate86cab1b2011-01-13 20:28:55 -08001289 }
1290
Joe Onorato664644d2011-01-23 17:53:23 -08001291 @Override
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -07001292 public void dispatchWindowSystemUiVisiblityChanged(int visible) {
1293 super.dispatchWindowSystemUiVisiblityChanged(visible);
1294
1295 final int count = mChildrenCount;
1296 final View[] children = mChildren;
1297 for (int i=0; i <count; i++) {
1298 final View child = children[i];
1299 child.dispatchWindowSystemUiVisiblityChanged(visible);
1300 }
1301 }
1302
1303 @Override
Joe Onorato664644d2011-01-23 17:53:23 -08001304 public void dispatchSystemUiVisibilityChanged(int visible) {
1305 super.dispatchSystemUiVisibilityChanged(visible);
1306
1307 final int count = mChildrenCount;
1308 final View[] children = mChildren;
1309 for (int i=0; i <count; i++) {
1310 final View child = children[i];
1311 child.dispatchSystemUiVisibilityChanged(visible);
1312 }
1313 }
1314
Dianne Hackborn9a230e02011-10-06 11:51:27 -07001315 @Override
Dianne Hackborncf675782012-05-10 15:07:24 -07001316 boolean updateLocalSystemUiVisibility(int localValue, int localChanges) {
1317 boolean changed = super.updateLocalSystemUiVisibility(localValue, localChanges);
Dianne Hackborn9a230e02011-10-06 11:51:27 -07001318
1319 final int count = mChildrenCount;
1320 final View[] children = mChildren;
1321 for (int i=0; i <count; i++) {
1322 final View child = children[i];
Dianne Hackborncf675782012-05-10 15:07:24 -07001323 changed |= child.updateLocalSystemUiVisibility(localValue, localChanges);
Dianne Hackborn9a230e02011-10-06 11:51:27 -07001324 }
Dianne Hackborncf675782012-05-10 15:07:24 -07001325 return changed;
Dianne Hackborn9a230e02011-10-06 11:51:27 -07001326 }
1327
Christopher Tatea53146c2010-09-07 11:57:52 -07001328 /**
1329 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 */
1331 @Override
1332 public boolean dispatchKeyEventPreIme(KeyEvent event) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07001333 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1334 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001335 return super.dispatchKeyEventPreIme(event);
Dianne Hackborn4702a852012-08-17 15:18:29 -07001336 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1337 == PFLAG_HAS_BOUNDS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001338 return mFocused.dispatchKeyEventPreIme(event);
1339 }
1340 return false;
1341 }
1342
1343 /**
1344 * {@inheritDoc}
1345 */
1346 @Override
1347 public boolean dispatchKeyEvent(KeyEvent event) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001348 if (mInputEventConsistencyVerifier != null) {
1349 mInputEventConsistencyVerifier.onKeyEvent(event, 1);
1350 }
1351
Dianne Hackborn4702a852012-08-17 15:18:29 -07001352 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1353 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001354 if (super.dispatchKeyEvent(event)) {
1355 return true;
1356 }
Dianne Hackborn4702a852012-08-17 15:18:29 -07001357 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1358 == PFLAG_HAS_BOUNDS) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001359 if (mFocused.dispatchKeyEvent(event)) {
1360 return true;
1361 }
1362 }
1363
1364 if (mInputEventConsistencyVerifier != null) {
1365 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001366 }
1367 return false;
1368 }
1369
1370 /**
1371 * {@inheritDoc}
1372 */
1373 @Override
1374 public boolean dispatchKeyShortcutEvent(KeyEvent event) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07001375 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1376 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377 return super.dispatchKeyShortcutEvent(event);
Dianne Hackborn4702a852012-08-17 15:18:29 -07001378 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1379 == PFLAG_HAS_BOUNDS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 return mFocused.dispatchKeyShortcutEvent(event);
1381 }
1382 return false;
1383 }
1384
1385 /**
1386 * {@inheritDoc}
1387 */
1388 @Override
1389 public boolean dispatchTrackballEvent(MotionEvent event) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001390 if (mInputEventConsistencyVerifier != null) {
1391 mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
1392 }
1393
Dianne Hackborn4702a852012-08-17 15:18:29 -07001394 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1395 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001396 if (super.dispatchTrackballEvent(event)) {
1397 return true;
1398 }
Dianne Hackborn4702a852012-08-17 15:18:29 -07001399 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1400 == PFLAG_HAS_BOUNDS) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001401 if (mFocused.dispatchTrackballEvent(event)) {
1402 return true;
1403 }
1404 }
1405
1406 if (mInputEventConsistencyVerifier != null) {
1407 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001408 }
1409 return false;
1410 }
1411
Jeff Brown10b62902011-06-20 16:40:37 -07001412 /**
1413 * {@inheritDoc}
1414 */
Romain Guya9489272011-06-22 20:58:11 -07001415 @SuppressWarnings({"ConstantConditions"})
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001416 @Override
Jeff Browna032cc02011-03-07 16:56:21 -08001417 protected boolean dispatchHoverEvent(MotionEvent event) {
Jeff Browna032cc02011-03-07 16:56:21 -08001418 final int action = event.getAction();
Jeff Browna032cc02011-03-07 16:56:21 -08001419
Jeff Brown10b62902011-06-20 16:40:37 -07001420 // First check whether the view group wants to intercept the hover event.
1421 final boolean interceptHover = onInterceptHoverEvent(event);
1422 event.setAction(action); // restore action in case it was changed
1423
Jeff Brown87b7f802011-06-21 18:35:45 -07001424 MotionEvent eventNoHistory = event;
1425 boolean handled = false;
1426
1427 // Send events to the hovered children and build a new list of hover targets until
1428 // one is found that handles the event.
1429 HoverTarget firstOldHoverTarget = mFirstHoverTarget;
1430 mFirstHoverTarget = null;
Jeff Brown10b62902011-06-20 16:40:37 -07001431 if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) {
Jeff Browna032cc02011-03-07 16:56:21 -08001432 final float x = event.getX();
1433 final float y = event.getY();
Jeff Brown33bbfd22011-02-24 20:55:35 -08001434 final int childrenCount = mChildrenCount;
1435 if (childrenCount != 0) {
1436 final View[] children = mChildren;
Jeff Brown87b7f802011-06-21 18:35:45 -07001437 HoverTarget lastHoverTarget = null;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001438 for (int i = childrenCount - 1; i >= 0; i--) {
1439 final View child = children[i];
Jeff Brown87b7f802011-06-21 18:35:45 -07001440 if (!canViewReceivePointerEvents(child)
1441 || !isTransformedTouchPointInView(x, y, child, null)) {
1442 continue;
1443 }
1444
1445 // Obtain a hover target for this child. Dequeue it from the
1446 // old hover target list if the child was previously hovered.
1447 HoverTarget hoverTarget = firstOldHoverTarget;
1448 final boolean wasHovered;
1449 for (HoverTarget predecessor = null; ;) {
1450 if (hoverTarget == null) {
1451 hoverTarget = HoverTarget.obtain(child);
1452 wasHovered = false;
1453 break;
1454 }
1455
1456 if (hoverTarget.child == child) {
1457 if (predecessor != null) {
1458 predecessor.next = hoverTarget.next;
1459 } else {
1460 firstOldHoverTarget = hoverTarget.next;
1461 }
1462 hoverTarget.next = null;
1463 wasHovered = true;
1464 break;
1465 }
1466
1467 predecessor = hoverTarget;
1468 hoverTarget = hoverTarget.next;
1469 }
1470
1471 // Enqueue the hover target onto the new hover target list.
1472 if (lastHoverTarget != null) {
1473 lastHoverTarget.next = hoverTarget;
1474 } else {
1475 lastHoverTarget = hoverTarget;
1476 mFirstHoverTarget = hoverTarget;
1477 }
1478
1479 // Dispatch the event to the child.
1480 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1481 if (!wasHovered) {
1482 // Send the enter as is.
1483 handled |= dispatchTransformedGenericPointerEvent(
1484 event, child); // enter
1485 }
1486 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1487 if (!wasHovered) {
1488 // Synthesize an enter from a move.
1489 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1490 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1491 handled |= dispatchTransformedGenericPointerEvent(
1492 eventNoHistory, child); // enter
1493 eventNoHistory.setAction(action);
1494
1495 handled |= dispatchTransformedGenericPointerEvent(
1496 eventNoHistory, child); // move
1497 } else {
1498 // Send the move as is.
1499 handled |= dispatchTransformedGenericPointerEvent(event, child);
1500 }
1501 }
1502 if (handled) {
Jeff Brown10b62902011-06-20 16:40:37 -07001503 break;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001504 }
Jeff Brown10b62902011-06-20 16:40:37 -07001505 }
1506 }
1507 }
Jeff Brown33bbfd22011-02-24 20:55:35 -08001508
Jeff Brown87b7f802011-06-21 18:35:45 -07001509 // Send exit events to all previously hovered children that are no longer hovered.
1510 while (firstOldHoverTarget != null) {
1511 final View child = firstOldHoverTarget.child;
Jeff Brown10b62902011-06-20 16:40:37 -07001512
Jeff Brown87b7f802011-06-21 18:35:45 -07001513 // Exit the old hovered child.
1514 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1515 // Send the exit as is.
1516 handled |= dispatchTransformedGenericPointerEvent(
1517 event, child); // exit
1518 } else {
1519 // Synthesize an exit from a move or enter.
1520 // Ignore the result because hover focus has moved to a different view.
1521 if (action == MotionEvent.ACTION_HOVER_MOVE) {
Jeff Brown10b62902011-06-20 16:40:37 -07001522 dispatchTransformedGenericPointerEvent(
Jeff Brown87b7f802011-06-21 18:35:45 -07001523 event, child); // move
Jeff Brown10b62902011-06-20 16:40:37 -07001524 }
Jeff Brown87b7f802011-06-21 18:35:45 -07001525 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1526 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1527 dispatchTransformedGenericPointerEvent(
1528 eventNoHistory, child); // exit
1529 eventNoHistory.setAction(action);
Jeff Brown10b62902011-06-20 16:40:37 -07001530 }
1531
Jeff Brown87b7f802011-06-21 18:35:45 -07001532 final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next;
1533 firstOldHoverTarget.recycle();
1534 firstOldHoverTarget = nextOldHoverTarget;
Jeff Brown10b62902011-06-20 16:40:37 -07001535 }
1536
Jeff Brown87b7f802011-06-21 18:35:45 -07001537 // Send events to the view group itself if no children have handled it.
Jeff Brown10b62902011-06-20 16:40:37 -07001538 boolean newHoveredSelf = !handled;
1539 if (newHoveredSelf == mHoveredSelf) {
1540 if (newHoveredSelf) {
1541 // Send event to the view group as before.
1542 handled |= super.dispatchHoverEvent(event);
1543 }
1544 } else {
1545 if (mHoveredSelf) {
1546 // Exit the view group.
1547 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1548 // Send the exit as is.
1549 handled |= super.dispatchHoverEvent(event); // exit
1550 } else {
1551 // Synthesize an exit from a move or enter.
1552 // Ignore the result because hover focus is moving to a different view.
1553 if (action == MotionEvent.ACTION_HOVER_MOVE) {
1554 super.dispatchHoverEvent(event); // move
1555 }
1556 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1557 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1558 super.dispatchHoverEvent(eventNoHistory); // exit
1559 eventNoHistory.setAction(action);
1560 }
1561 mHoveredSelf = false;
1562 }
1563
1564 if (newHoveredSelf) {
1565 // Enter the view group.
1566 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1567 // Send the enter as is.
1568 handled |= super.dispatchHoverEvent(event); // enter
1569 mHoveredSelf = true;
1570 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1571 // Synthesize an enter from a move.
1572 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1573 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1574 handled |= super.dispatchHoverEvent(eventNoHistory); // enter
1575 eventNoHistory.setAction(action);
1576
1577 handled |= super.dispatchHoverEvent(eventNoHistory); // move
1578 mHoveredSelf = true;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001579 }
1580 }
Jeff Brown33bbfd22011-02-24 20:55:35 -08001581 }
1582
Jeff Browna032cc02011-03-07 16:56:21 -08001583 // Recycle the copy of the event that we made.
1584 if (eventNoHistory != event) {
1585 eventNoHistory.recycle();
1586 }
1587
Jeff Browna032cc02011-03-07 16:56:21 -08001588 // Done.
1589 return handled;
1590 }
1591
Jeff Brown59a422e2012-04-19 15:19:19 -07001592 private void exitHoverTargets() {
1593 if (mHoveredSelf || mFirstHoverTarget != null) {
1594 final long now = SystemClock.uptimeMillis();
1595 MotionEvent event = MotionEvent.obtain(now, now,
1596 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
1597 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
1598 dispatchHoverEvent(event);
1599 event.recycle();
1600 }
1601 }
1602
1603 private void cancelHoverTarget(View view) {
1604 HoverTarget predecessor = null;
1605 HoverTarget target = mFirstHoverTarget;
1606 while (target != null) {
1607 final HoverTarget next = target.next;
1608 if (target.child == view) {
1609 if (predecessor == null) {
1610 mFirstHoverTarget = next;
1611 } else {
1612 predecessor.next = next;
1613 }
1614 target.recycle();
1615
1616 final long now = SystemClock.uptimeMillis();
1617 MotionEvent event = MotionEvent.obtain(now, now,
1618 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
1619 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
1620 view.dispatchHoverEvent(event);
1621 event.recycle();
1622 return;
1623 }
1624 predecessor = target;
1625 target = next;
1626 }
1627 }
1628
Jeff Brown87b7f802011-06-21 18:35:45 -07001629 /** @hide */
1630 @Override
1631 protected boolean hasHoveredChild() {
1632 return mFirstHoverTarget != null;
1633 }
1634
Svetoslav Ganov42138042012-03-20 11:51:39 -07001635 @Override
1636 public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001637 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
1638 try {
1639 final int childrenCount = children.getChildCount();
1640 for (int i = 0; i < childrenCount; i++) {
1641 View child = children.getChildAt(i);
Svetoslav Ganovc406be92012-05-11 16:12:32 -07001642 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001643 if (child.includeForAccessibility()) {
1644 childrenForAccessibility.add(child);
1645 } else {
1646 child.addChildrenForAccessibility(childrenForAccessibility);
1647 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001648 }
1649 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001650 } finally {
1651 children.recycle();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001652 }
1653 }
1654
1655 /**
1656 * @hide
1657 */
1658 @Override
1659 public void childAccessibilityStateChanged(View child) {
1660 if (mParent != null) {
1661 mParent.childAccessibilityStateChanged(child);
1662 }
1663 }
1664
Jeff Brown10b62902011-06-20 16:40:37 -07001665 /**
1666 * Implement this method to intercept hover events before they are handled
1667 * by child views.
1668 * <p>
1669 * This method is called before dispatching a hover event to a child of
1670 * the view group or to the view group's own {@link #onHoverEvent} to allow
1671 * the view group a chance to intercept the hover event.
1672 * This method can also be used to watch all pointer motions that occur within
1673 * the bounds of the view group even when the pointer is hovering over
1674 * a child of the view group rather than over the view group itself.
1675 * </p><p>
1676 * The view group can prevent its children from receiving hover events by
1677 * implementing this method and returning <code>true</code> to indicate
1678 * that it would like to intercept hover events. The view group must
1679 * continuously return <code>true</code> from {@link #onInterceptHoverEvent}
1680 * for as long as it wishes to continue intercepting hover events from
1681 * its children.
1682 * </p><p>
1683 * Interception preserves the invariant that at most one view can be
1684 * hovered at a time by transferring hover focus from the currently hovered
1685 * child to the view group or vice-versa as needed.
1686 * </p><p>
1687 * If this method returns <code>true</code> and a child is already hovered, then the
1688 * child view will first receive a hover exit event and then the view group
1689 * itself will receive a hover enter event in {@link #onHoverEvent}.
1690 * Likewise, if this method had previously returned <code>true</code> to intercept hover
1691 * events and instead returns <code>false</code> while the pointer is hovering
1692 * within the bounds of one of a child, then the view group will first receive a
1693 * hover exit event in {@link #onHoverEvent} and then the hovered child will
1694 * receive a hover enter event.
1695 * </p><p>
1696 * The default implementation always returns false.
1697 * </p>
1698 *
1699 * @param event The motion event that describes the hover.
1700 * @return True if the view group would like to intercept the hover event
1701 * and prevent its children from receiving it.
1702 */
1703 public boolean onInterceptHoverEvent(MotionEvent event) {
Svetoslav Ganov736c2752011-04-22 18:30:36 -07001704 return false;
1705 }
1706
Jeff Browna032cc02011-03-07 16:56:21 -08001707 private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
1708 if (event.getHistorySize() == 0) {
1709 return event;
1710 }
1711 return MotionEvent.obtainNoHistory(event);
1712 }
1713
Jeff Brown10b62902011-06-20 16:40:37 -07001714 /**
1715 * {@inheritDoc}
1716 */
Jeff Browna032cc02011-03-07 16:56:21 -08001717 @Override
1718 protected boolean dispatchGenericPointerEvent(MotionEvent event) {
1719 // Send the event to the child under the pointer.
1720 final int childrenCount = mChildrenCount;
1721 if (childrenCount != 0) {
1722 final View[] children = mChildren;
1723 final float x = event.getX();
1724 final float y = event.getY();
1725
Adam Powella6478a32012-08-17 16:40:00 -07001726 final boolean customOrder = isChildrenDrawingOrderEnabled();
Jeff Browna032cc02011-03-07 16:56:21 -08001727 for (int i = childrenCount - 1; i >= 0; i--) {
Adam Powella6478a32012-08-17 16:40:00 -07001728 final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
1729 final View child = children[childIndex];
Jeff Browna032cc02011-03-07 16:56:21 -08001730 if (!canViewReceivePointerEvents(child)
1731 || !isTransformedTouchPointInView(x, y, child, null)) {
1732 continue;
1733 }
1734
1735 if (dispatchTransformedGenericPointerEvent(event, child)) {
1736 return true;
1737 }
1738 }
1739 }
1740
1741 // No child handled the event. Send it to this view group.
1742 return super.dispatchGenericPointerEvent(event);
1743 }
1744
Jeff Brown10b62902011-06-20 16:40:37 -07001745 /**
1746 * {@inheritDoc}
1747 */
Jeff Browna032cc02011-03-07 16:56:21 -08001748 @Override
1749 protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
Jeff Brown33bbfd22011-02-24 20:55:35 -08001750 // Send the event to the focused child or to this view group if it has focus.
Dianne Hackborn4702a852012-08-17 15:18:29 -07001751 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1752 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
Jeff Browna032cc02011-03-07 16:56:21 -08001753 return super.dispatchGenericFocusedEvent(event);
Dianne Hackborn4702a852012-08-17 15:18:29 -07001754 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1755 == PFLAG_HAS_BOUNDS) {
Jeff Browncb1404e2011-01-15 18:14:15 -08001756 return mFocused.dispatchGenericMotionEvent(event);
1757 }
1758 return false;
1759 }
1760
1761 /**
Jeff Browna032cc02011-03-07 16:56:21 -08001762 * Dispatches a generic pointer event to a child, taking into account
1763 * transformations that apply to the child.
1764 *
1765 * @param event The event to send.
1766 * @param child The view to send the event to.
1767 * @return {@code true} if the child handled the event.
1768 */
1769 private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) {
1770 final float offsetX = mScrollX - child.mLeft;
1771 final float offsetY = mScrollY - child.mTop;
1772
1773 boolean handled;
1774 if (!child.hasIdentityMatrix()) {
1775 MotionEvent transformedEvent = MotionEvent.obtain(event);
1776 transformedEvent.offsetLocation(offsetX, offsetY);
1777 transformedEvent.transform(child.getInverseMatrix());
1778 handled = child.dispatchGenericMotionEvent(transformedEvent);
1779 transformedEvent.recycle();
1780 } else {
1781 event.offsetLocation(offsetX, offsetY);
1782 handled = child.dispatchGenericMotionEvent(event);
1783 event.offsetLocation(-offsetX, -offsetY);
1784 }
1785 return handled;
1786 }
1787
1788 /**
Jeff Browncb1404e2011-01-15 18:14:15 -08001789 * {@inheritDoc}
1790 */
1791 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792 public boolean dispatchTouchEvent(MotionEvent ev) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001793 if (mInputEventConsistencyVerifier != null) {
1794 mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
1795 }
1796
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001797 boolean handled = false;
1798 if (onFilterTouchEventForSecurity(ev)) {
1799 final int action = ev.getAction();
1800 final int actionMasked = action & MotionEvent.ACTION_MASK;
Jeff Brown85a31762010-09-01 17:01:00 -07001801
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001802 // Handle an initial down.
1803 if (actionMasked == MotionEvent.ACTION_DOWN) {
1804 // Throw away all previous state when starting a new touch gesture.
1805 // The framework may have dropped the up or cancel event for the previous gesture
1806 // due to an app switch, ANR, or some other state change.
1807 cancelAndClearTouchTargets(ev);
1808 resetTouchState();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001809 }
Adam Powellb08013c2010-09-16 16:28:11 -07001810
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001811 // Check for interception.
1812 final boolean intercepted;
Jeff Brown20e987b2010-08-23 12:01:02 -07001813 if (actionMasked == MotionEvent.ACTION_DOWN
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001814 || mFirstTouchTarget != null) {
1815 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
1816 if (!disallowIntercept) {
1817 intercepted = onInterceptTouchEvent(ev);
1818 ev.setAction(action); // restore action in case it was changed
1819 } else {
1820 intercepted = false;
1821 }
1822 } else {
1823 // There are no touch targets and this action is not an initial down
1824 // so this view group continues to intercept touches.
1825 intercepted = true;
1826 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001827
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001828 // Check for cancelation.
1829 final boolean canceled = resetCancelNextUpFlag(this)
1830 || actionMasked == MotionEvent.ACTION_CANCEL;
Jeff Brown20e987b2010-08-23 12:01:02 -07001831
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001832 // Update list of touch targets for pointer down, if needed.
1833 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
1834 TouchTarget newTouchTarget = null;
1835 boolean alreadyDispatchedToNewTouchTarget = false;
1836 if (!canceled && !intercepted) {
1837 if (actionMasked == MotionEvent.ACTION_DOWN
1838 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
1839 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
1840 final int actionIndex = ev.getActionIndex(); // always 0 for down
1841 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
1842 : TouchTarget.ALL_POINTER_IDS;
Jeff Brown20e987b2010-08-23 12:01:02 -07001843
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001844 // Clean up earlier touch targets for this pointer id in case they
1845 // have become out of sync.
1846 removePointersFromTouchTargets(idBitsToAssign);
1847
1848 final int childrenCount = mChildrenCount;
1849 if (childrenCount != 0) {
1850 // Find a child that can receive the event.
1851 // Scan children from front to back.
1852 final View[] children = mChildren;
1853 final float x = ev.getX(actionIndex);
1854 final float y = ev.getY(actionIndex);
1855
Adam Powella6478a32012-08-17 16:40:00 -07001856 final boolean customOrder = isChildrenDrawingOrderEnabled();
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001857 for (int i = childrenCount - 1; i >= 0; i--) {
Adam Powella6478a32012-08-17 16:40:00 -07001858 final int childIndex = customOrder ?
1859 getChildDrawingOrder(childrenCount, i) : i;
1860 final View child = children[childIndex];
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001861 if (!canViewReceivePointerEvents(child)
1862 || !isTransformedTouchPointInView(x, y, child, null)) {
1863 continue;
1864 }
1865
1866 newTouchTarget = getTouchTarget(child);
1867 if (newTouchTarget != null) {
1868 // Child is already receiving touch within its bounds.
1869 // Give it the new pointer in addition to the ones it is handling.
1870 newTouchTarget.pointerIdBits |= idBitsToAssign;
1871 break;
1872 }
1873
1874 resetCancelNextUpFlag(child);
1875 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
1876 // Child wants to receive touch within its bounds.
1877 mLastTouchDownTime = ev.getDownTime();
Adam Powella6478a32012-08-17 16:40:00 -07001878 mLastTouchDownIndex = childIndex;
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001879 mLastTouchDownX = ev.getX();
1880 mLastTouchDownY = ev.getY();
1881 newTouchTarget = addTouchTarget(child, idBitsToAssign);
1882 alreadyDispatchedToNewTouchTarget = true;
1883 break;
1884 }
1885 }
1886 }
1887
1888 if (newTouchTarget == null && mFirstTouchTarget != null) {
1889 // Did not find a child to receive the event.
1890 // Assign the pointer to the least recently added target.
1891 newTouchTarget = mFirstTouchTarget;
1892 while (newTouchTarget.next != null) {
1893 newTouchTarget = newTouchTarget.next;
1894 }
1895 newTouchTarget.pointerIdBits |= idBitsToAssign;
1896 }
1897 }
1898 }
1899
1900 // Dispatch to touch targets.
1901 if (mFirstTouchTarget == null) {
1902 // No touch targets so treat this as an ordinary view.
1903 handled = dispatchTransformedTouchEvent(ev, canceled, null,
1904 TouchTarget.ALL_POINTER_IDS);
1905 } else {
1906 // Dispatch to touch targets, excluding the new touch target if we already
1907 // dispatched to it. Cancel touch targets if necessary.
1908 TouchTarget predecessor = null;
1909 TouchTarget target = mFirstTouchTarget;
1910 while (target != null) {
1911 final TouchTarget next = target.next;
1912 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
1913 handled = true;
1914 } else {
1915 final boolean cancelChild = resetCancelNextUpFlag(target.child)
1916 || intercepted;
1917 if (dispatchTransformedTouchEvent(ev, cancelChild,
1918 target.child, target.pointerIdBits)) {
1919 handled = true;
1920 }
1921 if (cancelChild) {
1922 if (predecessor == null) {
1923 mFirstTouchTarget = next;
1924 } else {
1925 predecessor.next = next;
1926 }
1927 target.recycle();
1928 target = next;
Jeff Brown20e987b2010-08-23 12:01:02 -07001929 continue;
1930 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001931 }
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001932 predecessor = target;
1933 target = next;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001934 }
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001935 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001936
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001937 // Update list of touch targets for pointer up or cancel, if needed.
1938 if (canceled
1939 || actionMasked == MotionEvent.ACTION_UP
1940 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
1941 resetTouchState();
1942 } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
1943 final int actionIndex = ev.getActionIndex();
1944 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
1945 removePointersFromTouchTargets(idBitsToRemove);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001946 }
1947 }
Romain Guy8506ab42009-06-11 17:35:47 -07001948
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001949 if (!handled && mInputEventConsistencyVerifier != null) {
1950 mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
Jeff Brown20e987b2010-08-23 12:01:02 -07001951 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001952 return handled;
1953 }
1954
Romain Guy469b1db2010-10-05 11:49:57 -07001955 /**
1956 * Resets all touch state in preparation for a new cycle.
1957 */
1958 private void resetTouchState() {
Jeff Brown20e987b2010-08-23 12:01:02 -07001959 clearTouchTargets();
1960 resetCancelNextUpFlag(this);
1961 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
1962 }
1963
Romain Guy469b1db2010-10-05 11:49:57 -07001964 /**
1965 * Resets the cancel next up flag.
1966 * Returns true if the flag was previously set.
1967 */
Romain Guya998dff2012-03-23 18:58:36 -07001968 private static boolean resetCancelNextUpFlag(View view) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07001969 if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {
1970 view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
Jeff Brown20e987b2010-08-23 12:01:02 -07001971 return true;
Adam Cohen9b073942010-08-19 16:49:52 -07001972 }
1973 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001974 }
1975
Romain Guy469b1db2010-10-05 11:49:57 -07001976 /**
1977 * Clears all touch targets.
1978 */
1979 private void clearTouchTargets() {
Jeff Brown20e987b2010-08-23 12:01:02 -07001980 TouchTarget target = mFirstTouchTarget;
1981 if (target != null) {
1982 do {
1983 TouchTarget next = target.next;
1984 target.recycle();
1985 target = next;
1986 } while (target != null);
1987 mFirstTouchTarget = null;
1988 }
1989 }
1990
Romain Guy469b1db2010-10-05 11:49:57 -07001991 /**
1992 * Cancels and clears all touch targets.
1993 */
1994 private void cancelAndClearTouchTargets(MotionEvent event) {
Jeff Brown20e987b2010-08-23 12:01:02 -07001995 if (mFirstTouchTarget != null) {
1996 boolean syntheticEvent = false;
1997 if (event == null) {
1998 final long now = SystemClock.uptimeMillis();
1999 event = MotionEvent.obtain(now, now,
2000 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
Jeff Brown2fdbc5a2011-06-30 12:25:54 -07002001 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
Jeff Brown20e987b2010-08-23 12:01:02 -07002002 syntheticEvent = true;
2003 }
2004
2005 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2006 resetCancelNextUpFlag(target.child);
2007 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
2008 }
2009 clearTouchTargets();
2010
2011 if (syntheticEvent) {
2012 event.recycle();
2013 }
2014 }
2015 }
2016
Romain Guy469b1db2010-10-05 11:49:57 -07002017 /**
2018 * Gets the touch target for specified child view.
2019 * Returns null if not found.
2020 */
2021 private TouchTarget getTouchTarget(View child) {
Jeff Brown20e987b2010-08-23 12:01:02 -07002022 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2023 if (target.child == child) {
2024 return target;
2025 }
2026 }
2027 return null;
2028 }
2029
Romain Guy469b1db2010-10-05 11:49:57 -07002030 /**
2031 * Adds a touch target for specified child to the beginning of the list.
2032 * Assumes the target child is not already present.
2033 */
2034 private TouchTarget addTouchTarget(View child, int pointerIdBits) {
Jeff Brown20e987b2010-08-23 12:01:02 -07002035 TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
2036 target.next = mFirstTouchTarget;
2037 mFirstTouchTarget = target;
2038 return target;
2039 }
2040
Romain Guy469b1db2010-10-05 11:49:57 -07002041 /**
2042 * Removes the pointer ids from consideration.
2043 */
2044 private void removePointersFromTouchTargets(int pointerIdBits) {
Jeff Brown20e987b2010-08-23 12:01:02 -07002045 TouchTarget predecessor = null;
2046 TouchTarget target = mFirstTouchTarget;
2047 while (target != null) {
2048 final TouchTarget next = target.next;
2049 if ((target.pointerIdBits & pointerIdBits) != 0) {
2050 target.pointerIdBits &= ~pointerIdBits;
2051 if (target.pointerIdBits == 0) {
2052 if (predecessor == null) {
2053 mFirstTouchTarget = next;
2054 } else {
2055 predecessor.next = next;
2056 }
2057 target.recycle();
2058 target = next;
2059 continue;
2060 }
2061 }
2062 predecessor = target;
2063 target = next;
2064 }
2065 }
2066
Jeff Brown59a422e2012-04-19 15:19:19 -07002067 private void cancelTouchTarget(View view) {
2068 TouchTarget predecessor = null;
2069 TouchTarget target = mFirstTouchTarget;
2070 while (target != null) {
2071 final TouchTarget next = target.next;
2072 if (target.child == view) {
2073 if (predecessor == null) {
2074 mFirstTouchTarget = next;
2075 } else {
2076 predecessor.next = next;
2077 }
2078 target.recycle();
2079
2080 final long now = SystemClock.uptimeMillis();
2081 MotionEvent event = MotionEvent.obtain(now, now,
2082 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2083 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2084 view.dispatchTouchEvent(event);
2085 event.recycle();
2086 return;
2087 }
2088 predecessor = target;
2089 target = next;
2090 }
2091 }
2092
Romain Guy469b1db2010-10-05 11:49:57 -07002093 /**
Jeff Browna032cc02011-03-07 16:56:21 -08002094 * Returns true if a child view can receive pointer events.
2095 * @hide
2096 */
2097 private static boolean canViewReceivePointerEvents(View child) {
2098 return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
2099 || child.getAnimation() != null;
2100 }
2101
2102 /**
Romain Guy469b1db2010-10-05 11:49:57 -07002103 * Returns true if a child view contains the specified point when transformed
Jeff Brown20e987b2010-08-23 12:01:02 -07002104 * into its coordinate space.
Romain Guy469b1db2010-10-05 11:49:57 -07002105 * Child must not be null.
Adam Cohena32edd42010-10-26 10:35:01 -07002106 * @hide
Romain Guy469b1db2010-10-05 11:49:57 -07002107 */
Adam Cohena32edd42010-10-26 10:35:01 -07002108 protected boolean isTransformedTouchPointInView(float x, float y, View child,
Christopher Tate2c095f32010-10-04 14:13:40 -07002109 PointF outLocalPoint) {
Jeff Brown20e987b2010-08-23 12:01:02 -07002110 float localX = x + mScrollX - child.mLeft;
2111 float localY = y + mScrollY - child.mTop;
2112 if (! child.hasIdentityMatrix() && mAttachInfo != null) {
Adam Powell2b342f02010-08-18 18:14:13 -07002113 final float[] localXY = mAttachInfo.mTmpTransformLocation;
2114 localXY[0] = localX;
2115 localXY[1] = localY;
2116 child.getInverseMatrix().mapPoints(localXY);
2117 localX = localXY[0];
2118 localY = localXY[1];
2119 }
Christopher Tate2c095f32010-10-04 14:13:40 -07002120 final boolean isInView = child.pointInView(localX, localY);
2121 if (isInView && outLocalPoint != null) {
2122 outLocalPoint.set(localX, localY);
2123 }
2124 return isInView;
Adam Powell2b342f02010-08-18 18:14:13 -07002125 }
2126
Romain Guy469b1db2010-10-05 11:49:57 -07002127 /**
2128 * Transforms a motion event into the coordinate space of a particular child view,
Jeff Brown20e987b2010-08-23 12:01:02 -07002129 * filters out irrelevant pointer ids, and overrides its action if necessary.
Romain Guy469b1db2010-10-05 11:49:57 -07002130 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
2131 */
2132 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
Jeff Brown20e987b2010-08-23 12:01:02 -07002133 View child, int desiredPointerIdBits) {
2134 final boolean handled;
Adam Powell2b342f02010-08-18 18:14:13 -07002135
Jeff Brown20e987b2010-08-23 12:01:02 -07002136 // Canceling motions is a special case. We don't need to perform any transformations
2137 // or filtering. The important part is the action, not the contents.
2138 final int oldAction = event.getAction();
2139 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
2140 event.setAction(MotionEvent.ACTION_CANCEL);
2141 if (child == null) {
2142 handled = super.dispatchTouchEvent(event);
2143 } else {
2144 handled = child.dispatchTouchEvent(event);
2145 }
2146 event.setAction(oldAction);
2147 return handled;
2148 }
Adam Powell2b342f02010-08-18 18:14:13 -07002149
Jeff Brown20e987b2010-08-23 12:01:02 -07002150 // Calculate the number of pointers to deliver.
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07002151 final int oldPointerIdBits = event.getPointerIdBits();
2152 final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
Adam Powell2b342f02010-08-18 18:14:13 -07002153
Jeff Brown20e987b2010-08-23 12:01:02 -07002154 // If for some reason we ended up in an inconsistent state where it looks like we
2155 // might produce a motion event with no pointers in it, then drop the event.
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07002156 if (newPointerIdBits == 0) {
Jeff Brown20e987b2010-08-23 12:01:02 -07002157 return false;
2158 }
Adam Powell2b342f02010-08-18 18:14:13 -07002159
Jeff Brown20e987b2010-08-23 12:01:02 -07002160 // If the number of pointers is the same and we don't need to perform any fancy
2161 // irreversible transformations, then we can reuse the motion event for this
2162 // dispatch as long as we are careful to revert any changes we make.
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07002163 // Otherwise we need to make a copy.
2164 final MotionEvent transformedEvent;
2165 if (newPointerIdBits == oldPointerIdBits) {
2166 if (child == null || child.hasIdentityMatrix()) {
2167 if (child == null) {
2168 handled = super.dispatchTouchEvent(event);
2169 } else {
2170 final float offsetX = mScrollX - child.mLeft;
2171 final float offsetY = mScrollY - child.mTop;
2172 event.offsetLocation(offsetX, offsetY);
Adam Powell2b342f02010-08-18 18:14:13 -07002173
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07002174 handled = child.dispatchTouchEvent(event);
Jeff Brown20e987b2010-08-23 12:01:02 -07002175
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07002176 event.offsetLocation(-offsetX, -offsetY);
2177 }
2178 return handled;
Jeff Brown20e987b2010-08-23 12:01:02 -07002179 }
Jeff Brown20e987b2010-08-23 12:01:02 -07002180 transformedEvent = MotionEvent.obtain(event);
2181 } else {
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07002182 transformedEvent = event.split(newPointerIdBits);
Adam Powell2b342f02010-08-18 18:14:13 -07002183 }
2184
Jeff Brown20e987b2010-08-23 12:01:02 -07002185 // Perform any necessary transformations and dispatch.
2186 if (child == null) {
2187 handled = super.dispatchTouchEvent(transformedEvent);
2188 } else {
2189 final float offsetX = mScrollX - child.mLeft;
2190 final float offsetY = mScrollY - child.mTop;
2191 transformedEvent.offsetLocation(offsetX, offsetY);
2192 if (! child.hasIdentityMatrix()) {
2193 transformedEvent.transform(child.getInverseMatrix());
Adam Powell2b342f02010-08-18 18:14:13 -07002194 }
2195
Jeff Brown20e987b2010-08-23 12:01:02 -07002196 handled = child.dispatchTouchEvent(transformedEvent);
Adam Powell2b342f02010-08-18 18:14:13 -07002197 }
2198
Jeff Brown20e987b2010-08-23 12:01:02 -07002199 // Done.
2200 transformedEvent.recycle();
Adam Powell2b342f02010-08-18 18:14:13 -07002201 return handled;
2202 }
2203
Romain Guy469b1db2010-10-05 11:49:57 -07002204 /**
Adam Powell2b342f02010-08-18 18:14:13 -07002205 * Enable or disable the splitting of MotionEvents to multiple children during touch event
Jeff Brown995e7742010-12-22 16:59:36 -08002206 * dispatch. This behavior is enabled by default for applications that target an
2207 * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
Adam Powell2b342f02010-08-18 18:14:13 -07002208 *
2209 * <p>When this option is enabled MotionEvents may be split and dispatched to different child
2210 * views depending on where each pointer initially went down. This allows for user interactions
2211 * such as scrolling two panes of content independently, chording of buttons, and performing
2212 * independent gestures on different pieces of content.
2213 *
2214 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
2215 * child views. <code>false</code> to only allow one child view to be the target of
2216 * any MotionEvent received by this ViewGroup.
2217 */
2218 public void setMotionEventSplittingEnabled(boolean split) {
2219 // TODO Applications really shouldn't change this setting mid-touch event,
2220 // but perhaps this should handle that case and send ACTION_CANCELs to any child views
2221 // with gestures in progress when this is changed.
2222 if (split) {
Adam Powell2b342f02010-08-18 18:14:13 -07002223 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
2224 } else {
2225 mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
Adam Powell2b342f02010-08-18 18:14:13 -07002226 }
2227 }
2228
2229 /**
Jeff Brown995e7742010-12-22 16:59:36 -08002230 * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
Adam Powell2b342f02010-08-18 18:14:13 -07002231 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2232 */
2233 public boolean isMotionEventSplittingEnabled() {
2234 return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
2235 }
2236
2237 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002238 * {@inheritDoc}
2239 */
2240 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
Romain Guy8506ab42009-06-11 17:35:47 -07002241
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002242 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
2243 // We're already in this state, assume our ancestors are too
2244 return;
2245 }
Romain Guy8506ab42009-06-11 17:35:47 -07002246
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002247 if (disallowIntercept) {
2248 mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
2249 } else {
2250 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2251 }
Romain Guy8506ab42009-06-11 17:35:47 -07002252
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002253 // Pass it up to our parent
2254 if (mParent != null) {
2255 mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
2256 }
2257 }
2258
2259 /**
2260 * Implement this method to intercept all touch screen motion events. This
2261 * allows you to watch events as they are dispatched to your children, and
2262 * take ownership of the current gesture at any point.
2263 *
2264 * <p>Using this function takes some care, as it has a fairly complicated
2265 * interaction with {@link View#onTouchEvent(MotionEvent)
2266 * View.onTouchEvent(MotionEvent)}, and using it requires implementing
2267 * that method as well as this one in the correct way. Events will be
2268 * received in the following order:
2269 *
2270 * <ol>
2271 * <li> You will receive the down event here.
2272 * <li> The down event will be handled either by a child of this view
2273 * group, or given to your own onTouchEvent() method to handle; this means
2274 * you should implement onTouchEvent() to return true, so you will
2275 * continue to see the rest of the gesture (instead of looking for
2276 * a parent view to handle it). Also, by returning true from
2277 * onTouchEvent(), you will not receive any following
2278 * events in onInterceptTouchEvent() and all touch processing must
2279 * happen in onTouchEvent() like normal.
2280 * <li> For as long as you return false from this function, each following
2281 * event (up to and including the final up) will be delivered first here
2282 * and then to the target's onTouchEvent().
2283 * <li> If you return true from here, you will not receive any
2284 * following events: the target view will receive the same event but
2285 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
2286 * events will be delivered to your onTouchEvent() method and no longer
2287 * appear here.
2288 * </ol>
2289 *
2290 * @param ev The motion event being dispatched down the hierarchy.
2291 * @return Return true to steal motion events from the children and have
2292 * them dispatched to this ViewGroup through onTouchEvent().
2293 * The current target will receive an ACTION_CANCEL event, and no further
2294 * messages will be delivered here.
2295 */
2296 public boolean onInterceptTouchEvent(MotionEvent ev) {
2297 return false;
2298 }
2299
2300 /**
2301 * {@inheritDoc}
2302 *
2303 * Looks for a view to give focus to respecting the setting specified by
2304 * {@link #getDescendantFocusability()}.
2305 *
2306 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
2307 * find focus within the children of this group when appropriate.
2308 *
2309 * @see #FOCUS_BEFORE_DESCENDANTS
2310 * @see #FOCUS_AFTER_DESCENDANTS
2311 * @see #FOCUS_BLOCK_DESCENDANTS
Romain Guy02739a82011-05-16 11:43:18 -07002312 * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002313 */
2314 @Override
2315 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
2316 if (DBG) {
2317 System.out.println(this + " ViewGroup.requestFocus direction="
2318 + direction);
2319 }
2320 int descendantFocusability = getDescendantFocusability();
2321
2322 switch (descendantFocusability) {
2323 case FOCUS_BLOCK_DESCENDANTS:
2324 return super.requestFocus(direction, previouslyFocusedRect);
2325 case FOCUS_BEFORE_DESCENDANTS: {
2326 final boolean took = super.requestFocus(direction, previouslyFocusedRect);
2327 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
2328 }
2329 case FOCUS_AFTER_DESCENDANTS: {
2330 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
2331 return took ? took : super.requestFocus(direction, previouslyFocusedRect);
2332 }
2333 default:
2334 throw new IllegalStateException("descendant focusability must be "
2335 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
2336 + "but is " + descendantFocusability);
2337 }
2338 }
2339
2340 /**
2341 * Look for a descendant to call {@link View#requestFocus} on.
2342 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
2343 * when it wants to request focus within its children. Override this to
2344 * customize how your {@link ViewGroup} requests focus within its children.
2345 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
2346 * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
2347 * to give a finer grained hint about where focus is coming from. May be null
2348 * if there is no hint.
2349 * @return Whether focus was taken.
2350 */
2351 @SuppressWarnings({"ConstantConditions"})
2352 protected boolean onRequestFocusInDescendants(int direction,
2353 Rect previouslyFocusedRect) {
2354 int index;
2355 int increment;
2356 int end;
2357 int count = mChildrenCount;
2358 if ((direction & FOCUS_FORWARD) != 0) {
2359 index = 0;
2360 increment = 1;
2361 end = count;
2362 } else {
2363 index = count - 1;
2364 increment = -1;
2365 end = -1;
2366 }
2367 final View[] children = mChildren;
2368 for (int i = index; i != end; i += increment) {
2369 View child = children[i];
2370 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2371 if (child.requestFocus(direction, previouslyFocusedRect)) {
2372 return true;
2373 }
2374 }
2375 }
2376 return false;
2377 }
Chet Haase5c13d892010-10-08 08:37:55 -07002378
Romain Guya440b002010-02-24 15:57:54 -08002379 /**
2380 * {@inheritDoc}
Chet Haase5c13d892010-10-08 08:37:55 -07002381 *
Romain Guydcc490f2010-02-24 17:59:35 -08002382 * @hide
Romain Guya440b002010-02-24 15:57:54 -08002383 */
2384 @Override
2385 public void dispatchStartTemporaryDetach() {
2386 super.dispatchStartTemporaryDetach();
2387 final int count = mChildrenCount;
2388 final View[] children = mChildren;
2389 for (int i = 0; i < count; i++) {
2390 children[i].dispatchStartTemporaryDetach();
2391 }
2392 }
Chet Haase5c13d892010-10-08 08:37:55 -07002393
Romain Guya440b002010-02-24 15:57:54 -08002394 /**
2395 * {@inheritDoc}
Chet Haase5c13d892010-10-08 08:37:55 -07002396 *
Romain Guydcc490f2010-02-24 17:59:35 -08002397 * @hide
Romain Guya440b002010-02-24 15:57:54 -08002398 */
2399 @Override
2400 public void dispatchFinishTemporaryDetach() {
2401 super.dispatchFinishTemporaryDetach();
2402 final int count = mChildrenCount;
2403 final View[] children = mChildren;
2404 for (int i = 0; i < count; i++) {
2405 children[i].dispatchFinishTemporaryDetach();
2406 }
2407 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002408
2409 /**
2410 * {@inheritDoc}
2411 */
2412 @Override
2413 void dispatchAttachedToWindow(AttachInfo info, int visibility) {
Adam Powell4b867882011-09-16 12:59:46 -07002414 mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002415 super.dispatchAttachedToWindow(info, visibility);
Adam Powell4b867882011-09-16 12:59:46 -07002416 mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
2417
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002418 final int count = mChildrenCount;
2419 final View[] children = mChildren;
2420 for (int i = 0; i < count; i++) {
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -07002421 final View child = children[i];
2422 child.dispatchAttachedToWindow(info,
2423 visibility | (child.mViewFlags&VISIBILITY_MASK));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002424 }
2425 }
2426
svetoslavganov75986cf2009-05-14 22:28:01 -07002427 @Override
Romain Guybb9908b2012-03-08 11:14:07 -08002428 void dispatchScreenStateChanged(int screenState) {
2429 super.dispatchScreenStateChanged(screenState);
2430
2431 final int count = mChildrenCount;
2432 final View[] children = mChildren;
2433 for (int i = 0; i < count; i++) {
2434 children[i].dispatchScreenStateChanged(screenState);
2435 }
2436 }
2437
2438 @Override
Svetoslav Ganov031d9c12011-09-09 16:41:13 -07002439 boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07002440 boolean handled = false;
2441 if (includeForAccessibility()) {
2442 handled = super.dispatchPopulateAccessibilityEventInternal(event);
2443 if (handled) {
2444 return handled;
2445 }
Svetoslav Ganovb84b94e2011-09-22 19:26:56 -07002446 }
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002447 // Let our children have a shot in populating the event.
Svetoslav Ganov42138042012-03-20 11:51:39 -07002448 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07002449 try {
2450 final int childCount = children.getChildCount();
2451 for (int i = 0; i < childCount; i++) {
2452 View child = children.getChildAt(i);
2453 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2454 handled = child.dispatchPopulateAccessibilityEvent(event);
2455 if (handled) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07002456 return handled;
2457 }
Svetoslav Ganov6179ea32011-06-28 01:12:41 -07002458 }
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002459 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07002460 } finally {
2461 children.recycle();
svetoslavganov75986cf2009-05-14 22:28:01 -07002462 }
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002463 return false;
svetoslavganov75986cf2009-05-14 22:28:01 -07002464 }
2465
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07002466 @Override
Svetoslav Ganov031d9c12011-09-09 16:41:13 -07002467 void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
2468 super.onInitializeAccessibilityNodeInfoInternal(info);
Svetoslav Ganov42138042012-03-20 11:51:39 -07002469 if (mAttachInfo != null) {
2470 ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList;
2471 childrenForAccessibility.clear();
2472 addChildrenForAccessibility(childrenForAccessibility);
2473 final int childrenForAccessibilityCount = childrenForAccessibility.size();
2474 for (int i = 0; i < childrenForAccessibilityCount; i++) {
2475 View child = childrenForAccessibility.get(i);
Svetoslav Ganovea1da3d2011-06-15 17:16:02 -07002476 info.addChild(child);
2477 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07002478 childrenForAccessibility.clear();
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07002479 }
2480 }
2481
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08002482 @Override
2483 void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
2484 super.onInitializeAccessibilityEventInternal(event);
2485 event.setClassName(ViewGroup.class.getName());
2486 }
2487
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002488 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -07002489 * @hide
2490 */
2491 @Override
2492 public void resetAccessibilityStateChanged() {
2493 super.resetAccessibilityStateChanged();
2494 View[] children = mChildren;
2495 final int childCount = mChildrenCount;
2496 for (int i = 0; i < childCount; i++) {
2497 View child = children[i];
2498 child.resetAccessibilityStateChanged();
2499 }
2500 }
2501
2502 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002503 * {@inheritDoc}
2504 */
2505 @Override
2506 void dispatchDetachedFromWindow() {
Jeff Brown20e987b2010-08-23 12:01:02 -07002507 // If we still have a touch target, we are still in the process of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002508 // dispatching motion events to a child; we need to get rid of that
2509 // child to avoid dispatching events to it after the window is torn
2510 // down. To make sure we keep the child in a consistent state, we
2511 // first send it an ACTION_CANCEL motion event.
Jeff Brown20e987b2010-08-23 12:01:02 -07002512 cancelAndClearTouchTargets(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002513
Jeff Brown59a422e2012-04-19 15:19:19 -07002514 // Similarly, set ACTION_EXIT to all hover targets and clear them.
2515 exitHoverTargets();
2516
Chet Haase9c087442011-01-12 16:20:16 -08002517 // In case view is detached while transition is running
2518 mLayoutSuppressed = false;
2519
Christopher Tate86cab1b2011-01-13 20:28:55 -08002520 // Tear down our drag tracking
2521 mDragNotifiedChildren = null;
2522 if (mCurrentDrag != null) {
2523 mCurrentDrag.recycle();
2524 mCurrentDrag = null;
2525 }
2526
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002527 final int count = mChildrenCount;
2528 final View[] children = mChildren;
2529 for (int i = 0; i < count; i++) {
2530 children[i].dispatchDetachedFromWindow();
2531 }
2532 super.dispatchDetachedFromWindow();
2533 }
2534
Fabrice Di Meglio23c89fd2012-08-13 12:17:42 -07002535 /**
2536 * @hide
2537 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002538 @Override
Fabrice Di Meglio23c89fd2012-08-13 12:17:42 -07002539 protected void internalSetPadding(int left, int top, int right, int bottom) {
Romain Guy2440e672012-08-07 14:43:43 -07002540 super.internalSetPadding(left, top, right, bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002541
Romain Guy13f35f32011-03-24 12:03:17 -07002542 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002543 mGroupFlags |= FLAG_PADDING_NOT_NULL;
2544 } else {
2545 mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
2546 }
2547 }
2548
2549 /**
2550 * {@inheritDoc}
2551 */
2552 @Override
2553 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
2554 super.dispatchSaveInstanceState(container);
2555 final int count = mChildrenCount;
2556 final View[] children = mChildren;
2557 for (int i = 0; i < count; i++) {
Dianne Hackbornb4bc78b2010-05-12 18:59:50 -07002558 View c = children[i];
2559 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2560 c.dispatchSaveInstanceState(container);
2561 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002562 }
2563 }
2564
2565 /**
Romain Guy9fc27812011-04-27 14:21:41 -07002566 * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()}
2567 * to only this view, not to its children. For use when overriding
2568 * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow
2569 * subclasses to freeze their own state but not the state of their children.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002570 *
2571 * @param container the container
2572 */
2573 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
2574 super.dispatchSaveInstanceState(container);
2575 }
2576
2577 /**
2578 * {@inheritDoc}
2579 */
2580 @Override
2581 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
2582 super.dispatchRestoreInstanceState(container);
2583 final int count = mChildrenCount;
2584 final View[] children = mChildren;
2585 for (int i = 0; i < count; i++) {
Dianne Hackbornb4bc78b2010-05-12 18:59:50 -07002586 View c = children[i];
2587 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2588 c.dispatchRestoreInstanceState(container);
2589 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002590 }
2591 }
2592
2593 /**
Romain Guy02739a82011-05-16 11:43:18 -07002594 * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)}
2595 * to only this view, not to its children. For use when overriding
2596 * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow
2597 * subclasses to thaw their own state but not the state of their children.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002598 *
2599 * @param container the container
2600 */
2601 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
2602 super.dispatchRestoreInstanceState(container);
2603 }
2604
2605 /**
2606 * Enables or disables the drawing cache for each child of this view group.
2607 *
2608 * @param enabled true to enable the cache, false to dispose of it
2609 */
2610 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
2611 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
2612 final View[] children = mChildren;
2613 final int count = mChildrenCount;
2614 for (int i = 0; i < count; i++) {
2615 children[i].setDrawingCacheEnabled(enabled);
2616 }
2617 }
2618 }
2619
2620 @Override
2621 protected void onAnimationStart() {
2622 super.onAnimationStart();
2623
2624 // When this ViewGroup's animation starts, build the cache for the children
2625 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2626 final int count = mChildrenCount;
2627 final View[] children = mChildren;
Romain Guy0d9275e2010-10-26 14:22:30 -07002628 final boolean buildCache = !isHardwareAccelerated();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002629
2630 for (int i = 0; i < count; i++) {
2631 final View child = children[i];
2632 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2633 child.setDrawingCacheEnabled(true);
Romain Guy0d9275e2010-10-26 14:22:30 -07002634 if (buildCache) {
2635 child.buildDrawingCache(true);
2636 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002637 }
2638 }
2639
2640 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2641 }
2642 }
2643
2644 @Override
2645 protected void onAnimationEnd() {
2646 super.onAnimationEnd();
2647
2648 // When this ViewGroup's animation ends, destroy the cache of the children
2649 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2650 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2651
2652 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2653 setChildrenDrawingCacheEnabled(false);
2654 }
2655 }
2656 }
2657
Romain Guy223ff5c2010-03-02 17:07:47 -08002658 @Override
2659 Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
Romain Guy65554f22010-03-22 18:58:21 -07002660 int count = mChildrenCount;
2661 int[] visibilities = null;
2662
Romain Guy223ff5c2010-03-02 17:07:47 -08002663 if (skipChildren) {
Romain Guy65554f22010-03-22 18:58:21 -07002664 visibilities = new int[count];
2665 for (int i = 0; i < count; i++) {
2666 View child = getChildAt(i);
2667 visibilities[i] = child.getVisibility();
2668 if (visibilities[i] == View.VISIBLE) {
2669 child.setVisibility(INVISIBLE);
2670 }
2671 }
Romain Guy223ff5c2010-03-02 17:07:47 -08002672 }
2673
2674 Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
Romain Guy65554f22010-03-22 18:58:21 -07002675
2676 if (skipChildren) {
2677 for (int i = 0; i < count; i++) {
2678 getChildAt(i).setVisibility(visibilities[i]);
Chet Haase5c13d892010-10-08 08:37:55 -07002679 }
Romain Guy65554f22010-03-22 18:58:21 -07002680 }
Romain Guy223ff5c2010-03-02 17:07:47 -08002681
2682 return b;
2683 }
2684
Philip Milne604f4402012-04-24 19:27:11 -07002685 private static void drawRect(Canvas canvas, int x1, int y1, int x2, int y2, int color) {
2686 Paint paint = getDebugPaint();
2687 paint.setColor(color);
Romain Guycbc67742012-04-27 16:12:57 -07002688
2689 canvas.drawLines(getDebugLines(x1, y1, x2, y2), paint);
Philip Milne10ca24a2012-04-23 15:38:27 -07002690 }
2691
2692 /**
2693 * @hide
2694 */
2695 protected void onDebugDrawMargins(Canvas canvas) {
2696 for (int i = 0; i < getChildCount(); i++) {
2697 View c = getChildAt(i);
Philip Milne604f4402012-04-24 19:27:11 -07002698 c.getLayoutParams().onDebugDraw(c, canvas);
Philip Milne10ca24a2012-04-23 15:38:27 -07002699 }
2700 }
2701
2702 /**
2703 * @hide
2704 */
2705 protected void onDebugDraw(Canvas canvas) {
Philip Milne10ca24a2012-04-23 15:38:27 -07002706 // Draw optical bounds
Philip Milne7a23b492012-04-24 22:12:36 -07002707 if (getLayoutMode() == OPTICAL_BOUNDS) {
Philip Milne10ca24a2012-04-23 15:38:27 -07002708 for (int i = 0; i < getChildCount(); i++) {
2709 View c = getChildAt(i);
Philip Milne7a23b492012-04-24 22:12:36 -07002710 Insets insets = c.getOpticalInsets();
Philip Milne10ca24a2012-04-23 15:38:27 -07002711 drawRect(canvas,
2712 c.getLeft() + insets.left,
2713 c.getTop() + insets.top,
2714 c.getRight() - insets.right,
Philip Milne604f4402012-04-24 19:27:11 -07002715 c.getBottom() - insets.bottom, Color.RED);
Philip Milne10ca24a2012-04-23 15:38:27 -07002716 }
2717 }
2718
Philip Milne10ca24a2012-04-23 15:38:27 -07002719 // Draw margins
2720 onDebugDrawMargins(canvas);
Philip Milne604f4402012-04-24 19:27:11 -07002721
2722 // Draw bounds
2723 for (int i = 0; i < getChildCount(); i++) {
2724 View c = getChildAt(i);
2725 drawRect(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), Color.BLUE);
2726 }
Philip Milne10ca24a2012-04-23 15:38:27 -07002727 }
2728
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002729 /**
2730 * {@inheritDoc}
2731 */
2732 @Override
2733 protected void dispatchDraw(Canvas canvas) {
2734 final int count = mChildrenCount;
2735 final View[] children = mChildren;
2736 int flags = mGroupFlags;
2737
2738 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
2739 final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
2740
Romain Guy0d9275e2010-10-26 14:22:30 -07002741 final boolean buildCache = !isHardwareAccelerated();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002742 for (int i = 0; i < count; i++) {
2743 final View child = children[i];
2744 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2745 final LayoutParams params = child.getLayoutParams();
2746 attachLayoutAnimationParameters(child, params, i, count);
2747 bindLayoutAnimation(child);
2748 if (cache) {
2749 child.setDrawingCacheEnabled(true);
Romain Guy0d9275e2010-10-26 14:22:30 -07002750 if (buildCache) {
2751 child.buildDrawingCache(true);
2752 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002753 }
2754 }
2755 }
2756
2757 final LayoutAnimationController controller = mLayoutAnimationController;
2758 if (controller.willOverlap()) {
2759 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
2760 }
2761
2762 controller.start();
2763
2764 mGroupFlags &= ~FLAG_RUN_ANIMATION;
2765 mGroupFlags &= ~FLAG_ANIMATION_DONE;
2766
2767 if (cache) {
2768 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2769 }
2770
2771 if (mAnimationListener != null) {
2772 mAnimationListener.onAnimationStart(controller.getAnimation());
2773 }
2774 }
2775
2776 int saveCount = 0;
2777 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
2778 if (clipToPadding) {
2779 saveCount = canvas.save();
Romain Guy8f2d94f2009-03-25 18:04:42 -07002780 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
2781 mScrollX + mRight - mLeft - mPaddingRight,
2782 mScrollY + mBottom - mTop - mPaddingBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002783
2784 }
2785
2786 // We will draw our child's animation, let's reset the flag
Dianne Hackborn4702a852012-08-17 15:18:29 -07002787 mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002788 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
2789
2790 boolean more = false;
2791 final long drawingTime = getDrawingTime();
2792
2793 if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
2794 for (int i = 0; i < count; i++) {
2795 final View child = children[i];
2796 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2797 more |= drawChild(canvas, child, drawingTime);
2798 }
2799 }
2800 } else {
2801 for (int i = 0; i < count; i++) {
2802 final View child = children[getChildDrawingOrder(count, i)];
2803 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2804 more |= drawChild(canvas, child, drawingTime);
2805 }
2806 }
2807 }
2808
2809 // Draw any disappearing views that have animations
2810 if (mDisappearingChildren != null) {
2811 final ArrayList<View> disappearingChildren = mDisappearingChildren;
2812 final int disappearingCount = disappearingChildren.size() - 1;
2813 // Go backwards -- we may delete as animations finish
2814 for (int i = disappearingCount; i >= 0; i--) {
2815 final View child = disappearingChildren.get(i);
2816 more |= drawChild(canvas, child, drawingTime);
2817 }
2818 }
2819
Philip Milne10ca24a2012-04-23 15:38:27 -07002820 if (debugDraw()) {
2821 onDebugDraw(canvas);
2822 }
2823
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002824 if (clipToPadding) {
2825 canvas.restoreToCount(saveCount);
2826 }
2827
2828 // mGroupFlags might have been updated by drawChild()
2829 flags = mGroupFlags;
2830
2831 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
Romain Guy849d0a32011-02-01 17:20:48 -08002832 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002833 }
2834
2835 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
2836 mLayoutAnimationController.isDone() && !more) {
2837 // We want to erase the drawing cache and notify the listener after the
2838 // next frame is drawn because one extra invalidate() is caused by
2839 // drawChild() after the animation is over
2840 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
2841 final Runnable end = new Runnable() {
2842 public void run() {
2843 notifyAnimationListener();
2844 }
2845 };
2846 post(end);
2847 }
2848 }
Romain Guy8506ab42009-06-11 17:35:47 -07002849
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002850 /**
2851 * Returns the index of the child to draw for this iteration. Override this
2852 * if you want to change the drawing order of children. By default, it
2853 * returns i.
2854 * <p>
Romain Guy293451e2009-11-04 13:59:48 -08002855 * NOTE: In order for this method to be called, you must enable child ordering
2856 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
Romain Guy8506ab42009-06-11 17:35:47 -07002857 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002858 * @param i The current iteration.
2859 * @return The index of the child to draw this iteration.
Chet Haase5c13d892010-10-08 08:37:55 -07002860 *
Romain Guy293451e2009-11-04 13:59:48 -08002861 * @see #setChildrenDrawingOrderEnabled(boolean)
2862 * @see #isChildrenDrawingOrderEnabled()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002863 */
2864 protected int getChildDrawingOrder(int childCount, int i) {
2865 return i;
2866 }
Romain Guy8506ab42009-06-11 17:35:47 -07002867
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002868 private void notifyAnimationListener() {
2869 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
2870 mGroupFlags |= FLAG_ANIMATION_DONE;
2871
2872 if (mAnimationListener != null) {
2873 final Runnable end = new Runnable() {
2874 public void run() {
2875 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
2876 }
2877 };
2878 post(end);
2879 }
2880
2881 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2882 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2883 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2884 setChildrenDrawingCacheEnabled(false);
2885 }
2886 }
2887
Romain Guy849d0a32011-02-01 17:20:48 -08002888 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002889 }
2890
2891 /**
Chet Haasedaf98e92011-01-10 14:10:36 -08002892 * This method is used to cause children of this ViewGroup to restore or recreate their
2893 * display lists. It is called by getDisplayList() when the parent ViewGroup does not need
2894 * to recreate its own display list, which would happen if it went through the normal
2895 * draw/dispatchDraw mechanisms.
2896 *
2897 * @hide
2898 */
2899 @Override
2900 protected void dispatchGetDisplayList() {
2901 final int count = mChildrenCount;
2902 final View[] children = mChildren;
2903 for (int i = 0; i < count; i++) {
2904 final View child = children[i];
Romain Guy59c7f802011-09-29 17:21:45 -07002905 if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) &&
2906 child.hasStaticLayer()) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07002907 child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED)
2908 == PFLAG_INVALIDATED;
2909 child.mPrivateFlags &= ~PFLAG_INVALIDATED;
Romain Guy2f57ba52011-02-03 18:03:29 -08002910 child.getDisplayList();
2911 child.mRecreateDisplayList = false;
2912 }
Chet Haasedaf98e92011-01-10 14:10:36 -08002913 }
2914 }
2915
2916 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002917 * Draw one child of this View Group. This method is responsible for getting
2918 * the canvas in the right state. This includes clipping, translating so
2919 * that the child's scrolled origin is at 0, 0, and applying any animation
2920 * transformations.
2921 *
2922 * @param canvas The canvas on which to draw the child
2923 * @param child Who to draw
Chet Haasebcca79a2012-02-14 08:45:14 -08002924 * @param drawingTime The time at which draw is occurring
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002925 * @return True if an invalidate() was issued
2926 */
2927 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
Chet Haase64a48c12012-02-13 16:33:29 -08002928 return child.draw(canvas, this, drawingTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002929 }
2930
2931 /**
2932 * By default, children are clipped to their bounds before drawing. This
2933 * allows view groups to override this behavior for animations, etc.
2934 *
2935 * @param clipChildren true to clip children to their bounds,
2936 * false otherwise
2937 * @attr ref android.R.styleable#ViewGroup_clipChildren
2938 */
2939 public void setClipChildren(boolean clipChildren) {
Chet Haasea1cff502012-02-21 13:43:44 -08002940 boolean previousValue = (mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN;
2941 if (clipChildren != previousValue) {
2942 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
Chet Haase1271e2c2012-04-20 09:54:27 -07002943 for (int i = 0; i < mChildrenCount; ++i) {
2944 View child = getChildAt(i);
2945 if (child.mDisplayList != null) {
2946 child.mDisplayList.setClipChildren(clipChildren);
Chet Haasea1cff502012-02-21 13:43:44 -08002947 }
2948 }
2949 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002950 }
2951
2952 /**
2953 * By default, children are clipped to the padding of the ViewGroup. This
2954 * allows view groups to override this behavior
2955 *
2956 * @param clipToPadding true to clip children to the padding of the
2957 * group, false otherwise
2958 * @attr ref android.R.styleable#ViewGroup_clipToPadding
2959 */
2960 public void setClipToPadding(boolean clipToPadding) {
2961 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
2962 }
2963
2964 /**
2965 * {@inheritDoc}
2966 */
2967 @Override
2968 public void dispatchSetSelected(boolean selected) {
2969 final View[] children = mChildren;
2970 final int count = mChildrenCount;
2971 for (int i = 0; i < count; i++) {
2972 children[i].setSelected(selected);
2973 }
2974 }
Romain Guy8506ab42009-06-11 17:35:47 -07002975
Dianne Hackbornd0fa3712010-09-14 18:57:14 -07002976 /**
2977 * {@inheritDoc}
2978 */
2979 @Override
2980 public void dispatchSetActivated(boolean activated) {
2981 final View[] children = mChildren;
2982 final int count = mChildrenCount;
2983 for (int i = 0; i < count; i++) {
Dianne Hackbornd0fa3712010-09-14 18:57:14 -07002984 children[i].setActivated(activated);
2985 }
2986 }
2987
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002988 @Override
2989 protected void dispatchSetPressed(boolean pressed) {
2990 final View[] children = mChildren;
2991 final int count = mChildrenCount;
2992 for (int i = 0; i < count; i++) {
Adam Powell035a1fc2012-02-27 15:23:50 -08002993 final View child = children[i];
2994 // Children that are clickable on their own should not
2995 // show a pressed state when their parent view does.
2996 // Clearing a pressed state always propagates.
2997 if (!pressed || (!child.isClickable() && !child.isLongClickable())) {
2998 child.setPressed(pressed);
2999 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003000 }
3001 }
3002
3003 /**
3004 * When this property is set to true, this ViewGroup supports static transformations on
3005 * children; this causes
3006 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
3007 * invoked when a child is drawn.
3008 *
3009 * Any subclass overriding
3010 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
3011 * set this property to true.
3012 *
3013 * @param enabled True to enable static transformations on children, false otherwise.
3014 *
Chet Haase599913d2012-07-23 16:22:05 -07003015 * @see #getChildStaticTransformation(View, android.view.animation.Transformation)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003016 */
3017 protected void setStaticTransformationsEnabled(boolean enabled) {
3018 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
3019 }
3020
3021 /**
Chet Haase2d46fcc2011-12-19 18:01:05 -08003022 * Sets <code>t</code> to be the static transformation of the child, if set, returning a
3023 * boolean to indicate whether a static transform was set. The default implementation
3024 * simply returns <code>false</code>; subclasses may override this method for different
Chet Haase599913d2012-07-23 16:22:05 -07003025 * behavior. {@link #setStaticTransformationsEnabled(boolean)} must be set to true
3026 * for this method to be called.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003027 *
Chet Haase2d46fcc2011-12-19 18:01:05 -08003028 * @param child The child view whose static transform is being requested
3029 * @param t The Transformation which will hold the result
3030 * @return true if the transformation was set, false otherwise
Romain Guy8506ab42009-06-11 17:35:47 -07003031 * @see #setStaticTransformationsEnabled(boolean)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003032 */
3033 protected boolean getChildStaticTransformation(View child, Transformation t) {
3034 return false;
3035 }
3036
3037 /**
3038 * {@hide}
3039 */
3040 @Override
3041 protected View findViewTraversal(int id) {
3042 if (id == mID) {
3043 return this;
3044 }
3045
3046 final View[] where = mChildren;
3047 final int len = mChildrenCount;
3048
3049 for (int i = 0; i < len; i++) {
3050 View v = where[i];
3051
Dianne Hackborn4702a852012-08-17 15:18:29 -07003052 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003053 v = v.findViewById(id);
3054
3055 if (v != null) {
3056 return v;
3057 }
3058 }
3059 }
3060
3061 return null;
3062 }
3063
3064 /**
3065 * {@hide}
3066 */
3067 @Override
3068 protected View findViewWithTagTraversal(Object tag) {
3069 if (tag != null && tag.equals(mTag)) {
3070 return this;
3071 }
3072
3073 final View[] where = mChildren;
3074 final int len = mChildrenCount;
3075
3076 for (int i = 0; i < len; i++) {
3077 View v = where[i];
3078
Dianne Hackborn4702a852012-08-17 15:18:29 -07003079 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003080 v = v.findViewWithTag(tag);
3081
3082 if (v != null) {
3083 return v;
3084 }
3085 }
3086 }
3087
3088 return null;
3089 }
3090
3091 /**
Jeff Brown4e6319b2010-12-13 10:36:51 -08003092 * {@hide}
3093 */
3094 @Override
Jeff Brown4dfbec22011-08-15 14:55:37 -07003095 protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
Jeff Brown4e6319b2010-12-13 10:36:51 -08003096 if (predicate.apply(this)) {
3097 return this;
3098 }
3099
3100 final View[] where = mChildren;
3101 final int len = mChildrenCount;
3102
3103 for (int i = 0; i < len; i++) {
3104 View v = where[i];
3105
Dianne Hackborn4702a852012-08-17 15:18:29 -07003106 if (v != childToSkip && (v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
Jeff Brown4e6319b2010-12-13 10:36:51 -08003107 v = v.findViewByPredicate(predicate);
3108
3109 if (v != null) {
3110 return v;
3111 }
3112 }
3113 }
3114
3115 return null;
3116 }
3117
3118 /**
Romain Guy393a52c2012-05-22 20:21:08 -07003119 * <p>Adds a child view. If no layout parameters are already set on the child, the
3120 * default parameters for this ViewGroup are set on the child.</p>
3121 *
3122 * <p><strong>Note:</strong> do not invoke this method from
3123 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3124 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003125 *
3126 * @param child the child view to add
3127 *
3128 * @see #generateDefaultLayoutParams()
3129 */
3130 public void addView(View child) {
3131 addView(child, -1);
3132 }
3133
3134 /**
3135 * Adds a child view. If no layout parameters are already set on the child, the
3136 * default parameters for this ViewGroup are set on the child.
Romain Guy393a52c2012-05-22 20:21:08 -07003137 *
3138 * <p><strong>Note:</strong> do not invoke this method from
3139 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3140 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003141 *
3142 * @param child the child view to add
3143 * @param index the position at which to add the child
3144 *
3145 * @see #generateDefaultLayoutParams()
3146 */
3147 public void addView(View child, int index) {
3148 LayoutParams params = child.getLayoutParams();
3149 if (params == null) {
3150 params = generateDefaultLayoutParams();
3151 if (params == null) {
3152 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
3153 }
3154 }
3155 addView(child, index, params);
3156 }
3157
3158 /**
3159 * Adds a child view with this ViewGroup's default layout parameters and the
3160 * specified width and height.
3161 *
Romain Guy393a52c2012-05-22 20:21:08 -07003162 * <p><strong>Note:</strong> do not invoke this method from
3163 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3164 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3165 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003166 * @param child the child view to add
3167 */
3168 public void addView(View child, int width, int height) {
3169 final LayoutParams params = generateDefaultLayoutParams();
3170 params.width = width;
3171 params.height = height;
3172 addView(child, -1, params);
3173 }
3174
3175 /**
3176 * Adds a child view with the specified layout parameters.
3177 *
Romain Guy393a52c2012-05-22 20:21:08 -07003178 * <p><strong>Note:</strong> do not invoke this method from
3179 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3180 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3181 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003182 * @param child the child view to add
3183 * @param params the layout parameters to set on the child
3184 */
3185 public void addView(View child, LayoutParams params) {
3186 addView(child, -1, params);
3187 }
3188
3189 /**
3190 * Adds a child view with the specified layout parameters.
3191 *
Romain Guy393a52c2012-05-22 20:21:08 -07003192 * <p><strong>Note:</strong> do not invoke this method from
3193 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3194 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3195 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003196 * @param child the child view to add
3197 * @param index the position at which to add the child
3198 * @param params the layout parameters to set on the child
3199 */
3200 public void addView(View child, int index, LayoutParams params) {
3201 if (DBG) {
3202 System.out.println(this + " addView");
3203 }
3204
3205 // addViewInner() will call child.requestLayout() when setting the new LayoutParams
3206 // therefore, we call requestLayout() on ourselves before, so that the child's request
3207 // will be blocked at our level
3208 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003209 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003210 addViewInner(child, index, params, false);
3211 }
3212
3213 /**
3214 * {@inheritDoc}
3215 */
3216 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
3217 if (!checkLayoutParams(params)) {
3218 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
3219 }
3220 if (view.mParent != this) {
3221 throw new IllegalArgumentException("Given view not a child of " + this);
3222 }
3223 view.setLayoutParams(params);
3224 }
3225
3226 /**
3227 * {@inheritDoc}
3228 */
3229 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3230 return p != null;
3231 }
3232
3233 /**
3234 * Interface definition for a callback to be invoked when the hierarchy
3235 * within this view changed. The hierarchy changes whenever a child is added
3236 * to or removed from this view.
3237 */
3238 public interface OnHierarchyChangeListener {
3239 /**
3240 * Called when a new child is added to a parent view.
3241 *
3242 * @param parent the view in which a child was added
3243 * @param child the new child view added in the hierarchy
3244 */
3245 void onChildViewAdded(View parent, View child);
3246
3247 /**
3248 * Called when a child is removed from a parent view.
3249 *
3250 * @param parent the view from which the child was removed
3251 * @param child the child removed from the hierarchy
3252 */
3253 void onChildViewRemoved(View parent, View child);
3254 }
3255
3256 /**
3257 * Register a callback to be invoked when a child is added to or removed
3258 * from this view.
3259 *
3260 * @param listener the callback to invoke on hierarchy change
3261 */
3262 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
3263 mOnHierarchyChangeListener = listener;
3264 }
3265
3266 /**
Philip Milnef51d91c2011-07-18 16:12:19 -07003267 * @hide
3268 */
3269 protected void onViewAdded(View child) {
3270 if (mOnHierarchyChangeListener != null) {
3271 mOnHierarchyChangeListener.onChildViewAdded(this, child);
3272 }
3273 }
3274
3275 /**
3276 * @hide
3277 */
3278 protected void onViewRemoved(View child) {
3279 if (mOnHierarchyChangeListener != null) {
3280 mOnHierarchyChangeListener.onChildViewRemoved(this, child);
3281 }
3282 }
3283
3284 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003285 * Adds a view during layout. This is useful if in your onLayout() method,
3286 * you need to add more views (as does the list view for example).
3287 *
3288 * If index is negative, it means put it at the end of the list.
3289 *
3290 * @param child the view to add to the group
3291 * @param index the index at which the child must be added
3292 * @param params the layout parameters to associate with the child
3293 * @return true if the child was added, false otherwise
3294 */
3295 protected boolean addViewInLayout(View child, int index, LayoutParams params) {
3296 return addViewInLayout(child, index, params, false);
3297 }
3298
3299 /**
3300 * Adds a view during layout. This is useful if in your onLayout() method,
3301 * you need to add more views (as does the list view for example).
3302 *
3303 * If index is negative, it means put it at the end of the list.
3304 *
3305 * @param child the view to add to the group
3306 * @param index the index at which the child must be added
3307 * @param params the layout parameters to associate with the child
3308 * @param preventRequestLayout if true, calling this method will not trigger a
3309 * layout request on child
3310 * @return true if the child was added, false otherwise
3311 */
3312 protected boolean addViewInLayout(View child, int index, LayoutParams params,
3313 boolean preventRequestLayout) {
3314 child.mParent = null;
3315 addViewInner(child, index, params, preventRequestLayout);
Dianne Hackborn4702a852012-08-17 15:18:29 -07003316 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003317 return true;
3318 }
3319
3320 /**
3321 * Prevents the specified child to be laid out during the next layout pass.
3322 *
3323 * @param child the child on which to perform the cleanup
3324 */
3325 protected void cleanupLayoutState(View child) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07003326 child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003327 }
3328
3329 private void addViewInner(View child, int index, LayoutParams params,
3330 boolean preventRequestLayout) {
3331
Chet Haasee8e45d32011-03-02 17:07:35 -08003332 if (mTransition != null) {
3333 // Don't prevent other add transitions from completing, but cancel remove
3334 // transitions to let them complete the process before we add to the container
3335 mTransition.cancel(LayoutTransition.DISAPPEARING);
Chet Haaseadd65772011-02-09 16:47:29 -08003336 }
3337
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003338 if (child.getParent() != null) {
3339 throw new IllegalStateException("The specified child already has a parent. " +
3340 "You must call removeView() on the child's parent first.");
3341 }
3342
Chet Haase21cd1382010-09-01 17:42:29 -07003343 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003344 mTransition.addChild(this, child);
Chet Haase21cd1382010-09-01 17:42:29 -07003345 }
3346
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003347 if (!checkLayoutParams(params)) {
3348 params = generateLayoutParams(params);
3349 }
3350
3351 if (preventRequestLayout) {
3352 child.mLayoutParams = params;
3353 } else {
3354 child.setLayoutParams(params);
3355 }
3356
3357 if (index < 0) {
3358 index = mChildrenCount;
3359 }
3360
3361 addInArray(child, index);
3362
3363 // tell our children
3364 if (preventRequestLayout) {
3365 child.assignParent(this);
3366 } else {
3367 child.mParent = this;
3368 }
3369
3370 if (child.hasFocus()) {
3371 requestChildFocus(child, child.findFocus());
3372 }
Romain Guy8506ab42009-06-11 17:35:47 -07003373
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003374 AttachInfo ai = mAttachInfo;
Adam Powell4b867882011-09-16 12:59:46 -07003375 if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
Romain Guy8506ab42009-06-11 17:35:47 -07003376 boolean lastKeepOn = ai.mKeepScreenOn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003377 ai.mKeepScreenOn = false;
3378 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
3379 if (ai.mKeepScreenOn) {
3380 needGlobalAttributesUpdate(true);
3381 }
3382 ai.mKeepScreenOn = lastKeepOn;
3383 }
3384
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07003385 if (child.isLayoutDirectionInherited()) {
Fabrice Di Meglioa7e0bcd2012-10-16 19:55:01 -07003386 child.resetRtlProperties();
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07003387 }
3388
Philip Milnef51d91c2011-07-18 16:12:19 -07003389 onViewAdded(child);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003390
3391 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
3392 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
3393 }
Adam Powell539ee872012-02-03 19:00:49 -08003394
3395 if (child.hasTransientState()) {
3396 childHasTransientStateChanged(child, true);
3397 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003398 }
3399
3400 private void addInArray(View child, int index) {
3401 View[] children = mChildren;
3402 final int count = mChildrenCount;
3403 final int size = children.length;
3404 if (index == count) {
3405 if (size == count) {
3406 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3407 System.arraycopy(children, 0, mChildren, 0, size);
3408 children = mChildren;
3409 }
3410 children[mChildrenCount++] = child;
3411 } else if (index < count) {
3412 if (size == count) {
3413 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3414 System.arraycopy(children, 0, mChildren, 0, index);
3415 System.arraycopy(children, index, mChildren, index + 1, count - index);
3416 children = mChildren;
3417 } else {
3418 System.arraycopy(children, index, children, index + 1, count - index);
3419 }
3420 children[index] = child;
3421 mChildrenCount++;
Joe Onorato03ab0c72011-01-06 15:46:27 -08003422 if (mLastTouchDownIndex >= index) {
3423 mLastTouchDownIndex++;
3424 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003425 } else {
3426 throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
3427 }
3428 }
3429
3430 // This method also sets the child's mParent to null
3431 private void removeFromArray(int index) {
3432 final View[] children = mChildren;
Chet Haase21cd1382010-09-01 17:42:29 -07003433 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
3434 children[index].mParent = null;
3435 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003436 final int count = mChildrenCount;
3437 if (index == count - 1) {
3438 children[--mChildrenCount] = null;
3439 } else if (index >= 0 && index < count) {
3440 System.arraycopy(children, index + 1, children, index, count - index - 1);
3441 children[--mChildrenCount] = null;
3442 } else {
3443 throw new IndexOutOfBoundsException();
3444 }
Joe Onorato03ab0c72011-01-06 15:46:27 -08003445 if (mLastTouchDownIndex == index) {
3446 mLastTouchDownTime = 0;
3447 mLastTouchDownIndex = -1;
3448 } else if (mLastTouchDownIndex > index) {
3449 mLastTouchDownIndex--;
3450 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003451 }
3452
3453 // This method also sets the children's mParent to null
3454 private void removeFromArray(int start, int count) {
3455 final View[] children = mChildren;
3456 final int childrenCount = mChildrenCount;
3457
3458 start = Math.max(0, start);
3459 final int end = Math.min(childrenCount, start + count);
3460
3461 if (start == end) {
3462 return;
3463 }
3464
3465 if (end == childrenCount) {
3466 for (int i = start; i < end; i++) {
3467 children[i].mParent = null;
3468 children[i] = null;
3469 }
3470 } else {
3471 for (int i = start; i < end; i++) {
3472 children[i].mParent = null;
3473 }
3474
3475 // Since we're looping above, we might as well do the copy, but is arraycopy()
3476 // faster than the extra 2 bounds checks we would do in the loop?
3477 System.arraycopy(children, end, children, start, childrenCount - end);
3478
3479 for (int i = childrenCount - (end - start); i < childrenCount; i++) {
3480 children[i] = null;
3481 }
3482 }
3483
3484 mChildrenCount -= (end - start);
3485 }
3486
3487 private void bindLayoutAnimation(View child) {
3488 Animation a = mLayoutAnimationController.getAnimationForView(child);
3489 child.setAnimation(a);
3490 }
3491
3492 /**
3493 * Subclasses should override this method to set layout animation
3494 * parameters on the supplied child.
3495 *
3496 * @param child the child to associate with animation parameters
3497 * @param params the child's layout parameters which hold the animation
3498 * parameters
3499 * @param index the index of the child in the view group
3500 * @param count the number of children in the view group
3501 */
3502 protected void attachLayoutAnimationParameters(View child,
3503 LayoutParams params, int index, int count) {
3504 LayoutAnimationController.AnimationParameters animationParams =
3505 params.layoutAnimationParameters;
3506 if (animationParams == null) {
3507 animationParams = new LayoutAnimationController.AnimationParameters();
3508 params.layoutAnimationParameters = animationParams;
3509 }
3510
3511 animationParams.count = count;
3512 animationParams.index = index;
3513 }
3514
3515 /**
3516 * {@inheritDoc}
Romain Guy393a52c2012-05-22 20:21:08 -07003517 *
3518 * <p><strong>Note:</strong> do not invoke this method from
3519 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3520 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003521 */
3522 public void removeView(View view) {
3523 removeViewInternal(view);
3524 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003525 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003526 }
3527
3528 /**
3529 * Removes a view during layout. This is useful if in your onLayout() method,
3530 * you need to remove more views.
3531 *
Romain Guy393a52c2012-05-22 20:21:08 -07003532 * <p><strong>Note:</strong> do not invoke this method from
3533 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3534 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3535 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003536 * @param view the view to remove from the group
3537 */
3538 public void removeViewInLayout(View view) {
3539 removeViewInternal(view);
3540 }
3541
3542 /**
3543 * Removes a range of views during layout. This is useful if in your onLayout() method,
3544 * you need to remove more views.
3545 *
Romain Guy393a52c2012-05-22 20:21:08 -07003546 * <p><strong>Note:</strong> do not invoke this method from
3547 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3548 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3549 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003550 * @param start the index of the first view to remove from the group
3551 * @param count the number of views to remove from the group
3552 */
3553 public void removeViewsInLayout(int start, int count) {
3554 removeViewsInternal(start, count);
3555 }
3556
3557 /**
3558 * Removes the view at the specified position in the group.
3559 *
Romain Guy393a52c2012-05-22 20:21:08 -07003560 * <p><strong>Note:</strong> do not invoke this method from
3561 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3562 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3563 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003564 * @param index the position in the group of the view to remove
3565 */
3566 public void removeViewAt(int index) {
3567 removeViewInternal(index, getChildAt(index));
3568 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003569 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003570 }
3571
3572 /**
3573 * Removes the specified range of views from the group.
3574 *
Romain Guy393a52c2012-05-22 20:21:08 -07003575 * <p><strong>Note:</strong> do not invoke this method from
3576 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3577 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3578 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003579 * @param start the first position in the group of the range of views to remove
3580 * @param count the number of views to remove
3581 */
3582 public void removeViews(int start, int count) {
3583 removeViewsInternal(start, count);
3584 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003585 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003586 }
3587
3588 private void removeViewInternal(View view) {
3589 final int index = indexOfChild(view);
3590 if (index >= 0) {
3591 removeViewInternal(index, view);
3592 }
3593 }
3594
3595 private void removeViewInternal(int index, View view) {
Chet Haase21cd1382010-09-01 17:42:29 -07003596
3597 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003598 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003599 }
3600
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003601 boolean clearChildFocus = false;
3602 if (view == mFocused) {
Svetoslav Ganov57cadf22012-04-04 16:44:39 -07003603 view.unFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003604 clearChildFocus = true;
3605 }
3606
Svetoslav Ganov961bf0e2012-05-08 09:40:03 -07003607 view.clearAccessibilityFocus();
3608
Jeff Brown59a422e2012-04-19 15:19:19 -07003609 cancelTouchTarget(view);
3610 cancelHoverTarget(view);
3611
Chet Haase21cd1382010-09-01 17:42:29 -07003612 if (view.getAnimation() != null ||
3613 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003614 addDisappearingView(view);
3615 } else if (view.mAttachInfo != null) {
3616 view.dispatchDetachedFromWindow();
3617 }
3618
Adam Powell539ee872012-02-03 19:00:49 -08003619 if (view.hasTransientState()) {
3620 childHasTransientStateChanged(view, false);
3621 }
3622
Philip Milnef51d91c2011-07-18 16:12:19 -07003623 onViewRemoved(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003624
3625 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003626
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003627 removeFromArray(index);
3628
3629 if (clearChildFocus) {
3630 clearChildFocus(view);
Svetoslav Ganov57cadf22012-04-04 16:44:39 -07003631 ensureInputFocusOnFirstFocusable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003632 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07003633
3634 if (view.isAccessibilityFocused()) {
3635 view.clearAccessibilityFocus();
3636 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003637 }
3638
Chet Haase21cd1382010-09-01 17:42:29 -07003639 /**
3640 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
3641 * not null, changes in layout which occur because of children being added to or removed from
3642 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
3643 * object. By default, the transition object is null (so layout changes are not animated).
3644 *
3645 * @param transition The LayoutTransition object that will animated changes in layout. A value
3646 * of <code>null</code> means no transition will run on layout changes.
Chet Haase13cc1202010-09-03 15:39:20 -07003647 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
Chet Haase21cd1382010-09-01 17:42:29 -07003648 */
3649 public void setLayoutTransition(LayoutTransition transition) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07003650 if (mTransition != null) {
3651 mTransition.removeTransitionListener(mLayoutTransitionListener);
3652 }
Chet Haase21cd1382010-09-01 17:42:29 -07003653 mTransition = transition;
Chet Haase13cc1202010-09-03 15:39:20 -07003654 if (mTransition != null) {
3655 mTransition.addTransitionListener(mLayoutTransitionListener);
3656 }
3657 }
3658
3659 /**
3660 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
3661 * not null, changes in layout which occur because of children being added to or removed from
3662 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
3663 * object. By default, the transition object is null (so layout changes are not animated).
3664 *
3665 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
3666 * A value of <code>null</code> means no transition will run on layout changes.
3667 */
3668 public LayoutTransition getLayoutTransition() {
3669 return mTransition;
Chet Haase21cd1382010-09-01 17:42:29 -07003670 }
3671
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003672 private void removeViewsInternal(int start, int count) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003673 final View focused = mFocused;
3674 final boolean detach = mAttachInfo != null;
3675 View clearChildFocus = null;
3676
3677 final View[] children = mChildren;
3678 final int end = start + count;
3679
3680 for (int i = start; i < end; i++) {
3681 final View view = children[i];
3682
Chet Haase21cd1382010-09-01 17:42:29 -07003683 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003684 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003685 }
3686
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003687 if (view == focused) {
Svetoslav Ganov57cadf22012-04-04 16:44:39 -07003688 view.unFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003689 clearChildFocus = view;
3690 }
3691
Svetoslav Ganov961bf0e2012-05-08 09:40:03 -07003692 view.clearAccessibilityFocus();
3693
Jeff Brown59a422e2012-04-19 15:19:19 -07003694 cancelTouchTarget(view);
3695 cancelHoverTarget(view);
3696
Chet Haase21cd1382010-09-01 17:42:29 -07003697 if (view.getAnimation() != null ||
3698 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003699 addDisappearingView(view);
3700 } else if (detach) {
3701 view.dispatchDetachedFromWindow();
3702 }
3703
Adam Powell539ee872012-02-03 19:00:49 -08003704 if (view.hasTransientState()) {
3705 childHasTransientStateChanged(view, false);
3706 }
3707
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003708 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003709
Philip Milnef51d91c2011-07-18 16:12:19 -07003710 onViewRemoved(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003711 }
3712
3713 removeFromArray(start, count);
3714
3715 if (clearChildFocus != null) {
3716 clearChildFocus(clearChildFocus);
Svetoslav Ganov57cadf22012-04-04 16:44:39 -07003717 ensureInputFocusOnFirstFocusable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003718 }
3719 }
3720
3721 /**
3722 * Call this method to remove all child views from the
3723 * ViewGroup.
Romain Guy393a52c2012-05-22 20:21:08 -07003724 *
3725 * <p><strong>Note:</strong> do not invoke this method from
3726 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3727 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003728 */
3729 public void removeAllViews() {
3730 removeAllViewsInLayout();
3731 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003732 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003733 }
3734
3735 /**
3736 * Called by a ViewGroup subclass to remove child views from itself,
3737 * when it must first know its size on screen before it can calculate how many
3738 * child views it will render. An example is a Gallery or a ListView, which
3739 * may "have" 50 children, but actually only render the number of children
3740 * that can currently fit inside the object on screen. Do not call
3741 * this method unless you are extending ViewGroup and understand the
3742 * view measuring and layout pipeline.
Romain Guy393a52c2012-05-22 20:21:08 -07003743 *
3744 * <p><strong>Note:</strong> do not invoke this method from
3745 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3746 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003747 */
3748 public void removeAllViewsInLayout() {
3749 final int count = mChildrenCount;
3750 if (count <= 0) {
3751 return;
3752 }
3753
3754 final View[] children = mChildren;
3755 mChildrenCount = 0;
3756
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003757 final View focused = mFocused;
3758 final boolean detach = mAttachInfo != null;
3759 View clearChildFocus = null;
3760
3761 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003762
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003763 for (int i = count - 1; i >= 0; i--) {
3764 final View view = children[i];
3765
Chet Haase21cd1382010-09-01 17:42:29 -07003766 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003767 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003768 }
3769
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003770 if (view == focused) {
Svetoslav Ganov57cadf22012-04-04 16:44:39 -07003771 view.unFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003772 clearChildFocus = view;
3773 }
3774
Svetoslav Ganov961bf0e2012-05-08 09:40:03 -07003775 view.clearAccessibilityFocus();
3776
Jeff Brown59a422e2012-04-19 15:19:19 -07003777 cancelTouchTarget(view);
3778 cancelHoverTarget(view);
3779
Chet Haase21cd1382010-09-01 17:42:29 -07003780 if (view.getAnimation() != null ||
3781 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003782 addDisappearingView(view);
3783 } else if (detach) {
3784 view.dispatchDetachedFromWindow();
3785 }
3786
Adam Powell539ee872012-02-03 19:00:49 -08003787 if (view.hasTransientState()) {
3788 childHasTransientStateChanged(view, false);
3789 }
3790
Philip Milnef51d91c2011-07-18 16:12:19 -07003791 onViewRemoved(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003792
3793 view.mParent = null;
3794 children[i] = null;
3795 }
3796
3797 if (clearChildFocus != null) {
3798 clearChildFocus(clearChildFocus);
Svetoslav Ganov57cadf22012-04-04 16:44:39 -07003799 ensureInputFocusOnFirstFocusable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003800 }
3801 }
3802
3803 /**
3804 * Finishes the removal of a detached view. This method will dispatch the detached from
3805 * window event and notify the hierarchy change listener.
Chet Haaseca479d42012-08-30 17:20:08 -07003806 * <p>
3807 * This method is intended to be lightweight and makes no assumptions about whether the
3808 * parent or child should be redrawn. Proper use of this method will include also making
3809 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
3810 * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
3811 * which performs a {@link #requestLayout()} on the next frame, after all detach/remove
3812 * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003813 *
3814 * @param child the child to be definitely removed from the view hierarchy
3815 * @param animate if true and the view has an animation, the view is placed in the
3816 * disappearing views list, otherwise, it is detached from the window
3817 *
3818 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3819 * @see #detachAllViewsFromParent()
3820 * @see #detachViewFromParent(View)
3821 * @see #detachViewFromParent(int)
3822 */
3823 protected void removeDetachedView(View child, boolean animate) {
Chet Haase21cd1382010-09-01 17:42:29 -07003824 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003825 mTransition.removeChild(this, child);
Chet Haase21cd1382010-09-01 17:42:29 -07003826 }
3827
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003828 if (child == mFocused) {
3829 child.clearFocus();
3830 }
Romain Guy8506ab42009-06-11 17:35:47 -07003831
Svetoslav Ganov961bf0e2012-05-08 09:40:03 -07003832 child.clearAccessibilityFocus();
3833
Jeff Brown59a422e2012-04-19 15:19:19 -07003834 cancelTouchTarget(child);
3835 cancelHoverTarget(child);
3836
Chet Haase21cd1382010-09-01 17:42:29 -07003837 if ((animate && child.getAnimation() != null) ||
3838 (mTransitioningViews != null && mTransitioningViews.contains(child))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003839 addDisappearingView(child);
3840 } else if (child.mAttachInfo != null) {
3841 child.dispatchDetachedFromWindow();
3842 }
3843
Adam Powell539ee872012-02-03 19:00:49 -08003844 if (child.hasTransientState()) {
3845 childHasTransientStateChanged(child, false);
3846 }
3847
Philip Milnef51d91c2011-07-18 16:12:19 -07003848 onViewRemoved(child);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003849 }
3850
3851 /**
3852 * Attaches a view to this view group. Attaching a view assigns this group as the parent,
Chet Haaseca479d42012-08-30 17:20:08 -07003853 * sets the layout parameters and puts the view in the list of children so that
3854 * it can be retrieved by calling {@link #getChildAt(int)}.
3855 * <p>
3856 * This method is intended to be lightweight and makes no assumptions about whether the
3857 * parent or child should be redrawn. Proper use of this method will include also making
3858 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
3859 * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
3860 * which performs a {@link #requestLayout()} on the next frame, after all detach/attach
3861 * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
3862 * <p>
3863 * This method should be called only for views which were detached from their parent.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003864 *
3865 * @param child the child to attach
3866 * @param index the index at which the child should be attached
3867 * @param params the layout parameters of the child
3868 *
3869 * @see #removeDetachedView(View, boolean)
3870 * @see #detachAllViewsFromParent()
3871 * @see #detachViewFromParent(View)
3872 * @see #detachViewFromParent(int)
3873 */
3874 protected void attachViewToParent(View child, int index, LayoutParams params) {
3875 child.mLayoutParams = params;
3876
3877 if (index < 0) {
3878 index = mChildrenCount;
3879 }
3880
3881 addInArray(child, index);
3882
3883 child.mParent = this;
Dianne Hackborn4702a852012-08-17 15:18:29 -07003884 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK
3885 & ~PFLAG_DRAWING_CACHE_VALID)
3886 | PFLAG_DRAWN | PFLAG_INVALIDATED;
3887 this.mPrivateFlags |= PFLAG_INVALIDATED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003888
3889 if (child.hasFocus()) {
3890 requestChildFocus(child, child.findFocus());
3891 }
3892 }
3893
3894 /**
Chet Haaseca479d42012-08-30 17:20:08 -07003895 * Detaches a view from its parent. Detaching a view should be followed
3896 * either by a call to
3897 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3898 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
3899 * temporary; reattachment or removal should happen within the same drawing cycle as
3900 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
3901 * call to {@link #getChildAt(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003902 *
3903 * @param child the child to detach
3904 *
3905 * @see #detachViewFromParent(int)
3906 * @see #detachViewsFromParent(int, int)
3907 * @see #detachAllViewsFromParent()
3908 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3909 * @see #removeDetachedView(View, boolean)
3910 */
3911 protected void detachViewFromParent(View child) {
3912 removeFromArray(indexOfChild(child));
3913 }
3914
3915 /**
Chet Haaseca479d42012-08-30 17:20:08 -07003916 * Detaches a view from its parent. Detaching a view should be followed
3917 * either by a call to
3918 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3919 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
3920 * temporary; reattachment or removal should happen within the same drawing cycle as
3921 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
3922 * call to {@link #getChildAt(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003923 *
3924 * @param index the index of the child to detach
3925 *
3926 * @see #detachViewFromParent(View)
3927 * @see #detachAllViewsFromParent()
3928 * @see #detachViewsFromParent(int, int)
3929 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3930 * @see #removeDetachedView(View, boolean)
3931 */
3932 protected void detachViewFromParent(int index) {
3933 removeFromArray(index);
3934 }
3935
3936 /**
Chet Haaseca479d42012-08-30 17:20:08 -07003937 * Detaches a range of views from their parents. Detaching a view should be followed
3938 * either by a call to
3939 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3940 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
3941 * temporary; reattachment or removal should happen within the same drawing cycle as
3942 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
3943 * call to {@link #getChildAt(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003944 *
3945 * @param start the first index of the childrend range to detach
3946 * @param count the number of children to detach
3947 *
3948 * @see #detachViewFromParent(View)
3949 * @see #detachViewFromParent(int)
3950 * @see #detachAllViewsFromParent()
3951 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3952 * @see #removeDetachedView(View, boolean)
3953 */
3954 protected void detachViewsFromParent(int start, int count) {
3955 removeFromArray(start, count);
3956 }
3957
3958 /**
Chet Haaseca479d42012-08-30 17:20:08 -07003959 * Detaches all views from the parent. Detaching a view should be followed
3960 * either by a call to
3961 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3962 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
3963 * temporary; reattachment or removal should happen within the same drawing cycle as
3964 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
3965 * call to {@link #getChildAt(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003966 *
3967 * @see #detachViewFromParent(View)
3968 * @see #detachViewFromParent(int)
3969 * @see #detachViewsFromParent(int, int)
3970 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3971 * @see #removeDetachedView(View, boolean)
3972 */
3973 protected void detachAllViewsFromParent() {
3974 final int count = mChildrenCount;
3975 if (count <= 0) {
3976 return;
3977 }
3978
3979 final View[] children = mChildren;
3980 mChildrenCount = 0;
3981
3982 for (int i = count - 1; i >= 0; i--) {
3983 children[i].mParent = null;
3984 children[i] = null;
3985 }
3986 }
3987
3988 /**
3989 * Don't call or override this method. It is used for the implementation of
3990 * the view hierarchy.
3991 */
3992 public final void invalidateChild(View child, final Rect dirty) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003993 ViewParent parent = this;
3994
3995 final AttachInfo attachInfo = mAttachInfo;
3996 if (attachInfo != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003997 // If the child is drawing an animation, we want to copy this flag onto
3998 // ourselves and the parent to make sure the invalidate request goes
3999 // through
Dianne Hackborn4702a852012-08-17 15:18:29 -07004000 final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)
4001 == PFLAG_DRAW_ANIMATION;
Romain Guy24443ea2009-05-11 11:56:30 -07004002
Romain Guyfe455af2012-02-15 16:40:20 -08004003 // Check whether the child that requests the invalidate is fully opaque
4004 // Views being animated or transformed are not considered opaque because we may
4005 // be invalidating their old position and need the parent to paint behind them.
4006 Matrix childMatrix = child.getMatrix();
4007 final boolean isOpaque = child.isOpaque() && !drawAnimation &&
4008 child.getAnimation() == null && childMatrix.isIdentity();
4009 // Mark the child as dirty, using the appropriate flag
4010 // Make sure we do not set both flags at the same time
Dianne Hackborn4702a852012-08-17 15:18:29 -07004011 int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
Romain Guyfe455af2012-02-15 16:40:20 -08004012
4013 if (child.mLayerType != LAYER_TYPE_NONE) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004014 mPrivateFlags |= PFLAG_INVALIDATED;
4015 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
Romain Guyfe455af2012-02-15 16:40:20 -08004016 child.mLocalDirtyRect.union(dirty);
4017 }
4018
4019 final int[] location = attachInfo.mInvalidateChildLocation;
4020 location[CHILD_LEFT_INDEX] = child.mLeft;
4021 location[CHILD_TOP_INDEX] = child.mTop;
Chet Haase599913d2012-07-23 16:22:05 -07004022 if (!childMatrix.isIdentity() ||
4023 (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
Romain Guyfe455af2012-02-15 16:40:20 -08004024 RectF boundingRect = attachInfo.mTmpTransformRect;
4025 boundingRect.set(dirty);
Chet Haase599913d2012-07-23 16:22:05 -07004026 Matrix transformMatrix;
4027 if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
4028 Transformation t = attachInfo.mTmpTransformation;
4029 boolean transformed = getChildStaticTransformation(child, t);
4030 if (transformed) {
4031 transformMatrix = attachInfo.mTmpMatrix;
4032 transformMatrix.set(t.getMatrix());
4033 if (!childMatrix.isIdentity()) {
4034 transformMatrix.preConcat(childMatrix);
4035 }
4036 } else {
4037 transformMatrix = childMatrix;
4038 }
4039 } else {
4040 transformMatrix = childMatrix;
4041 }
4042 transformMatrix.mapRect(boundingRect);
Romain Guyfe455af2012-02-15 16:40:20 -08004043 dirty.set((int) (boundingRect.left - 0.5f),
4044 (int) (boundingRect.top - 0.5f),
4045 (int) (boundingRect.right + 0.5f),
4046 (int) (boundingRect.bottom + 0.5f));
4047 }
4048
4049 do {
4050 View view = null;
4051 if (parent instanceof View) {
4052 view = (View) parent;
Romain Guyfe455af2012-02-15 16:40:20 -08004053 }
4054
4055 if (drawAnimation) {
4056 if (view != null) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004057 view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
Romain Guyfe455af2012-02-15 16:40:20 -08004058 } else if (parent instanceof ViewRootImpl) {
4059 ((ViewRootImpl) parent).mIsAnimating = true;
4060 }
4061 }
4062
4063 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
4064 // flag coming from the child that initiated the invalidate
4065 if (view != null) {
4066 if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
4067 view.getSolidColor() == 0) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004068 opaqueFlag = PFLAG_DIRTY;
Romain Guyfe455af2012-02-15 16:40:20 -08004069 }
Dianne Hackborn4702a852012-08-17 15:18:29 -07004070 if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
4071 view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
Romain Guyfe455af2012-02-15 16:40:20 -08004072 }
4073 }
4074
4075 parent = parent.invalidateChildInParent(location, dirty);
4076 if (view != null) {
4077 // Account for transform on current parent
4078 Matrix m = view.getMatrix();
4079 if (!m.isIdentity()) {
4080 RectF boundingRect = attachInfo.mTmpTransformRect;
4081 boundingRect.set(dirty);
4082 m.mapRect(boundingRect);
Romain Guye8585b12012-02-17 18:28:47 -08004083 dirty.set((int) (boundingRect.left - 0.5f),
4084 (int) (boundingRect.top - 0.5f),
Romain Guyfe455af2012-02-15 16:40:20 -08004085 (int) (boundingRect.right + 0.5f),
4086 (int) (boundingRect.bottom + 0.5f));
4087 }
4088 }
4089 } while (parent != null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004090 }
4091 }
4092
4093 /**
4094 * Don't call or override this method. It is used for the implementation of
4095 * the view hierarchy.
4096 *
4097 * This implementation returns null if this ViewGroup does not have a parent,
4098 * if this ViewGroup is already fully invalidated or if the dirty rectangle
4099 * does not intersect with this ViewGroup's bounds.
4100 */
4101 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004102 if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
4103 (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004104 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
4105 FLAG_OPTIMIZE_INVALIDATE) {
4106 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
4107 location[CHILD_TOP_INDEX] - mScrollY);
4108
4109 final int left = mLeft;
4110 final int top = mTop;
4111
Chet Haase05e91ed2012-07-03 14:17:57 -07004112 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
4113 if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
4114 dirty.setEmpty();
Romain Guy3a3133d2011-02-01 22:59:58 -08004115 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004116 }
Dianne Hackborn4702a852012-08-17 15:18:29 -07004117 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
Chet Haase05e91ed2012-07-03 14:17:57 -07004118
4119 location[CHILD_LEFT_INDEX] = left;
4120 location[CHILD_TOP_INDEX] = top;
4121
4122 if (mLayerType != LAYER_TYPE_NONE) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004123 mPrivateFlags |= PFLAG_INVALIDATED;
Chet Haase05e91ed2012-07-03 14:17:57 -07004124 mLocalDirtyRect.union(dirty);
4125 }
4126
4127 return mParent;
4128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004129 } else {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004130 mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004131
4132 location[CHILD_LEFT_INDEX] = mLeft;
4133 location[CHILD_TOP_INDEX] = mTop;
Chet Haasea3db8662011-07-19 10:36:05 -07004134 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
4135 dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
4136 } else {
4137 // in case the dirty rect extends outside the bounds of this container
4138 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
4139 }
Romain Guy3a3133d2011-02-01 22:59:58 -08004140
4141 if (mLayerType != LAYER_TYPE_NONE) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004142 mPrivateFlags |= PFLAG_INVALIDATED;
Romain Guy3a3133d2011-02-01 22:59:58 -08004143 mLocalDirtyRect.union(dirty);
4144 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004145
4146 return mParent;
4147 }
4148 }
4149
4150 return null;
4151 }
4152
4153 /**
Chet Haase9d1992d2012-03-13 11:03:25 -07004154 * Quick invalidation method called by View.invalidateViewProperty. This doesn't set the
4155 * DRAWN flags and doesn't handle the Animation logic that the default invalidation methods
4156 * do; all we want to do here is schedule a traversal with the appropriate dirty rect.
4157 *
4158 * @hide
4159 */
4160 public void invalidateChildFast(View child, final Rect dirty) {
4161 ViewParent parent = this;
4162
4163 final AttachInfo attachInfo = mAttachInfo;
4164 if (attachInfo != null) {
4165 if (child.mLayerType != LAYER_TYPE_NONE) {
4166 child.mLocalDirtyRect.union(dirty);
4167 }
4168
4169 int left = child.mLeft;
4170 int top = child.mTop;
4171 if (!child.getMatrix().isIdentity()) {
4172 child.transformRect(dirty);
4173 }
4174
4175 do {
4176 if (parent instanceof ViewGroup) {
4177 ViewGroup parentVG = (ViewGroup) parent;
Chet Haaseb85967b2012-03-26 14:37:51 -07004178 if (parentVG.mLayerType != LAYER_TYPE_NONE) {
4179 // Layered parents should be recreated, not just re-issued
4180 parentVG.invalidate();
4181 parent = null;
4182 } else {
4183 parent = parentVG.invalidateChildInParentFast(left, top, dirty);
4184 left = parentVG.mLeft;
4185 top = parentVG.mTop;
4186 }
Chet Haase9d1992d2012-03-13 11:03:25 -07004187 } else {
4188 // Reached the top; this calls into the usual invalidate method in
4189 // ViewRootImpl, which schedules a traversal
4190 final int[] location = attachInfo.mInvalidateChildLocation;
4191 location[0] = left;
4192 location[1] = top;
4193 parent = parent.invalidateChildInParent(location, dirty);
4194 }
4195 } while (parent != null);
4196 }
4197 }
4198
4199 /**
4200 * Quick invalidation method that simply transforms the dirty rect into the parent's
4201 * coordinate system, pruning the invalidation if the parent has already been invalidated.
4202 */
4203 private ViewParent invalidateChildInParentFast(int left, int top, final Rect dirty) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004204 if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
4205 (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
Chet Haase9d1992d2012-03-13 11:03:25 -07004206 dirty.offset(left - mScrollX, top - mScrollY);
4207
4208 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0 ||
4209 dirty.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
4210
4211 if (mLayerType != LAYER_TYPE_NONE) {
4212 mLocalDirtyRect.union(dirty);
4213 }
4214 if (!getMatrix().isIdentity()) {
4215 transformRect(dirty);
4216 }
4217
4218 return mParent;
4219 }
4220 }
4221
4222 return null;
4223 }
4224
4225 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004226 * Offset a rectangle that is in a descendant's coordinate
4227 * space into our coordinate space.
4228 * @param descendant A descendant of this view
4229 * @param rect A rectangle defined in descendant's coordinate space.
4230 */
4231 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
4232 offsetRectBetweenParentAndChild(descendant, rect, true, false);
4233 }
4234
4235 /**
4236 * Offset a rectangle that is in our coordinate space into an ancestor's
4237 * coordinate space.
4238 * @param descendant A descendant of this view
4239 * @param rect A rectangle defined in descendant's coordinate space.
4240 */
4241 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
4242 offsetRectBetweenParentAndChild(descendant, rect, false, false);
4243 }
4244
4245 /**
4246 * Helper method that offsets a rect either from parent to descendant or
4247 * descendant to parent.
4248 */
4249 void offsetRectBetweenParentAndChild(View descendant, Rect rect,
4250 boolean offsetFromChildToParent, boolean clipToBounds) {
4251
4252 // already in the same coord system :)
4253 if (descendant == this) {
4254 return;
4255 }
4256
4257 ViewParent theParent = descendant.mParent;
4258
4259 // search and offset up to the parent
4260 while ((theParent != null)
4261 && (theParent instanceof View)
4262 && (theParent != this)) {
4263
4264 if (offsetFromChildToParent) {
4265 rect.offset(descendant.mLeft - descendant.mScrollX,
4266 descendant.mTop - descendant.mScrollY);
4267 if (clipToBounds) {
4268 View p = (View) theParent;
4269 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4270 }
4271 } else {
4272 if (clipToBounds) {
4273 View p = (View) theParent;
4274 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4275 }
4276 rect.offset(descendant.mScrollX - descendant.mLeft,
4277 descendant.mScrollY - descendant.mTop);
4278 }
4279
4280 descendant = (View) theParent;
4281 theParent = descendant.mParent;
4282 }
4283
4284 // now that we are up to this view, need to offset one more time
4285 // to get into our coordinate space
4286 if (theParent == this) {
4287 if (offsetFromChildToParent) {
4288 rect.offset(descendant.mLeft - descendant.mScrollX,
4289 descendant.mTop - descendant.mScrollY);
4290 } else {
4291 rect.offset(descendant.mScrollX - descendant.mLeft,
4292 descendant.mScrollY - descendant.mTop);
4293 }
4294 } else {
4295 throw new IllegalArgumentException("parameter must be a descendant of this view");
4296 }
4297 }
4298
4299 /**
4300 * Offset the vertical location of all children of this view by the specified number of pixels.
4301 *
4302 * @param offset the number of pixels to offset
4303 *
4304 * @hide
4305 */
4306 public void offsetChildrenTopAndBottom(int offset) {
4307 final int count = mChildrenCount;
4308 final View[] children = mChildren;
4309
4310 for (int i = 0; i < count; i++) {
4311 final View v = children[i];
4312 v.mTop += offset;
4313 v.mBottom += offset;
Chet Haase1271e2c2012-04-20 09:54:27 -07004314 if (v.mDisplayList != null) {
Chet Haasea1cff502012-02-21 13:43:44 -08004315 v.mDisplayList.offsetTopBottom(offset);
Chet Haase9d1992d2012-03-13 11:03:25 -07004316 invalidateViewProperty(false, false);
Chet Haasea1cff502012-02-21 13:43:44 -08004317 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004318 }
4319 }
4320
4321 /**
4322 * {@inheritDoc}
4323 */
4324 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
Adam Powellf93bb6d2011-12-12 15:21:57 -08004325 // It doesn't make a whole lot of sense to call this on a view that isn't attached,
4326 // but for some simple tests it can be useful. If we don't have attach info this
4327 // will allocate memory.
4328 final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF();
Gilles Debunnecea45132011-11-24 02:19:27 +01004329 rect.set(r);
4330
4331 if (!child.hasIdentityMatrix()) {
4332 child.getMatrix().mapRect(rect);
4333 }
4334
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004335 int dx = child.mLeft - mScrollX;
4336 int dy = child.mTop - mScrollY;
Gilles Debunnecea45132011-11-24 02:19:27 +01004337
4338 rect.offset(dx, dy);
4339
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004340 if (offset != null) {
Gilles Debunnecea45132011-11-24 02:19:27 +01004341 if (!child.hasIdentityMatrix()) {
Adam Powellf93bb6d2011-12-12 15:21:57 -08004342 float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation
4343 : new float[2];
Gilles Debunnecea45132011-11-24 02:19:27 +01004344 position[0] = offset.x;
4345 position[1] = offset.y;
4346 child.getMatrix().mapPoints(position);
4347 offset.x = (int) (position[0] + 0.5f);
4348 offset.y = (int) (position[1] + 0.5f);
4349 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004350 offset.x += dx;
4351 offset.y += dy;
4352 }
Gilles Debunnecea45132011-11-24 02:19:27 +01004353
4354 if (rect.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
4355 if (mParent == null) return true;
4356 r.set((int) (rect.left + 0.5f), (int) (rect.top + 0.5f),
4357 (int) (rect.right + 0.5f), (int) (rect.bottom + 0.5f));
4358 return mParent.getChildVisibleRect(this, r, offset);
4359 }
4360
4361 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004362 }
4363
4364 /**
4365 * {@inheritDoc}
4366 */
4367 @Override
Chet Haase9c087442011-01-12 16:20:16 -08004368 public final void layout(int l, int t, int r, int b) {
4369 if (mTransition == null || !mTransition.isChangingLayout()) {
Chet Haase7dd4a532012-04-16 13:35:09 -07004370 if (mTransition != null) {
4371 mTransition.layoutChange(this);
4372 }
Chet Haase9c087442011-01-12 16:20:16 -08004373 super.layout(l, t, r, b);
4374 } else {
4375 // record the fact that we noop'd it; request layout when transition finishes
4376 mLayoutSuppressed = true;
4377 }
4378 }
4379
4380 /**
4381 * {@inheritDoc}
4382 */
4383 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004384 protected abstract void onLayout(boolean changed,
4385 int l, int t, int r, int b);
4386
4387 /**
4388 * Indicates whether the view group has the ability to animate its children
4389 * after the first layout.
4390 *
4391 * @return true if the children can be animated, false otherwise
4392 */
4393 protected boolean canAnimate() {
4394 return mLayoutAnimationController != null;
4395 }
4396
4397 /**
4398 * Runs the layout animation. Calling this method triggers a relayout of
4399 * this view group.
4400 */
4401 public void startLayoutAnimation() {
4402 if (mLayoutAnimationController != null) {
4403 mGroupFlags |= FLAG_RUN_ANIMATION;
4404 requestLayout();
4405 }
4406 }
4407
4408 /**
4409 * Schedules the layout animation to be played after the next layout pass
4410 * of this view group. This can be used to restart the layout animation
4411 * when the content of the view group changes or when the activity is
4412 * paused and resumed.
4413 */
4414 public void scheduleLayoutAnimation() {
4415 mGroupFlags |= FLAG_RUN_ANIMATION;
4416 }
4417
4418 /**
4419 * Sets the layout animation controller used to animate the group's
4420 * children after the first layout.
4421 *
4422 * @param controller the animation controller
4423 */
4424 public void setLayoutAnimation(LayoutAnimationController controller) {
4425 mLayoutAnimationController = controller;
4426 if (mLayoutAnimationController != null) {
4427 mGroupFlags |= FLAG_RUN_ANIMATION;
4428 }
4429 }
4430
4431 /**
4432 * Returns the layout animation controller used to animate the group's
4433 * children.
4434 *
4435 * @return the current animation controller
4436 */
4437 public LayoutAnimationController getLayoutAnimation() {
4438 return mLayoutAnimationController;
4439 }
4440
4441 /**
4442 * Indicates whether the children's drawing cache is used during a layout
4443 * animation. By default, the drawing cache is enabled but this will prevent
4444 * nested layout animations from working. To nest animations, you must disable
4445 * the cache.
4446 *
4447 * @return true if the animation cache is enabled, false otherwise
4448 *
4449 * @see #setAnimationCacheEnabled(boolean)
4450 * @see View#setDrawingCacheEnabled(boolean)
4451 */
4452 @ViewDebug.ExportedProperty
4453 public boolean isAnimationCacheEnabled() {
4454 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
4455 }
4456
4457 /**
4458 * Enables or disables the children's drawing cache during a layout animation.
4459 * By default, the drawing cache is enabled but this will prevent nested
4460 * layout animations from working. To nest animations, you must disable the
4461 * cache.
4462 *
4463 * @param enabled true to enable the animation cache, false otherwise
4464 *
4465 * @see #isAnimationCacheEnabled()
4466 * @see View#setDrawingCacheEnabled(boolean)
4467 */
4468 public void setAnimationCacheEnabled(boolean enabled) {
4469 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
4470 }
4471
4472 /**
4473 * Indicates whether this ViewGroup will always try to draw its children using their
4474 * drawing cache. By default this property is enabled.
4475 *
4476 * @return true if the animation cache is enabled, false otherwise
4477 *
4478 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4479 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4480 * @see View#setDrawingCacheEnabled(boolean)
4481 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004482 @ViewDebug.ExportedProperty(category = "drawing")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004483 public boolean isAlwaysDrawnWithCacheEnabled() {
4484 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
4485 }
4486
4487 /**
4488 * Indicates whether this ViewGroup will always try to draw its children using their
4489 * drawing cache. This property can be set to true when the cache rendering is
4490 * slightly different from the children's normal rendering. Renderings can be different,
4491 * for instance, when the cache's quality is set to low.
4492 *
4493 * When this property is disabled, the ViewGroup will use the drawing cache of its
4494 * children only when asked to. It's usually the task of subclasses to tell ViewGroup
4495 * when to start using the drawing cache and when to stop using it.
4496 *
4497 * @param always true to always draw with the drawing cache, false otherwise
4498 *
4499 * @see #isAlwaysDrawnWithCacheEnabled()
4500 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4501 * @see View#setDrawingCacheEnabled(boolean)
4502 * @see View#setDrawingCacheQuality(int)
4503 */
4504 public void setAlwaysDrawnWithCacheEnabled(boolean always) {
4505 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
4506 }
4507
4508 /**
4509 * Indicates whether the ViewGroup is currently drawing its children using
4510 * their drawing cache.
4511 *
4512 * @return true if children should be drawn with their cache, false otherwise
4513 *
4514 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4515 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4516 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004517 @ViewDebug.ExportedProperty(category = "drawing")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004518 protected boolean isChildrenDrawnWithCacheEnabled() {
4519 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
4520 }
4521
4522 /**
4523 * Tells the ViewGroup to draw its children using their drawing cache. This property
4524 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
4525 * will be used only if it has been enabled.
4526 *
4527 * Subclasses should call this method to start and stop using the drawing cache when
4528 * they perform performance sensitive operations, like scrolling or animating.
4529 *
4530 * @param enabled true if children should be drawn with their cache, false otherwise
4531 *
4532 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4533 * @see #isChildrenDrawnWithCacheEnabled()
4534 */
4535 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
4536 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
4537 }
4538
Romain Guy293451e2009-11-04 13:59:48 -08004539 /**
4540 * Indicates whether the ViewGroup is drawing its children in the order defined by
4541 * {@link #getChildDrawingOrder(int, int)}.
4542 *
4543 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
4544 * false otherwise
4545 *
4546 * @see #setChildrenDrawingOrderEnabled(boolean)
4547 * @see #getChildDrawingOrder(int, int)
4548 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004549 @ViewDebug.ExportedProperty(category = "drawing")
Romain Guy293451e2009-11-04 13:59:48 -08004550 protected boolean isChildrenDrawingOrderEnabled() {
4551 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
4552 }
4553
4554 /**
4555 * Tells the ViewGroup whether to draw its children in the order defined by the method
4556 * {@link #getChildDrawingOrder(int, int)}.
4557 *
4558 * @param enabled true if the order of the children when drawing is determined by
4559 * {@link #getChildDrawingOrder(int, int)}, false otherwise
4560 *
4561 * @see #isChildrenDrawingOrderEnabled()
4562 * @see #getChildDrawingOrder(int, int)
4563 */
4564 protected void setChildrenDrawingOrderEnabled(boolean enabled) {
4565 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
4566 }
4567
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004568 private void setBooleanFlag(int flag, boolean value) {
4569 if (value) {
4570 mGroupFlags |= flag;
4571 } else {
4572 mGroupFlags &= ~flag;
4573 }
4574 }
4575
4576 /**
4577 * Returns an integer indicating what types of drawing caches are kept in memory.
4578 *
4579 * @see #setPersistentDrawingCache(int)
4580 * @see #setAnimationCacheEnabled(boolean)
4581 *
4582 * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
4583 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
4584 * and {@link #PERSISTENT_ALL_CACHES}
4585 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004586 @ViewDebug.ExportedProperty(category = "drawing", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004587 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
Romain Guy203688c2010-05-12 15:41:32 -07004588 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004589 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
4590 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL")
4591 })
4592 public int getPersistentDrawingCache() {
4593 return mPersistentDrawingCache;
4594 }
4595
4596 /**
4597 * Indicates what types of drawing caches should be kept in memory after
4598 * they have been created.
4599 *
4600 * @see #getPersistentDrawingCache()
4601 * @see #setAnimationCacheEnabled(boolean)
4602 *
4603 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
4604 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
4605 * and {@link #PERSISTENT_ALL_CACHES}
4606 */
4607 public void setPersistentDrawingCache(int drawingCacheToKeep) {
4608 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
4609 }
4610
4611 /**
Philip Milne7a23b492012-04-24 22:12:36 -07004612 * Returns the basis of alignment during layout operations on this view group:
4613 * either {@link #CLIP_BOUNDS} or {@link #OPTICAL_BOUNDS}.
Philip Milne1557fd72012-04-04 23:41:34 -07004614 *
Philip Milnefcc6a0f2012-04-16 16:12:19 -07004615 * @return the layout mode to use during layout operations
Philip Milne1557fd72012-04-04 23:41:34 -07004616 *
4617 * @see #setLayoutMode(int)
Philip Milne7bb66c92012-05-07 11:42:57 -07004618 *
4619 * @hide
Philip Milne1557fd72012-04-04 23:41:34 -07004620 */
4621 public int getLayoutMode() {
Philip Milne1557fd72012-04-04 23:41:34 -07004622 return mLayoutMode;
4623 }
4624
4625 /**
Philip Milne7a23b492012-04-24 22:12:36 -07004626 * Sets the basis of alignment during the layout of this view group.
4627 * Valid values are either {@link #CLIP_BOUNDS} or {@link #OPTICAL_BOUNDS}.
Philip Milne1557fd72012-04-04 23:41:34 -07004628 * <p>
Philip Milne7a23b492012-04-24 22:12:36 -07004629 * The default is {@link #CLIP_BOUNDS}.
Philip Milne1557fd72012-04-04 23:41:34 -07004630 *
Philip Milnefcc6a0f2012-04-16 16:12:19 -07004631 * @param layoutMode the layout mode to use during layout operations
Philip Milne1557fd72012-04-04 23:41:34 -07004632 *
4633 * @see #getLayoutMode()
Philip Milne7bb66c92012-05-07 11:42:57 -07004634 *
4635 * @hide
Philip Milne1557fd72012-04-04 23:41:34 -07004636 */
4637 public void setLayoutMode(int layoutMode) {
4638 if (mLayoutMode != layoutMode) {
4639 mLayoutMode = layoutMode;
4640 requestLayout();
4641 }
4642 }
4643
4644 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004645 * Returns a new set of layout parameters based on the supplied attributes set.
4646 *
4647 * @param attrs the attributes to build the layout parameters from
4648 *
4649 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
4650 * of its descendants
4651 */
4652 public LayoutParams generateLayoutParams(AttributeSet attrs) {
4653 return new LayoutParams(getContext(), attrs);
4654 }
4655
4656 /**
4657 * Returns a safe set of layout parameters based on the supplied layout params.
4658 * When a ViewGroup is passed a View whose layout params do not pass the test of
4659 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
4660 * is invoked. This method should return a new set of layout params suitable for
4661 * this ViewGroup, possibly by copying the appropriate attributes from the
4662 * specified set of layout params.
4663 *
4664 * @param p The layout parameters to convert into a suitable set of layout parameters
4665 * for this ViewGroup.
4666 *
4667 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
4668 * of its descendants
4669 */
4670 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4671 return p;
4672 }
4673
4674 /**
4675 * Returns a set of default layout parameters. These parameters are requested
4676 * when the View passed to {@link #addView(View)} has no layout parameters
4677 * already set. If null is returned, an exception is thrown from addView.
4678 *
4679 * @return a set of default layout parameters or null
4680 */
4681 protected LayoutParams generateDefaultLayoutParams() {
4682 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
4683 }
4684
4685 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004686 * {@inheritDoc}
4687 */
4688 @Override
4689 protected void debug(int depth) {
4690 super.debug(depth);
4691 String output;
4692
4693 if (mFocused != null) {
4694 output = debugIndent(depth);
4695 output += "mFocused";
4696 Log.d(VIEW_LOG_TAG, output);
4697 }
4698 if (mChildrenCount != 0) {
4699 output = debugIndent(depth);
4700 output += "{";
4701 Log.d(VIEW_LOG_TAG, output);
4702 }
4703 int count = mChildrenCount;
4704 for (int i = 0; i < count; i++) {
4705 View child = mChildren[i];
4706 child.debug(depth + 1);
4707 }
4708
4709 if (mChildrenCount != 0) {
4710 output = debugIndent(depth);
4711 output += "}";
4712 Log.d(VIEW_LOG_TAG, output);
4713 }
4714 }
4715
4716 /**
4717 * Returns the position in the group of the specified child view.
4718 *
4719 * @param child the view for which to get the position
4720 * @return a positive integer representing the position of the view in the
4721 * group, or -1 if the view does not exist in the group
4722 */
4723 public int indexOfChild(View child) {
4724 final int count = mChildrenCount;
4725 final View[] children = mChildren;
4726 for (int i = 0; i < count; i++) {
4727 if (children[i] == child) {
4728 return i;
4729 }
4730 }
4731 return -1;
4732 }
4733
4734 /**
4735 * Returns the number of children in the group.
4736 *
4737 * @return a positive integer representing the number of children in
4738 * the group
4739 */
4740 public int getChildCount() {
4741 return mChildrenCount;
4742 }
4743
4744 /**
4745 * Returns the view at the specified position in the group.
4746 *
4747 * @param index the position at which to get the view from
4748 * @return the view at the specified position or null if the position
4749 * does not exist within the group
4750 */
4751 public View getChildAt(int index) {
Adam Powell3ba8f5d62011-03-07 15:36:33 -08004752 if (index < 0 || index >= mChildrenCount) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004753 return null;
4754 }
Adam Powell3ba8f5d62011-03-07 15:36:33 -08004755 return mChildren[index];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004756 }
4757
4758 /**
4759 * Ask all of the children of this view to measure themselves, taking into
4760 * account both the MeasureSpec requirements for this view and its padding.
4761 * We skip children that are in the GONE state The heavy lifting is done in
4762 * getChildMeasureSpec.
4763 *
4764 * @param widthMeasureSpec The width requirements for this view
4765 * @param heightMeasureSpec The height requirements for this view
4766 */
4767 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
4768 final int size = mChildrenCount;
4769 final View[] children = mChildren;
4770 for (int i = 0; i < size; ++i) {
4771 final View child = children[i];
4772 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
4773 measureChild(child, widthMeasureSpec, heightMeasureSpec);
4774 }
4775 }
4776 }
4777
4778 /**
4779 * Ask one of the children of this view to measure itself, taking into
4780 * account both the MeasureSpec requirements for this view and its padding.
4781 * The heavy lifting is done in getChildMeasureSpec.
4782 *
4783 * @param child The child to measure
4784 * @param parentWidthMeasureSpec The width requirements for this view
4785 * @param parentHeightMeasureSpec The height requirements for this view
4786 */
4787 protected void measureChild(View child, int parentWidthMeasureSpec,
4788 int parentHeightMeasureSpec) {
4789 final LayoutParams lp = child.getLayoutParams();
4790
4791 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
4792 mPaddingLeft + mPaddingRight, lp.width);
4793 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
4794 mPaddingTop + mPaddingBottom, lp.height);
4795
4796 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4797 }
4798
4799 /**
4800 * Ask one of the children of this view to measure itself, taking into
4801 * account both the MeasureSpec requirements for this view and its padding
4802 * and margins. The child must have MarginLayoutParams The heavy lifting is
4803 * done in getChildMeasureSpec.
4804 *
4805 * @param child The child to measure
4806 * @param parentWidthMeasureSpec The width requirements for this view
4807 * @param widthUsed Extra space that has been used up by the parent
4808 * horizontally (possibly by other children of the parent)
4809 * @param parentHeightMeasureSpec The height requirements for this view
4810 * @param heightUsed Extra space that has been used up by the parent
4811 * vertically (possibly by other children of the parent)
4812 */
4813 protected void measureChildWithMargins(View child,
4814 int parentWidthMeasureSpec, int widthUsed,
4815 int parentHeightMeasureSpec, int heightUsed) {
4816 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
4817
4818 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
4819 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
4820 + widthUsed, lp.width);
4821 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
4822 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
4823 + heightUsed, lp.height);
4824
4825 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
4826 }
4827
4828 /**
4829 * Does the hard part of measureChildren: figuring out the MeasureSpec to
4830 * pass to a particular child. This method figures out the right MeasureSpec
4831 * for one dimension (height or width) of one child view.
4832 *
4833 * The goal is to combine information from our MeasureSpec with the
4834 * LayoutParams of the child to get the best possible results. For example,
4835 * if the this view knows its size (because its MeasureSpec has a mode of
4836 * EXACTLY), and the child has indicated in its LayoutParams that it wants
4837 * to be the same size as the parent, the parent should ask the child to
4838 * layout given an exact size.
4839 *
4840 * @param spec The requirements for this view
4841 * @param padding The padding of this view for the current dimension and
4842 * margins, if applicable
4843 * @param childDimension How big the child wants to be in the current
4844 * dimension
4845 * @return a MeasureSpec integer for the child
4846 */
4847 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
4848 int specMode = MeasureSpec.getMode(spec);
4849 int specSize = MeasureSpec.getSize(spec);
4850
4851 int size = Math.max(0, specSize - padding);
4852
4853 int resultSize = 0;
4854 int resultMode = 0;
4855
4856 switch (specMode) {
4857 // Parent has imposed an exact size on us
4858 case MeasureSpec.EXACTLY:
4859 if (childDimension >= 0) {
4860 resultSize = childDimension;
4861 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08004862 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004863 // Child wants to be our size. So be it.
4864 resultSize = size;
4865 resultMode = MeasureSpec.EXACTLY;
4866 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4867 // Child wants to determine its own size. It can't be
4868 // bigger than us.
4869 resultSize = size;
4870 resultMode = MeasureSpec.AT_MOST;
4871 }
4872 break;
4873
4874 // Parent has imposed a maximum size on us
4875 case MeasureSpec.AT_MOST:
4876 if (childDimension >= 0) {
4877 // Child wants a specific size... so be it
4878 resultSize = childDimension;
4879 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08004880 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004881 // Child wants to be our size, but our size is not fixed.
4882 // Constrain child to not be bigger than us.
4883 resultSize = size;
4884 resultMode = MeasureSpec.AT_MOST;
4885 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4886 // Child wants to determine its own size. It can't be
4887 // bigger than us.
4888 resultSize = size;
4889 resultMode = MeasureSpec.AT_MOST;
4890 }
4891 break;
4892
4893 // Parent asked to see how big we want to be
4894 case MeasureSpec.UNSPECIFIED:
4895 if (childDimension >= 0) {
4896 // Child wants a specific size... let him have it
4897 resultSize = childDimension;
4898 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08004899 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004900 // Child wants to be our size... find out how big it should
4901 // be
4902 resultSize = 0;
4903 resultMode = MeasureSpec.UNSPECIFIED;
4904 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4905 // Child wants to determine its own size.... find out how
4906 // big it should be
4907 resultSize = 0;
4908 resultMode = MeasureSpec.UNSPECIFIED;
4909 }
4910 break;
4911 }
4912 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
4913 }
4914
4915
4916 /**
4917 * Removes any pending animations for views that have been removed. Call
4918 * this if you don't want animations for exiting views to stack up.
4919 */
4920 public void clearDisappearingChildren() {
4921 if (mDisappearingChildren != null) {
4922 mDisappearingChildren.clear();
Chet Haaseb85967b2012-03-26 14:37:51 -07004923 invalidate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004924 }
4925 }
4926
4927 /**
4928 * Add a view which is removed from mChildren but still needs animation
4929 *
4930 * @param v View to add
4931 */
4932 private void addDisappearingView(View v) {
4933 ArrayList<View> disappearingChildren = mDisappearingChildren;
4934
4935 if (disappearingChildren == null) {
4936 disappearingChildren = mDisappearingChildren = new ArrayList<View>();
4937 }
4938
4939 disappearingChildren.add(v);
4940 }
4941
4942 /**
4943 * Cleanup a view when its animation is done. This may mean removing it from
4944 * the list of disappearing views.
4945 *
4946 * @param view The view whose animation has finished
4947 * @param animation The animation, cannot be null
4948 */
Chet Haase64a48c12012-02-13 16:33:29 -08004949 void finishAnimatingView(final View view, Animation animation) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004950 final ArrayList<View> disappearingChildren = mDisappearingChildren;
4951 if (disappearingChildren != null) {
4952 if (disappearingChildren.contains(view)) {
4953 disappearingChildren.remove(view);
4954
4955 if (view.mAttachInfo != null) {
4956 view.dispatchDetachedFromWindow();
4957 }
4958
4959 view.clearAnimation();
4960 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4961 }
4962 }
4963
4964 if (animation != null && !animation.getFillAfter()) {
4965 view.clearAnimation();
4966 }
4967
Dianne Hackborn4702a852012-08-17 15:18:29 -07004968 if ((view.mPrivateFlags & PFLAG_ANIMATION_STARTED) == PFLAG_ANIMATION_STARTED) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004969 view.onAnimationEnd();
4970 // Should be performed by onAnimationEnd() but this avoid an infinite loop,
4971 // so we'd rather be safe than sorry
Dianne Hackborn4702a852012-08-17 15:18:29 -07004972 view.mPrivateFlags &= ~PFLAG_ANIMATION_STARTED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004973 // Draw one more frame after the animation is done
4974 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4975 }
4976 }
4977
Chet Haaseb20db3e2010-09-10 13:07:30 -07004978 /**
Chet Haaseaceafe62011-08-26 15:44:33 -07004979 * Utility function called by View during invalidation to determine whether a view that
4980 * is invisible or gone should still be invalidated because it is being transitioned (and
4981 * therefore still needs to be drawn).
4982 */
4983 boolean isViewTransitioning(View view) {
4984 return (mTransitioningViews != null && mTransitioningViews.contains(view));
4985 }
4986
4987 /**
Chet Haaseb20db3e2010-09-10 13:07:30 -07004988 * This method tells the ViewGroup that the given View object, which should have this
4989 * ViewGroup as its parent,
4990 * should be kept around (re-displayed when the ViewGroup draws its children) even if it
4991 * is removed from its parent. This allows animations, such as those used by
4992 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
4993 * the removal of views. A call to this method should always be accompanied by a later call
4994 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
4995 * so that the View finally gets removed.
4996 *
4997 * @param view The View object to be kept visible even if it gets removed from its parent.
4998 */
4999 public void startViewTransition(View view) {
5000 if (view.mParent == this) {
5001 if (mTransitioningViews == null) {
5002 mTransitioningViews = new ArrayList<View>();
5003 }
5004 mTransitioningViews.add(view);
5005 }
5006 }
5007
5008 /**
5009 * This method should always be called following an earlier call to
5010 * {@link #startViewTransition(View)}. The given View is finally removed from its parent
5011 * and will no longer be displayed. Note that this method does not perform the functionality
5012 * of removing a view from its parent; it just discontinues the display of a View that
5013 * has previously been removed.
5014 *
5015 * @return view The View object that has been removed but is being kept around in the visible
5016 * hierarchy by an earlier call to {@link #startViewTransition(View)}.
5017 */
5018 public void endViewTransition(View view) {
5019 if (mTransitioningViews != null) {
5020 mTransitioningViews.remove(view);
5021 final ArrayList<View> disappearingChildren = mDisappearingChildren;
5022 if (disappearingChildren != null && disappearingChildren.contains(view)) {
5023 disappearingChildren.remove(view);
Chet Haase5e25c2c2010-09-16 11:15:56 -07005024 if (mVisibilityChangingChildren != null &&
5025 mVisibilityChangingChildren.contains(view)) {
5026 mVisibilityChangingChildren.remove(view);
5027 } else {
5028 if (view.mAttachInfo != null) {
5029 view.dispatchDetachedFromWindow();
5030 }
5031 if (view.mParent != null) {
5032 view.mParent = null;
5033 }
Chet Haaseb20db3e2010-09-10 13:07:30 -07005034 }
Chet Haaseb85967b2012-03-26 14:37:51 -07005035 invalidate();
Chet Haaseb20db3e2010-09-10 13:07:30 -07005036 }
5037 }
5038 }
5039
Chet Haase21cd1382010-09-01 17:42:29 -07005040 private LayoutTransition.TransitionListener mLayoutTransitionListener =
5041 new LayoutTransition.TransitionListener() {
5042 @Override
5043 public void startTransition(LayoutTransition transition, ViewGroup container,
5044 View view, int transitionType) {
5045 // We only care about disappearing items, since we need special logic to keep
5046 // those items visible after they've been 'removed'
5047 if (transitionType == LayoutTransition.DISAPPEARING) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07005048 startViewTransition(view);
Chet Haase21cd1382010-09-01 17:42:29 -07005049 }
5050 }
5051
5052 @Override
5053 public void endTransition(LayoutTransition transition, ViewGroup container,
5054 View view, int transitionType) {
Chet Haase9c087442011-01-12 16:20:16 -08005055 if (mLayoutSuppressed && !transition.isChangingLayout()) {
5056 requestLayout();
5057 mLayoutSuppressed = false;
5058 }
Chet Haase21cd1382010-09-01 17:42:29 -07005059 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07005060 endViewTransition(view);
Chet Haase21cd1382010-09-01 17:42:29 -07005061 }
5062 }
5063 };
5064
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005065 /**
5066 * {@inheritDoc}
5067 */
5068 @Override
5069 public boolean gatherTransparentRegion(Region region) {
5070 // If no transparent regions requested, we are always opaque.
Dianne Hackborn4702a852012-08-17 15:18:29 -07005071 final boolean meOpaque = (mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005072 if (meOpaque && region == null) {
5073 // The caller doesn't care about the region, so stop now.
5074 return true;
5075 }
5076 super.gatherTransparentRegion(region);
5077 final View[] children = mChildren;
5078 final int count = mChildrenCount;
5079 boolean noneOfTheChildrenAreTransparent = true;
5080 for (int i = 0; i < count; i++) {
5081 final View child = children[i];
Mathias Agopiane3381152010-12-02 15:19:36 -08005082 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005083 if (!child.gatherTransparentRegion(region)) {
5084 noneOfTheChildrenAreTransparent = false;
5085 }
5086 }
5087 }
5088 return meOpaque || noneOfTheChildrenAreTransparent;
5089 }
5090
5091 /**
5092 * {@inheritDoc}
5093 */
5094 public void requestTransparentRegion(View child) {
5095 if (child != null) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07005096 child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005097 if (mParent != null) {
5098 mParent.requestTransparentRegion(this);
5099 }
5100 }
5101 }
Romain Guy8506ab42009-06-11 17:35:47 -07005102
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005103
5104 @Override
5105 protected boolean fitSystemWindows(Rect insets) {
5106 boolean done = super.fitSystemWindows(insets);
5107 if (!done) {
5108 final int count = mChildrenCount;
5109 final View[] children = mChildren;
5110 for (int i = 0; i < count; i++) {
5111 done = children[i].fitSystemWindows(insets);
5112 if (done) {
5113 break;
5114 }
5115 }
5116 }
5117 return done;
5118 }
5119
5120 /**
5121 * Returns the animation listener to which layout animation events are
5122 * sent.
5123 *
5124 * @return an {@link android.view.animation.Animation.AnimationListener}
5125 */
5126 public Animation.AnimationListener getLayoutAnimationListener() {
5127 return mAnimationListener;
5128 }
5129
5130 @Override
5131 protected void drawableStateChanged() {
5132 super.drawableStateChanged();
5133
5134 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
5135 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
5136 throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
5137 + " child has duplicateParentState set to true");
5138 }
5139
5140 final View[] children = mChildren;
5141 final int count = mChildrenCount;
5142
5143 for (int i = 0; i < count; i++) {
5144 final View child = children[i];
5145 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
5146 child.refreshDrawableState();
5147 }
5148 }
5149 }
5150 }
5151
5152 @Override
Dianne Hackborne2136772010-11-04 15:08:59 -07005153 public void jumpDrawablesToCurrentState() {
5154 super.jumpDrawablesToCurrentState();
5155 final View[] children = mChildren;
5156 final int count = mChildrenCount;
5157 for (int i = 0; i < count; i++) {
5158 children[i].jumpDrawablesToCurrentState();
5159 }
5160 }
5161
5162 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005163 protected int[] onCreateDrawableState(int extraSpace) {
5164 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
5165 return super.onCreateDrawableState(extraSpace);
5166 }
5167
5168 int need = 0;
5169 int n = getChildCount();
5170 for (int i = 0; i < n; i++) {
5171 int[] childState = getChildAt(i).getDrawableState();
5172
5173 if (childState != null) {
5174 need += childState.length;
5175 }
5176 }
5177
5178 int[] state = super.onCreateDrawableState(extraSpace + need);
5179
5180 for (int i = 0; i < n; i++) {
5181 int[] childState = getChildAt(i).getDrawableState();
5182
5183 if (childState != null) {
5184 state = mergeDrawableStates(state, childState);
5185 }
5186 }
5187
5188 return state;
5189 }
5190
5191 /**
5192 * Sets whether this ViewGroup's drawable states also include
5193 * its children's drawable states. This is used, for example, to
5194 * make a group appear to be focused when its child EditText or button
5195 * is focused.
5196 */
5197 public void setAddStatesFromChildren(boolean addsStates) {
5198 if (addsStates) {
5199 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
5200 } else {
5201 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
5202 }
5203
5204 refreshDrawableState();
5205 }
5206
5207 /**
5208 * Returns whether this ViewGroup's drawable states also include
5209 * its children's drawable states. This is used, for example, to
5210 * make a group appear to be focused when its child EditText or button
5211 * is focused.
5212 */
5213 public boolean addStatesFromChildren() {
5214 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
5215 }
5216
5217 /**
Jeff Smitha45746e2012-07-19 14:19:24 -05005218 * If {@link #addStatesFromChildren} is true, refreshes this group's
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005219 * drawable state (to include the states from its children).
5220 */
5221 public void childDrawableStateChanged(View child) {
5222 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
5223 refreshDrawableState();
5224 }
5225 }
5226
5227 /**
5228 * Specifies the animation listener to which layout animation events must
5229 * be sent. Only
5230 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
5231 * and
5232 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
5233 * are invoked.
5234 *
5235 * @param animationListener the layout animation listener
5236 */
5237 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
5238 mAnimationListener = animationListener;
5239 }
5240
5241 /**
Chet Haasecca2c982011-05-20 14:34:18 -07005242 * This method is called by LayoutTransition when there are 'changing' animations that need
5243 * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
5244 * starts all pending transitions prior to the drawing phase in the current traversal.
5245 *
5246 * @param transition The LayoutTransition to be started on the next traversal.
5247 *
5248 * @hide
5249 */
5250 public void requestTransitionStart(LayoutTransition transition) {
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07005251 ViewRootImpl viewAncestor = getViewRootImpl();
Chet Haase1abf7fa2011-08-17 18:31:56 -07005252 if (viewAncestor != null) {
5253 viewAncestor.requestTransitionStart(transition);
5254 }
Chet Haasecca2c982011-05-20 14:34:18 -07005255 }
5256
Fabrice Di Meglio4457e852012-09-18 19:23:12 -07005257 /**
5258 * @hide
5259 */
Fabrice Di Meglio80dc53d2011-06-21 18:36:33 -07005260 @Override
Fabrice Di Meglio84ebb352012-10-11 16:27:37 -07005261 public void resolveRtlPropertiesIfNeeded() {
5262 super.resolveRtlPropertiesIfNeeded();
5263 int count = getChildCount();
5264 for (int i = 0; i < count; i++) {
5265 final View child = getChildAt(i);
5266 if (child.isLayoutDirectionInherited()) {
5267 child.resolveRtlPropertiesIfNeeded();
5268 }
5269 }
5270 }
5271
5272 /**
5273 * @hide
5274 */
5275 @Override
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07005276 public boolean resolveLayoutDirection() {
5277 final boolean result = super.resolveLayoutDirection();
5278 if (result) {
5279 int count = getChildCount();
5280 for (int i = 0; i < count; i++) {
5281 final View child = getChildAt(i);
5282 if (child.isLayoutDirectionInherited()) {
5283 child.resolveLayoutDirection();
5284 }
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005285 }
5286 }
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07005287 return result;
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005288 }
5289
5290 /**
5291 * @hide
5292 */
5293 @Override
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07005294 public boolean resolveTextDirection() {
5295 final boolean result = super.resolveTextDirection();
5296 if (result) {
5297 int count = getChildCount();
5298 for (int i = 0; i < count; i++) {
5299 final View child = getChildAt(i);
5300 if (child.isTextDirectionInherited()) {
5301 child.resolveTextDirection();
5302 }
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005303 }
5304 }
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07005305 return result;
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005306 }
5307
5308 /**
5309 * @hide
5310 */
5311 @Override
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07005312 public boolean resolveTextAlignment() {
5313 final boolean result = super.resolveTextAlignment();
5314 if (result) {
5315 int count = getChildCount();
5316 for (int i = 0; i < count; i++) {
5317 final View child = getChildAt(i);
5318 if (child.isTextAlignmentInherited()) {
5319 child.resolveTextAlignment();
5320 }
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005321 }
5322 }
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07005323 return result;
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005324 }
5325
5326 /**
5327 * @hide
5328 */
5329 @Override
Fabrice Di Meglio84ebb352012-10-11 16:27:37 -07005330 public void resolvePadding() {
5331 super.resolvePadding();
5332 int count = getChildCount();
5333 for (int i = 0; i < count; i++) {
5334 final View child = getChildAt(i);
5335 if (child.isLayoutDirectionInherited()) {
5336 child.resolvePadding();
5337 }
5338 }
5339 }
5340
5341 /**
5342 * @hide
5343 */
5344 @Override
5345 protected void resolveDrawables() {
5346 super.resolveDrawables();
5347 int count = getChildCount();
5348 for (int i = 0; i < count; i++) {
5349 final View child = getChildAt(i);
5350 if (child.isLayoutDirectionInherited()) {
5351 child.resolveDrawables();
5352 }
5353 }
5354 }
5355
Fabrice Di Meglio1e0ed6b2012-10-18 16:06:52 -07005356 /**
5357 * @hide
5358 */
Fabrice Di Megliofcc33482012-10-18 11:11:51 -07005359 @Override
5360 public void resolveLayoutParams() {
5361 super.resolveLayoutParams();
5362 int count = getChildCount();
5363 for (int i = 0; i < count; i++) {
5364 final View child = getChildAt(i);
5365 child.resolveLayoutParams();
5366 }
5367 }
5368
Fabrice Di Meglio84ebb352012-10-11 16:27:37 -07005369 /**
5370 * @hide
5371 */
5372 @Override
Fabrice Di Meglio4457e852012-09-18 19:23:12 -07005373 public void resetResolvedLayoutDirection() {
5374 super.resetResolvedLayoutDirection();
5375
Fabrice Di Meglio4457e852012-09-18 19:23:12 -07005376 int count = getChildCount();
Fabrice Di Meglio80dc53d2011-06-21 18:36:33 -07005377 for (int i = 0; i < count; i++) {
5378 final View child = getChildAt(i);
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -07005379 if (child.isLayoutDirectionInherited()) {
Fabrice Di Meglio7f86c802011-07-01 15:09:24 -07005380 child.resetResolvedLayoutDirection();
Fabrice Di Meglio80dc53d2011-06-21 18:36:33 -07005381 }
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005382 }
5383 }
5384
5385 /**
5386 * @hide
5387 */
5388 @Override
5389 public void resetResolvedTextDirection() {
5390 super.resetResolvedTextDirection();
5391
5392 int count = getChildCount();
5393 for (int i = 0; i < count; i++) {
5394 final View child = getChildAt(i);
Fabrice Di Meglio97e146c2012-09-23 15:45:16 -07005395 if (child.isTextDirectionInherited()) {
Fabrice Di Meglio22268862011-06-27 18:13:18 -07005396 child.resetResolvedTextDirection();
5397 }
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005398 }
5399 }
5400
5401 /**
5402 * @hide
5403 */
5404 @Override
5405 public void resetResolvedTextAlignment() {
5406 super.resetResolvedTextAlignment();
5407
5408 int count = getChildCount();
5409 for (int i = 0; i < count; i++) {
5410 final View child = getChildAt(i);
Fabrice Di Meglio1a7d4872012-09-23 16:19:58 -07005411 if (child.isTextAlignmentInherited()) {
Fabrice Di Meglio9da0f8a2012-03-13 19:37:57 -07005412 child.resetResolvedTextAlignment();
5413 }
5414 }
5415 }
5416
Fabrice Di Meglio22268862011-06-27 18:13:18 -07005417 /**
Fabrice Di Meglio84ebb352012-10-11 16:27:37 -07005418 * @hide
5419 */
5420 @Override
5421 public void resetResolvedPadding() {
5422 super.resetResolvedPadding();
5423
5424 int count = getChildCount();
5425 for (int i = 0; i < count; i++) {
5426 final View child = getChildAt(i);
5427 if (child.isLayoutDirectionInherited()) {
5428 child.resetResolvedPadding();
5429 }
5430 }
5431 }
5432
5433 /**
5434 * @hide
5435 */
5436 @Override
5437 protected void resetResolvedDrawables() {
5438 super.resetResolvedDrawables();
5439
5440 int count = getChildCount();
5441 for (int i = 0; i < count; i++) {
5442 final View child = getChildAt(i);
5443 if (child.isLayoutDirectionInherited()) {
5444 child.resetResolvedDrawables();
5445 }
5446 }
5447 }
5448
5449 /**
Patrick Dubroye0a799a2011-05-04 16:19:22 -07005450 * Return true if the pressed state should be delayed for children or descendants of this
5451 * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
5452 * This prevents the pressed state from appearing when the user is actually trying to scroll
5453 * the content.
5454 *
5455 * The default implementation returns true for compatibility reasons. Subclasses that do
5456 * not scroll should generally override this method and return false.
5457 */
5458 public boolean shouldDelayChildPressedState() {
5459 return true;
5460 }
5461
Philip Milned7dd8902012-01-26 16:55:30 -08005462 /** @hide */
5463 protected void onSetLayoutParams(View child, LayoutParams layoutParams) {
5464 }
5465
Patrick Dubroye0a799a2011-05-04 16:19:22 -07005466 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005467 * LayoutParams are used by views to tell their parents how they want to be
5468 * laid out. See
5469 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
5470 * for a list of all child view attributes that this class supports.
Romain Guy8506ab42009-06-11 17:35:47 -07005471 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005472 * <p>
5473 * The base LayoutParams class just describes how big the view wants to be
5474 * for both width and height. For each dimension, it can specify one of:
5475 * <ul>
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005476 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
5477 * means that the view wants to be as big as its parent (minus padding)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005478 * <li> WRAP_CONTENT, which means that the view wants to be just big enough
5479 * to enclose its content (plus padding)
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005480 * <li> an exact number
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005481 * </ul>
5482 * There are subclasses of LayoutParams for different subclasses of
5483 * ViewGroup. For example, AbsoluteLayout has its own subclass of
Joe Fernandez558459f2011-10-13 16:47:36 -07005484 * LayoutParams which adds an X and Y value.</p>
5485 *
5486 * <div class="special reference">
5487 * <h3>Developer Guides</h3>
5488 * <p>For more information about creating user interface layouts, read the
5489 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
5490 * guide.</p></div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005491 *
5492 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
5493 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
5494 */
5495 public static class LayoutParams {
5496 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005497 * Special value for the height or width requested by a View.
5498 * FILL_PARENT means that the view wants to be as big as its parent,
5499 * minus the parent's padding, if any. This value is deprecated
5500 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005501 */
Romain Guy980a9382010-01-08 15:06:28 -08005502 @SuppressWarnings({"UnusedDeclaration"})
5503 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005504 public static final int FILL_PARENT = -1;
5505
5506 /**
5507 * Special value for the height or width requested by a View.
Gilles Debunnef5c6eff2010-02-09 19:08:36 -08005508 * MATCH_PARENT means that the view wants to be as big as its parent,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005509 * minus the parent's padding, if any. Introduced in API Level 8.
Romain Guy980a9382010-01-08 15:06:28 -08005510 */
5511 public static final int MATCH_PARENT = -1;
5512
5513 /**
5514 * Special value for the height or width requested by a View.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005515 * WRAP_CONTENT means that the view wants to be just large enough to fit
5516 * its own internal content, taking its own padding into account.
5517 */
5518 public static final int WRAP_CONTENT = -2;
5519
5520 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005521 * Information about how wide the view wants to be. Can be one of the
5522 * constants FILL_PARENT (replaced by MATCH_PARENT ,
5523 * in API Level 8) or WRAP_CONTENT. or an exact size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005524 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005525 @ViewDebug.ExportedProperty(category = "layout", mapping = {
Romain Guy980a9382010-01-08 15:06:28 -08005526 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005527 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
5528 })
5529 public int width;
5530
5531 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005532 * Information about how tall the view wants to be. Can be one of the
5533 * constants FILL_PARENT (replaced by MATCH_PARENT ,
5534 * in API Level 8) or WRAP_CONTENT. or an exact size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005535 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005536 @ViewDebug.ExportedProperty(category = "layout", mapping = {
Romain Guy980a9382010-01-08 15:06:28 -08005537 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005538 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
5539 })
5540 public int height;
5541
5542 /**
5543 * Used to animate layouts.
5544 */
5545 public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
5546
5547 /**
5548 * Creates a new set of layout parameters. The values are extracted from
5549 * the supplied attributes set and context. The XML attributes mapped
5550 * to this set of layout parameters are:
5551 *
5552 * <ul>
5553 * <li><code>layout_width</code>: the width, either an exact value,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005554 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
5555 * {@link #MATCH_PARENT} in API Level 8)</li>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005556 * <li><code>layout_height</code>: the height, either an exact value,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005557 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
5558 * {@link #MATCH_PARENT} in API Level 8)</li>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005559 * </ul>
5560 *
5561 * @param c the application environment
5562 * @param attrs the set of attributes from which to extract the layout
5563 * parameters' values
5564 */
5565 public LayoutParams(Context c, AttributeSet attrs) {
5566 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
5567 setBaseAttributes(a,
5568 R.styleable.ViewGroup_Layout_layout_width,
5569 R.styleable.ViewGroup_Layout_layout_height);
5570 a.recycle();
5571 }
5572
5573 /**
5574 * Creates a new set of layout parameters with the specified width
5575 * and height.
5576 *
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005577 * @param width the width, either {@link #WRAP_CONTENT},
5578 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
5579 * API Level 8), or a fixed size in pixels
5580 * @param height the height, either {@link #WRAP_CONTENT},
5581 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
5582 * API Level 8), or a fixed size in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005583 */
5584 public LayoutParams(int width, int height) {
5585 this.width = width;
5586 this.height = height;
5587 }
5588
5589 /**
5590 * Copy constructor. Clones the width and height values of the source.
5591 *
5592 * @param source The layout params to copy from.
5593 */
5594 public LayoutParams(LayoutParams source) {
5595 this.width = source.width;
5596 this.height = source.height;
5597 }
5598
5599 /**
5600 * Used internally by MarginLayoutParams.
5601 * @hide
5602 */
5603 LayoutParams() {
5604 }
5605
5606 /**
Dave Burke579e1402012-10-18 20:41:55 -07005607 * Extracts the layout parameters from the supplied attributes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005608 *
5609 * @param a the style attributes to extract the parameters from
5610 * @param widthAttr the identifier of the width attribute
5611 * @param heightAttr the identifier of the height attribute
5612 */
5613 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
Dave Burke579e1402012-10-18 20:41:55 -07005614 width = a.getLayoutDimension(widthAttr, "layout_width");
5615 height = a.getLayoutDimension(heightAttr, "layout_height");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005616 }
5617
5618 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005619 * Resolve layout parameters depending on the layout direction. Subclasses that care about
5620 * layoutDirection changes should override this method. The default implementation does
5621 * nothing.
5622 *
5623 * @param layoutDirection the direction of the layout
5624 *
5625 * {@link View#LAYOUT_DIRECTION_LTR}
5626 * {@link View#LAYOUT_DIRECTION_RTL}
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005627 */
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07005628 public void resolveLayoutDirection(int layoutDirection) {
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005629 }
5630
5631 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005632 * Returns a String representation of this set of layout parameters.
5633 *
5634 * @param output the String to prepend to the internal representation
5635 * @return a String with the following format: output +
5636 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
Romain Guy8506ab42009-06-11 17:35:47 -07005637 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005638 * @hide
5639 */
5640 public String debug(String output) {
5641 return output + "ViewGroup.LayoutParams={ width="
5642 + sizeToString(width) + ", height=" + sizeToString(height) + " }";
5643 }
5644
5645 /**
Philip Milne10ca24a2012-04-23 15:38:27 -07005646 * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
5647 *
5648 * @param view the view that contains these layout parameters
5649 * @param canvas the canvas on which to draw
5650 *
5651 * @hide
5652 */
5653 public void onDebugDraw(View view, Canvas canvas) {
5654 }
5655
5656 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005657 * Converts the specified size to a readable String.
5658 *
5659 * @param size the size to convert
5660 * @return a String instance representing the supplied size
Romain Guy8506ab42009-06-11 17:35:47 -07005661 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005662 * @hide
5663 */
5664 protected static String sizeToString(int size) {
5665 if (size == WRAP_CONTENT) {
5666 return "wrap-content";
5667 }
Romain Guy980a9382010-01-08 15:06:28 -08005668 if (size == MATCH_PARENT) {
5669 return "match-parent";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005670 }
5671 return String.valueOf(size);
5672 }
5673 }
5674
5675 /**
5676 * Per-child layout information for layouts that support margins.
5677 * See
5678 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
5679 * for a list of all child view attributes that this class supports.
5680 */
5681 public static class MarginLayoutParams extends ViewGroup.LayoutParams {
5682 /**
Philip Milned7dd8902012-01-26 16:55:30 -08005683 * The left margin in pixels of the child.
5684 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
5685 * to this field.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005686 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005687 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005688 public int leftMargin;
5689
5690 /**
Philip Milned7dd8902012-01-26 16:55:30 -08005691 * The top margin in pixels of the child.
5692 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
5693 * to this field.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005694 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005695 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005696 public int topMargin;
5697
5698 /**
Philip Milned7dd8902012-01-26 16:55:30 -08005699 * The right margin in pixels of the child.
5700 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
5701 * to this field.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005702 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005703 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005704 public int rightMargin;
5705
5706 /**
Philip Milned7dd8902012-01-26 16:55:30 -08005707 * The bottom margin in pixels of the child.
5708 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
5709 * to this field.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005710 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005711 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005712 public int bottomMargin;
5713
5714 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005715 * The start margin in pixels of the child.
Fabrice Di Meglio54546f22012-02-14 16:26:16 -08005716 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
5717 * to this field.
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005718 */
5719 @ViewDebug.ExportedProperty(category = "layout")
Fabrice Di Meglioa40627d2012-09-11 16:47:21 -07005720 private int startMargin = DEFAULT_RELATIVE;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005721
5722 /**
5723 * The end margin in pixels of the child.
Fabrice Di Meglio54546f22012-02-14 16:26:16 -08005724 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
5725 * to this field.
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005726 */
5727 @ViewDebug.ExportedProperty(category = "layout")
Fabrice Di Meglioa40627d2012-09-11 16:47:21 -07005728 private int endMargin = DEFAULT_RELATIVE;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005729
5730 /**
5731 * The default start and end margin.
Fabrice Di Megliof443f982012-07-13 20:24:03 -07005732 * @hide
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005733 */
Fabrice Di Megliof443f982012-07-13 20:24:03 -07005734 public static final int DEFAULT_RELATIVE = Integer.MIN_VALUE;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005735
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07005736 private int initialLeftMargin;
5737 private int initialRightMargin;
5738
Fabrice Di Megliof443f982012-07-13 20:24:03 -07005739 private static int LAYOUT_DIRECTION_UNDEFINED = -1;
5740
5741 // Layout direction undefined by default
5742 private int layoutDirection = LAYOUT_DIRECTION_UNDEFINED;
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07005743
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005744 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005745 * Creates a new set of layout parameters. The values are extracted from
5746 * the supplied attributes set and context.
5747 *
5748 * @param c the application environment
5749 * @param attrs the set of attributes from which to extract the layout
5750 * parameters' values
5751 */
5752 public MarginLayoutParams(Context c, AttributeSet attrs) {
5753 super();
5754
5755 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
5756 setBaseAttributes(a,
5757 R.styleable.ViewGroup_MarginLayout_layout_width,
5758 R.styleable.ViewGroup_MarginLayout_layout_height);
5759
5760 int margin = a.getDimensionPixelSize(
5761 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
5762 if (margin >= 0) {
5763 leftMargin = margin;
5764 topMargin = margin;
5765 rightMargin= margin;
5766 bottomMargin = margin;
5767 } else {
5768 leftMargin = a.getDimensionPixelSize(
5769 R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
5770 topMargin = a.getDimensionPixelSize(
5771 R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
5772 rightMargin = a.getDimensionPixelSize(
5773 R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
5774 bottomMargin = a.getDimensionPixelSize(
5775 R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005776 startMargin = a.getDimensionPixelSize(
5777 R.styleable.ViewGroup_MarginLayout_layout_marginStart, DEFAULT_RELATIVE);
5778 endMargin = a.getDimensionPixelSize(
5779 R.styleable.ViewGroup_MarginLayout_layout_marginEnd, DEFAULT_RELATIVE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005780 }
5781
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07005782 initialLeftMargin = leftMargin;
5783 initialRightMargin = rightMargin;
5784
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005785 a.recycle();
5786 }
5787
5788 /**
5789 * {@inheritDoc}
5790 */
5791 public MarginLayoutParams(int width, int height) {
5792 super(width, height);
5793 }
5794
5795 /**
5796 * Copy constructor. Clones the width, height and margin values of the source.
5797 *
5798 * @param source The layout params to copy from.
5799 */
5800 public MarginLayoutParams(MarginLayoutParams source) {
5801 this.width = source.width;
5802 this.height = source.height;
5803
5804 this.leftMargin = source.leftMargin;
5805 this.topMargin = source.topMargin;
5806 this.rightMargin = source.rightMargin;
5807 this.bottomMargin = source.bottomMargin;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005808 this.startMargin = source.startMargin;
5809 this.endMargin = source.endMargin;
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07005810
5811 this.initialLeftMargin = source.leftMargin;
5812 this.initialRightMargin = source.rightMargin;
5813
Fabrice Di Megliof443f982012-07-13 20:24:03 -07005814 setLayoutDirection(source.layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005815 }
5816
5817 /**
5818 * {@inheritDoc}
5819 */
5820 public MarginLayoutParams(LayoutParams source) {
5821 super(source);
5822 }
5823
5824 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005825 * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
5826 * to be done so that the new margins are taken into account. Left and right margins may be
5827 * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005828 *
5829 * @param left the left margin size
5830 * @param top the top margin size
5831 * @param right the right margin size
5832 * @param bottom the bottom margin size
5833 *
5834 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
5835 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
5836 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
5837 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
5838 */
5839 public void setMargins(int left, int top, int right, int bottom) {
5840 leftMargin = left;
5841 topMargin = top;
5842 rightMargin = right;
5843 bottomMargin = bottom;
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07005844 initialLeftMargin = left;
5845 initialRightMargin = right;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005846 }
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005847
5848 /**
5849 * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
5850 * needs to be done so that the new relative margins are taken into account. Left and right
5851 * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout
5852 * direction.
5853 *
5854 * @param start the start margin size
5855 * @param top the top margin size
5856 * @param end the right margin size
5857 * @param bottom the bottom margin size
5858 *
5859 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5860 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
5861 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5862 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
5863 *
5864 * @hide
5865 */
5866 public void setMarginsRelative(int start, int top, int end, int bottom) {
5867 startMargin = start;
5868 topMargin = top;
5869 endMargin = end;
5870 bottomMargin = bottom;
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07005871 initialLeftMargin = 0;
5872 initialRightMargin = 0;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005873 }
5874
5875 /**
Fabrice Di Meglioa40627d2012-09-11 16:47:21 -07005876 * Sets the relative start margin.
5877 *
Fabrice Di Meglio61a21772012-09-12 16:33:13 -07005878 * @param start the start margin size
Fabrice Di Meglioa40627d2012-09-11 16:47:21 -07005879 *
5880 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5881 */
5882 public void setMarginStart(int start) {
5883 startMargin = start;
5884 }
5885
5886 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005887 * Returns the start margin in pixels.
5888 *
5889 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5890 *
5891 * @return the start margin in pixels.
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005892 */
5893 public int getMarginStart() {
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07005894 if (startMargin != DEFAULT_RELATIVE) return startMargin;
5895 switch(layoutDirection) {
5896 case View.LAYOUT_DIRECTION_RTL:
5897 return rightMargin;
5898 case View.LAYOUT_DIRECTION_LTR:
5899 default:
5900 return leftMargin;
5901 }
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005902 }
5903
5904 /**
Fabrice Di Meglioa40627d2012-09-11 16:47:21 -07005905 * Sets the relative end margin.
5906 *
Fabrice Di Meglio61a21772012-09-12 16:33:13 -07005907 * @param end the end margin size
Fabrice Di Meglioa40627d2012-09-11 16:47:21 -07005908 *
5909 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5910 */
5911 public void setMarginEnd(int end) {
5912 endMargin = end;
5913 }
5914
5915 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005916 * Returns the end margin in pixels.
5917 *
5918 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5919 *
5920 * @return the end margin in pixels.
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005921 */
5922 public int getMarginEnd() {
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07005923 if (endMargin != DEFAULT_RELATIVE) return endMargin;
5924 switch(layoutDirection) {
5925 case View.LAYOUT_DIRECTION_RTL:
5926 return leftMargin;
5927 case View.LAYOUT_DIRECTION_LTR:
5928 default:
5929 return rightMargin;
5930 }
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005931 }
5932
5933 /**
5934 * Check if margins are relative.
5935 *
5936 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
5937 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
5938 *
Fabrice Di Megliof443f982012-07-13 20:24:03 -07005939 * @return true if either marginStart or marginEnd has been set.
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005940 */
5941 public boolean isMarginRelative() {
5942 return (startMargin != DEFAULT_RELATIVE) || (endMargin != DEFAULT_RELATIVE);
5943 }
5944
5945 /**
Fabrice Di Megliof443f982012-07-13 20:24:03 -07005946 * Set the layout direction
5947 * @param layoutDirection the layout direction.
5948 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
5949 * or {@link View#LAYOUT_DIRECTION_RTL}.
5950 */
5951 public void setLayoutDirection(int layoutDirection) {
5952 if (layoutDirection != View.LAYOUT_DIRECTION_LTR &&
5953 layoutDirection != View.LAYOUT_DIRECTION_RTL) return;
5954 this.layoutDirection = layoutDirection;
5955 }
5956
5957 /**
5958 * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or
5959 * {@link View#LAYOUT_DIRECTION_RTL}.
5960 *
5961 * @return the layout direction.
5962 */
5963 public int getLayoutDirection() {
5964 return layoutDirection;
5965 }
5966
5967 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005968 * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
Fabrice Di Meglio98aec1c2012-02-13 16:54:05 -08005969 * may be overridden depending on layout direction.
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005970 */
5971 @Override
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07005972 public void resolveLayoutDirection(int layoutDirection) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -07005973 setLayoutDirection(layoutDirection);
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07005974
5975 if (!isMarginRelative()) return;
5976
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005977 switch(layoutDirection) {
5978 case View.LAYOUT_DIRECTION_RTL:
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07005979 leftMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : initialLeftMargin;
5980 rightMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : initialRightMargin;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005981 break;
5982 case View.LAYOUT_DIRECTION_LTR:
5983 default:
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07005984 leftMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : initialLeftMargin;
5985 rightMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : initialRightMargin;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005986 break;
5987 }
5988 }
Philip Milne10ca24a2012-04-23 15:38:27 -07005989
Fabrice Di Meglio03b8d3a2012-09-27 17:05:27 -07005990 /**
5991 * @hide
5992 */
5993 public boolean isLayoutRtl() {
Fabrice Di Megliof443f982012-07-13 20:24:03 -07005994 return (layoutDirection == View.LAYOUT_DIRECTION_RTL);
5995 }
5996
Philip Milne10ca24a2012-04-23 15:38:27 -07005997 /**
5998 * @hide
5999 */
6000 @Override
6001 public void onDebugDraw(View view, Canvas canvas) {
Philip Milne10ca24a2012-04-23 15:38:27 -07006002 drawRect(canvas,
6003 view.getLeft() - leftMargin,
6004 view.getTop() - topMargin,
6005 view.getRight() + rightMargin,
Philip Milne604f4402012-04-24 19:27:11 -07006006 view.getBottom() + bottomMargin, Color.MAGENTA);
Philip Milne10ca24a2012-04-23 15:38:27 -07006007 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006008 }
Adam Powell2b342f02010-08-18 18:14:13 -07006009
Jeff Brown20e987b2010-08-23 12:01:02 -07006010 /* Describes a touched view and the ids of the pointers that it has captured.
6011 *
6012 * This code assumes that pointer ids are always in the range 0..31 such that
6013 * it can use a bitfield to track which pointer ids are present.
6014 * As it happens, the lower layers of the input dispatch pipeline also use the
6015 * same trick so the assumption should be safe here...
6016 */
6017 private static final class TouchTarget {
6018 private static final int MAX_RECYCLED = 32;
6019 private static final Object sRecycleLock = new Object();
6020 private static TouchTarget sRecycleBin;
6021 private static int sRecycledCount;
Adam Powell2b342f02010-08-18 18:14:13 -07006022
Jeff Brown20e987b2010-08-23 12:01:02 -07006023 public static final int ALL_POINTER_IDS = -1; // all ones
Adam Powell2b342f02010-08-18 18:14:13 -07006024
Jeff Brown20e987b2010-08-23 12:01:02 -07006025 // The touched child view.
6026 public View child;
6027
6028 // The combined bit mask of pointer ids for all pointers captured by the target.
6029 public int pointerIdBits;
6030
6031 // The next target in the target list.
6032 public TouchTarget next;
6033
6034 private TouchTarget() {
Adam Powell2b342f02010-08-18 18:14:13 -07006035 }
6036
Jeff Brown20e987b2010-08-23 12:01:02 -07006037 public static TouchTarget obtain(View child, int pointerIdBits) {
6038 final TouchTarget target;
6039 synchronized (sRecycleLock) {
Adam Powell816c3be2010-08-23 18:00:05 -07006040 if (sRecycleBin == null) {
Jeff Brown20e987b2010-08-23 12:01:02 -07006041 target = new TouchTarget();
Adam Powell816c3be2010-08-23 18:00:05 -07006042 } else {
Jeff Brown20e987b2010-08-23 12:01:02 -07006043 target = sRecycleBin;
6044 sRecycleBin = target.next;
6045 sRecycledCount--;
6046 target.next = null;
Adam Powell816c3be2010-08-23 18:00:05 -07006047 }
Adam Powell816c3be2010-08-23 18:00:05 -07006048 }
Jeff Brown20e987b2010-08-23 12:01:02 -07006049 target.child = child;
6050 target.pointerIdBits = pointerIdBits;
6051 return target;
6052 }
Adam Powell816c3be2010-08-23 18:00:05 -07006053
Jeff Brown20e987b2010-08-23 12:01:02 -07006054 public void recycle() {
6055 synchronized (sRecycleLock) {
6056 if (sRecycledCount < MAX_RECYCLED) {
6057 next = sRecycleBin;
6058 sRecycleBin = this;
6059 sRecycledCount += 1;
Patrick Dubroyfb0547d22010-10-19 17:36:18 -07006060 } else {
6061 next = null;
Adam Powell816c3be2010-08-23 18:00:05 -07006062 }
Patrick Dubroyfb0547d22010-10-19 17:36:18 -07006063 child = null;
Adam Powell816c3be2010-08-23 18:00:05 -07006064 }
6065 }
Adam Powell2b342f02010-08-18 18:14:13 -07006066 }
Jeff Brown87b7f802011-06-21 18:35:45 -07006067
6068 /* Describes a hovered view. */
6069 private static final class HoverTarget {
6070 private static final int MAX_RECYCLED = 32;
6071 private static final Object sRecycleLock = new Object();
6072 private static HoverTarget sRecycleBin;
6073 private static int sRecycledCount;
6074
6075 // The hovered child view.
6076 public View child;
6077
6078 // The next target in the target list.
6079 public HoverTarget next;
6080
6081 private HoverTarget() {
6082 }
6083
6084 public static HoverTarget obtain(View child) {
6085 final HoverTarget target;
6086 synchronized (sRecycleLock) {
6087 if (sRecycleBin == null) {
6088 target = new HoverTarget();
6089 } else {
6090 target = sRecycleBin;
6091 sRecycleBin = target.next;
6092 sRecycledCount--;
6093 target.next = null;
6094 }
6095 }
6096 target.child = child;
6097 return target;
6098 }
6099
6100 public void recycle() {
6101 synchronized (sRecycleLock) {
6102 if (sRecycledCount < MAX_RECYCLED) {
6103 next = sRecycleBin;
6104 sRecycleBin = this;
6105 sRecycledCount += 1;
6106 } else {
6107 next = null;
6108 }
6109 child = null;
6110 }
6111 }
6112 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07006113
6114 /**
6115 * Pooled class that orderes the children of a ViewGroup from start
6116 * to end based on how they are laid out and the layout direction.
6117 */
6118 static class ChildListForAccessibility {
6119
6120 private static final int MAX_POOL_SIZE = 32;
6121
6122 private static final Object sPoolLock = new Object();
6123
6124 private static ChildListForAccessibility sPool;
6125
6126 private static int sPoolSize;
6127
6128 private boolean mIsPooled;
6129
6130 private ChildListForAccessibility mNext;
6131
6132 private final ArrayList<View> mChildren = new ArrayList<View>();
6133
6134 private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>();
6135
6136 public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) {
6137 ChildListForAccessibility list = null;
6138 synchronized (sPoolLock) {
6139 if (sPool != null) {
6140 list = sPool;
6141 sPool = list.mNext;
6142 list.mNext = null;
6143 list.mIsPooled = false;
6144 sPoolSize--;
6145 } else {
6146 list = new ChildListForAccessibility();
6147 }
6148 list.init(parent, sort);
6149 return list;
6150 }
6151 }
6152
6153 public void recycle() {
6154 if (mIsPooled) {
6155 throw new IllegalStateException("Instance already recycled.");
6156 }
6157 clear();
Svetoslav Ganov005b83b2012-04-16 18:17:17 -07006158 synchronized (sPoolLock) {
6159 if (sPoolSize < MAX_POOL_SIZE) {
6160 mNext = sPool;
6161 mIsPooled = true;
6162 sPool = this;
6163 sPoolSize++;
6164 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07006165 }
6166 }
6167
6168 public int getChildCount() {
6169 return mChildren.size();
6170 }
6171
6172 public View getChildAt(int index) {
6173 return mChildren.get(index);
6174 }
6175
6176 public int getChildIndex(View child) {
6177 return mChildren.indexOf(child);
6178 }
6179
6180 private void init(ViewGroup parent, boolean sort) {
6181 ArrayList<View> children = mChildren;
6182 final int childCount = parent.getChildCount();
6183 for (int i = 0; i < childCount; i++) {
6184 View child = parent.getChildAt(i);
6185 children.add(child);
6186 }
6187 if (sort) {
6188 ArrayList<ViewLocationHolder> holders = mHolders;
6189 for (int i = 0; i < childCount; i++) {
6190 View child = children.get(i);
6191 ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child);
6192 holders.add(holder);
6193 }
6194 Collections.sort(holders);
6195 for (int i = 0; i < childCount; i++) {
6196 ViewLocationHolder holder = holders.get(i);
6197 children.set(i, holder.mView);
6198 holder.recycle();
6199 }
6200 holders.clear();
6201 }
6202 }
6203
6204 private void clear() {
6205 mChildren.clear();
6206 }
6207 }
6208
6209 /**
6210 * Pooled class that holds a View and its location with respect to
6211 * a specified root. This enables sorting of views based on their
6212 * coordinates without recomputing the position relative to the root
6213 * on every comparison.
6214 */
6215 static class ViewLocationHolder implements Comparable<ViewLocationHolder> {
6216
6217 private static final int MAX_POOL_SIZE = 32;
6218
6219 private static final Object sPoolLock = new Object();
6220
6221 private static ViewLocationHolder sPool;
6222
6223 private static int sPoolSize;
6224
6225 private boolean mIsPooled;
6226
6227 private ViewLocationHolder mNext;
6228
6229 private final Rect mLocation = new Rect();
6230
6231 public View mView;
6232
6233 private int mLayoutDirection;
6234
6235 public static ViewLocationHolder obtain(ViewGroup root, View view) {
6236 ViewLocationHolder holder = null;
6237 synchronized (sPoolLock) {
6238 if (sPool != null) {
6239 holder = sPool;
6240 sPool = holder.mNext;
6241 holder.mNext = null;
6242 holder.mIsPooled = false;
6243 sPoolSize--;
6244 } else {
6245 holder = new ViewLocationHolder();
6246 }
6247 holder.init(root, view);
6248 return holder;
6249 }
6250 }
6251
6252 public void recycle() {
6253 if (mIsPooled) {
6254 throw new IllegalStateException("Instance already recycled.");
6255 }
6256 clear();
Svetoslav Ganov005b83b2012-04-16 18:17:17 -07006257 synchronized (sPoolLock) {
6258 if (sPoolSize < MAX_POOL_SIZE) {
6259 mNext = sPool;
6260 mIsPooled = true;
6261 sPool = this;
6262 sPoolSize++;
6263 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07006264 }
6265 }
6266
6267 @Override
6268 public int compareTo(ViewLocationHolder another) {
6269 // This instance is greater than an invalid argument.
6270 if (another == null) {
6271 return 1;
6272 }
6273 if (getClass() != another.getClass()) {
6274 return 1;
6275 }
6276 // First is above second.
6277 if (mLocation.bottom - another.mLocation.top <= 0) {
6278 return -1;
6279 }
6280 // First is below second.
6281 if (mLocation.top - another.mLocation.bottom >= 0) {
6282 return 1;
6283 }
6284 // LTR
6285 if (mLayoutDirection == LAYOUT_DIRECTION_LTR) {
6286 final int leftDifference = mLocation.left - another.mLocation.left;
6287 // First more to the left than second.
6288 if (leftDifference != 0) {
6289 return leftDifference;
6290 }
6291 } else { // RTL
6292 final int rightDifference = mLocation.right - another.mLocation.right;
6293 // First more to the right than second.
6294 if (rightDifference != 0) {
6295 return -rightDifference;
6296 }
6297 }
6298 // Break tie by top.
6299 final int topDiference = mLocation.top - another.mLocation.top;
6300 if (topDiference != 0) {
6301 return topDiference;
6302 }
6303 // Break tie by height.
6304 final int heightDiference = mLocation.height() - another.mLocation.height();
6305 if (heightDiference != 0) {
6306 return -heightDiference;
6307 }
6308 // Break tie by width.
6309 final int widthDiference = mLocation.width() - another.mLocation.width();
6310 if (widthDiference != 0) {
6311 return -widthDiference;
6312 }
Svetoslav Ganov005b83b2012-04-16 18:17:17 -07006313 // Just break the tie somehow. The accessibliity ids are unique
6314 // and stable, hence this is deterministic tie breaking.
6315 return mView.getAccessibilityViewId() - another.mView.getAccessibilityViewId();
Svetoslav Ganov42138042012-03-20 11:51:39 -07006316 }
6317
6318 private void init(ViewGroup root, View view) {
6319 Rect viewLocation = mLocation;
6320 view.getDrawingRect(viewLocation);
6321 root.offsetDescendantRectToMyCoords(view, viewLocation);
6322 mView = view;
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -07006323 mLayoutDirection = root.getLayoutDirection();
Svetoslav Ganov42138042012-03-20 11:51:39 -07006324 }
6325
6326 private void clear() {
6327 mView = null;
6328 mLocation.set(0, 0, 0, 0);
6329 }
6330 }
Romain Guycbc67742012-04-27 16:12:57 -07006331
6332 private static Paint getDebugPaint() {
6333 if (sDebugPaint == null) {
6334 sDebugPaint = new Paint();
6335 sDebugPaint.setAntiAlias(false);
6336 }
6337 return sDebugPaint;
6338 }
6339
6340 private static float[] getDebugLines(int x1, int y1, int x2, int y2) {
6341 if (sDebugLines== null) {
6342 sDebugLines = new float[16];
6343 }
6344
6345 x2--;
6346 y2--;
6347
6348 sDebugLines[0] = x1;
6349 sDebugLines[1] = y1;
6350 sDebugLines[2] = x2;
6351 sDebugLines[3] = y1;
6352
6353 sDebugLines[4] = x2;
6354 sDebugLines[5] = y1;
6355 sDebugLines[6] = x2;
6356 sDebugLines[7] = y2 + 1;
6357
6358 sDebugLines[8] = x2 + 1;
6359 sDebugLines[9] = y2;
6360 sDebugLines[10] = x1;
6361 sDebugLines[11] = y2;
6362
6363 sDebugLines[12] = x1;
6364 sDebugLines[13] = y2;
6365 sDebugLines[14] = x1;
6366 sDebugLines[15] = y1;
6367
6368 return sDebugLines;
6369 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006370}