blob: f574efadbc300167bce26006b6eff952ab269f71 [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;
Svetoslav Ganovbe922dc2012-11-30 16:46:26 -080038import android.util.Pools.SynchronizedPool;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.util.SparseArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070040import android.view.accessibility.AccessibilityEvent;
Svetoslav Ganov8643aa02011-04-20 12:12:33 -070041import android.view.accessibility.AccessibilityNodeInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.view.animation.Animation;
43import android.view.animation.AnimationUtils;
44import android.view.animation.LayoutAnimationController;
45import android.view.animation.Transformation;
Doug Feltcb3791202011-07-07 11:57:48 -070046
Romain Guy0211a0a2011-02-14 16:34:59 -080047import com.android.internal.R;
48import com.android.internal.util.Predicate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
50import java.util.ArrayList;
Svetoslav Ganov42138042012-03-20 11:51:39 -070051import java.util.Collections;
Christopher Tate86cab1b2011-01-13 20:28:55 -080052import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053
Fabrice Di Meglio0072f642013-03-26 15:50:24 -070054import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
55
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056/**
57 * <p>
58 * A <code>ViewGroup</code> is a special view that can contain other views
59 * (called children.) The view group is the base class for layouts and views
60 * containers. This class also defines the
61 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
62 * class for layouts parameters.
63 * </p>
64 *
65 * <p>
66 * Also see {@link LayoutParams} for layout attributes.
67 * </p>
Romain Guyd6a463a2009-05-21 23:10:10 -070068 *
Joe Fernandez558459f2011-10-13 16:47:36 -070069 * <div class="special reference">
70 * <h3>Developer Guides</h3>
71 * <p>For more information about creating user interface layouts, read the
72 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
73 * guide.</p></div>
74 *
Dianne Hackborn7caab0f2013-03-06 13:47:04 -080075 * <p>Here is a complete implementation of a custom ViewGroup that implements
76 * a simple {@link android.widget.FrameLayout} along with the ability to stack
77 * children in left and right gutters.</p>
78 *
79 * {@sample development/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java
80 * Complete}
81 *
82 * <p>If you are implementing XML layout attributes as shown in the example, this is the
83 * corresponding definition for them that would go in <code>res/values/attrs.xml</code>:</p>
84 *
85 * {@sample development/samples/ApiDemos/res/values/attrs.xml CustomLayout}
86 *
87 * <p>Finally the layout manager can be used in an XML layout like so:</p>
88 *
89 * {@sample development/samples/ApiDemos/res/layout/custom_layout.xml Complete}
90 *
Romain Guyd6a463a2009-05-21 23:10:10 -070091 * @attr ref android.R.styleable#ViewGroup_clipChildren
92 * @attr ref android.R.styleable#ViewGroup_clipToPadding
93 * @attr ref android.R.styleable#ViewGroup_layoutAnimation
94 * @attr ref android.R.styleable#ViewGroup_animationCache
95 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
96 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
97 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
98 * @attr ref android.R.styleable#ViewGroup_descendantFocusability
Chet Haase13cc1202010-09-03 15:39:20 -070099 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
Scott Main27a85082013-06-10 10:39:48 -0700100 * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
101 * @attr ref android.R.styleable#ViewGroup_layoutMode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 */
103public abstract class ViewGroup extends View implements ViewParent, ViewManager {
Adam Powell539ee872012-02-03 19:00:49 -0800104 private static final String TAG = "ViewGroup";
Chet Haase21cd1382010-09-01 17:42:29 -0700105
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 private static final boolean DBG = false;
Philip Milne7b757812012-09-19 18:13:44 -0700107 /** @hide */
108 public static boolean DEBUG_DRAW = false;
Gilles Debunnecea45132011-11-24 02:19:27 +0100109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 /**
111 * Views which have been hidden or removed which need to be animated on
112 * their way out.
113 * This field should be made private, so it is hidden from the SDK.
114 * {@hide}
115 */
116 protected ArrayList<View> mDisappearingChildren;
117
118 /**
119 * Listener used to propagate events indicating when children are added
120 * and/or removed from a view group.
121 * This field should be made private, so it is hidden from the SDK.
122 * {@hide}
123 */
124 protected OnHierarchyChangeListener mOnHierarchyChangeListener;
125
126 // The view contained within this ViewGroup that has or contains focus.
127 private View mFocused;
128
Chet Haase48460322010-06-11 14:22:25 -0700129 /**
130 * A Transformation used when drawing children, to
131 * apply on the child being drawn.
132 */
Romain Guyf6991302013-06-05 17:19:01 -0700133 private Transformation mChildTransformation;
Chet Haase48460322010-06-11 14:22:25 -0700134
135 /**
136 * Used to track the current invalidation region.
137 */
Chet Haase64a48c12012-02-13 16:33:29 -0800138 RectF mInvalidateRegion;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139
Chet Haase48460322010-06-11 14:22:25 -0700140 /**
141 * A Transformation used to calculate a correct
142 * invalidation area when the application is autoscaled.
143 */
Chet Haase64a48c12012-02-13 16:33:29 -0800144 Transformation mInvalidationTransformation;
Chet Haase48460322010-06-11 14:22:25 -0700145
Christopher Tatea53146c2010-09-07 11:57:52 -0700146 // View currently under an ongoing drag
147 private View mCurrentDragView;
148
Christopher Tate86cab1b2011-01-13 20:28:55 -0800149 // Metadata about the ongoing drag
150 private DragEvent mCurrentDrag;
151 private HashSet<View> mDragNotifiedChildren;
152
Christopher Tatea53146c2010-09-07 11:57:52 -0700153 // Does this group have a child that can accept the current drag payload?
154 private boolean mChildAcceptsDrag;
155
156 // Used during drag dispatch
Romain Guy6410c0a2013-06-17 11:21:58 -0700157 private PointF mLocalPoint;
Christopher Tatea53146c2010-09-07 11:57:52 -0700158
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 // Layout animation
160 private LayoutAnimationController mLayoutAnimationController;
161 private Animation.AnimationListener mAnimationListener;
162
Jeff Brown20e987b2010-08-23 12:01:02 -0700163 // First touch target in the linked list of touch targets.
164 private TouchTarget mFirstTouchTarget;
165
Joe Onorato03ab0c72011-01-06 15:46:27 -0800166 // For debugging only. You can see these in hierarchyviewer.
Romain Guye95003e2011-01-09 13:53:06 -0800167 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800168 @ViewDebug.ExportedProperty(category = "events")
169 private long mLastTouchDownTime;
170 @ViewDebug.ExportedProperty(category = "events")
171 private int mLastTouchDownIndex = -1;
Romain Guye95003e2011-01-09 13:53:06 -0800172 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800173 @ViewDebug.ExportedProperty(category = "events")
174 private float mLastTouchDownX;
Romain Guye95003e2011-01-09 13:53:06 -0800175 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
Joe Onorato03ab0c72011-01-06 15:46:27 -0800176 @ViewDebug.ExportedProperty(category = "events")
177 private float mLastTouchDownY;
178
Jeff Brown87b7f802011-06-21 18:35:45 -0700179 // First hover target in the linked list of hover targets.
180 // The hover targets are children which have received ACTION_HOVER_ENTER.
181 // They might not have actually handled the hover event, but we will
182 // continue sending hover events to them as long as the pointer remains over
183 // their bounds and the view group does not intercept hover.
184 private HoverTarget mFirstHoverTarget;
Jeff Browna032cc02011-03-07 16:56:21 -0800185
Jeff Brown10b62902011-06-20 16:40:37 -0700186 // True if the view group itself received a hover event.
187 // It might not have actually handled the hover event.
188 private boolean mHoveredSelf;
189
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 /**
191 * Internal flags.
Romain Guy8506ab42009-06-11 17:35:47 -0700192 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 * This field should be made private, so it is hidden from the SDK.
194 * {@hide}
195 */
Romain Guy2440e672012-08-07 14:43:43 -0700196 @ViewDebug.ExportedProperty(flagMapping = {
197 @ViewDebug.FlagToString(mask = FLAG_CLIP_CHILDREN, equals = FLAG_CLIP_CHILDREN,
198 name = "CLIP_CHILDREN"),
199 @ViewDebug.FlagToString(mask = FLAG_CLIP_TO_PADDING, equals = FLAG_CLIP_TO_PADDING,
200 name = "CLIP_TO_PADDING"),
201 @ViewDebug.FlagToString(mask = FLAG_PADDING_NOT_NULL, equals = FLAG_PADDING_NOT_NULL,
202 name = "PADDING_NOT_NULL")
203 })
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 protected int mGroupFlags;
205
Philip Milne7b757812012-09-19 18:13:44 -0700206 /**
207 * Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
Philip Milne1557fd72012-04-04 23:41:34 -0700208 */
Philip Milnecfb631b2012-10-26 10:51:46 -0700209 private int mLayoutMode = LAYOUT_MODE_UNDEFINED;
Philip Milne1557fd72012-04-04 23:41:34 -0700210
Romain Guy33f6beb2012-02-16 19:24:51 -0800211 /**
212 * NOTE: If you change the flags below make sure to reflect the changes
213 * the DisplayList class
214 */
215
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 // When set, ViewGroup invalidates only the child's rectangle
217 // Set by default
Chet Haase64a48c12012-02-13 16:33:29 -0800218 static final int FLAG_CLIP_CHILDREN = 0x1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219
220 // When set, ViewGroup excludes the padding area from the invalidate rectangle
221 // Set by default
222 private static final int FLAG_CLIP_TO_PADDING = 0x2;
223
224 // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
225 // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
Chet Haase64a48c12012-02-13 16:33:29 -0800226 static final int FLAG_INVALIDATE_REQUIRED = 0x4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227
228 // When set, dispatchDraw() will run the layout animation and unset the flag
229 private static final int FLAG_RUN_ANIMATION = 0x8;
230
231 // When set, there is either no layout animation on the ViewGroup or the layout
232 // animation is over
233 // Set by default
Chet Haase64a48c12012-02-13 16:33:29 -0800234 static final int FLAG_ANIMATION_DONE = 0x10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235
236 // If set, this ViewGroup has padding; if unset there is no padding and we don't need
237 // to clip it, even if FLAG_CLIP_TO_PADDING is set
238 private static final int FLAG_PADDING_NOT_NULL = 0x20;
239
240 // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation
241 // Set by default
242 private static final int FLAG_ANIMATION_CACHE = 0x40;
243
244 // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
245 // layout animation; this avoid clobbering the hierarchy
246 // Automatically set when the layout animation starts, depending on the animation's
247 // characteristics
Chet Haase64a48c12012-02-13 16:33:29 -0800248 static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249
250 // When set, the next call to drawChild() will clear mChildTransformation's matrix
Chet Haase64a48c12012-02-13 16:33:29 -0800251 static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252
253 // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
254 // the children's Bitmap caches if necessary
255 // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
256 private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
257
258 /**
259 * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
260 * to get the index of the child to draw for that iteration.
Romain Guy293451e2009-11-04 13:59:48 -0800261 *
262 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 */
264 protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
Romain Guy8506ab42009-06-11 17:35:47 -0700265
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 /**
267 * When set, this ViewGroup supports static transformations on children; this causes
268 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
269 * invoked when a child is drawn.
270 *
271 * Any subclass overriding
272 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
273 * set this flags in {@link #mGroupFlags}.
Romain Guy8506ab42009-06-11 17:35:47 -0700274 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 * {@hide}
276 */
277 protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
278
279 // When the previous drawChild() invocation used an alpha value that was lower than
280 // 1.0 and set it in mCachePaint
Chet Haase64a48c12012-02-13 16:33:29 -0800281 static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282
283 /**
284 * When set, this ViewGroup's drawable states also include those
285 * of its children.
286 */
287 private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
288
289 /**
290 * When set, this ViewGroup tries to always draw its children using their drawing cache.
291 */
Chet Haase64a48c12012-02-13 16:33:29 -0800292 static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293
294 /**
295 * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to
296 * draw its children with their drawing cache.
297 */
Chet Haase64a48c12012-02-13 16:33:29 -0800298 static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299
300 /**
301 * When set, this group will go through its list of children to notify them of
302 * any drawable state change.
303 */
304 private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
305
306 private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
307
308 /**
309 * This view will get focus before any of its descendants.
310 */
311 public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
312
313 /**
314 * This view will get focus only if none of its descendants want it.
315 */
316 public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
317
318 /**
319 * This view will block any of its descendants from getting focus, even
320 * if they are focusable.
321 */
322 public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
323
324 /**
325 * Used to map between enum in attrubutes and flag values.
326 */
327 private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
328 {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
329 FOCUS_BLOCK_DESCENDANTS};
330
331 /**
332 * When set, this ViewGroup should not intercept touch events.
Adam Powell110486f2010-06-22 17:14:44 -0700333 * {@hide}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 */
Adam Powell110486f2010-06-22 17:14:44 -0700335 protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
Romain Guy8506ab42009-06-11 17:35:47 -0700336
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 /**
Adam Powell2b342f02010-08-18 18:14:13 -0700338 * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
339 */
Adam Powellf37df072010-09-17 16:22:49 -0700340 private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
Adam Powell2b342f02010-08-18 18:14:13 -0700341
342 /**
Adam Powell4b867882011-09-16 12:59:46 -0700343 * When set, this ViewGroup will not dispatch onAttachedToWindow calls
344 * to children when adding new views. This is used to prevent multiple
345 * onAttached calls when a ViewGroup adds children in its own onAttached method.
346 */
347 private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000;
348
349 /**
Philip Milnecfb631b2012-10-26 10:51:46 -0700350 * When true, indicates that a layoutMode has been explicitly set, either with
351 * an explicit call to {@link #setLayoutMode(int)} in code or from an XML resource.
352 * This distinguishes the situation in which a layout mode was inherited from
353 * one of the ViewGroup's ancestors and cached locally.
354 */
355 private static final int FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET = 0x800000;
356
357 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 * Indicates which types of drawing caches are to be kept in memory.
359 * This field should be made private, so it is hidden from the SDK.
360 * {@hide}
361 */
362 protected int mPersistentDrawingCache;
363
364 /**
365 * Used to indicate that no drawing cache should be kept in memory.
366 */
367 public static final int PERSISTENT_NO_CACHE = 0x0;
368
369 /**
370 * Used to indicate that the animation drawing cache should be kept in memory.
371 */
372 public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
373
374 /**
375 * Used to indicate that the scrolling drawing cache should be kept in memory.
376 */
377 public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
378
379 /**
380 * Used to indicate that all drawing caches should be kept in memory.
381 */
382 public static final int PERSISTENT_ALL_CACHES = 0x3;
383
Philip Milne1557fd72012-04-04 23:41:34 -0700384 // Layout Modes
385
Philip Milnecfb631b2012-10-26 10:51:46 -0700386 private static final int LAYOUT_MODE_UNDEFINED = -1;
387
Philip Milne1557fd72012-04-04 23:41:34 -0700388 /**
389 * This constant is a {@link #setLayoutMode(int) layoutMode}.
Philip Milne7a23b492012-04-24 22:12:36 -0700390 * Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top},
Philip Milne1557fd72012-04-04 23:41:34 -0700391 * {@link #getRight() right} and {@link #getBottom() bottom}.
392 */
Philip Milne7b757812012-09-19 18:13:44 -0700393 public static final int LAYOUT_MODE_CLIP_BOUNDS = 0;
Philip Milne1557fd72012-04-04 23:41:34 -0700394
395 /**
396 * This constant is a {@link #setLayoutMode(int) layoutMode}.
Philip Milne7a23b492012-04-24 22:12:36 -0700397 * Optical bounds describe where a widget appears to be. They sit inside the clip
398 * bounds which need to cover a larger area to allow other effects,
399 * such as shadows and glows, to be drawn.
Philip Milne1557fd72012-04-04 23:41:34 -0700400 */
Philip Milne7b757812012-09-19 18:13:44 -0700401 public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
402
403 /** @hide */
Philip Milnecfb631b2012-10-26 10:51:46 -0700404 public static int LAYOUT_MODE_DEFAULT = LAYOUT_MODE_CLIP_BOUNDS;
Philip Milne1557fd72012-04-04 23:41:34 -0700405
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 /**
407 * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
408 * are set at the same time.
409 */
410 protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
411
412 // Index of the child's left position in the mLocation array
413 private static final int CHILD_LEFT_INDEX = 0;
414 // Index of the child's top position in the mLocation array
415 private static final int CHILD_TOP_INDEX = 1;
416
417 // Child views of this ViewGroup
418 private View[] mChildren;
419 // Number of valid children in the mChildren array, the rest should be null or not
420 // considered as children
421 private int mChildrenCount;
422
Chet Haaseb9895022013-04-02 15:10:58 -0700423 // Whether layout calls are currently being suppressed, controlled by calls to
424 // suppressLayout()
425 boolean mSuppressLayout = false;
426
427 // Whether any layout calls have actually been suppressed while mSuppressLayout
428 // has been true. This tracks whether we need to issue a requestLayout() when
429 // layout is later re-enabled.
430 private boolean mLayoutCalledWhileSuppressed = false;
431
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 private static final int ARRAY_INITIAL_CAPACITY = 12;
433 private static final int ARRAY_CAPACITY_INCREMENT = 12;
434
Romain Guycbc67742012-04-27 16:12:57 -0700435 private static Paint sDebugPaint;
436 private static float[] sDebugLines;
Philip Milne604f4402012-04-24 19:27:11 -0700437
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 // Used to draw cached views
Chet Haase64a48c12012-02-13 16:33:29 -0800439 Paint mCachePaint;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440
Chet Haase21cd1382010-09-01 17:42:29 -0700441 // Used to animate add/remove changes in layout
442 private LayoutTransition mTransition;
443
444 // The set of views that are currently being transitioned. This list is used to track views
445 // being removed that should not actually be removed from the parent yet because they are
446 // being animated.
447 private ArrayList<View> mTransitioningViews;
448
Chet Haase5e25c2c2010-09-16 11:15:56 -0700449 // List of children changing visibility. This is used to potentially keep rendering
450 // views during a transition when they otherwise would have become gone/invisible
451 private ArrayList<View> mVisibilityChangingChildren;
452
Adam Powell539ee872012-02-03 19:00:49 -0800453 // Indicates how many of this container's child subtrees contain transient state
454 @ViewDebug.ExportedProperty(category = "layout")
455 private int mChildCountWithTransientState = 0;
456
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 public ViewGroup(Context context) {
458 super(context);
459 initViewGroup();
460 }
461
462 public ViewGroup(Context context, AttributeSet attrs) {
463 super(context, attrs);
464 initViewGroup();
465 initFromAttributes(context, attrs);
466 }
467
468 public ViewGroup(Context context, AttributeSet attrs, int defStyle) {
469 super(context, attrs, defStyle);
470 initViewGroup();
471 initFromAttributes(context, attrs);
472 }
473
Philip Milne10ca24a2012-04-23 15:38:27 -0700474 private boolean debugDraw() {
Philip Milne7b757812012-09-19 18:13:44 -0700475 return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout;
Philip Milne10ca24a2012-04-23 15:38:27 -0700476 }
477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 private void initViewGroup() {
479 // ViewGroup doesn't draw by default
Philip Milne10ca24a2012-04-23 15:38:27 -0700480 if (!debugDraw()) {
481 setFlags(WILL_NOT_DRAW, DRAW_MASK);
482 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 mGroupFlags |= FLAG_CLIP_CHILDREN;
484 mGroupFlags |= FLAG_CLIP_TO_PADDING;
485 mGroupFlags |= FLAG_ANIMATION_DONE;
486 mGroupFlags |= FLAG_ANIMATION_CACHE;
487 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
488
Jeff Brown995e7742010-12-22 16:59:36 -0800489 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
490 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
491 }
492
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800493 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
494
495 mChildren = new View[ARRAY_INITIAL_CAPACITY];
496 mChildrenCount = 0;
497
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
499 }
500
501 private void initFromAttributes(Context context, AttributeSet attrs) {
502 TypedArray a = context.obtainStyledAttributes(attrs,
503 R.styleable.ViewGroup);
504
505 final int N = a.getIndexCount();
506 for (int i = 0; i < N; i++) {
507 int attr = a.getIndex(i);
508 switch (attr) {
509 case R.styleable.ViewGroup_clipChildren:
510 setClipChildren(a.getBoolean(attr, true));
511 break;
512 case R.styleable.ViewGroup_clipToPadding:
513 setClipToPadding(a.getBoolean(attr, true));
514 break;
515 case R.styleable.ViewGroup_animationCache:
516 setAnimationCacheEnabled(a.getBoolean(attr, true));
517 break;
518 case R.styleable.ViewGroup_persistentDrawingCache:
519 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
520 break;
521 case R.styleable.ViewGroup_addStatesFromChildren:
522 setAddStatesFromChildren(a.getBoolean(attr, false));
523 break;
524 case R.styleable.ViewGroup_alwaysDrawnWithCache:
525 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
526 break;
527 case R.styleable.ViewGroup_layoutAnimation:
528 int id = a.getResourceId(attr, -1);
529 if (id > 0) {
530 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
531 }
532 break;
533 case R.styleable.ViewGroup_descendantFocusability:
534 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
535 break;
Adam Powell2b342f02010-08-18 18:14:13 -0700536 case R.styleable.ViewGroup_splitMotionEvents:
537 setMotionEventSplittingEnabled(a.getBoolean(attr, false));
538 break;
Chet Haase13cc1202010-09-03 15:39:20 -0700539 case R.styleable.ViewGroup_animateLayoutChanges:
540 boolean animateLayoutChanges = a.getBoolean(attr, false);
541 if (animateLayoutChanges) {
542 setLayoutTransition(new LayoutTransition());
543 }
544 break;
Philip Milne7b757812012-09-19 18:13:44 -0700545 case R.styleable.ViewGroup_layoutMode:
Philip Milnecfb631b2012-10-26 10:51:46 -0700546 setLayoutMode(a.getInt(attr, LAYOUT_MODE_UNDEFINED));
Philip Milne7b757812012-09-19 18:13:44 -0700547 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 }
549 }
550
551 a.recycle();
552 }
553
554 /**
555 * Gets the descendant focusability of this view group. The descendant
556 * focusability defines the relationship between this view group and its
557 * descendants when looking for a view to take focus in
558 * {@link #requestFocus(int, android.graphics.Rect)}.
559 *
560 * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
561 * {@link #FOCUS_BLOCK_DESCENDANTS}.
562 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700563 @ViewDebug.ExportedProperty(category = "focus", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
565 @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
566 @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
567 })
568 public int getDescendantFocusability() {
569 return mGroupFlags & FLAG_MASK_FOCUSABILITY;
570 }
571
572 /**
573 * Set the descendant focusability of this view group. This defines the relationship
574 * between this view group and its descendants when looking for a view to
575 * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
576 *
577 * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
578 * {@link #FOCUS_BLOCK_DESCENDANTS}.
579 */
580 public void setDescendantFocusability(int focusability) {
581 switch (focusability) {
582 case FOCUS_BEFORE_DESCENDANTS:
583 case FOCUS_AFTER_DESCENDANTS:
584 case FOCUS_BLOCK_DESCENDANTS:
585 break;
586 default:
587 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
588 + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
589 }
590 mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
591 mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
592 }
593
594 /**
595 * {@inheritDoc}
596 */
597 @Override
598 void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
599 if (mFocused != null) {
600 mFocused.unFocus();
601 mFocused = null;
602 }
603 super.handleFocusGainInternal(direction, previouslyFocusedRect);
604 }
605
606 /**
607 * {@inheritDoc}
608 */
609 public void requestChildFocus(View child, View focused) {
610 if (DBG) {
611 System.out.println(this + " requestChildFocus()");
612 }
613 if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
614 return;
615 }
616
617 // Unfocus us, if necessary
618 super.unFocus();
619
620 // We had a previous notion of who had focus. Clear it.
621 if (mFocused != child) {
622 if (mFocused != null) {
623 mFocused.unFocus();
624 }
625
626 mFocused = child;
627 }
628 if (mParent != null) {
629 mParent.requestChildFocus(this, focused);
630 }
631 }
632
633 /**
634 * {@inheritDoc}
635 */
636 public void focusableViewAvailable(View v) {
637 if (mParent != null
638 // shortcut: don't report a new focusable view if we block our descendants from
639 // getting focus
640 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
641 // shortcut: don't report a new focusable view if we already are focused
642 // (and we don't prefer our descendants)
643 //
644 // note: knowing that mFocused is non-null is not a good enough reason
645 // to break the traversal since in that case we'd actually have to find
646 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
Joe Onoratoc6cc0f82011-04-12 11:53:13 -0700647 // an ancestor of v; this will get checked for at ViewAncestor
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800648 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
649 mParent.focusableViewAvailable(v);
650 }
651 }
652
653 /**
654 * {@inheritDoc}
655 */
656 public boolean showContextMenuForChild(View originalView) {
657 return mParent != null && mParent.showContextMenuForChild(originalView);
658 }
659
660 /**
Adam Powell6e346362010-07-23 10:18:23 -0700661 * {@inheritDoc}
662 */
663 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
664 return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null;
665 }
666
667 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 * Find the nearest view in the specified direction that wants to take
669 * focus.
670 *
671 * @param focused The view that currently has focus
672 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
673 * FOCUS_RIGHT, or 0 for not applicable.
674 */
675 public View focusSearch(View focused, int direction) {
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700676 if (isRootNamespace()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 // root namespace means we should consider ourselves the top of the
678 // tree for focus searching; otherwise we could be focus searching
679 // into other tabs. see LocalActivityManager and TabHost for more info
680 return FocusFinder.getInstance().findNextFocus(this, focused, direction);
681 } else if (mParent != null) {
682 return mParent.focusSearch(focused, direction);
683 }
684 return null;
685 }
686
687 /**
688 * {@inheritDoc}
689 */
690 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
691 return false;
692 }
693
694 /**
695 * {@inheritDoc}
696 */
Svetoslav Ganov42138042012-03-20 11:51:39 -0700697 @Override
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700698 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700699 ViewParent parent = mParent;
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700700 if (parent == null) {
701 return false;
702 }
703 final boolean propagate = onRequestSendAccessibilityEvent(child, event);
704 if (!propagate) {
705 return false;
706 }
707 return parent.requestSendAccessibilityEvent(this, event);
708 }
709
710 /**
711 * Called when a child has requested sending an {@link AccessibilityEvent} and
712 * gives an opportunity to its parent to augment the event.
Svetoslav Ganov031d9c12011-09-09 16:41:13 -0700713 * <p>
Adam Powell2fcbbd02011-09-28 18:56:43 -0700714 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
715 * {@link android.view.View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
716 * {@link android.view.View.AccessibilityDelegate#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)}
Svetoslav Ganov031d9c12011-09-09 16:41:13 -0700717 * is responsible for handling this call.
718 * </p>
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700719 *
720 * @param child The child which requests sending the event.
721 * @param event The event to be sent.
722 * @return True if the event should be sent.
723 *
724 * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
725 */
726 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
Svetoslav Ganov031d9c12011-09-09 16:41:13 -0700727 if (mAccessibilityDelegate != null) {
728 return mAccessibilityDelegate.onRequestSendAccessibilityEvent(this, child, event);
729 } else {
730 return onRequestSendAccessibilityEventInternal(child, event);
731 }
732 }
733
734 /**
735 * @see #onRequestSendAccessibilityEvent(View, AccessibilityEvent)
736 *
737 * Note: Called from the default {@link View.AccessibilityDelegate}.
738 */
739 boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700740 return true;
741 }
742
743 /**
Adam Powell539ee872012-02-03 19:00:49 -0800744 * Called when a child view has changed whether or not it is tracking transient state.
Adam Powell539ee872012-02-03 19:00:49 -0800745 */
746 public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
747 final boolean oldHasTransientState = hasTransientState();
748 if (childHasTransientState) {
749 mChildCountWithTransientState++;
750 } else {
751 mChildCountWithTransientState--;
752 }
753
754 final boolean newHasTransientState = hasTransientState();
755 if (mParent != null && oldHasTransientState != newHasTransientState) {
756 try {
757 mParent.childHasTransientStateChanged(this, newHasTransientState);
758 } catch (AbstractMethodError e) {
759 Log.e(TAG, mParent.getClass().getSimpleName() +
760 " does not fully implement ViewParent", e);
761 }
762 }
763 }
764
Adam Powell539ee872012-02-03 19:00:49 -0800765 @Override
766 public boolean hasTransientState() {
767 return mChildCountWithTransientState > 0 || super.hasTransientState();
768 }
769
770 /**
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700771 * {@inheritDoc}
772 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 @Override
774 public boolean dispatchUnhandledMove(View focused, int direction) {
775 return mFocused != null &&
776 mFocused.dispatchUnhandledMove(focused, direction);
777 }
778
779 /**
780 * {@inheritDoc}
781 */
782 public void clearChildFocus(View child) {
783 if (DBG) {
784 System.out.println(this + " clearChildFocus()");
785 }
786
787 mFocused = null;
788 if (mParent != null) {
789 mParent.clearChildFocus(this);
790 }
791 }
792
793 /**
794 * {@inheritDoc}
795 */
796 @Override
797 public void clearFocus() {
Svetoslav Ganovb36a0ac2012-02-14 17:46:47 -0800798 if (DBG) {
799 System.out.println(this + " clearFocus()");
800 }
801 if (mFocused == null) {
802 super.clearFocus();
803 } else {
Svetoslav Ganovb552d892012-06-02 14:35:02 -0700804 View focused = mFocused;
Svetoslav Ganovb36a0ac2012-02-14 17:46:47 -0800805 mFocused = null;
Svetoslav Ganovb552d892012-06-02 14:35:02 -0700806 focused.clearFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 }
808 }
809
810 /**
811 * {@inheritDoc}
812 */
813 @Override
814 void unFocus() {
815 if (DBG) {
816 System.out.println(this + " unFocus()");
817 }
Svetoslav Ganovb36a0ac2012-02-14 17:46:47 -0800818 if (mFocused == null) {
819 super.unFocus();
820 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 mFocused.unFocus();
Svetoslav Ganovb36a0ac2012-02-14 17:46:47 -0800822 mFocused = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 }
825
826 /**
827 * Returns the focused child of this view, if any. The child may have focus
828 * or contain focus.
829 *
830 * @return the focused child or null.
831 */
832 public View getFocusedChild() {
833 return mFocused;
834 }
835
836 /**
837 * Returns true if this view has or contains focus
838 *
839 * @return true if this view has or contains focus
840 */
841 @Override
842 public boolean hasFocus() {
Dianne Hackborn4702a852012-08-17 15:18:29 -0700843 return (mPrivateFlags & PFLAG_FOCUSED) != 0 || mFocused != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 }
845
846 /*
847 * (non-Javadoc)
848 *
849 * @see android.view.View#findFocus()
850 */
851 @Override
852 public View findFocus() {
853 if (DBG) {
854 System.out.println("Find focus in " + this + ": flags="
855 + isFocused() + ", child=" + mFocused);
856 }
857
858 if (isFocused()) {
859 return this;
860 }
861
862 if (mFocused != null) {
863 return mFocused.findFocus();
864 }
865 return null;
866 }
867
868 /**
869 * {@inheritDoc}
870 */
871 @Override
872 public boolean hasFocusable() {
873 if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
874 return false;
875 }
876
877 if (isFocusable()) {
878 return true;
879 }
880
881 final int descendantFocusability = getDescendantFocusability();
882 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
883 final int count = mChildrenCount;
884 final View[] children = mChildren;
885
886 for (int i = 0; i < count; i++) {
887 final View child = children[i];
888 if (child.hasFocusable()) {
889 return true;
890 }
891 }
892 }
893
894 return false;
895 }
896
897 /**
898 * {@inheritDoc}
899 */
900 @Override
svetoslavganov75986cf2009-05-14 22:28:01 -0700901 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902 final int focusableCount = views.size();
903
904 final int descendantFocusability = getDescendantFocusability();
905
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700906 if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 final int count = mChildrenCount;
908 final View[] children = mChildren;
909
910 for (int i = 0; i < count; i++) {
911 final View child = children[i];
912 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700913 child.addFocusables(views, direction, focusableMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 }
915 }
916 }
917
918 // we add ourselves (if focusable) in all cases except for when we are
919 // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
920 // to avoid the focus search finding layouts when a more precise search
921 // among the focusable children would be more interesting.
Svetoslav Ganove5dfa47d2012-05-08 15:58:32 -0700922 if (descendantFocusability != FOCUS_AFTER_DESCENDANTS
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 // No focusable descendants
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700924 || (focusableCount == views.size())) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700925 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926 }
927 }
928
Svetoslav Ganov8643aa02011-04-20 12:12:33 -0700929 @Override
Svetoslav Ganovea515ae2011-09-14 18:15:32 -0700930 public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) {
931 super.findViewsWithText(outViews, text, flags);
Svetoslav Ganov8643aa02011-04-20 12:12:33 -0700932 final int childrenCount = mChildrenCount;
933 final View[] children = mChildren;
934 for (int i = 0; i < childrenCount; i++) {
935 View child = children[i];
Svetoslav Ganovea515ae2011-09-14 18:15:32 -0700936 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
Dianne Hackborn4702a852012-08-17 15:18:29 -0700937 && (child.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
Svetoslav Ganovea515ae2011-09-14 18:15:32 -0700938 child.findViewsWithText(outViews, text, flags);
Svetoslav Ganov8643aa02011-04-20 12:12:33 -0700939 }
940 }
941 }
942
Svetoslav5b578da2013-05-08 14:23:32 -0700943 /** @hide */
Svetoslav Ganov2cdedff2011-10-03 14:18:42 -0700944 @Override
Svetoslav5b578da2013-05-08 14:23:32 -0700945 public View findViewByAccessibilityIdTraversal(int accessibilityId) {
Svetoslav Ganov2cdedff2011-10-03 14:18:42 -0700946 View foundView = super.findViewByAccessibilityIdTraversal(accessibilityId);
947 if (foundView != null) {
948 return foundView;
949 }
950 final int childrenCount = mChildrenCount;
951 final View[] children = mChildren;
952 for (int i = 0; i < childrenCount; i++) {
953 View child = children[i];
954 foundView = child.findViewByAccessibilityIdTraversal(accessibilityId);
955 if (foundView != null) {
956 return foundView;
957 }
958 }
959 return null;
960 }
961
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 /**
963 * {@inheritDoc}
964 */
965 @Override
966 public void dispatchWindowFocusChanged(boolean hasFocus) {
967 super.dispatchWindowFocusChanged(hasFocus);
968 final int count = mChildrenCount;
969 final View[] children = mChildren;
970 for (int i = 0; i < count; i++) {
971 children[i].dispatchWindowFocusChanged(hasFocus);
972 }
973 }
974
975 /**
976 * {@inheritDoc}
977 */
978 @Override
979 public void addTouchables(ArrayList<View> views) {
980 super.addTouchables(views);
981
982 final int count = mChildrenCount;
983 final View[] children = mChildren;
984
985 for (int i = 0; i < count; i++) {
986 final View child = children[i];
987 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
988 child.addTouchables(views);
989 }
990 }
991 }
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700992
993 /**
994 * @hide
995 */
996 @Override
997 public void makeOptionalFitsSystemWindows() {
998 super.makeOptionalFitsSystemWindows();
999 final int count = mChildrenCount;
1000 final View[] children = mChildren;
1001 for (int i = 0; i < count; i++) {
1002 children[i].makeOptionalFitsSystemWindows();
1003 }
1004 }
1005
Romain Guy43c9cdf2010-01-27 13:53:55 -08001006 /**
1007 * {@inheritDoc}
1008 */
1009 @Override
1010 public void dispatchDisplayHint(int hint) {
1011 super.dispatchDisplayHint(hint);
1012 final int count = mChildrenCount;
1013 final View[] children = mChildren;
1014 for (int i = 0; i < count; i++) {
1015 children[i].dispatchDisplayHint(hint);
1016 }
1017 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018
1019 /**
Chet Haase0d299362012-01-26 10:51:48 -08001020 * Called when a view's visibility has changed. Notify the parent to take any appropriate
1021 * action.
1022 *
1023 * @param child The view whose visibility has changed
1024 * @param oldVisibility The previous visibility value (GONE, INVISIBLE, or VISIBLE).
1025 * @param newVisibility The new visibility value (GONE, INVISIBLE, or VISIBLE).
Chet Haase5e25c2c2010-09-16 11:15:56 -07001026 * @hide
Chet Haase5e25c2c2010-09-16 11:15:56 -07001027 */
Chet Haase0d299362012-01-26 10:51:48 -08001028 protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07001029 if (mTransition != null) {
Chet Haase0d299362012-01-26 10:51:48 -08001030 if (newVisibility == VISIBLE) {
1031 mTransition.showChild(this, child, oldVisibility);
Chet Haase5e25c2c2010-09-16 11:15:56 -07001032 } else {
Chet Haase0d299362012-01-26 10:51:48 -08001033 mTransition.hideChild(this, child, newVisibility);
Chet Haase5e25c2c2010-09-16 11:15:56 -07001034 if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
Chet Haaseddbb3462012-06-19 13:54:29 -07001035 // Only track this on disappearing views - appearing views are already visible
1036 // and don't need special handling during drawChild()
1037 if (mVisibilityChangingChildren == null) {
1038 mVisibilityChangingChildren = new ArrayList<View>();
1039 }
1040 mVisibilityChangingChildren.add(child);
Chet Haase5e25c2c2010-09-16 11:15:56 -07001041 addDisappearingView(child);
1042 }
1043 }
1044 }
Christopher Tate86cab1b2011-01-13 20:28:55 -08001045
1046 // in all cases, for drags
1047 if (mCurrentDrag != null) {
Chet Haase0d299362012-01-26 10:51:48 -08001048 if (newVisibility == VISIBLE) {
Christopher Tate86cab1b2011-01-13 20:28:55 -08001049 notifyChildOfDrag(child);
1050 }
1051 }
Chet Haase5e25c2c2010-09-16 11:15:56 -07001052 }
1053
1054 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 * {@inheritDoc}
1056 */
1057 @Override
Adam Powell326d8082009-12-09 15:10:07 -08001058 protected void dispatchVisibilityChanged(View changedView, int visibility) {
1059 super.dispatchVisibilityChanged(changedView, visibility);
1060 final int count = mChildrenCount;
1061 final View[] children = mChildren;
1062 for (int i = 0; i < count; i++) {
1063 children[i].dispatchVisibilityChanged(changedView, visibility);
1064 }
1065 }
1066
1067 /**
1068 * {@inheritDoc}
1069 */
1070 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 public void dispatchWindowVisibilityChanged(int visibility) {
1072 super.dispatchWindowVisibilityChanged(visibility);
1073 final int count = mChildrenCount;
1074 final View[] children = mChildren;
1075 for (int i = 0; i < count; i++) {
1076 children[i].dispatchWindowVisibilityChanged(visibility);
1077 }
1078 }
1079
1080 /**
1081 * {@inheritDoc}
1082 */
Dianne Hackborne36d6e22010-02-17 19:46:25 -08001083 @Override
1084 public void dispatchConfigurationChanged(Configuration newConfig) {
1085 super.dispatchConfigurationChanged(newConfig);
1086 final int count = mChildrenCount;
1087 final View[] children = mChildren;
1088 for (int i = 0; i < count; i++) {
1089 children[i].dispatchConfigurationChanged(newConfig);
1090 }
1091 }
1092
1093 /**
1094 * {@inheritDoc}
1095 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001096 public void recomputeViewAttributes(View child) {
Joe Onorato664644d2011-01-23 17:53:23 -08001097 if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
1098 ViewParent parent = mParent;
1099 if (parent != null) parent.recomputeViewAttributes(this);
1100 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101 }
Romain Guy8506ab42009-06-11 17:35:47 -07001102
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103 @Override
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -07001104 void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {
1105 if ((visibility & VISIBILITY_MASK) == VISIBLE) {
1106 super.dispatchCollectViewAttributes(attachInfo, visibility);
1107 final int count = mChildrenCount;
1108 final View[] children = mChildren;
1109 for (int i = 0; i < count; i++) {
1110 final View child = children[i];
1111 child.dispatchCollectViewAttributes(attachInfo,
1112 visibility | (child.mViewFlags&VISIBILITY_MASK));
1113 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 }
1115 }
1116
1117 /**
1118 * {@inheritDoc}
1119 */
1120 public void bringChildToFront(View child) {
1121 int index = indexOfChild(child);
1122 if (index >= 0) {
1123 removeFromArray(index);
1124 addInArray(child, mChildrenCount);
1125 child.mParent = this;
1126 }
1127 }
1128
Romain Guy6410c0a2013-06-17 11:21:58 -07001129 private PointF getLocalPoint() {
1130 if (mLocalPoint == null) mLocalPoint = new PointF();
1131 return mLocalPoint;
1132 }
1133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 /**
1135 * {@inheritDoc}
Christopher Tatea53146c2010-09-07 11:57:52 -07001136 */
Steve Block8a7259b2012-03-01 11:24:41 +00001137 // TODO: Write real docs
Christopher Tatea53146c2010-09-07 11:57:52 -07001138 @Override
1139 public boolean dispatchDragEvent(DragEvent event) {
1140 boolean retval = false;
1141 final float tx = event.mX;
1142 final float ty = event.mY;
1143
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07001144 ViewRootImpl root = getViewRootImpl();
Christopher Tatea53146c2010-09-07 11:57:52 -07001145
1146 // Dispatch down the view hierarchy
Romain Guy6410c0a2013-06-17 11:21:58 -07001147 final PointF localPoint = getLocalPoint();
1148
Christopher Tatea53146c2010-09-07 11:57:52 -07001149 switch (event.mAction) {
1150 case DragEvent.ACTION_DRAG_STARTED: {
1151 // clear state to recalculate which views we drag over
Chris Tate9d1ab882010-11-02 15:55:39 -07001152 mCurrentDragView = null;
Christopher Tatea53146c2010-09-07 11:57:52 -07001153
Christopher Tate86cab1b2011-01-13 20:28:55 -08001154 // Set up our tracking of drag-started notifications
1155 mCurrentDrag = DragEvent.obtain(event);
1156 if (mDragNotifiedChildren == null) {
1157 mDragNotifiedChildren = new HashSet<View>();
1158 } else {
1159 mDragNotifiedChildren.clear();
1160 }
1161
Christopher Tatea53146c2010-09-07 11:57:52 -07001162 // Now dispatch down to our children, caching the responses
1163 mChildAcceptsDrag = false;
1164 final int count = mChildrenCount;
1165 final View[] children = mChildren;
1166 for (int i = 0; i < count; i++) {
Christopher Tate2c095f32010-10-04 14:13:40 -07001167 final View child = children[i];
Christopher Tate3d4bf172011-03-28 16:16:46 -07001168 child.mPrivateFlags2 &= ~View.DRAG_MASK;
Christopher Tate2c095f32010-10-04 14:13:40 -07001169 if (child.getVisibility() == VISIBLE) {
Christopher Tate86cab1b2011-01-13 20:28:55 -08001170 final boolean handled = notifyChildOfDrag(children[i]);
Christopher Tate2c095f32010-10-04 14:13:40 -07001171 if (handled) {
1172 mChildAcceptsDrag = true;
1173 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001174 }
1175 }
1176
1177 // Return HANDLED if one of our children can accept the drag
1178 if (mChildAcceptsDrag) {
1179 retval = true;
1180 }
1181 } break;
1182
1183 case DragEvent.ACTION_DRAG_ENDED: {
Christopher Tate86cab1b2011-01-13 20:28:55 -08001184 // Release the bookkeeping now that the drag lifecycle has ended
Christopher Tate1fc014f2011-01-19 12:56:26 -08001185 if (mDragNotifiedChildren != null) {
1186 for (View child : mDragNotifiedChildren) {
1187 // If a child was notified about an ongoing drag, it's told that it's over
1188 child.dispatchDragEvent(event);
Christopher Tate3d4bf172011-03-28 16:16:46 -07001189 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1190 child.refreshDrawableState();
Christopher Tate1fc014f2011-01-19 12:56:26 -08001191 }
1192
1193 mDragNotifiedChildren.clear();
Christopher Tatee9accff2013-03-04 12:57:23 -08001194 if (mCurrentDrag != null) {
1195 mCurrentDrag.recycle();
1196 mCurrentDrag = null;
1197 }
Christopher Tate1fc014f2011-01-19 12:56:26 -08001198 }
Christopher Tate86cab1b2011-01-13 20:28:55 -08001199
Christopher Tatea53146c2010-09-07 11:57:52 -07001200 // We consider drag-ended to have been handled if one of our children
1201 // had offered to handle the drag.
1202 if (mChildAcceptsDrag) {
1203 retval = true;
1204 }
1205 } break;
1206
1207 case DragEvent.ACTION_DRAG_LOCATION: {
1208 // Find the [possibly new] drag target
Romain Guy6410c0a2013-06-17 11:21:58 -07001209 final View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
Christopher Tatea53146c2010-09-07 11:57:52 -07001210
1211 // If we've changed apparent drag target, tell the view root which view
Chris Tate9d1ab882010-11-02 15:55:39 -07001212 // we're over now [for purposes of the eventual drag-recipient-changed
1213 // notifications to the framework] and tell the new target that the drag
1214 // has entered its bounds. The root will see setDragFocus() calls all
1215 // the way down to the final leaf view that is handling the LOCATION event
1216 // before reporting the new potential recipient to the framework.
Christopher Tatea53146c2010-09-07 11:57:52 -07001217 if (mCurrentDragView != target) {
Chris Tate9d1ab882010-11-02 15:55:39 -07001218 root.setDragFocus(target);
1219
1220 final int action = event.mAction;
1221 // If we've dragged off of a child view, send it the EXITED message
1222 if (mCurrentDragView != null) {
Christopher Tate3d4bf172011-03-28 16:16:46 -07001223 final View view = mCurrentDragView;
Chris Tate9d1ab882010-11-02 15:55:39 -07001224 event.mAction = DragEvent.ACTION_DRAG_EXITED;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001225 view.dispatchDragEvent(event);
Dianne Hackborn4702a852012-08-17 15:18:29 -07001226 view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001227 view.refreshDrawableState();
Chris Tate9d1ab882010-11-02 15:55:39 -07001228 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001229 mCurrentDragView = target;
Chris Tate9d1ab882010-11-02 15:55:39 -07001230
1231 // If we've dragged over a new child view, send it the ENTERED message
1232 if (target != null) {
1233 event.mAction = DragEvent.ACTION_DRAG_ENTERED;
1234 target.dispatchDragEvent(event);
Dianne Hackborn4702a852012-08-17 15:18:29 -07001235 target.mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001236 target.refreshDrawableState();
Chris Tate9d1ab882010-11-02 15:55:39 -07001237 }
1238 event.mAction = action; // restore the event's original state
Christopher Tatea53146c2010-09-07 11:57:52 -07001239 }
Christopher Tate2c095f32010-10-04 14:13:40 -07001240
Christopher Tatea53146c2010-09-07 11:57:52 -07001241 // Dispatch the actual drag location notice, localized into its coordinates
1242 if (target != null) {
Romain Guy6410c0a2013-06-17 11:21:58 -07001243 event.mX = localPoint.x;
1244 event.mY = localPoint.y;
Christopher Tatea53146c2010-09-07 11:57:52 -07001245
1246 retval = target.dispatchDragEvent(event);
1247
1248 event.mX = tx;
1249 event.mY = ty;
1250 }
1251 } break;
1252
Chris Tate9d1ab882010-11-02 15:55:39 -07001253 /* Entered / exited dispatch
1254 *
1255 * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is
1256 * that we're about to get the corresponding LOCATION event, which we will use to
1257 * determine which of our children is the new target; at that point we will
1258 * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
1259 *
1260 * DRAG_EXITED *is* dispatched all the way down immediately: once we know the
1261 * drag has left this ViewGroup, we know by definition that every contained subview
1262 * is also no longer under the drag point.
1263 */
1264
1265 case DragEvent.ACTION_DRAG_EXITED: {
1266 if (mCurrentDragView != null) {
Christopher Tate3d4bf172011-03-28 16:16:46 -07001267 final View view = mCurrentDragView;
1268 view.dispatchDragEvent(event);
Dianne Hackborn4702a852012-08-17 15:18:29 -07001269 view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001270 view.refreshDrawableState();
1271
Chris Tate9d1ab882010-11-02 15:55:39 -07001272 mCurrentDragView = null;
1273 }
1274 } break;
1275
Christopher Tatea53146c2010-09-07 11:57:52 -07001276 case DragEvent.ACTION_DROP: {
Christopher Tate2c095f32010-10-04 14:13:40 -07001277 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
Romain Guy6410c0a2013-06-17 11:21:58 -07001278 View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
Christopher Tatea53146c2010-09-07 11:57:52 -07001279 if (target != null) {
Christopher Tate5ada6cb2010-10-05 14:15:29 -07001280 if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target);
Romain Guy6410c0a2013-06-17 11:21:58 -07001281 event.mX = localPoint.x;
1282 event.mY = localPoint.y;
Christopher Tatea53146c2010-09-07 11:57:52 -07001283 retval = target.dispatchDragEvent(event);
1284 event.mX = tx;
1285 event.mY = ty;
Christopher Tate5ada6cb2010-10-05 14:15:29 -07001286 } else {
1287 if (ViewDebug.DEBUG_DRAG) {
1288 Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view");
1289 }
Christopher Tatea53146c2010-09-07 11:57:52 -07001290 }
1291 } break;
1292 }
1293
1294 // If none of our children could handle the event, try here
1295 if (!retval) {
Chris Tate32affef2010-10-18 15:29:21 -07001296 // Call up to the View implementation that dispatches to installed listeners
1297 retval = super.dispatchDragEvent(event);
Christopher Tatea53146c2010-09-07 11:57:52 -07001298 }
1299 return retval;
1300 }
1301
1302 // Find the frontmost child view that lies under the given point, and calculate
1303 // the position within its own local coordinate system.
1304 View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001305 final int count = mChildrenCount;
1306 final View[] children = mChildren;
1307 for (int i = count - 1; i >= 0; i--) {
1308 final View child = children[i];
Christopher Tate3d4bf172011-03-28 16:16:46 -07001309 if (!child.canAcceptDrag()) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001310 continue;
1311 }
1312
Christopher Tate2c095f32010-10-04 14:13:40 -07001313 if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
Christopher Tatea53146c2010-09-07 11:57:52 -07001314 return child;
1315 }
1316 }
1317 return null;
1318 }
1319
Christopher Tate86cab1b2011-01-13 20:28:55 -08001320 boolean notifyChildOfDrag(View child) {
1321 if (ViewDebug.DEBUG_DRAG) {
1322 Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
1323 }
1324
Christopher Tate3d4bf172011-03-28 16:16:46 -07001325 boolean canAccept = false;
Christopher Tate86cab1b2011-01-13 20:28:55 -08001326 if (! mDragNotifiedChildren.contains(child)) {
1327 mDragNotifiedChildren.add(child);
Christopher Tate3d4bf172011-03-28 16:16:46 -07001328 canAccept = child.dispatchDragEvent(mCurrentDrag);
1329 if (canAccept && !child.canAcceptDrag()) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07001330 child.mPrivateFlags2 |= View.PFLAG2_DRAG_CAN_ACCEPT;
Christopher Tate3d4bf172011-03-28 16:16:46 -07001331 child.refreshDrawableState();
1332 }
Christopher Tate86cab1b2011-01-13 20:28:55 -08001333 }
Christopher Tate3d4bf172011-03-28 16:16:46 -07001334 return canAccept;
Christopher Tate86cab1b2011-01-13 20:28:55 -08001335 }
1336
Joe Onorato664644d2011-01-23 17:53:23 -08001337 @Override
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -07001338 public void dispatchWindowSystemUiVisiblityChanged(int visible) {
1339 super.dispatchWindowSystemUiVisiblityChanged(visible);
1340
1341 final int count = mChildrenCount;
1342 final View[] children = mChildren;
1343 for (int i=0; i <count; i++) {
1344 final View child = children[i];
1345 child.dispatchWindowSystemUiVisiblityChanged(visible);
1346 }
1347 }
1348
1349 @Override
Joe Onorato664644d2011-01-23 17:53:23 -08001350 public void dispatchSystemUiVisibilityChanged(int visible) {
1351 super.dispatchSystemUiVisibilityChanged(visible);
1352
1353 final int count = mChildrenCount;
1354 final View[] children = mChildren;
1355 for (int i=0; i <count; i++) {
1356 final View child = children[i];
1357 child.dispatchSystemUiVisibilityChanged(visible);
1358 }
1359 }
1360
Dianne Hackborn9a230e02011-10-06 11:51:27 -07001361 @Override
Dianne Hackborncf675782012-05-10 15:07:24 -07001362 boolean updateLocalSystemUiVisibility(int localValue, int localChanges) {
1363 boolean changed = super.updateLocalSystemUiVisibility(localValue, localChanges);
Dianne Hackborn9a230e02011-10-06 11:51:27 -07001364
1365 final int count = mChildrenCount;
1366 final View[] children = mChildren;
1367 for (int i=0; i <count; i++) {
1368 final View child = children[i];
Dianne Hackborncf675782012-05-10 15:07:24 -07001369 changed |= child.updateLocalSystemUiVisibility(localValue, localChanges);
Dianne Hackborn9a230e02011-10-06 11:51:27 -07001370 }
Dianne Hackborncf675782012-05-10 15:07:24 -07001371 return changed;
Dianne Hackborn9a230e02011-10-06 11:51:27 -07001372 }
1373
Christopher Tatea53146c2010-09-07 11:57:52 -07001374 /**
1375 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001376 */
1377 @Override
1378 public boolean dispatchKeyEventPreIme(KeyEvent event) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07001379 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1380 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001381 return super.dispatchKeyEventPreIme(event);
Dianne Hackborn4702a852012-08-17 15:18:29 -07001382 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1383 == PFLAG_HAS_BOUNDS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 return mFocused.dispatchKeyEventPreIme(event);
1385 }
1386 return false;
1387 }
1388
1389 /**
1390 * {@inheritDoc}
1391 */
1392 @Override
1393 public boolean dispatchKeyEvent(KeyEvent event) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001394 if (mInputEventConsistencyVerifier != null) {
1395 mInputEventConsistencyVerifier.onKeyEvent(event, 1);
1396 }
1397
Dianne Hackborn4702a852012-08-17 15:18:29 -07001398 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1399 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001400 if (super.dispatchKeyEvent(event)) {
1401 return true;
1402 }
Dianne Hackborn4702a852012-08-17 15:18:29 -07001403 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1404 == PFLAG_HAS_BOUNDS) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001405 if (mFocused.dispatchKeyEvent(event)) {
1406 return true;
1407 }
1408 }
1409
1410 if (mInputEventConsistencyVerifier != null) {
1411 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001412 }
1413 return false;
1414 }
1415
1416 /**
1417 * {@inheritDoc}
1418 */
1419 @Override
1420 public boolean dispatchKeyShortcutEvent(KeyEvent event) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07001421 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1422 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001423 return super.dispatchKeyShortcutEvent(event);
Dianne Hackborn4702a852012-08-17 15:18:29 -07001424 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1425 == PFLAG_HAS_BOUNDS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426 return mFocused.dispatchKeyShortcutEvent(event);
1427 }
1428 return false;
1429 }
1430
1431 /**
1432 * {@inheritDoc}
1433 */
1434 @Override
1435 public boolean dispatchTrackballEvent(MotionEvent event) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001436 if (mInputEventConsistencyVerifier != null) {
1437 mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
1438 }
1439
Dianne Hackborn4702a852012-08-17 15:18:29 -07001440 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1441 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001442 if (super.dispatchTrackballEvent(event)) {
1443 return true;
1444 }
Dianne Hackborn4702a852012-08-17 15:18:29 -07001445 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1446 == PFLAG_HAS_BOUNDS) {
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001447 if (mFocused.dispatchTrackballEvent(event)) {
1448 return true;
1449 }
1450 }
1451
1452 if (mInputEventConsistencyVerifier != null) {
1453 mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001454 }
1455 return false;
1456 }
1457
Jeff Brown10b62902011-06-20 16:40:37 -07001458 /**
1459 * {@inheritDoc}
1460 */
Romain Guya9489272011-06-22 20:58:11 -07001461 @SuppressWarnings({"ConstantConditions"})
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001462 @Override
Jeff Browna032cc02011-03-07 16:56:21 -08001463 protected boolean dispatchHoverEvent(MotionEvent event) {
Jeff Browna032cc02011-03-07 16:56:21 -08001464 final int action = event.getAction();
Jeff Browna032cc02011-03-07 16:56:21 -08001465
Jeff Brown10b62902011-06-20 16:40:37 -07001466 // First check whether the view group wants to intercept the hover event.
1467 final boolean interceptHover = onInterceptHoverEvent(event);
1468 event.setAction(action); // restore action in case it was changed
1469
Jeff Brown87b7f802011-06-21 18:35:45 -07001470 MotionEvent eventNoHistory = event;
1471 boolean handled = false;
1472
1473 // Send events to the hovered children and build a new list of hover targets until
1474 // one is found that handles the event.
1475 HoverTarget firstOldHoverTarget = mFirstHoverTarget;
1476 mFirstHoverTarget = null;
Jeff Brown10b62902011-06-20 16:40:37 -07001477 if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) {
Jeff Browna032cc02011-03-07 16:56:21 -08001478 final float x = event.getX();
1479 final float y = event.getY();
Jeff Brown33bbfd22011-02-24 20:55:35 -08001480 final int childrenCount = mChildrenCount;
1481 if (childrenCount != 0) {
Svetoslav0e5e9aa2013-04-12 14:13:20 -07001482 final boolean customChildOrder = isChildrenDrawingOrderEnabled();
Jeff Brown33bbfd22011-02-24 20:55:35 -08001483 final View[] children = mChildren;
Jeff Brown87b7f802011-06-21 18:35:45 -07001484 HoverTarget lastHoverTarget = null;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001485 for (int i = childrenCount - 1; i >= 0; i--) {
Svetoslav0e5e9aa2013-04-12 14:13:20 -07001486 final int childIndex = customChildOrder
1487 ? getChildDrawingOrder(childrenCount, i) : i;
1488 final View child = children[childIndex];
Jeff Brown87b7f802011-06-21 18:35:45 -07001489 if (!canViewReceivePointerEvents(child)
1490 || !isTransformedTouchPointInView(x, y, child, null)) {
1491 continue;
1492 }
1493
1494 // Obtain a hover target for this child. Dequeue it from the
1495 // old hover target list if the child was previously hovered.
1496 HoverTarget hoverTarget = firstOldHoverTarget;
1497 final boolean wasHovered;
1498 for (HoverTarget predecessor = null; ;) {
1499 if (hoverTarget == null) {
1500 hoverTarget = HoverTarget.obtain(child);
1501 wasHovered = false;
1502 break;
1503 }
1504
1505 if (hoverTarget.child == child) {
1506 if (predecessor != null) {
1507 predecessor.next = hoverTarget.next;
1508 } else {
1509 firstOldHoverTarget = hoverTarget.next;
1510 }
1511 hoverTarget.next = null;
1512 wasHovered = true;
1513 break;
1514 }
1515
1516 predecessor = hoverTarget;
1517 hoverTarget = hoverTarget.next;
1518 }
1519
1520 // Enqueue the hover target onto the new hover target list.
1521 if (lastHoverTarget != null) {
1522 lastHoverTarget.next = hoverTarget;
1523 } else {
Jeff Brown87b7f802011-06-21 18:35:45 -07001524 mFirstHoverTarget = hoverTarget;
1525 }
Sangkyu Lee8725f362013-03-13 09:38:45 +09001526 lastHoverTarget = hoverTarget;
Jeff Brown87b7f802011-06-21 18:35:45 -07001527
1528 // Dispatch the event to the child.
1529 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1530 if (!wasHovered) {
1531 // Send the enter as is.
1532 handled |= dispatchTransformedGenericPointerEvent(
1533 event, child); // enter
1534 }
1535 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1536 if (!wasHovered) {
1537 // Synthesize an enter from a move.
1538 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1539 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1540 handled |= dispatchTransformedGenericPointerEvent(
1541 eventNoHistory, child); // enter
1542 eventNoHistory.setAction(action);
1543
1544 handled |= dispatchTransformedGenericPointerEvent(
1545 eventNoHistory, child); // move
1546 } else {
1547 // Send the move as is.
1548 handled |= dispatchTransformedGenericPointerEvent(event, child);
1549 }
1550 }
1551 if (handled) {
Jeff Brown10b62902011-06-20 16:40:37 -07001552 break;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001553 }
Jeff Brown10b62902011-06-20 16:40:37 -07001554 }
1555 }
1556 }
Jeff Brown33bbfd22011-02-24 20:55:35 -08001557
Jeff Brown87b7f802011-06-21 18:35:45 -07001558 // Send exit events to all previously hovered children that are no longer hovered.
1559 while (firstOldHoverTarget != null) {
1560 final View child = firstOldHoverTarget.child;
Jeff Brown10b62902011-06-20 16:40:37 -07001561
Jeff Brown87b7f802011-06-21 18:35:45 -07001562 // Exit the old hovered child.
1563 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1564 // Send the exit as is.
1565 handled |= dispatchTransformedGenericPointerEvent(
1566 event, child); // exit
1567 } else {
1568 // Synthesize an exit from a move or enter.
1569 // Ignore the result because hover focus has moved to a different view.
1570 if (action == MotionEvent.ACTION_HOVER_MOVE) {
Jeff Brown10b62902011-06-20 16:40:37 -07001571 dispatchTransformedGenericPointerEvent(
Jeff Brown87b7f802011-06-21 18:35:45 -07001572 event, child); // move
Jeff Brown10b62902011-06-20 16:40:37 -07001573 }
Jeff Brown87b7f802011-06-21 18:35:45 -07001574 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1575 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1576 dispatchTransformedGenericPointerEvent(
1577 eventNoHistory, child); // exit
1578 eventNoHistory.setAction(action);
Jeff Brown10b62902011-06-20 16:40:37 -07001579 }
1580
Jeff Brown87b7f802011-06-21 18:35:45 -07001581 final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next;
1582 firstOldHoverTarget.recycle();
1583 firstOldHoverTarget = nextOldHoverTarget;
Jeff Brown10b62902011-06-20 16:40:37 -07001584 }
1585
Jeff Brown87b7f802011-06-21 18:35:45 -07001586 // Send events to the view group itself if no children have handled it.
Jeff Brown10b62902011-06-20 16:40:37 -07001587 boolean newHoveredSelf = !handled;
1588 if (newHoveredSelf == mHoveredSelf) {
1589 if (newHoveredSelf) {
1590 // Send event to the view group as before.
1591 handled |= super.dispatchHoverEvent(event);
1592 }
1593 } else {
1594 if (mHoveredSelf) {
1595 // Exit the view group.
1596 if (action == MotionEvent.ACTION_HOVER_EXIT) {
1597 // Send the exit as is.
1598 handled |= super.dispatchHoverEvent(event); // exit
1599 } else {
1600 // Synthesize an exit from a move or enter.
1601 // Ignore the result because hover focus is moving to a different view.
1602 if (action == MotionEvent.ACTION_HOVER_MOVE) {
1603 super.dispatchHoverEvent(event); // move
1604 }
1605 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1606 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
1607 super.dispatchHoverEvent(eventNoHistory); // exit
1608 eventNoHistory.setAction(action);
1609 }
1610 mHoveredSelf = false;
1611 }
1612
1613 if (newHoveredSelf) {
1614 // Enter the view group.
1615 if (action == MotionEvent.ACTION_HOVER_ENTER) {
1616 // Send the enter as is.
1617 handled |= super.dispatchHoverEvent(event); // enter
1618 mHoveredSelf = true;
1619 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
1620 // Synthesize an enter from a move.
1621 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
1622 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
1623 handled |= super.dispatchHoverEvent(eventNoHistory); // enter
1624 eventNoHistory.setAction(action);
1625
1626 handled |= super.dispatchHoverEvent(eventNoHistory); // move
1627 mHoveredSelf = true;
Jeff Brown33bbfd22011-02-24 20:55:35 -08001628 }
1629 }
Jeff Brown33bbfd22011-02-24 20:55:35 -08001630 }
1631
Jeff Browna032cc02011-03-07 16:56:21 -08001632 // Recycle the copy of the event that we made.
1633 if (eventNoHistory != event) {
1634 eventNoHistory.recycle();
1635 }
1636
Jeff Browna032cc02011-03-07 16:56:21 -08001637 // Done.
1638 return handled;
1639 }
1640
Jeff Brown59a422e2012-04-19 15:19:19 -07001641 private void exitHoverTargets() {
1642 if (mHoveredSelf || mFirstHoverTarget != null) {
1643 final long now = SystemClock.uptimeMillis();
1644 MotionEvent event = MotionEvent.obtain(now, now,
1645 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
1646 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
1647 dispatchHoverEvent(event);
1648 event.recycle();
1649 }
1650 }
1651
1652 private void cancelHoverTarget(View view) {
1653 HoverTarget predecessor = null;
1654 HoverTarget target = mFirstHoverTarget;
1655 while (target != null) {
1656 final HoverTarget next = target.next;
1657 if (target.child == view) {
1658 if (predecessor == null) {
1659 mFirstHoverTarget = next;
1660 } else {
1661 predecessor.next = next;
1662 }
1663 target.recycle();
1664
1665 final long now = SystemClock.uptimeMillis();
1666 MotionEvent event = MotionEvent.obtain(now, now,
1667 MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
1668 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
1669 view.dispatchHoverEvent(event);
1670 event.recycle();
1671 return;
1672 }
1673 predecessor = target;
1674 target = next;
1675 }
1676 }
1677
Jeff Brown87b7f802011-06-21 18:35:45 -07001678 /** @hide */
1679 @Override
1680 protected boolean hasHoveredChild() {
1681 return mFirstHoverTarget != null;
1682 }
1683
Svetoslav Ganov42138042012-03-20 11:51:39 -07001684 @Override
1685 public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001686 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
1687 try {
1688 final int childrenCount = children.getChildCount();
1689 for (int i = 0; i < childrenCount; i++) {
1690 View child = children.getChildAt(i);
Svetoslav Ganovc406be92012-05-11 16:12:32 -07001691 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001692 if (child.includeForAccessibility()) {
1693 childrenForAccessibility.add(child);
1694 } else {
1695 child.addChildrenForAccessibility(childrenForAccessibility);
1696 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001697 }
1698 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001699 } finally {
1700 children.recycle();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001701 }
1702 }
1703
1704 /**
Jeff Brown10b62902011-06-20 16:40:37 -07001705 * Implement this method to intercept hover events before they are handled
1706 * by child views.
1707 * <p>
1708 * This method is called before dispatching a hover event to a child of
1709 * the view group or to the view group's own {@link #onHoverEvent} to allow
1710 * the view group a chance to intercept the hover event.
1711 * This method can also be used to watch all pointer motions that occur within
1712 * the bounds of the view group even when the pointer is hovering over
1713 * a child of the view group rather than over the view group itself.
1714 * </p><p>
1715 * The view group can prevent its children from receiving hover events by
1716 * implementing this method and returning <code>true</code> to indicate
1717 * that it would like to intercept hover events. The view group must
1718 * continuously return <code>true</code> from {@link #onInterceptHoverEvent}
1719 * for as long as it wishes to continue intercepting hover events from
1720 * its children.
1721 * </p><p>
1722 * Interception preserves the invariant that at most one view can be
1723 * hovered at a time by transferring hover focus from the currently hovered
1724 * child to the view group or vice-versa as needed.
1725 * </p><p>
1726 * If this method returns <code>true</code> and a child is already hovered, then the
1727 * child view will first receive a hover exit event and then the view group
1728 * itself will receive a hover enter event in {@link #onHoverEvent}.
1729 * Likewise, if this method had previously returned <code>true</code> to intercept hover
1730 * events and instead returns <code>false</code> while the pointer is hovering
1731 * within the bounds of one of a child, then the view group will first receive a
1732 * hover exit event in {@link #onHoverEvent} and then the hovered child will
1733 * receive a hover enter event.
1734 * </p><p>
1735 * The default implementation always returns false.
1736 * </p>
1737 *
1738 * @param event The motion event that describes the hover.
1739 * @return True if the view group would like to intercept the hover event
1740 * and prevent its children from receiving it.
1741 */
1742 public boolean onInterceptHoverEvent(MotionEvent event) {
Svetoslav Ganov736c2752011-04-22 18:30:36 -07001743 return false;
1744 }
1745
Jeff Browna032cc02011-03-07 16:56:21 -08001746 private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
1747 if (event.getHistorySize() == 0) {
1748 return event;
1749 }
1750 return MotionEvent.obtainNoHistory(event);
1751 }
1752
Jeff Brown10b62902011-06-20 16:40:37 -07001753 /**
1754 * {@inheritDoc}
1755 */
Jeff Browna032cc02011-03-07 16:56:21 -08001756 @Override
1757 protected boolean dispatchGenericPointerEvent(MotionEvent event) {
1758 // Send the event to the child under the pointer.
1759 final int childrenCount = mChildrenCount;
1760 if (childrenCount != 0) {
1761 final View[] children = mChildren;
1762 final float x = event.getX();
1763 final float y = event.getY();
1764
Adam Powella6478a32012-08-17 16:40:00 -07001765 final boolean customOrder = isChildrenDrawingOrderEnabled();
Jeff Browna032cc02011-03-07 16:56:21 -08001766 for (int i = childrenCount - 1; i >= 0; i--) {
Adam Powella6478a32012-08-17 16:40:00 -07001767 final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
1768 final View child = children[childIndex];
Jeff Browna032cc02011-03-07 16:56:21 -08001769 if (!canViewReceivePointerEvents(child)
1770 || !isTransformedTouchPointInView(x, y, child, null)) {
1771 continue;
1772 }
1773
1774 if (dispatchTransformedGenericPointerEvent(event, child)) {
1775 return true;
1776 }
1777 }
1778 }
1779
1780 // No child handled the event. Send it to this view group.
1781 return super.dispatchGenericPointerEvent(event);
1782 }
1783
Jeff Brown10b62902011-06-20 16:40:37 -07001784 /**
1785 * {@inheritDoc}
1786 */
Jeff Browna032cc02011-03-07 16:56:21 -08001787 @Override
1788 protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
Jeff Brown33bbfd22011-02-24 20:55:35 -08001789 // Send the event to the focused child or to this view group if it has focus.
Dianne Hackborn4702a852012-08-17 15:18:29 -07001790 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1791 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
Jeff Browna032cc02011-03-07 16:56:21 -08001792 return super.dispatchGenericFocusedEvent(event);
Dianne Hackborn4702a852012-08-17 15:18:29 -07001793 } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1794 == PFLAG_HAS_BOUNDS) {
Jeff Browncb1404e2011-01-15 18:14:15 -08001795 return mFocused.dispatchGenericMotionEvent(event);
1796 }
1797 return false;
1798 }
1799
1800 /**
Jeff Browna032cc02011-03-07 16:56:21 -08001801 * Dispatches a generic pointer event to a child, taking into account
1802 * transformations that apply to the child.
1803 *
1804 * @param event The event to send.
1805 * @param child The view to send the event to.
1806 * @return {@code true} if the child handled the event.
1807 */
1808 private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) {
1809 final float offsetX = mScrollX - child.mLeft;
1810 final float offsetY = mScrollY - child.mTop;
1811
1812 boolean handled;
1813 if (!child.hasIdentityMatrix()) {
1814 MotionEvent transformedEvent = MotionEvent.obtain(event);
1815 transformedEvent.offsetLocation(offsetX, offsetY);
1816 transformedEvent.transform(child.getInverseMatrix());
1817 handled = child.dispatchGenericMotionEvent(transformedEvent);
1818 transformedEvent.recycle();
1819 } else {
1820 event.offsetLocation(offsetX, offsetY);
1821 handled = child.dispatchGenericMotionEvent(event);
1822 event.offsetLocation(-offsetX, -offsetY);
1823 }
1824 return handled;
1825 }
1826
1827 /**
Jeff Browncb1404e2011-01-15 18:14:15 -08001828 * {@inheritDoc}
1829 */
1830 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001831 public boolean dispatchTouchEvent(MotionEvent ev) {
Jeff Brown21bc5c92011-02-28 18:27:14 -08001832 if (mInputEventConsistencyVerifier != null) {
1833 mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
1834 }
1835
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001836 boolean handled = false;
1837 if (onFilterTouchEventForSecurity(ev)) {
1838 final int action = ev.getAction();
1839 final int actionMasked = action & MotionEvent.ACTION_MASK;
Jeff Brown85a31762010-09-01 17:01:00 -07001840
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001841 // Handle an initial down.
1842 if (actionMasked == MotionEvent.ACTION_DOWN) {
1843 // Throw away all previous state when starting a new touch gesture.
1844 // The framework may have dropped the up or cancel event for the previous gesture
1845 // due to an app switch, ANR, or some other state change.
1846 cancelAndClearTouchTargets(ev);
1847 resetTouchState();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848 }
Adam Powellb08013c2010-09-16 16:28:11 -07001849
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001850 // Check for interception.
1851 final boolean intercepted;
Jeff Brown20e987b2010-08-23 12:01:02 -07001852 if (actionMasked == MotionEvent.ACTION_DOWN
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001853 || mFirstTouchTarget != null) {
1854 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
1855 if (!disallowIntercept) {
1856 intercepted = onInterceptTouchEvent(ev);
1857 ev.setAction(action); // restore action in case it was changed
1858 } else {
1859 intercepted = false;
1860 }
1861 } else {
1862 // There are no touch targets and this action is not an initial down
1863 // so this view group continues to intercept touches.
1864 intercepted = true;
1865 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001866
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001867 // Check for cancelation.
1868 final boolean canceled = resetCancelNextUpFlag(this)
1869 || actionMasked == MotionEvent.ACTION_CANCEL;
Jeff Brown20e987b2010-08-23 12:01:02 -07001870
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001871 // Update list of touch targets for pointer down, if needed.
1872 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
1873 TouchTarget newTouchTarget = null;
1874 boolean alreadyDispatchedToNewTouchTarget = false;
1875 if (!canceled && !intercepted) {
1876 if (actionMasked == MotionEvent.ACTION_DOWN
1877 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
1878 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
1879 final int actionIndex = ev.getActionIndex(); // always 0 for down
1880 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
1881 : TouchTarget.ALL_POINTER_IDS;
Jeff Brown20e987b2010-08-23 12:01:02 -07001882
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001883 // Clean up earlier touch targets for this pointer id in case they
1884 // have become out of sync.
1885 removePointersFromTouchTargets(idBitsToAssign);
1886
1887 final int childrenCount = mChildrenCount;
Chet Haase9c17fe62013-03-22 17:05:55 -07001888 if (newTouchTarget == null && childrenCount != 0) {
Chet Haaseedf6f4b2013-03-26 07:55:30 -07001889 final float x = ev.getX(actionIndex);
1890 final float y = ev.getY(actionIndex);
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001891 // Find a child that can receive the event.
1892 // Scan children from front to back.
1893 final View[] children = mChildren;
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001894
Adam Powella6478a32012-08-17 16:40:00 -07001895 final boolean customOrder = isChildrenDrawingOrderEnabled();
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001896 for (int i = childrenCount - 1; i >= 0; i--) {
Adam Powella6478a32012-08-17 16:40:00 -07001897 final int childIndex = customOrder ?
1898 getChildDrawingOrder(childrenCount, i) : i;
1899 final View child = children[childIndex];
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001900 if (!canViewReceivePointerEvents(child)
1901 || !isTransformedTouchPointInView(x, y, child, null)) {
1902 continue;
1903 }
1904
1905 newTouchTarget = getTouchTarget(child);
1906 if (newTouchTarget != null) {
1907 // Child is already receiving touch within its bounds.
1908 // Give it the new pointer in addition to the ones it is handling.
1909 newTouchTarget.pointerIdBits |= idBitsToAssign;
1910 break;
1911 }
1912
1913 resetCancelNextUpFlag(child);
1914 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
1915 // Child wants to receive touch within its bounds.
1916 mLastTouchDownTime = ev.getDownTime();
Adam Powella6478a32012-08-17 16:40:00 -07001917 mLastTouchDownIndex = childIndex;
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001918 mLastTouchDownX = ev.getX();
1919 mLastTouchDownY = ev.getY();
1920 newTouchTarget = addTouchTarget(child, idBitsToAssign);
1921 alreadyDispatchedToNewTouchTarget = true;
1922 break;
1923 }
1924 }
1925 }
1926
1927 if (newTouchTarget == null && mFirstTouchTarget != null) {
1928 // Did not find a child to receive the event.
1929 // Assign the pointer to the least recently added target.
1930 newTouchTarget = mFirstTouchTarget;
1931 while (newTouchTarget.next != null) {
1932 newTouchTarget = newTouchTarget.next;
1933 }
1934 newTouchTarget.pointerIdBits |= idBitsToAssign;
1935 }
1936 }
1937 }
1938
1939 // Dispatch to touch targets.
1940 if (mFirstTouchTarget == null) {
1941 // No touch targets so treat this as an ordinary view.
1942 handled = dispatchTransformedTouchEvent(ev, canceled, null,
1943 TouchTarget.ALL_POINTER_IDS);
1944 } else {
1945 // Dispatch to touch targets, excluding the new touch target if we already
1946 // dispatched to it. Cancel touch targets if necessary.
1947 TouchTarget predecessor = null;
1948 TouchTarget target = mFirstTouchTarget;
1949 while (target != null) {
1950 final TouchTarget next = target.next;
1951 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
1952 handled = true;
1953 } else {
1954 final boolean cancelChild = resetCancelNextUpFlag(target.child)
Chet Haase9c17fe62013-03-22 17:05:55 -07001955 || intercepted;
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001956 if (dispatchTransformedTouchEvent(ev, cancelChild,
1957 target.child, target.pointerIdBits)) {
1958 handled = true;
1959 }
1960 if (cancelChild) {
1961 if (predecessor == null) {
1962 mFirstTouchTarget = next;
1963 } else {
1964 predecessor.next = next;
1965 }
1966 target.recycle();
1967 target = next;
Jeff Brown20e987b2010-08-23 12:01:02 -07001968 continue;
1969 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001970 }
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001971 predecessor = target;
1972 target = next;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001973 }
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001974 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001975
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001976 // Update list of touch targets for pointer up or cancel, if needed.
1977 if (canceled
1978 || actionMasked == MotionEvent.ACTION_UP
1979 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
1980 resetTouchState();
1981 } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
1982 final int actionIndex = ev.getActionIndex();
1983 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
1984 removePointersFromTouchTargets(idBitsToRemove);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001985 }
1986 }
Romain Guy8506ab42009-06-11 17:35:47 -07001987
Jeff Brownbbdc50b2011-04-19 23:46:52 -07001988 if (!handled && mInputEventConsistencyVerifier != null) {
1989 mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
Jeff Brown20e987b2010-08-23 12:01:02 -07001990 }
Jeff Brown20e987b2010-08-23 12:01:02 -07001991 return handled;
1992 }
1993
Romain Guy469b1db2010-10-05 11:49:57 -07001994 /**
1995 * Resets all touch state in preparation for a new cycle.
1996 */
1997 private void resetTouchState() {
Jeff Brown20e987b2010-08-23 12:01:02 -07001998 clearTouchTargets();
1999 resetCancelNextUpFlag(this);
2000 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2001 }
2002
Romain Guy469b1db2010-10-05 11:49:57 -07002003 /**
2004 * Resets the cancel next up flag.
2005 * Returns true if the flag was previously set.
2006 */
Romain Guya998dff2012-03-23 18:58:36 -07002007 private static boolean resetCancelNextUpFlag(View view) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07002008 if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {
2009 view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
Jeff Brown20e987b2010-08-23 12:01:02 -07002010 return true;
Adam Cohen9b073942010-08-19 16:49:52 -07002011 }
2012 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013 }
2014
Romain Guy469b1db2010-10-05 11:49:57 -07002015 /**
2016 * Clears all touch targets.
2017 */
2018 private void clearTouchTargets() {
Jeff Brown20e987b2010-08-23 12:01:02 -07002019 TouchTarget target = mFirstTouchTarget;
2020 if (target != null) {
2021 do {
2022 TouchTarget next = target.next;
2023 target.recycle();
2024 target = next;
2025 } while (target != null);
2026 mFirstTouchTarget = null;
2027 }
2028 }
2029
Romain Guy469b1db2010-10-05 11:49:57 -07002030 /**
2031 * Cancels and clears all touch targets.
2032 */
2033 private void cancelAndClearTouchTargets(MotionEvent event) {
Jeff Brown20e987b2010-08-23 12:01:02 -07002034 if (mFirstTouchTarget != null) {
2035 boolean syntheticEvent = false;
2036 if (event == null) {
2037 final long now = SystemClock.uptimeMillis();
2038 event = MotionEvent.obtain(now, now,
2039 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
Jeff Brown2fdbc5a2011-06-30 12:25:54 -07002040 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
Jeff Brown20e987b2010-08-23 12:01:02 -07002041 syntheticEvent = true;
2042 }
2043
2044 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2045 resetCancelNextUpFlag(target.child);
2046 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
2047 }
2048 clearTouchTargets();
2049
2050 if (syntheticEvent) {
2051 event.recycle();
2052 }
2053 }
2054 }
2055
Romain Guy469b1db2010-10-05 11:49:57 -07002056 /**
2057 * Gets the touch target for specified child view.
2058 * Returns null if not found.
2059 */
2060 private TouchTarget getTouchTarget(View child) {
Jeff Brown20e987b2010-08-23 12:01:02 -07002061 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2062 if (target.child == child) {
2063 return target;
2064 }
2065 }
2066 return null;
2067 }
2068
Romain Guy469b1db2010-10-05 11:49:57 -07002069 /**
2070 * Adds a touch target for specified child to the beginning of the list.
2071 * Assumes the target child is not already present.
2072 */
2073 private TouchTarget addTouchTarget(View child, int pointerIdBits) {
Jeff Brown20e987b2010-08-23 12:01:02 -07002074 TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
2075 target.next = mFirstTouchTarget;
2076 mFirstTouchTarget = target;
2077 return target;
2078 }
2079
Romain Guy469b1db2010-10-05 11:49:57 -07002080 /**
2081 * Removes the pointer ids from consideration.
2082 */
2083 private void removePointersFromTouchTargets(int pointerIdBits) {
Jeff Brown20e987b2010-08-23 12:01:02 -07002084 TouchTarget predecessor = null;
2085 TouchTarget target = mFirstTouchTarget;
2086 while (target != null) {
2087 final TouchTarget next = target.next;
2088 if ((target.pointerIdBits & pointerIdBits) != 0) {
2089 target.pointerIdBits &= ~pointerIdBits;
2090 if (target.pointerIdBits == 0) {
2091 if (predecessor == null) {
2092 mFirstTouchTarget = next;
2093 } else {
2094 predecessor.next = next;
2095 }
2096 target.recycle();
2097 target = next;
2098 continue;
2099 }
2100 }
2101 predecessor = target;
2102 target = next;
2103 }
2104 }
2105
Jeff Brown59a422e2012-04-19 15:19:19 -07002106 private void cancelTouchTarget(View view) {
2107 TouchTarget predecessor = null;
2108 TouchTarget target = mFirstTouchTarget;
2109 while (target != null) {
2110 final TouchTarget next = target.next;
2111 if (target.child == view) {
2112 if (predecessor == null) {
2113 mFirstTouchTarget = next;
2114 } else {
2115 predecessor.next = next;
2116 }
2117 target.recycle();
2118
2119 final long now = SystemClock.uptimeMillis();
2120 MotionEvent event = MotionEvent.obtain(now, now,
2121 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2122 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2123 view.dispatchTouchEvent(event);
2124 event.recycle();
2125 return;
2126 }
2127 predecessor = target;
2128 target = next;
2129 }
2130 }
2131
Romain Guy469b1db2010-10-05 11:49:57 -07002132 /**
Jeff Browna032cc02011-03-07 16:56:21 -08002133 * Returns true if a child view can receive pointer events.
2134 * @hide
2135 */
2136 private static boolean canViewReceivePointerEvents(View child) {
2137 return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
2138 || child.getAnimation() != null;
2139 }
2140
2141 /**
Romain Guy469b1db2010-10-05 11:49:57 -07002142 * Returns true if a child view contains the specified point when transformed
Jeff Brown20e987b2010-08-23 12:01:02 -07002143 * into its coordinate space.
Romain Guy469b1db2010-10-05 11:49:57 -07002144 * Child must not be null.
Adam Cohena32edd42010-10-26 10:35:01 -07002145 * @hide
Romain Guy469b1db2010-10-05 11:49:57 -07002146 */
Adam Cohena32edd42010-10-26 10:35:01 -07002147 protected boolean isTransformedTouchPointInView(float x, float y, View child,
Christopher Tate2c095f32010-10-04 14:13:40 -07002148 PointF outLocalPoint) {
Jeff Brown20e987b2010-08-23 12:01:02 -07002149 float localX = x + mScrollX - child.mLeft;
2150 float localY = y + mScrollY - child.mTop;
2151 if (! child.hasIdentityMatrix() && mAttachInfo != null) {
Adam Powell2b342f02010-08-18 18:14:13 -07002152 final float[] localXY = mAttachInfo.mTmpTransformLocation;
2153 localXY[0] = localX;
2154 localXY[1] = localY;
2155 child.getInverseMatrix().mapPoints(localXY);
2156 localX = localXY[0];
2157 localY = localXY[1];
2158 }
Christopher Tate2c095f32010-10-04 14:13:40 -07002159 final boolean isInView = child.pointInView(localX, localY);
2160 if (isInView && outLocalPoint != null) {
2161 outLocalPoint.set(localX, localY);
2162 }
2163 return isInView;
Adam Powell2b342f02010-08-18 18:14:13 -07002164 }
2165
Romain Guy469b1db2010-10-05 11:49:57 -07002166 /**
2167 * Transforms a motion event into the coordinate space of a particular child view,
Jeff Brown20e987b2010-08-23 12:01:02 -07002168 * filters out irrelevant pointer ids, and overrides its action if necessary.
Romain Guy469b1db2010-10-05 11:49:57 -07002169 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
2170 */
2171 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
Jeff Brown20e987b2010-08-23 12:01:02 -07002172 View child, int desiredPointerIdBits) {
2173 final boolean handled;
Adam Powell2b342f02010-08-18 18:14:13 -07002174
Jeff Brown20e987b2010-08-23 12:01:02 -07002175 // Canceling motions is a special case. We don't need to perform any transformations
2176 // or filtering. The important part is the action, not the contents.
2177 final int oldAction = event.getAction();
2178 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
2179 event.setAction(MotionEvent.ACTION_CANCEL);
2180 if (child == null) {
2181 handled = super.dispatchTouchEvent(event);
2182 } else {
2183 handled = child.dispatchTouchEvent(event);
2184 }
2185 event.setAction(oldAction);
2186 return handled;
2187 }
Adam Powell2b342f02010-08-18 18:14:13 -07002188
Jeff Brown20e987b2010-08-23 12:01:02 -07002189 // Calculate the number of pointers to deliver.
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07002190 final int oldPointerIdBits = event.getPointerIdBits();
2191 final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
Adam Powell2b342f02010-08-18 18:14:13 -07002192
Jeff Brown20e987b2010-08-23 12:01:02 -07002193 // If for some reason we ended up in an inconsistent state where it looks like we
2194 // might produce a motion event with no pointers in it, then drop the event.
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07002195 if (newPointerIdBits == 0) {
Jeff Brown20e987b2010-08-23 12:01:02 -07002196 return false;
2197 }
Adam Powell2b342f02010-08-18 18:14:13 -07002198
Jeff Brown20e987b2010-08-23 12:01:02 -07002199 // If the number of pointers is the same and we don't need to perform any fancy
2200 // irreversible transformations, then we can reuse the motion event for this
2201 // dispatch as long as we are careful to revert any changes we make.
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07002202 // Otherwise we need to make a copy.
2203 final MotionEvent transformedEvent;
2204 if (newPointerIdBits == oldPointerIdBits) {
2205 if (child == null || child.hasIdentityMatrix()) {
2206 if (child == null) {
2207 handled = super.dispatchTouchEvent(event);
2208 } else {
2209 final float offsetX = mScrollX - child.mLeft;
2210 final float offsetY = mScrollY - child.mTop;
2211 event.offsetLocation(offsetX, offsetY);
Adam Powell2b342f02010-08-18 18:14:13 -07002212
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07002213 handled = child.dispatchTouchEvent(event);
Jeff Brown20e987b2010-08-23 12:01:02 -07002214
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07002215 event.offsetLocation(-offsetX, -offsetY);
2216 }
2217 return handled;
Jeff Brown20e987b2010-08-23 12:01:02 -07002218 }
Jeff Brown20e987b2010-08-23 12:01:02 -07002219 transformedEvent = MotionEvent.obtain(event);
2220 } else {
Jeff Brownfe9f8ab2011-05-06 18:20:01 -07002221 transformedEvent = event.split(newPointerIdBits);
Adam Powell2b342f02010-08-18 18:14:13 -07002222 }
2223
Jeff Brown20e987b2010-08-23 12:01:02 -07002224 // Perform any necessary transformations and dispatch.
2225 if (child == null) {
2226 handled = super.dispatchTouchEvent(transformedEvent);
2227 } else {
2228 final float offsetX = mScrollX - child.mLeft;
2229 final float offsetY = mScrollY - child.mTop;
2230 transformedEvent.offsetLocation(offsetX, offsetY);
2231 if (! child.hasIdentityMatrix()) {
2232 transformedEvent.transform(child.getInverseMatrix());
Adam Powell2b342f02010-08-18 18:14:13 -07002233 }
2234
Jeff Brown20e987b2010-08-23 12:01:02 -07002235 handled = child.dispatchTouchEvent(transformedEvent);
Adam Powell2b342f02010-08-18 18:14:13 -07002236 }
2237
Jeff Brown20e987b2010-08-23 12:01:02 -07002238 // Done.
2239 transformedEvent.recycle();
Adam Powell2b342f02010-08-18 18:14:13 -07002240 return handled;
2241 }
2242
Romain Guy469b1db2010-10-05 11:49:57 -07002243 /**
Adam Powell2b342f02010-08-18 18:14:13 -07002244 * Enable or disable the splitting of MotionEvents to multiple children during touch event
Jeff Brown995e7742010-12-22 16:59:36 -08002245 * dispatch. This behavior is enabled by default for applications that target an
2246 * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
Adam Powell2b342f02010-08-18 18:14:13 -07002247 *
2248 * <p>When this option is enabled MotionEvents may be split and dispatched to different child
2249 * views depending on where each pointer initially went down. This allows for user interactions
2250 * such as scrolling two panes of content independently, chording of buttons, and performing
2251 * independent gestures on different pieces of content.
2252 *
2253 * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
2254 * child views. <code>false</code> to only allow one child view to be the target of
2255 * any MotionEvent received by this ViewGroup.
Scott Main27a85082013-06-10 10:39:48 -07002256 * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
Adam Powell2b342f02010-08-18 18:14:13 -07002257 */
2258 public void setMotionEventSplittingEnabled(boolean split) {
2259 // TODO Applications really shouldn't change this setting mid-touch event,
2260 // but perhaps this should handle that case and send ACTION_CANCELs to any child views
2261 // with gestures in progress when this is changed.
2262 if (split) {
Adam Powell2b342f02010-08-18 18:14:13 -07002263 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
2264 } else {
2265 mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
Adam Powell2b342f02010-08-18 18:14:13 -07002266 }
2267 }
2268
2269 /**
Jeff Brown995e7742010-12-22 16:59:36 -08002270 * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
Adam Powell2b342f02010-08-18 18:14:13 -07002271 * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2272 */
2273 public boolean isMotionEventSplittingEnabled() {
2274 return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
2275 }
2276
2277 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002278 * {@inheritDoc}
2279 */
2280 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
Romain Guy8506ab42009-06-11 17:35:47 -07002281
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002282 if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
2283 // We're already in this state, assume our ancestors are too
2284 return;
2285 }
Romain Guy8506ab42009-06-11 17:35:47 -07002286
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002287 if (disallowIntercept) {
2288 mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
2289 } else {
2290 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2291 }
Romain Guy8506ab42009-06-11 17:35:47 -07002292
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002293 // Pass it up to our parent
2294 if (mParent != null) {
2295 mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
2296 }
2297 }
2298
2299 /**
2300 * Implement this method to intercept all touch screen motion events. This
2301 * allows you to watch events as they are dispatched to your children, and
2302 * take ownership of the current gesture at any point.
2303 *
2304 * <p>Using this function takes some care, as it has a fairly complicated
2305 * interaction with {@link View#onTouchEvent(MotionEvent)
2306 * View.onTouchEvent(MotionEvent)}, and using it requires implementing
2307 * that method as well as this one in the correct way. Events will be
2308 * received in the following order:
2309 *
2310 * <ol>
2311 * <li> You will receive the down event here.
2312 * <li> The down event will be handled either by a child of this view
2313 * group, or given to your own onTouchEvent() method to handle; this means
2314 * you should implement onTouchEvent() to return true, so you will
2315 * continue to see the rest of the gesture (instead of looking for
2316 * a parent view to handle it). Also, by returning true from
2317 * onTouchEvent(), you will not receive any following
2318 * events in onInterceptTouchEvent() and all touch processing must
2319 * happen in onTouchEvent() like normal.
2320 * <li> For as long as you return false from this function, each following
2321 * event (up to and including the final up) will be delivered first here
2322 * and then to the target's onTouchEvent().
2323 * <li> If you return true from here, you will not receive any
2324 * following events: the target view will receive the same event but
2325 * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
2326 * events will be delivered to your onTouchEvent() method and no longer
2327 * appear here.
2328 * </ol>
2329 *
2330 * @param ev The motion event being dispatched down the hierarchy.
2331 * @return Return true to steal motion events from the children and have
2332 * them dispatched to this ViewGroup through onTouchEvent().
2333 * The current target will receive an ACTION_CANCEL event, and no further
2334 * messages will be delivered here.
2335 */
2336 public boolean onInterceptTouchEvent(MotionEvent ev) {
2337 return false;
2338 }
2339
2340 /**
2341 * {@inheritDoc}
2342 *
2343 * Looks for a view to give focus to respecting the setting specified by
2344 * {@link #getDescendantFocusability()}.
2345 *
2346 * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
2347 * find focus within the children of this group when appropriate.
2348 *
2349 * @see #FOCUS_BEFORE_DESCENDANTS
2350 * @see #FOCUS_AFTER_DESCENDANTS
2351 * @see #FOCUS_BLOCK_DESCENDANTS
Romain Guy02739a82011-05-16 11:43:18 -07002352 * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002353 */
2354 @Override
2355 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
2356 if (DBG) {
2357 System.out.println(this + " ViewGroup.requestFocus direction="
2358 + direction);
2359 }
2360 int descendantFocusability = getDescendantFocusability();
2361
2362 switch (descendantFocusability) {
2363 case FOCUS_BLOCK_DESCENDANTS:
2364 return super.requestFocus(direction, previouslyFocusedRect);
2365 case FOCUS_BEFORE_DESCENDANTS: {
2366 final boolean took = super.requestFocus(direction, previouslyFocusedRect);
2367 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
2368 }
2369 case FOCUS_AFTER_DESCENDANTS: {
2370 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
2371 return took ? took : super.requestFocus(direction, previouslyFocusedRect);
2372 }
2373 default:
2374 throw new IllegalStateException("descendant focusability must be "
2375 + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
2376 + "but is " + descendantFocusability);
2377 }
2378 }
2379
2380 /**
2381 * Look for a descendant to call {@link View#requestFocus} on.
2382 * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
2383 * when it wants to request focus within its children. Override this to
2384 * customize how your {@link ViewGroup} requests focus within its children.
2385 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
2386 * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
2387 * to give a finer grained hint about where focus is coming from. May be null
2388 * if there is no hint.
2389 * @return Whether focus was taken.
2390 */
2391 @SuppressWarnings({"ConstantConditions"})
2392 protected boolean onRequestFocusInDescendants(int direction,
2393 Rect previouslyFocusedRect) {
2394 int index;
2395 int increment;
2396 int end;
2397 int count = mChildrenCount;
2398 if ((direction & FOCUS_FORWARD) != 0) {
2399 index = 0;
2400 increment = 1;
2401 end = count;
2402 } else {
2403 index = count - 1;
2404 increment = -1;
2405 end = -1;
2406 }
2407 final View[] children = mChildren;
2408 for (int i = index; i != end; i += increment) {
2409 View child = children[i];
2410 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2411 if (child.requestFocus(direction, previouslyFocusedRect)) {
2412 return true;
2413 }
2414 }
2415 }
2416 return false;
2417 }
Chet Haase5c13d892010-10-08 08:37:55 -07002418
Romain Guya440b002010-02-24 15:57:54 -08002419 /**
2420 * {@inheritDoc}
Chet Haase5c13d892010-10-08 08:37:55 -07002421 *
Romain Guydcc490f2010-02-24 17:59:35 -08002422 * @hide
Romain Guya440b002010-02-24 15:57:54 -08002423 */
2424 @Override
2425 public void dispatchStartTemporaryDetach() {
2426 super.dispatchStartTemporaryDetach();
2427 final int count = mChildrenCount;
2428 final View[] children = mChildren;
2429 for (int i = 0; i < count; i++) {
2430 children[i].dispatchStartTemporaryDetach();
2431 }
2432 }
Chet Haase5c13d892010-10-08 08:37:55 -07002433
Romain Guya440b002010-02-24 15:57:54 -08002434 /**
2435 * {@inheritDoc}
Chet Haase5c13d892010-10-08 08:37:55 -07002436 *
Romain Guydcc490f2010-02-24 17:59:35 -08002437 * @hide
Romain Guya440b002010-02-24 15:57:54 -08002438 */
2439 @Override
2440 public void dispatchFinishTemporaryDetach() {
2441 super.dispatchFinishTemporaryDetach();
2442 final int count = mChildrenCount;
2443 final View[] children = mChildren;
2444 for (int i = 0; i < count; i++) {
2445 children[i].dispatchFinishTemporaryDetach();
2446 }
2447 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002448
2449 /**
2450 * {@inheritDoc}
2451 */
2452 @Override
2453 void dispatchAttachedToWindow(AttachInfo info, int visibility) {
Adam Powell4b867882011-09-16 12:59:46 -07002454 mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002455 super.dispatchAttachedToWindow(info, visibility);
Adam Powell4b867882011-09-16 12:59:46 -07002456 mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
2457
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002458 final int count = mChildrenCount;
2459 final View[] children = mChildren;
2460 for (int i = 0; i < count; i++) {
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -07002461 final View child = children[i];
2462 child.dispatchAttachedToWindow(info,
Philip Milne7b757812012-09-19 18:13:44 -07002463 visibility | (child.mViewFlags & VISIBILITY_MASK));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002464 }
2465 }
2466
svetoslavganov75986cf2009-05-14 22:28:01 -07002467 @Override
Romain Guybb9908b2012-03-08 11:14:07 -08002468 void dispatchScreenStateChanged(int screenState) {
2469 super.dispatchScreenStateChanged(screenState);
2470
2471 final int count = mChildrenCount;
2472 final View[] children = mChildren;
2473 for (int i = 0; i < count; i++) {
2474 children[i].dispatchScreenStateChanged(screenState);
2475 }
2476 }
2477
2478 @Override
Svetoslav Ganov031d9c12011-09-09 16:41:13 -07002479 boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07002480 boolean handled = false;
2481 if (includeForAccessibility()) {
2482 handled = super.dispatchPopulateAccessibilityEventInternal(event);
2483 if (handled) {
2484 return handled;
2485 }
Svetoslav Ganovb84b94e2011-09-22 19:26:56 -07002486 }
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002487 // Let our children have a shot in populating the event.
Svetoslav Ganov42138042012-03-20 11:51:39 -07002488 ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07002489 try {
2490 final int childCount = children.getChildCount();
2491 for (int i = 0; i < childCount; i++) {
2492 View child = children.getChildAt(i);
2493 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2494 handled = child.dispatchPopulateAccessibilityEvent(event);
2495 if (handled) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07002496 return handled;
2497 }
Svetoslav Ganov6179ea32011-06-28 01:12:41 -07002498 }
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002499 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07002500 } finally {
2501 children.recycle();
svetoslavganov75986cf2009-05-14 22:28:01 -07002502 }
Svetoslav Ganov736c2752011-04-22 18:30:36 -07002503 return false;
svetoslavganov75986cf2009-05-14 22:28:01 -07002504 }
2505
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07002506 @Override
Svetoslav Ganov031d9c12011-09-09 16:41:13 -07002507 void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
2508 super.onInitializeAccessibilityNodeInfoInternal(info);
Svetoslav Ganov42138042012-03-20 11:51:39 -07002509 if (mAttachInfo != null) {
2510 ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList;
2511 childrenForAccessibility.clear();
2512 addChildrenForAccessibility(childrenForAccessibility);
2513 final int childrenForAccessibilityCount = childrenForAccessibility.size();
2514 for (int i = 0; i < childrenForAccessibilityCount; i++) {
2515 View child = childrenForAccessibility.get(i);
Svetoslav Ganovea1da3d2011-06-15 17:16:02 -07002516 info.addChild(child);
2517 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07002518 childrenForAccessibility.clear();
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07002519 }
2520 }
2521
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08002522 @Override
2523 void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
2524 super.onInitializeAccessibilityEventInternal(event);
2525 event.setClassName(ViewGroup.class.getName());
2526 }
2527
Svetoslav Ganov42138042012-03-20 11:51:39 -07002528 @Override
Svetoslav6254f482013-06-04 17:22:14 -07002529 public void childAccessibilityStateChanged(View root) {
2530 if (mParent != null) {
Adam Powell504a10f2013-07-11 15:25:59 -07002531 try {
2532 mParent.childAccessibilityStateChanged(root);
2533 } catch (AbstractMethodError e) {
2534 Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
2535 " does not fully implement ViewParent", e);
2536 }
Svetoslav6254f482013-06-04 17:22:14 -07002537 }
2538 }
2539
2540 @Override
2541 void resetSubtreeAccessibilityStateChanged() {
2542 super.resetSubtreeAccessibilityStateChanged();
Svetoslav Ganov42138042012-03-20 11:51:39 -07002543 View[] children = mChildren;
2544 final int childCount = mChildrenCount;
2545 for (int i = 0; i < childCount; i++) {
Svetoslav6254f482013-06-04 17:22:14 -07002546 children[i].resetSubtreeAccessibilityStateChanged();
Svetoslav Ganov42138042012-03-20 11:51:39 -07002547 }
2548 }
2549
2550 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002551 * {@inheritDoc}
2552 */
2553 @Override
2554 void dispatchDetachedFromWindow() {
Jeff Brown20e987b2010-08-23 12:01:02 -07002555 // If we still have a touch target, we are still in the process of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002556 // dispatching motion events to a child; we need to get rid of that
2557 // child to avoid dispatching events to it after the window is torn
2558 // down. To make sure we keep the child in a consistent state, we
2559 // first send it an ACTION_CANCEL motion event.
Jeff Brown20e987b2010-08-23 12:01:02 -07002560 cancelAndClearTouchTargets(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002561
Jeff Brown59a422e2012-04-19 15:19:19 -07002562 // Similarly, set ACTION_EXIT to all hover targets and clear them.
2563 exitHoverTargets();
2564
Chet Haase9c087442011-01-12 16:20:16 -08002565 // In case view is detached while transition is running
Chet Haaseb9895022013-04-02 15:10:58 -07002566 mLayoutCalledWhileSuppressed = false;
Chet Haase9c087442011-01-12 16:20:16 -08002567
Christopher Tate86cab1b2011-01-13 20:28:55 -08002568 // Tear down our drag tracking
2569 mDragNotifiedChildren = null;
2570 if (mCurrentDrag != null) {
2571 mCurrentDrag.recycle();
2572 mCurrentDrag = null;
2573 }
2574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002575 final int count = mChildrenCount;
2576 final View[] children = mChildren;
2577 for (int i = 0; i < count; i++) {
2578 children[i].dispatchDetachedFromWindow();
2579 }
2580 super.dispatchDetachedFromWindow();
2581 }
2582
Fabrice Di Meglio23c89fd2012-08-13 12:17:42 -07002583 /**
2584 * @hide
2585 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002586 @Override
Fabrice Di Meglio23c89fd2012-08-13 12:17:42 -07002587 protected void internalSetPadding(int left, int top, int right, int bottom) {
Romain Guy2440e672012-08-07 14:43:43 -07002588 super.internalSetPadding(left, top, right, bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002589
Romain Guy13f35f32011-03-24 12:03:17 -07002590 if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002591 mGroupFlags |= FLAG_PADDING_NOT_NULL;
2592 } else {
2593 mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
2594 }
2595 }
2596
2597 /**
2598 * {@inheritDoc}
2599 */
2600 @Override
2601 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
2602 super.dispatchSaveInstanceState(container);
2603 final int count = mChildrenCount;
2604 final View[] children = mChildren;
2605 for (int i = 0; i < count; i++) {
Dianne Hackbornb4bc78b2010-05-12 18:59:50 -07002606 View c = children[i];
2607 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2608 c.dispatchSaveInstanceState(container);
2609 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002610 }
2611 }
2612
2613 /**
Romain Guy9fc27812011-04-27 14:21:41 -07002614 * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()}
2615 * to only this view, not to its children. For use when overriding
2616 * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow
2617 * subclasses to freeze their own state but not the state of their children.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002618 *
2619 * @param container the container
2620 */
2621 protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
2622 super.dispatchSaveInstanceState(container);
2623 }
2624
2625 /**
2626 * {@inheritDoc}
2627 */
2628 @Override
2629 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
2630 super.dispatchRestoreInstanceState(container);
2631 final int count = mChildrenCount;
2632 final View[] children = mChildren;
2633 for (int i = 0; i < count; i++) {
Dianne Hackbornb4bc78b2010-05-12 18:59:50 -07002634 View c = children[i];
2635 if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2636 c.dispatchRestoreInstanceState(container);
2637 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002638 }
2639 }
2640
2641 /**
Romain Guy02739a82011-05-16 11:43:18 -07002642 * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)}
2643 * to only this view, not to its children. For use when overriding
2644 * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow
2645 * subclasses to thaw their own state but not the state of their children.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002646 *
2647 * @param container the container
2648 */
2649 protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
2650 super.dispatchRestoreInstanceState(container);
2651 }
2652
2653 /**
2654 * Enables or disables the drawing cache for each child of this view group.
2655 *
2656 * @param enabled true to enable the cache, false to dispose of it
2657 */
2658 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
2659 if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
2660 final View[] children = mChildren;
2661 final int count = mChildrenCount;
2662 for (int i = 0; i < count; i++) {
2663 children[i].setDrawingCacheEnabled(enabled);
2664 }
2665 }
2666 }
2667
2668 @Override
2669 protected void onAnimationStart() {
2670 super.onAnimationStart();
2671
2672 // When this ViewGroup's animation starts, build the cache for the children
2673 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2674 final int count = mChildrenCount;
2675 final View[] children = mChildren;
Romain Guy0d9275e2010-10-26 14:22:30 -07002676 final boolean buildCache = !isHardwareAccelerated();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002677
2678 for (int i = 0; i < count; i++) {
2679 final View child = children[i];
2680 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2681 child.setDrawingCacheEnabled(true);
Romain Guy0d9275e2010-10-26 14:22:30 -07002682 if (buildCache) {
2683 child.buildDrawingCache(true);
2684 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002685 }
2686 }
2687
2688 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2689 }
2690 }
2691
2692 @Override
2693 protected void onAnimationEnd() {
2694 super.onAnimationEnd();
2695
2696 // When this ViewGroup's animation ends, destroy the cache of the children
2697 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2698 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2699
2700 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2701 setChildrenDrawingCacheEnabled(false);
2702 }
2703 }
2704 }
2705
Romain Guy223ff5c2010-03-02 17:07:47 -08002706 @Override
2707 Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
Romain Guy65554f22010-03-22 18:58:21 -07002708 int count = mChildrenCount;
2709 int[] visibilities = null;
2710
Romain Guy223ff5c2010-03-02 17:07:47 -08002711 if (skipChildren) {
Romain Guy65554f22010-03-22 18:58:21 -07002712 visibilities = new int[count];
2713 for (int i = 0; i < count; i++) {
2714 View child = getChildAt(i);
2715 visibilities[i] = child.getVisibility();
2716 if (visibilities[i] == View.VISIBLE) {
2717 child.setVisibility(INVISIBLE);
2718 }
2719 }
Romain Guy223ff5c2010-03-02 17:07:47 -08002720 }
2721
2722 Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
Romain Guy65554f22010-03-22 18:58:21 -07002723
2724 if (skipChildren) {
2725 for (int i = 0; i < count; i++) {
2726 getChildAt(i).setVisibility(visibilities[i]);
Chet Haase5c13d892010-10-08 08:37:55 -07002727 }
Romain Guy65554f22010-03-22 18:58:21 -07002728 }
Romain Guy223ff5c2010-03-02 17:07:47 -08002729
2730 return b;
2731 }
2732
Philip Milne7b757812012-09-19 18:13:44 -07002733 /** Return true if this ViewGroup is laying out using optical bounds. */
2734 boolean isLayoutModeOptical() {
2735 return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS;
2736 }
Romain Guycbc67742012-04-27 16:12:57 -07002737
Philip Milne7b757812012-09-19 18:13:44 -07002738 Insets computeOpticalInsets() {
2739 if (isLayoutModeOptical()) {
2740 int left = 0;
2741 int top = 0;
2742 int right = 0;
2743 int bottom = 0;
2744 for (int i = 0; i < mChildrenCount; i++) {
2745 View child = getChildAt(i);
2746 if (child.getVisibility() == VISIBLE) {
2747 Insets insets = child.getOpticalInsets();
2748 left = Math.max(left, insets.left);
2749 top = Math.max(top, insets.top);
2750 right = Math.max(right, insets.right);
2751 bottom = Math.max(bottom, insets.bottom);
2752 }
2753 }
2754 return Insets.of(left, top, right, bottom);
2755 } else {
2756 return Insets.NONE;
2757 }
2758 }
2759
2760 private static void fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
2761 if (x1 != x2 && y1 != y2) {
2762 if (x1 > x2) {
2763 int tmp = x1; x1 = x2; x2 = tmp;
2764 }
2765 if (y1 > y2) {
2766 int tmp = y1; y1 = y2; y2 = tmp;
2767 }
2768 canvas.drawRect(x1, y1, x2, y2, paint);
2769 }
2770 }
2771
2772 private static int sign(int x) {
2773 return (x >= 0) ? 1 : -1;
2774 }
2775
2776 private static void drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw) {
2777 fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy));
2778 fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy);
2779 }
2780
2781 private int dipsToPixels(int dips) {
2782 float scale = getContext().getResources().getDisplayMetrics().density;
2783 return (int) (dips * scale + 0.5f);
2784 }
2785
Romain Guy6410c0a2013-06-17 11:21:58 -07002786 private static void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint,
2787 int lineLength, int lineWidth) {
Philip Milne7b757812012-09-19 18:13:44 -07002788 drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth);
2789 drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth);
2790 drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth);
2791 drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth);
2792 }
2793
2794 private static void fillDifference(Canvas canvas,
2795 int x2, int y2, int x3, int y3,
2796 int dx1, int dy1, int dx2, int dy2, Paint paint) {
2797 int x1 = x2 - dx1;
2798 int y1 = y2 - dy1;
2799
2800 int x4 = x3 + dx2;
2801 int y4 = y3 + dy2;
2802
2803 fillRect(canvas, paint, x1, y1, x4, y2);
2804 fillRect(canvas, paint, x1, y2, x2, y3);
2805 fillRect(canvas, paint, x3, y2, x4, y3);
2806 fillRect(canvas, paint, x1, y3, x4, y4);
Philip Milne10ca24a2012-04-23 15:38:27 -07002807 }
2808
2809 /**
2810 * @hide
2811 */
Philip Milne7b757812012-09-19 18:13:44 -07002812 protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
Philip Milne10ca24a2012-04-23 15:38:27 -07002813 for (int i = 0; i < getChildCount(); i++) {
2814 View c = getChildAt(i);
Philip Milne7b757812012-09-19 18:13:44 -07002815 c.getLayoutParams().onDebugDraw(c, canvas, paint);
Philip Milne10ca24a2012-04-23 15:38:27 -07002816 }
2817 }
2818
2819 /**
2820 * @hide
2821 */
2822 protected void onDebugDraw(Canvas canvas) {
Philip Milne7b757812012-09-19 18:13:44 -07002823 Paint paint = getDebugPaint();
2824
Philip Milne10ca24a2012-04-23 15:38:27 -07002825 // Draw optical bounds
Philip Milne7b757812012-09-19 18:13:44 -07002826 {
2827 paint.setColor(Color.RED);
2828 paint.setStyle(Paint.Style.STROKE);
Philip Milne7b757812012-09-19 18:13:44 -07002829
Philip Milne10ca24a2012-04-23 15:38:27 -07002830 for (int i = 0; i < getChildCount(); i++) {
2831 View c = getChildAt(i);
Philip Milne7a23b492012-04-24 22:12:36 -07002832 Insets insets = c.getOpticalInsets();
Philip Milne7b757812012-09-19 18:13:44 -07002833
2834 drawRect(canvas, paint,
2835 c.getLeft() + insets.left,
2836 c.getTop() + insets.top,
2837 c.getRight() - insets.right - 1,
2838 c.getBottom() - insets.bottom - 1);
Philip Milne10ca24a2012-04-23 15:38:27 -07002839 }
2840 }
2841
Philip Milne10ca24a2012-04-23 15:38:27 -07002842 // Draw margins
Philip Milne7b757812012-09-19 18:13:44 -07002843 {
2844 paint.setColor(Color.argb(63, 255, 0, 255));
2845 paint.setStyle(Paint.Style.FILL);
Philip Milne604f4402012-04-24 19:27:11 -07002846
Philip Milne7b757812012-09-19 18:13:44 -07002847 onDebugDrawMargins(canvas, paint);
2848 }
2849
2850 // Draw clip bounds
2851 {
2852 paint.setColor(Color.rgb(63, 127, 255));
2853 paint.setStyle(Paint.Style.FILL);
2854
2855 int lineLength = dipsToPixels(8);
2856 int lineWidth = dipsToPixels(1);
2857 for (int i = 0; i < getChildCount(); i++) {
2858 View c = getChildAt(i);
2859 drawRectCorners(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(),
2860 paint, lineLength, lineWidth);
2861 }
Philip Milne604f4402012-04-24 19:27:11 -07002862 }
Philip Milne10ca24a2012-04-23 15:38:27 -07002863 }
2864
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002865 /**
2866 * {@inheritDoc}
2867 */
2868 @Override
2869 protected void dispatchDraw(Canvas canvas) {
2870 final int count = mChildrenCount;
2871 final View[] children = mChildren;
2872 int flags = mGroupFlags;
2873
2874 if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
2875 final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
2876
Romain Guy0d9275e2010-10-26 14:22:30 -07002877 final boolean buildCache = !isHardwareAccelerated();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002878 for (int i = 0; i < count; i++) {
2879 final View child = children[i];
2880 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2881 final LayoutParams params = child.getLayoutParams();
2882 attachLayoutAnimationParameters(child, params, i, count);
2883 bindLayoutAnimation(child);
2884 if (cache) {
2885 child.setDrawingCacheEnabled(true);
Romain Guy0d9275e2010-10-26 14:22:30 -07002886 if (buildCache) {
2887 child.buildDrawingCache(true);
2888 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002889 }
2890 }
2891 }
2892
2893 final LayoutAnimationController controller = mLayoutAnimationController;
2894 if (controller.willOverlap()) {
2895 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
2896 }
2897
2898 controller.start();
2899
2900 mGroupFlags &= ~FLAG_RUN_ANIMATION;
2901 mGroupFlags &= ~FLAG_ANIMATION_DONE;
2902
2903 if (cache) {
2904 mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2905 }
2906
2907 if (mAnimationListener != null) {
2908 mAnimationListener.onAnimationStart(controller.getAnimation());
2909 }
2910 }
2911
2912 int saveCount = 0;
2913 final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
2914 if (clipToPadding) {
2915 saveCount = canvas.save();
Romain Guy8f2d94f2009-03-25 18:04:42 -07002916 canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
2917 mScrollX + mRight - mLeft - mPaddingRight,
2918 mScrollY + mBottom - mTop - mPaddingBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002919
2920 }
2921
2922 // We will draw our child's animation, let's reset the flag
Dianne Hackborn4702a852012-08-17 15:18:29 -07002923 mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002924 mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
2925
2926 boolean more = false;
2927 final long drawingTime = getDrawingTime();
2928
2929 if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
2930 for (int i = 0; i < count; i++) {
2931 final View child = children[i];
2932 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2933 more |= drawChild(canvas, child, drawingTime);
2934 }
2935 }
2936 } else {
2937 for (int i = 0; i < count; i++) {
2938 final View child = children[getChildDrawingOrder(count, i)];
2939 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2940 more |= drawChild(canvas, child, drawingTime);
2941 }
2942 }
2943 }
2944
2945 // Draw any disappearing views that have animations
2946 if (mDisappearingChildren != null) {
2947 final ArrayList<View> disappearingChildren = mDisappearingChildren;
2948 final int disappearingCount = disappearingChildren.size() - 1;
2949 // Go backwards -- we may delete as animations finish
2950 for (int i = disappearingCount; i >= 0; i--) {
2951 final View child = disappearingChildren.get(i);
2952 more |= drawChild(canvas, child, drawingTime);
2953 }
2954 }
2955
Philip Milne10ca24a2012-04-23 15:38:27 -07002956 if (debugDraw()) {
2957 onDebugDraw(canvas);
2958 }
2959
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002960 if (clipToPadding) {
2961 canvas.restoreToCount(saveCount);
2962 }
2963
2964 // mGroupFlags might have been updated by drawChild()
2965 flags = mGroupFlags;
2966
2967 if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
Romain Guy849d0a32011-02-01 17:20:48 -08002968 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002969 }
2970
2971 if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
2972 mLayoutAnimationController.isDone() && !more) {
2973 // We want to erase the drawing cache and notify the listener after the
2974 // next frame is drawn because one extra invalidate() is caused by
2975 // drawChild() after the animation is over
2976 mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
2977 final Runnable end = new Runnable() {
2978 public void run() {
2979 notifyAnimationListener();
2980 }
2981 };
2982 post(end);
2983 }
2984 }
Romain Guy8506ab42009-06-11 17:35:47 -07002985
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002986 /**
Chet Haaseedf6f4b2013-03-26 07:55:30 -07002987 * Returns the ViewGroupOverlay for this view group, creating it if it does
2988 * not yet exist. In addition to {@link ViewOverlay}'s support for drawables,
2989 * {@link ViewGroupOverlay} allows views to be added to the overlay. These
2990 * views, like overlay drawables, are visual-only; they do not receive input
2991 * events and should not be used as anything other than a temporary
2992 * representation of a view in a parent container, such as might be used
2993 * by an animation effect.
2994 *
Chet Haase95399492013-04-08 14:30:31 -07002995 * <p>Note: Overlays do not currently work correctly with {@link
2996 * SurfaceView} or {@link TextureView}; contents in overlays for these
2997 * types of views may not display correctly.</p>
2998 *
Chet Haaseedf6f4b2013-03-26 07:55:30 -07002999 * @return The ViewGroupOverlay object for this view.
3000 * @see ViewGroupOverlay
3001 */
3002 @Override
3003 public ViewGroupOverlay getOverlay() {
3004 if (mOverlay == null) {
3005 mOverlay = new ViewGroupOverlay(mContext, this);
3006 }
3007 return (ViewGroupOverlay) mOverlay;
3008 }
3009
3010 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003011 * Returns the index of the child to draw for this iteration. Override this
3012 * if you want to change the drawing order of children. By default, it
3013 * returns i.
3014 * <p>
Romain Guy293451e2009-11-04 13:59:48 -08003015 * NOTE: In order for this method to be called, you must enable child ordering
3016 * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
Romain Guy8506ab42009-06-11 17:35:47 -07003017 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003018 * @param i The current iteration.
3019 * @return The index of the child to draw this iteration.
Chet Haase5c13d892010-10-08 08:37:55 -07003020 *
Romain Guy293451e2009-11-04 13:59:48 -08003021 * @see #setChildrenDrawingOrderEnabled(boolean)
3022 * @see #isChildrenDrawingOrderEnabled()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003023 */
3024 protected int getChildDrawingOrder(int childCount, int i) {
3025 return i;
3026 }
Romain Guy8506ab42009-06-11 17:35:47 -07003027
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003028 private void notifyAnimationListener() {
3029 mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
3030 mGroupFlags |= FLAG_ANIMATION_DONE;
3031
3032 if (mAnimationListener != null) {
3033 final Runnable end = new Runnable() {
3034 public void run() {
3035 mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
3036 }
3037 };
3038 post(end);
3039 }
3040
3041 if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
3042 mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
3043 if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
3044 setChildrenDrawingCacheEnabled(false);
3045 }
3046 }
3047
Romain Guy849d0a32011-02-01 17:20:48 -08003048 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003049 }
3050
3051 /**
Chet Haasedaf98e92011-01-10 14:10:36 -08003052 * This method is used to cause children of this ViewGroup to restore or recreate their
3053 * display lists. It is called by getDisplayList() when the parent ViewGroup does not need
3054 * to recreate its own display list, which would happen if it went through the normal
3055 * draw/dispatchDraw mechanisms.
3056 *
3057 * @hide
3058 */
3059 @Override
3060 protected void dispatchGetDisplayList() {
3061 final int count = mChildrenCount;
3062 final View[] children = mChildren;
3063 for (int i = 0; i < count; i++) {
3064 final View child = children[i];
Romain Guy59c7f802011-09-29 17:21:45 -07003065 if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) &&
3066 child.hasStaticLayer()) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07003067 child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED)
3068 == PFLAG_INVALIDATED;
3069 child.mPrivateFlags &= ~PFLAG_INVALIDATED;
Romain Guy2f57ba52011-02-03 18:03:29 -08003070 child.getDisplayList();
3071 child.mRecreateDisplayList = false;
3072 }
Chet Haasedaf98e92011-01-10 14:10:36 -08003073 }
Chet Haase91cedf12013-03-11 07:56:30 -07003074 if (mOverlay != null) {
Chet Haaseedf6f4b2013-03-26 07:55:30 -07003075 View overlayView = mOverlay.getOverlayView();
3076 overlayView.mRecreateDisplayList = (overlayView.mPrivateFlags & PFLAG_INVALIDATED)
Chet Haase91cedf12013-03-11 07:56:30 -07003077 == PFLAG_INVALIDATED;
Chet Haaseedf6f4b2013-03-26 07:55:30 -07003078 overlayView.mPrivateFlags &= ~PFLAG_INVALIDATED;
3079 overlayView.getDisplayList();
3080 overlayView.mRecreateDisplayList = false;
Chet Haase91cedf12013-03-11 07:56:30 -07003081 }
Chet Haasedaf98e92011-01-10 14:10:36 -08003082 }
3083
3084 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003085 * Draw one child of this View Group. This method is responsible for getting
3086 * the canvas in the right state. This includes clipping, translating so
3087 * that the child's scrolled origin is at 0, 0, and applying any animation
3088 * transformations.
3089 *
3090 * @param canvas The canvas on which to draw the child
3091 * @param child Who to draw
Chet Haasebcca79a2012-02-14 08:45:14 -08003092 * @param drawingTime The time at which draw is occurring
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003093 * @return True if an invalidate() was issued
3094 */
3095 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
Chet Haase64a48c12012-02-13 16:33:29 -08003096 return child.draw(canvas, this, drawingTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003097 }
3098
3099 /**
Chet Haase430742f2013-04-12 11:18:36 -07003100 * Returns whether ths group's children are clipped to their bounds before drawing.
3101 * The default value is true.
3102 * @see #setClipChildren(boolean)
3103 *
3104 * @return True if the group's children will be clipped to their bounds,
3105 * false otherwise.
3106 */
3107 public boolean getClipChildren() {
3108 return ((mGroupFlags & FLAG_CLIP_CHILDREN) != 0);
3109 }
3110
3111 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003112 * By default, children are clipped to their bounds before drawing. This
3113 * allows view groups to override this behavior for animations, etc.
3114 *
3115 * @param clipChildren true to clip children to their bounds,
3116 * false otherwise
3117 * @attr ref android.R.styleable#ViewGroup_clipChildren
3118 */
3119 public void setClipChildren(boolean clipChildren) {
Chet Haasea1cff502012-02-21 13:43:44 -08003120 boolean previousValue = (mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN;
3121 if (clipChildren != previousValue) {
3122 setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
Chet Haase1271e2c2012-04-20 09:54:27 -07003123 for (int i = 0; i < mChildrenCount; ++i) {
3124 View child = getChildAt(i);
3125 if (child.mDisplayList != null) {
Chet Haasedd671592013-04-19 14:54:34 -07003126 child.mDisplayList.setClipToBounds(clipChildren);
Chet Haasea1cff502012-02-21 13:43:44 -08003127 }
3128 }
3129 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003130 }
3131
3132 /**
3133 * By default, children are clipped to the padding of the ViewGroup. This
3134 * allows view groups to override this behavior
3135 *
3136 * @param clipToPadding true to clip children to the padding of the
3137 * group, false otherwise
3138 * @attr ref android.R.styleable#ViewGroup_clipToPadding
3139 */
3140 public void setClipToPadding(boolean clipToPadding) {
3141 setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
3142 }
3143
3144 /**
3145 * {@inheritDoc}
3146 */
3147 @Override
3148 public void dispatchSetSelected(boolean selected) {
3149 final View[] children = mChildren;
3150 final int count = mChildrenCount;
3151 for (int i = 0; i < count; i++) {
3152 children[i].setSelected(selected);
3153 }
3154 }
Romain Guy8506ab42009-06-11 17:35:47 -07003155
Dianne Hackbornd0fa3712010-09-14 18:57:14 -07003156 /**
3157 * {@inheritDoc}
3158 */
3159 @Override
3160 public void dispatchSetActivated(boolean activated) {
3161 final View[] children = mChildren;
3162 final int count = mChildrenCount;
3163 for (int i = 0; i < count; i++) {
Dianne Hackbornd0fa3712010-09-14 18:57:14 -07003164 children[i].setActivated(activated);
3165 }
3166 }
3167
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003168 @Override
3169 protected void dispatchSetPressed(boolean pressed) {
3170 final View[] children = mChildren;
3171 final int count = mChildrenCount;
3172 for (int i = 0; i < count; i++) {
Adam Powell035a1fc2012-02-27 15:23:50 -08003173 final View child = children[i];
3174 // Children that are clickable on their own should not
3175 // show a pressed state when their parent view does.
3176 // Clearing a pressed state always propagates.
3177 if (!pressed || (!child.isClickable() && !child.isLongClickable())) {
3178 child.setPressed(pressed);
3179 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003180 }
3181 }
3182
3183 /**
3184 * When this property is set to true, this ViewGroup supports static transformations on
3185 * children; this causes
3186 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
3187 * invoked when a child is drawn.
3188 *
3189 * Any subclass overriding
3190 * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
3191 * set this property to true.
3192 *
3193 * @param enabled True to enable static transformations on children, false otherwise.
3194 *
Chet Haase599913d2012-07-23 16:22:05 -07003195 * @see #getChildStaticTransformation(View, android.view.animation.Transformation)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003196 */
3197 protected void setStaticTransformationsEnabled(boolean enabled) {
3198 setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
3199 }
3200
3201 /**
Chet Haase2d46fcc2011-12-19 18:01:05 -08003202 * Sets <code>t</code> to be the static transformation of the child, if set, returning a
3203 * boolean to indicate whether a static transform was set. The default implementation
3204 * simply returns <code>false</code>; subclasses may override this method for different
Chet Haase599913d2012-07-23 16:22:05 -07003205 * behavior. {@link #setStaticTransformationsEnabled(boolean)} must be set to true
3206 * for this method to be called.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003207 *
Chet Haase2d46fcc2011-12-19 18:01:05 -08003208 * @param child The child view whose static transform is being requested
3209 * @param t The Transformation which will hold the result
3210 * @return true if the transformation was set, false otherwise
Romain Guy8506ab42009-06-11 17:35:47 -07003211 * @see #setStaticTransformationsEnabled(boolean)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003212 */
3213 protected boolean getChildStaticTransformation(View child, Transformation t) {
3214 return false;
3215 }
3216
Romain Guyf6991302013-06-05 17:19:01 -07003217 Transformation getChildTransformation() {
3218 if (mChildTransformation == null) {
3219 mChildTransformation = new Transformation();
3220 }
3221 return mChildTransformation;
3222 }
3223
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003224 /**
3225 * {@hide}
3226 */
3227 @Override
3228 protected View findViewTraversal(int id) {
3229 if (id == mID) {
3230 return this;
3231 }
3232
3233 final View[] where = mChildren;
3234 final int len = mChildrenCount;
3235
3236 for (int i = 0; i < len; i++) {
3237 View v = where[i];
3238
Dianne Hackborn4702a852012-08-17 15:18:29 -07003239 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003240 v = v.findViewById(id);
3241
3242 if (v != null) {
3243 return v;
3244 }
3245 }
3246 }
3247
3248 return null;
3249 }
3250
3251 /**
3252 * {@hide}
3253 */
3254 @Override
3255 protected View findViewWithTagTraversal(Object tag) {
3256 if (tag != null && tag.equals(mTag)) {
3257 return this;
3258 }
3259
3260 final View[] where = mChildren;
3261 final int len = mChildrenCount;
3262
3263 for (int i = 0; i < len; i++) {
3264 View v = where[i];
3265
Dianne Hackborn4702a852012-08-17 15:18:29 -07003266 if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003267 v = v.findViewWithTag(tag);
3268
3269 if (v != null) {
3270 return v;
3271 }
3272 }
3273 }
3274
3275 return null;
3276 }
3277
3278 /**
Jeff Brown4e6319b2010-12-13 10:36:51 -08003279 * {@hide}
3280 */
3281 @Override
Jeff Brown4dfbec22011-08-15 14:55:37 -07003282 protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
Jeff Brown4e6319b2010-12-13 10:36:51 -08003283 if (predicate.apply(this)) {
3284 return this;
3285 }
3286
3287 final View[] where = mChildren;
3288 final int len = mChildrenCount;
3289
3290 for (int i = 0; i < len; i++) {
3291 View v = where[i];
3292
Dianne Hackborn4702a852012-08-17 15:18:29 -07003293 if (v != childToSkip && (v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
Jeff Brown4e6319b2010-12-13 10:36:51 -08003294 v = v.findViewByPredicate(predicate);
3295
3296 if (v != null) {
3297 return v;
3298 }
3299 }
3300 }
3301
3302 return null;
3303 }
3304
3305 /**
Romain Guy393a52c2012-05-22 20:21:08 -07003306 * <p>Adds a child view. If no layout parameters are already set on the child, the
3307 * default parameters for this ViewGroup are set on the child.</p>
3308 *
3309 * <p><strong>Note:</strong> do not invoke this method from
3310 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3311 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003312 *
3313 * @param child the child view to add
3314 *
3315 * @see #generateDefaultLayoutParams()
3316 */
3317 public void addView(View child) {
3318 addView(child, -1);
3319 }
3320
3321 /**
3322 * Adds a child view. If no layout parameters are already set on the child, the
3323 * default parameters for this ViewGroup are set on the child.
Romain Guy393a52c2012-05-22 20:21:08 -07003324 *
3325 * <p><strong>Note:</strong> do not invoke this method from
3326 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3327 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003328 *
3329 * @param child the child view to add
3330 * @param index the position at which to add the child
3331 *
3332 * @see #generateDefaultLayoutParams()
3333 */
3334 public void addView(View child, int index) {
3335 LayoutParams params = child.getLayoutParams();
3336 if (params == null) {
3337 params = generateDefaultLayoutParams();
3338 if (params == null) {
3339 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
3340 }
3341 }
3342 addView(child, index, params);
3343 }
3344
3345 /**
3346 * Adds a child view with this ViewGroup's default layout parameters and the
3347 * specified width and height.
3348 *
Romain Guy393a52c2012-05-22 20:21:08 -07003349 * <p><strong>Note:</strong> do not invoke this method from
3350 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3351 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3352 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003353 * @param child the child view to add
3354 */
3355 public void addView(View child, int width, int height) {
3356 final LayoutParams params = generateDefaultLayoutParams();
3357 params.width = width;
3358 params.height = height;
3359 addView(child, -1, params);
3360 }
3361
3362 /**
3363 * Adds a child view with the specified layout parameters.
3364 *
Romain Guy393a52c2012-05-22 20:21:08 -07003365 * <p><strong>Note:</strong> do not invoke this method from
3366 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3367 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3368 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003369 * @param child the child view to add
3370 * @param params the layout parameters to set on the child
3371 */
3372 public void addView(View child, LayoutParams params) {
3373 addView(child, -1, params);
3374 }
3375
3376 /**
3377 * Adds a child view with the specified layout parameters.
3378 *
Romain Guy393a52c2012-05-22 20:21:08 -07003379 * <p><strong>Note:</strong> do not invoke this method from
3380 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3381 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3382 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003383 * @param child the child view to add
3384 * @param index the position at which to add the child
3385 * @param params the layout parameters to set on the child
3386 */
3387 public void addView(View child, int index, LayoutParams params) {
3388 if (DBG) {
3389 System.out.println(this + " addView");
3390 }
3391
3392 // addViewInner() will call child.requestLayout() when setting the new LayoutParams
3393 // therefore, we call requestLayout() on ourselves before, so that the child's request
3394 // will be blocked at our level
3395 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003396 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003397 addViewInner(child, index, params, false);
3398 }
3399
3400 /**
3401 * {@inheritDoc}
3402 */
3403 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
3404 if (!checkLayoutParams(params)) {
3405 throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
3406 }
3407 if (view.mParent != this) {
3408 throw new IllegalArgumentException("Given view not a child of " + this);
3409 }
3410 view.setLayoutParams(params);
3411 }
3412
3413 /**
3414 * {@inheritDoc}
3415 */
3416 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3417 return p != null;
3418 }
3419
3420 /**
3421 * Interface definition for a callback to be invoked when the hierarchy
3422 * within this view changed. The hierarchy changes whenever a child is added
3423 * to or removed from this view.
3424 */
3425 public interface OnHierarchyChangeListener {
3426 /**
3427 * Called when a new child is added to a parent view.
3428 *
3429 * @param parent the view in which a child was added
3430 * @param child the new child view added in the hierarchy
3431 */
3432 void onChildViewAdded(View parent, View child);
3433
3434 /**
3435 * Called when a child is removed from a parent view.
3436 *
3437 * @param parent the view from which the child was removed
3438 * @param child the child removed from the hierarchy
3439 */
3440 void onChildViewRemoved(View parent, View child);
3441 }
3442
3443 /**
3444 * Register a callback to be invoked when a child is added to or removed
3445 * from this view.
3446 *
3447 * @param listener the callback to invoke on hierarchy change
3448 */
3449 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
3450 mOnHierarchyChangeListener = listener;
3451 }
3452
3453 /**
Philip Milnef51d91c2011-07-18 16:12:19 -07003454 * @hide
3455 */
3456 protected void onViewAdded(View child) {
3457 if (mOnHierarchyChangeListener != null) {
3458 mOnHierarchyChangeListener.onChildViewAdded(this, child);
3459 }
3460 }
3461
3462 /**
3463 * @hide
3464 */
3465 protected void onViewRemoved(View child) {
3466 if (mOnHierarchyChangeListener != null) {
3467 mOnHierarchyChangeListener.onChildViewRemoved(this, child);
3468 }
3469 }
3470
Philip Milnecfb631b2012-10-26 10:51:46 -07003471 private void clearCachedLayoutMode() {
Svetoslav6254f482013-06-04 17:22:14 -07003472 if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
Philip Milnecfb631b2012-10-26 10:51:46 -07003473 mLayoutMode = LAYOUT_MODE_UNDEFINED;
3474 }
3475 }
3476
3477 @Override
3478 protected void onAttachedToWindow() {
3479 super.onAttachedToWindow();
3480 clearCachedLayoutMode();
3481 }
3482
3483 @Override
3484 protected void onDetachedFromWindow() {
3485 super.onDetachedFromWindow();
3486 clearCachedLayoutMode();
3487 }
3488
Philip Milnef51d91c2011-07-18 16:12:19 -07003489 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003490 * Adds a view during layout. This is useful if in your onLayout() method,
3491 * you need to add more views (as does the list view for example).
3492 *
3493 * If index is negative, it means put it at the end of the list.
3494 *
3495 * @param child the view to add to the group
3496 * @param index the index at which the child must be added
3497 * @param params the layout parameters to associate with the child
3498 * @return true if the child was added, false otherwise
3499 */
3500 protected boolean addViewInLayout(View child, int index, LayoutParams params) {
3501 return addViewInLayout(child, index, params, false);
3502 }
3503
3504 /**
3505 * Adds a view during layout. This is useful if in your onLayout() method,
3506 * you need to add more views (as does the list view for example).
3507 *
3508 * If index is negative, it means put it at the end of the list.
3509 *
3510 * @param child the view to add to the group
3511 * @param index the index at which the child must be added
3512 * @param params the layout parameters to associate with the child
3513 * @param preventRequestLayout if true, calling this method will not trigger a
3514 * layout request on child
3515 * @return true if the child was added, false otherwise
3516 */
3517 protected boolean addViewInLayout(View child, int index, LayoutParams params,
3518 boolean preventRequestLayout) {
3519 child.mParent = null;
3520 addViewInner(child, index, params, preventRequestLayout);
Dianne Hackborn4702a852012-08-17 15:18:29 -07003521 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003522 return true;
3523 }
3524
3525 /**
3526 * Prevents the specified child to be laid out during the next layout pass.
3527 *
3528 * @param child the child on which to perform the cleanup
3529 */
3530 protected void cleanupLayoutState(View child) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07003531 child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003532 }
3533
3534 private void addViewInner(View child, int index, LayoutParams params,
3535 boolean preventRequestLayout) {
3536
Chet Haasee8e45d32011-03-02 17:07:35 -08003537 if (mTransition != null) {
3538 // Don't prevent other add transitions from completing, but cancel remove
3539 // transitions to let them complete the process before we add to the container
3540 mTransition.cancel(LayoutTransition.DISAPPEARING);
Chet Haaseadd65772011-02-09 16:47:29 -08003541 }
3542
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003543 if (child.getParent() != null) {
3544 throw new IllegalStateException("The specified child already has a parent. " +
3545 "You must call removeView() on the child's parent first.");
3546 }
3547
Chet Haase21cd1382010-09-01 17:42:29 -07003548 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003549 mTransition.addChild(this, child);
Chet Haase21cd1382010-09-01 17:42:29 -07003550 }
3551
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003552 if (!checkLayoutParams(params)) {
3553 params = generateLayoutParams(params);
3554 }
3555
3556 if (preventRequestLayout) {
3557 child.mLayoutParams = params;
3558 } else {
3559 child.setLayoutParams(params);
3560 }
3561
3562 if (index < 0) {
3563 index = mChildrenCount;
3564 }
3565
3566 addInArray(child, index);
3567
3568 // tell our children
3569 if (preventRequestLayout) {
3570 child.assignParent(this);
3571 } else {
3572 child.mParent = this;
3573 }
3574
3575 if (child.hasFocus()) {
3576 requestChildFocus(child, child.findFocus());
3577 }
Romain Guy8506ab42009-06-11 17:35:47 -07003578
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003579 AttachInfo ai = mAttachInfo;
Adam Powell4b867882011-09-16 12:59:46 -07003580 if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
Romain Guy8506ab42009-06-11 17:35:47 -07003581 boolean lastKeepOn = ai.mKeepScreenOn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003582 ai.mKeepScreenOn = false;
3583 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
3584 if (ai.mKeepScreenOn) {
3585 needGlobalAttributesUpdate(true);
3586 }
3587 ai.mKeepScreenOn = lastKeepOn;
3588 }
3589
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07003590 if (child.isLayoutDirectionInherited()) {
Fabrice Di Meglioa7e0bcd2012-10-16 19:55:01 -07003591 child.resetRtlProperties();
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07003592 }
3593
Philip Milnef51d91c2011-07-18 16:12:19 -07003594 onViewAdded(child);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003595
3596 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
3597 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
3598 }
Adam Powell539ee872012-02-03 19:00:49 -08003599
3600 if (child.hasTransientState()) {
3601 childHasTransientStateChanged(child, true);
3602 }
Svetoslav6254f482013-06-04 17:22:14 -07003603
3604 if (child.isImportantForAccessibility() && child.getVisibility() != View.GONE) {
Svetoslav00dbe812013-06-10 12:51:09 -07003605 notifySubtreeAccessibilityStateChangedIfNeeded();
Svetoslav6254f482013-06-04 17:22:14 -07003606 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003607 }
3608
3609 private void addInArray(View child, int index) {
3610 View[] children = mChildren;
3611 final int count = mChildrenCount;
3612 final int size = children.length;
3613 if (index == count) {
3614 if (size == count) {
3615 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3616 System.arraycopy(children, 0, mChildren, 0, size);
3617 children = mChildren;
3618 }
3619 children[mChildrenCount++] = child;
3620 } else if (index < count) {
3621 if (size == count) {
3622 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3623 System.arraycopy(children, 0, mChildren, 0, index);
3624 System.arraycopy(children, index, mChildren, index + 1, count - index);
3625 children = mChildren;
3626 } else {
3627 System.arraycopy(children, index, children, index + 1, count - index);
3628 }
3629 children[index] = child;
3630 mChildrenCount++;
Joe Onorato03ab0c72011-01-06 15:46:27 -08003631 if (mLastTouchDownIndex >= index) {
3632 mLastTouchDownIndex++;
3633 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003634 } else {
3635 throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
3636 }
3637 }
3638
3639 // This method also sets the child's mParent to null
3640 private void removeFromArray(int index) {
3641 final View[] children = mChildren;
Chet Haase21cd1382010-09-01 17:42:29 -07003642 if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
3643 children[index].mParent = null;
3644 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003645 final int count = mChildrenCount;
3646 if (index == count - 1) {
3647 children[--mChildrenCount] = null;
3648 } else if (index >= 0 && index < count) {
3649 System.arraycopy(children, index + 1, children, index, count - index - 1);
3650 children[--mChildrenCount] = null;
3651 } else {
3652 throw new IndexOutOfBoundsException();
3653 }
Joe Onorato03ab0c72011-01-06 15:46:27 -08003654 if (mLastTouchDownIndex == index) {
3655 mLastTouchDownTime = 0;
3656 mLastTouchDownIndex = -1;
3657 } else if (mLastTouchDownIndex > index) {
3658 mLastTouchDownIndex--;
3659 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003660 }
3661
3662 // This method also sets the children's mParent to null
3663 private void removeFromArray(int start, int count) {
3664 final View[] children = mChildren;
3665 final int childrenCount = mChildrenCount;
3666
3667 start = Math.max(0, start);
3668 final int end = Math.min(childrenCount, start + count);
3669
3670 if (start == end) {
3671 return;
3672 }
3673
3674 if (end == childrenCount) {
3675 for (int i = start; i < end; i++) {
3676 children[i].mParent = null;
3677 children[i] = null;
3678 }
3679 } else {
3680 for (int i = start; i < end; i++) {
3681 children[i].mParent = null;
3682 }
3683
3684 // Since we're looping above, we might as well do the copy, but is arraycopy()
3685 // faster than the extra 2 bounds checks we would do in the loop?
3686 System.arraycopy(children, end, children, start, childrenCount - end);
3687
3688 for (int i = childrenCount - (end - start); i < childrenCount; i++) {
3689 children[i] = null;
3690 }
3691 }
3692
3693 mChildrenCount -= (end - start);
3694 }
3695
3696 private void bindLayoutAnimation(View child) {
3697 Animation a = mLayoutAnimationController.getAnimationForView(child);
3698 child.setAnimation(a);
3699 }
3700
3701 /**
3702 * Subclasses should override this method to set layout animation
3703 * parameters on the supplied child.
3704 *
3705 * @param child the child to associate with animation parameters
3706 * @param params the child's layout parameters which hold the animation
3707 * parameters
3708 * @param index the index of the child in the view group
3709 * @param count the number of children in the view group
3710 */
3711 protected void attachLayoutAnimationParameters(View child,
3712 LayoutParams params, int index, int count) {
3713 LayoutAnimationController.AnimationParameters animationParams =
3714 params.layoutAnimationParameters;
3715 if (animationParams == null) {
3716 animationParams = new LayoutAnimationController.AnimationParameters();
3717 params.layoutAnimationParameters = animationParams;
3718 }
3719
3720 animationParams.count = count;
3721 animationParams.index = index;
3722 }
3723
3724 /**
3725 * {@inheritDoc}
Romain Guy393a52c2012-05-22 20:21:08 -07003726 *
3727 * <p><strong>Note:</strong> do not invoke this method from
3728 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3729 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003730 */
3731 public void removeView(View view) {
3732 removeViewInternal(view);
3733 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003734 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003735 }
3736
3737 /**
3738 * Removes a view during layout. This is useful if in your onLayout() method,
3739 * you need to remove more views.
3740 *
Romain Guy393a52c2012-05-22 20:21:08 -07003741 * <p><strong>Note:</strong> do not invoke this method from
3742 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3743 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3744 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003745 * @param view the view to remove from the group
3746 */
3747 public void removeViewInLayout(View view) {
3748 removeViewInternal(view);
3749 }
3750
3751 /**
3752 * Removes a range of views during layout. This is useful if in your onLayout() method,
3753 * you need to remove more views.
3754 *
Romain Guy393a52c2012-05-22 20:21:08 -07003755 * <p><strong>Note:</strong> do not invoke this method from
3756 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3757 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3758 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003759 * @param start the index of the first view to remove from the group
3760 * @param count the number of views to remove from the group
3761 */
3762 public void removeViewsInLayout(int start, int count) {
3763 removeViewsInternal(start, count);
3764 }
3765
3766 /**
3767 * Removes the view at the specified position in the group.
3768 *
Romain Guy393a52c2012-05-22 20:21:08 -07003769 * <p><strong>Note:</strong> do not invoke this method from
3770 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3771 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3772 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003773 * @param index the position in the group of the view to remove
3774 */
3775 public void removeViewAt(int index) {
3776 removeViewInternal(index, getChildAt(index));
3777 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003778 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003779 }
3780
3781 /**
3782 * Removes the specified range of views from the group.
3783 *
Romain Guy393a52c2012-05-22 20:21:08 -07003784 * <p><strong>Note:</strong> do not invoke this method from
3785 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3786 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3787 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003788 * @param start the first position in the group of the range of views to remove
3789 * @param count the number of views to remove
3790 */
3791 public void removeViews(int start, int count) {
3792 removeViewsInternal(start, count);
3793 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003794 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003795 }
3796
3797 private void removeViewInternal(View view) {
3798 final int index = indexOfChild(view);
3799 if (index >= 0) {
3800 removeViewInternal(index, view);
3801 }
3802 }
3803
3804 private void removeViewInternal(int index, View view) {
Chet Haase21cd1382010-09-01 17:42:29 -07003805
3806 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003807 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003808 }
3809
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003810 boolean clearChildFocus = false;
3811 if (view == mFocused) {
Svetoslav Ganov57cadf22012-04-04 16:44:39 -07003812 view.unFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003813 clearChildFocus = true;
3814 }
3815
Svetoslav Ganov149567f2013-01-08 15:23:34 -08003816 if (view.isAccessibilityFocused()) {
3817 view.clearAccessibilityFocus();
3818 }
Svetoslav Ganov961bf0e2012-05-08 09:40:03 -07003819
Jeff Brown59a422e2012-04-19 15:19:19 -07003820 cancelTouchTarget(view);
3821 cancelHoverTarget(view);
3822
Chet Haase21cd1382010-09-01 17:42:29 -07003823 if (view.getAnimation() != null ||
3824 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003825 addDisappearingView(view);
3826 } else if (view.mAttachInfo != null) {
3827 view.dispatchDetachedFromWindow();
3828 }
3829
Adam Powell539ee872012-02-03 19:00:49 -08003830 if (view.hasTransientState()) {
3831 childHasTransientStateChanged(view, false);
3832 }
3833
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003834 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003835
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003836 removeFromArray(index);
3837
3838 if (clearChildFocus) {
3839 clearChildFocus(view);
Svetoslav Ganov149567f2013-01-08 15:23:34 -08003840 if (!rootViewRequestFocus()) {
3841 notifyGlobalFocusCleared(this);
3842 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07003843 }
Romain Guy6fb05632012-11-29 10:50:33 -08003844
3845 onViewRemoved(view);
Svetoslav6254f482013-06-04 17:22:14 -07003846
3847 if (view.isImportantForAccessibility() && view.getVisibility() != View.GONE) {
Svetoslav00dbe812013-06-10 12:51:09 -07003848 notifySubtreeAccessibilityStateChangedIfNeeded();
Svetoslav6254f482013-06-04 17:22:14 -07003849 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003850 }
3851
Chet Haase21cd1382010-09-01 17:42:29 -07003852 /**
3853 * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
3854 * not null, changes in layout which occur because of children being added to or removed from
3855 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
3856 * object. By default, the transition object is null (so layout changes are not animated).
3857 *
3858 * @param transition The LayoutTransition object that will animated changes in layout. A value
3859 * of <code>null</code> means no transition will run on layout changes.
Chet Haase13cc1202010-09-03 15:39:20 -07003860 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
Chet Haase21cd1382010-09-01 17:42:29 -07003861 */
3862 public void setLayoutTransition(LayoutTransition transition) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07003863 if (mTransition != null) {
3864 mTransition.removeTransitionListener(mLayoutTransitionListener);
3865 }
Chet Haase21cd1382010-09-01 17:42:29 -07003866 mTransition = transition;
Chet Haase13cc1202010-09-03 15:39:20 -07003867 if (mTransition != null) {
3868 mTransition.addTransitionListener(mLayoutTransitionListener);
3869 }
3870 }
3871
3872 /**
3873 * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
3874 * not null, changes in layout which occur because of children being added to or removed from
3875 * the ViewGroup will be animated according to the animations defined in that LayoutTransition
3876 * object. By default, the transition object is null (so layout changes are not animated).
3877 *
3878 * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
3879 * A value of <code>null</code> means no transition will run on layout changes.
3880 */
3881 public LayoutTransition getLayoutTransition() {
3882 return mTransition;
Chet Haase21cd1382010-09-01 17:42:29 -07003883 }
3884
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003885 private void removeViewsInternal(int start, int count) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003886 final View focused = mFocused;
3887 final boolean detach = mAttachInfo != null;
Svetoslav Ganov149567f2013-01-08 15:23:34 -08003888 boolean clearChildFocus = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003889
3890 final View[] children = mChildren;
3891 final int end = start + count;
3892
3893 for (int i = start; i < end; i++) {
3894 final View view = children[i];
3895
Chet Haase21cd1382010-09-01 17:42:29 -07003896 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003897 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003898 }
3899
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003900 if (view == focused) {
Svetoslav Ganov57cadf22012-04-04 16:44:39 -07003901 view.unFocus();
Svetoslav Ganov149567f2013-01-08 15:23:34 -08003902 clearChildFocus = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003903 }
3904
Svetoslav Ganov149567f2013-01-08 15:23:34 -08003905 if (view.isAccessibilityFocused()) {
3906 view.clearAccessibilityFocus();
3907 }
Svetoslav Ganov961bf0e2012-05-08 09:40:03 -07003908
Jeff Brown59a422e2012-04-19 15:19:19 -07003909 cancelTouchTarget(view);
3910 cancelHoverTarget(view);
3911
Chet Haase21cd1382010-09-01 17:42:29 -07003912 if (view.getAnimation() != null ||
3913 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003914 addDisappearingView(view);
3915 } else if (detach) {
3916 view.dispatchDetachedFromWindow();
3917 }
3918
Adam Powell539ee872012-02-03 19:00:49 -08003919 if (view.hasTransientState()) {
3920 childHasTransientStateChanged(view, false);
3921 }
3922
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003923 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003924
Philip Milnef51d91c2011-07-18 16:12:19 -07003925 onViewRemoved(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003926 }
3927
3928 removeFromArray(start, count);
3929
Svetoslav Ganov149567f2013-01-08 15:23:34 -08003930 if (clearChildFocus) {
3931 clearChildFocus(focused);
3932 if (!rootViewRequestFocus()) {
3933 notifyGlobalFocusCleared(focused);
3934 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003935 }
3936 }
3937
3938 /**
3939 * Call this method to remove all child views from the
3940 * ViewGroup.
Romain Guy393a52c2012-05-22 20:21:08 -07003941 *
3942 * <p><strong>Note:</strong> do not invoke this method from
3943 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3944 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003945 */
3946 public void removeAllViews() {
3947 removeAllViewsInLayout();
3948 requestLayout();
Romain Guy849d0a32011-02-01 17:20:48 -08003949 invalidate(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003950 }
3951
3952 /**
3953 * Called by a ViewGroup subclass to remove child views from itself,
3954 * when it must first know its size on screen before it can calculate how many
3955 * child views it will render. An example is a Gallery or a ListView, which
3956 * may "have" 50 children, but actually only render the number of children
3957 * that can currently fit inside the object on screen. Do not call
3958 * this method unless you are extending ViewGroup and understand the
3959 * view measuring and layout pipeline.
Romain Guy393a52c2012-05-22 20:21:08 -07003960 *
3961 * <p><strong>Note:</strong> do not invoke this method from
3962 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3963 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003964 */
3965 public void removeAllViewsInLayout() {
3966 final int count = mChildrenCount;
3967 if (count <= 0) {
3968 return;
3969 }
3970
3971 final View[] children = mChildren;
3972 mChildrenCount = 0;
3973
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003974 final View focused = mFocused;
3975 final boolean detach = mAttachInfo != null;
Svetoslav Ganov149567f2013-01-08 15:23:34 -08003976 boolean clearChildFocus = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003977
3978 needGlobalAttributesUpdate(false);
Romain Guy8506ab42009-06-11 17:35:47 -07003979
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003980 for (int i = count - 1; i >= 0; i--) {
3981 final View view = children[i];
3982
Chet Haase21cd1382010-09-01 17:42:29 -07003983 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07003984 mTransition.removeChild(this, view);
Chet Haase21cd1382010-09-01 17:42:29 -07003985 }
3986
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003987 if (view == focused) {
Svetoslav Ganov57cadf22012-04-04 16:44:39 -07003988 view.unFocus();
Svetoslav Ganov149567f2013-01-08 15:23:34 -08003989 clearChildFocus = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003990 }
3991
Svetoslav Ganov149567f2013-01-08 15:23:34 -08003992 if (view.isAccessibilityFocused()) {
3993 view.clearAccessibilityFocus();
3994 }
Svetoslav Ganov961bf0e2012-05-08 09:40:03 -07003995
Jeff Brown59a422e2012-04-19 15:19:19 -07003996 cancelTouchTarget(view);
3997 cancelHoverTarget(view);
3998
Chet Haase21cd1382010-09-01 17:42:29 -07003999 if (view.getAnimation() != null ||
4000 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004001 addDisappearingView(view);
4002 } else if (detach) {
4003 view.dispatchDetachedFromWindow();
4004 }
4005
Adam Powell539ee872012-02-03 19:00:49 -08004006 if (view.hasTransientState()) {
4007 childHasTransientStateChanged(view, false);
4008 }
4009
Philip Milnef51d91c2011-07-18 16:12:19 -07004010 onViewRemoved(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004011
4012 view.mParent = null;
4013 children[i] = null;
4014 }
4015
Svetoslav Ganov149567f2013-01-08 15:23:34 -08004016 if (clearChildFocus) {
4017 clearChildFocus(focused);
4018 if (!rootViewRequestFocus()) {
4019 notifyGlobalFocusCleared(focused);
4020 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004021 }
4022 }
4023
4024 /**
4025 * Finishes the removal of a detached view. This method will dispatch the detached from
4026 * window event and notify the hierarchy change listener.
Chet Haaseca479d42012-08-30 17:20:08 -07004027 * <p>
4028 * This method is intended to be lightweight and makes no assumptions about whether the
4029 * parent or child should be redrawn. Proper use of this method will include also making
4030 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
4031 * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
4032 * which performs a {@link #requestLayout()} on the next frame, after all detach/remove
4033 * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004034 *
4035 * @param child the child to be definitely removed from the view hierarchy
4036 * @param animate if true and the view has an animation, the view is placed in the
4037 * disappearing views list, otherwise, it is detached from the window
4038 *
4039 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4040 * @see #detachAllViewsFromParent()
4041 * @see #detachViewFromParent(View)
4042 * @see #detachViewFromParent(int)
4043 */
4044 protected void removeDetachedView(View child, boolean animate) {
Chet Haase21cd1382010-09-01 17:42:29 -07004045 if (mTransition != null) {
Chet Haase5e25c2c2010-09-16 11:15:56 -07004046 mTransition.removeChild(this, child);
Chet Haase21cd1382010-09-01 17:42:29 -07004047 }
4048
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004049 if (child == mFocused) {
4050 child.clearFocus();
4051 }
Romain Guy8506ab42009-06-11 17:35:47 -07004052
Svetoslav Ganov961bf0e2012-05-08 09:40:03 -07004053 child.clearAccessibilityFocus();
4054
Jeff Brown59a422e2012-04-19 15:19:19 -07004055 cancelTouchTarget(child);
4056 cancelHoverTarget(child);
4057
Chet Haase21cd1382010-09-01 17:42:29 -07004058 if ((animate && child.getAnimation() != null) ||
4059 (mTransitioningViews != null && mTransitioningViews.contains(child))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004060 addDisappearingView(child);
4061 } else if (child.mAttachInfo != null) {
4062 child.dispatchDetachedFromWindow();
4063 }
4064
Adam Powell539ee872012-02-03 19:00:49 -08004065 if (child.hasTransientState()) {
4066 childHasTransientStateChanged(child, false);
4067 }
4068
Philip Milnef51d91c2011-07-18 16:12:19 -07004069 onViewRemoved(child);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004070 }
4071
4072 /**
4073 * Attaches a view to this view group. Attaching a view assigns this group as the parent,
Chet Haaseca479d42012-08-30 17:20:08 -07004074 * sets the layout parameters and puts the view in the list of children so that
4075 * it can be retrieved by calling {@link #getChildAt(int)}.
4076 * <p>
4077 * This method is intended to be lightweight and makes no assumptions about whether the
4078 * parent or child should be redrawn. Proper use of this method will include also making
4079 * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
4080 * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
4081 * which performs a {@link #requestLayout()} on the next frame, after all detach/attach
4082 * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
4083 * <p>
4084 * This method should be called only for views which were detached from their parent.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004085 *
4086 * @param child the child to attach
4087 * @param index the index at which the child should be attached
4088 * @param params the layout parameters of the child
4089 *
4090 * @see #removeDetachedView(View, boolean)
4091 * @see #detachAllViewsFromParent()
4092 * @see #detachViewFromParent(View)
4093 * @see #detachViewFromParent(int)
4094 */
4095 protected void attachViewToParent(View child, int index, LayoutParams params) {
4096 child.mLayoutParams = params;
4097
4098 if (index < 0) {
4099 index = mChildrenCount;
4100 }
4101
4102 addInArray(child, index);
4103
4104 child.mParent = this;
Dianne Hackborn4702a852012-08-17 15:18:29 -07004105 child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK
4106 & ~PFLAG_DRAWING_CACHE_VALID)
4107 | PFLAG_DRAWN | PFLAG_INVALIDATED;
4108 this.mPrivateFlags |= PFLAG_INVALIDATED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004109
4110 if (child.hasFocus()) {
4111 requestChildFocus(child, child.findFocus());
4112 }
4113 }
4114
4115 /**
Chet Haaseca479d42012-08-30 17:20:08 -07004116 * Detaches a view from its parent. Detaching a view should be followed
4117 * either by a call to
4118 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4119 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4120 * temporary; reattachment or removal should happen within the same drawing cycle as
4121 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4122 * call to {@link #getChildAt(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004123 *
4124 * @param child the child to detach
4125 *
4126 * @see #detachViewFromParent(int)
4127 * @see #detachViewsFromParent(int, int)
4128 * @see #detachAllViewsFromParent()
4129 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4130 * @see #removeDetachedView(View, boolean)
4131 */
4132 protected void detachViewFromParent(View child) {
4133 removeFromArray(indexOfChild(child));
4134 }
4135
4136 /**
Chet Haaseca479d42012-08-30 17:20:08 -07004137 * Detaches a view from its parent. Detaching a view should be followed
4138 * either by a call to
4139 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4140 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4141 * temporary; reattachment or removal should happen within the same drawing cycle as
4142 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4143 * call to {@link #getChildAt(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004144 *
4145 * @param index the index of the child to detach
4146 *
4147 * @see #detachViewFromParent(View)
4148 * @see #detachAllViewsFromParent()
4149 * @see #detachViewsFromParent(int, int)
4150 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4151 * @see #removeDetachedView(View, boolean)
4152 */
4153 protected void detachViewFromParent(int index) {
4154 removeFromArray(index);
4155 }
4156
4157 /**
Chet Haaseca479d42012-08-30 17:20:08 -07004158 * Detaches a range of views from their parents. Detaching a view should be followed
4159 * either by a call to
4160 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4161 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4162 * temporary; reattachment or removal should happen within the same drawing cycle as
4163 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4164 * call to {@link #getChildAt(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004165 *
4166 * @param start the first index of the childrend range to detach
4167 * @param count the number of children to detach
4168 *
4169 * @see #detachViewFromParent(View)
4170 * @see #detachViewFromParent(int)
4171 * @see #detachAllViewsFromParent()
4172 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4173 * @see #removeDetachedView(View, boolean)
4174 */
4175 protected void detachViewsFromParent(int start, int count) {
4176 removeFromArray(start, count);
4177 }
4178
4179 /**
Chet Haaseca479d42012-08-30 17:20:08 -07004180 * Detaches all views from the parent. Detaching a view should be followed
4181 * either by a call to
4182 * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4183 * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4184 * temporary; reattachment or removal should happen within the same drawing cycle as
4185 * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4186 * call to {@link #getChildAt(int)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004187 *
4188 * @see #detachViewFromParent(View)
4189 * @see #detachViewFromParent(int)
4190 * @see #detachViewsFromParent(int, int)
4191 * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4192 * @see #removeDetachedView(View, boolean)
4193 */
4194 protected void detachAllViewsFromParent() {
4195 final int count = mChildrenCount;
4196 if (count <= 0) {
4197 return;
4198 }
4199
4200 final View[] children = mChildren;
4201 mChildrenCount = 0;
4202
4203 for (int i = count - 1; i >= 0; i--) {
4204 children[i].mParent = null;
4205 children[i] = null;
4206 }
4207 }
4208
4209 /**
4210 * Don't call or override this method. It is used for the implementation of
4211 * the view hierarchy.
4212 */
4213 public final void invalidateChild(View child, final Rect dirty) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004214 ViewParent parent = this;
4215
4216 final AttachInfo attachInfo = mAttachInfo;
4217 if (attachInfo != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004218 // If the child is drawing an animation, we want to copy this flag onto
4219 // ourselves and the parent to make sure the invalidate request goes
4220 // through
Dianne Hackborn4702a852012-08-17 15:18:29 -07004221 final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)
4222 == PFLAG_DRAW_ANIMATION;
Romain Guy24443ea2009-05-11 11:56:30 -07004223
Romain Guyfe455af2012-02-15 16:40:20 -08004224 // Check whether the child that requests the invalidate is fully opaque
4225 // Views being animated or transformed are not considered opaque because we may
4226 // be invalidating their old position and need the parent to paint behind them.
4227 Matrix childMatrix = child.getMatrix();
4228 final boolean isOpaque = child.isOpaque() && !drawAnimation &&
4229 child.getAnimation() == null && childMatrix.isIdentity();
4230 // Mark the child as dirty, using the appropriate flag
4231 // Make sure we do not set both flags at the same time
Dianne Hackborn4702a852012-08-17 15:18:29 -07004232 int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
Romain Guyfe455af2012-02-15 16:40:20 -08004233
4234 if (child.mLayerType != LAYER_TYPE_NONE) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004235 mPrivateFlags |= PFLAG_INVALIDATED;
4236 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
Romain Guyfe455af2012-02-15 16:40:20 -08004237 child.mLocalDirtyRect.union(dirty);
4238 }
4239
4240 final int[] location = attachInfo.mInvalidateChildLocation;
4241 location[CHILD_LEFT_INDEX] = child.mLeft;
4242 location[CHILD_TOP_INDEX] = child.mTop;
Chet Haase599913d2012-07-23 16:22:05 -07004243 if (!childMatrix.isIdentity() ||
4244 (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
Romain Guyfe455af2012-02-15 16:40:20 -08004245 RectF boundingRect = attachInfo.mTmpTransformRect;
4246 boundingRect.set(dirty);
Chet Haase599913d2012-07-23 16:22:05 -07004247 Matrix transformMatrix;
4248 if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
4249 Transformation t = attachInfo.mTmpTransformation;
4250 boolean transformed = getChildStaticTransformation(child, t);
4251 if (transformed) {
4252 transformMatrix = attachInfo.mTmpMatrix;
4253 transformMatrix.set(t.getMatrix());
4254 if (!childMatrix.isIdentity()) {
4255 transformMatrix.preConcat(childMatrix);
4256 }
4257 } else {
4258 transformMatrix = childMatrix;
4259 }
4260 } else {
4261 transformMatrix = childMatrix;
4262 }
4263 transformMatrix.mapRect(boundingRect);
Romain Guyfe455af2012-02-15 16:40:20 -08004264 dirty.set((int) (boundingRect.left - 0.5f),
4265 (int) (boundingRect.top - 0.5f),
4266 (int) (boundingRect.right + 0.5f),
4267 (int) (boundingRect.bottom + 0.5f));
4268 }
4269
4270 do {
4271 View view = null;
4272 if (parent instanceof View) {
4273 view = (View) parent;
Romain Guyfe455af2012-02-15 16:40:20 -08004274 }
4275
4276 if (drawAnimation) {
4277 if (view != null) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004278 view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
Romain Guyfe455af2012-02-15 16:40:20 -08004279 } else if (parent instanceof ViewRootImpl) {
4280 ((ViewRootImpl) parent).mIsAnimating = true;
4281 }
4282 }
4283
4284 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
4285 // flag coming from the child that initiated the invalidate
4286 if (view != null) {
4287 if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
4288 view.getSolidColor() == 0) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004289 opaqueFlag = PFLAG_DIRTY;
Romain Guyfe455af2012-02-15 16:40:20 -08004290 }
Dianne Hackborn4702a852012-08-17 15:18:29 -07004291 if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
4292 view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
Romain Guyfe455af2012-02-15 16:40:20 -08004293 }
4294 }
4295
4296 parent = parent.invalidateChildInParent(location, dirty);
4297 if (view != null) {
4298 // Account for transform on current parent
4299 Matrix m = view.getMatrix();
4300 if (!m.isIdentity()) {
4301 RectF boundingRect = attachInfo.mTmpTransformRect;
4302 boundingRect.set(dirty);
4303 m.mapRect(boundingRect);
Romain Guye8585b12012-02-17 18:28:47 -08004304 dirty.set((int) (boundingRect.left - 0.5f),
4305 (int) (boundingRect.top - 0.5f),
Romain Guyfe455af2012-02-15 16:40:20 -08004306 (int) (boundingRect.right + 0.5f),
4307 (int) (boundingRect.bottom + 0.5f));
4308 }
4309 }
4310 } while (parent != null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004311 }
4312 }
4313
4314 /**
4315 * Don't call or override this method. It is used for the implementation of
4316 * the view hierarchy.
4317 *
4318 * This implementation returns null if this ViewGroup does not have a parent,
4319 * if this ViewGroup is already fully invalidated or if the dirty rectangle
4320 * does not intersect with this ViewGroup's bounds.
4321 */
4322 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004323 if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
4324 (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004325 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
4326 FLAG_OPTIMIZE_INVALIDATE) {
4327 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
4328 location[CHILD_TOP_INDEX] - mScrollY);
Chet Haasea4f14eb2013-04-22 11:11:39 -07004329 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
4330 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
4331 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004332
4333 final int left = mLeft;
4334 final int top = mTop;
4335
Chet Haase05e91ed2012-07-03 14:17:57 -07004336 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
4337 if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
4338 dirty.setEmpty();
Romain Guy3a3133d2011-02-01 22:59:58 -08004339 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004340 }
Dianne Hackborn4702a852012-08-17 15:18:29 -07004341 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
Chet Haase05e91ed2012-07-03 14:17:57 -07004342
4343 location[CHILD_LEFT_INDEX] = left;
4344 location[CHILD_TOP_INDEX] = top;
4345
4346 if (mLayerType != LAYER_TYPE_NONE) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004347 mPrivateFlags |= PFLAG_INVALIDATED;
Chet Haase05e91ed2012-07-03 14:17:57 -07004348 mLocalDirtyRect.union(dirty);
4349 }
4350
4351 return mParent;
4352
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004353 } else {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004354 mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004355
4356 location[CHILD_LEFT_INDEX] = mLeft;
4357 location[CHILD_TOP_INDEX] = mTop;
Chet Haasea3db8662011-07-19 10:36:05 -07004358 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
4359 dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
4360 } else {
4361 // in case the dirty rect extends outside the bounds of this container
4362 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
4363 }
Romain Guy3a3133d2011-02-01 22:59:58 -08004364
4365 if (mLayerType != LAYER_TYPE_NONE) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004366 mPrivateFlags |= PFLAG_INVALIDATED;
Romain Guy3a3133d2011-02-01 22:59:58 -08004367 mLocalDirtyRect.union(dirty);
4368 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004369
4370 return mParent;
4371 }
4372 }
4373
4374 return null;
4375 }
4376
4377 /**
Chet Haase9d1992d2012-03-13 11:03:25 -07004378 * Quick invalidation method called by View.invalidateViewProperty. This doesn't set the
4379 * DRAWN flags and doesn't handle the Animation logic that the default invalidation methods
4380 * do; all we want to do here is schedule a traversal with the appropriate dirty rect.
4381 *
4382 * @hide
4383 */
4384 public void invalidateChildFast(View child, final Rect dirty) {
4385 ViewParent parent = this;
4386
4387 final AttachInfo attachInfo = mAttachInfo;
4388 if (attachInfo != null) {
4389 if (child.mLayerType != LAYER_TYPE_NONE) {
4390 child.mLocalDirtyRect.union(dirty);
4391 }
4392
4393 int left = child.mLeft;
4394 int top = child.mTop;
4395 if (!child.getMatrix().isIdentity()) {
4396 child.transformRect(dirty);
4397 }
4398
4399 do {
4400 if (parent instanceof ViewGroup) {
4401 ViewGroup parentVG = (ViewGroup) parent;
Chet Haaseb85967b2012-03-26 14:37:51 -07004402 if (parentVG.mLayerType != LAYER_TYPE_NONE) {
4403 // Layered parents should be recreated, not just re-issued
4404 parentVG.invalidate();
4405 parent = null;
4406 } else {
4407 parent = parentVG.invalidateChildInParentFast(left, top, dirty);
4408 left = parentVG.mLeft;
4409 top = parentVG.mTop;
4410 }
Chet Haase9d1992d2012-03-13 11:03:25 -07004411 } else {
4412 // Reached the top; this calls into the usual invalidate method in
4413 // ViewRootImpl, which schedules a traversal
4414 final int[] location = attachInfo.mInvalidateChildLocation;
4415 location[0] = left;
4416 location[1] = top;
4417 parent = parent.invalidateChildInParent(location, dirty);
4418 }
4419 } while (parent != null);
4420 }
4421 }
4422
4423 /**
4424 * Quick invalidation method that simply transforms the dirty rect into the parent's
4425 * coordinate system, pruning the invalidation if the parent has already been invalidated.
Chet Haasee4a2d7c2013-06-21 17:49:36 -07004426 *
4427 * @hide
Chet Haase9d1992d2012-03-13 11:03:25 -07004428 */
Chet Haasee4a2d7c2013-06-21 17:49:36 -07004429 protected ViewParent invalidateChildInParentFast(int left, int top, final Rect dirty) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07004430 if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
4431 (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
Chet Haase9d1992d2012-03-13 11:03:25 -07004432 dirty.offset(left - mScrollX, top - mScrollY);
Chet Haasea4f14eb2013-04-22 11:11:39 -07004433 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
4434 dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
4435 }
Chet Haase9d1992d2012-03-13 11:03:25 -07004436
4437 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0 ||
4438 dirty.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
4439
4440 if (mLayerType != LAYER_TYPE_NONE) {
4441 mLocalDirtyRect.union(dirty);
4442 }
4443 if (!getMatrix().isIdentity()) {
4444 transformRect(dirty);
4445 }
4446
4447 return mParent;
4448 }
4449 }
4450
4451 return null;
4452 }
4453
4454 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004455 * Offset a rectangle that is in a descendant's coordinate
4456 * space into our coordinate space.
4457 * @param descendant A descendant of this view
4458 * @param rect A rectangle defined in descendant's coordinate space.
4459 */
4460 public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
4461 offsetRectBetweenParentAndChild(descendant, rect, true, false);
4462 }
4463
4464 /**
4465 * Offset a rectangle that is in our coordinate space into an ancestor's
4466 * coordinate space.
4467 * @param descendant A descendant of this view
4468 * @param rect A rectangle defined in descendant's coordinate space.
4469 */
4470 public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
4471 offsetRectBetweenParentAndChild(descendant, rect, false, false);
4472 }
4473
4474 /**
4475 * Helper method that offsets a rect either from parent to descendant or
4476 * descendant to parent.
4477 */
4478 void offsetRectBetweenParentAndChild(View descendant, Rect rect,
4479 boolean offsetFromChildToParent, boolean clipToBounds) {
4480
4481 // already in the same coord system :)
4482 if (descendant == this) {
4483 return;
4484 }
4485
4486 ViewParent theParent = descendant.mParent;
4487
4488 // search and offset up to the parent
4489 while ((theParent != null)
4490 && (theParent instanceof View)
4491 && (theParent != this)) {
4492
4493 if (offsetFromChildToParent) {
4494 rect.offset(descendant.mLeft - descendant.mScrollX,
4495 descendant.mTop - descendant.mScrollY);
4496 if (clipToBounds) {
4497 View p = (View) theParent;
4498 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4499 }
4500 } else {
4501 if (clipToBounds) {
4502 View p = (View) theParent;
4503 rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4504 }
4505 rect.offset(descendant.mScrollX - descendant.mLeft,
4506 descendant.mScrollY - descendant.mTop);
4507 }
4508
4509 descendant = (View) theParent;
4510 theParent = descendant.mParent;
4511 }
4512
4513 // now that we are up to this view, need to offset one more time
4514 // to get into our coordinate space
4515 if (theParent == this) {
4516 if (offsetFromChildToParent) {
4517 rect.offset(descendant.mLeft - descendant.mScrollX,
4518 descendant.mTop - descendant.mScrollY);
4519 } else {
4520 rect.offset(descendant.mScrollX - descendant.mLeft,
4521 descendant.mScrollY - descendant.mTop);
4522 }
4523 } else {
4524 throw new IllegalArgumentException("parameter must be a descendant of this view");
4525 }
4526 }
4527
4528 /**
4529 * Offset the vertical location of all children of this view by the specified number of pixels.
4530 *
4531 * @param offset the number of pixels to offset
4532 *
4533 * @hide
4534 */
4535 public void offsetChildrenTopAndBottom(int offset) {
4536 final int count = mChildrenCount;
4537 final View[] children = mChildren;
Romain Guy5549cb52013-05-06 18:42:08 -07004538 boolean invalidate = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004539
4540 for (int i = 0; i < count; i++) {
4541 final View v = children[i];
4542 v.mTop += offset;
4543 v.mBottom += offset;
Chet Haase1271e2c2012-04-20 09:54:27 -07004544 if (v.mDisplayList != null) {
Romain Guy5549cb52013-05-06 18:42:08 -07004545 invalidate = true;
Romain Guy52036b12013-02-14 18:03:37 -08004546 v.mDisplayList.offsetTopAndBottom(offset);
Chet Haasea1cff502012-02-21 13:43:44 -08004547 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004548 }
Romain Guy5549cb52013-05-06 18:42:08 -07004549
4550 if (invalidate) {
4551 invalidateViewProperty(false, false);
4552 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004553 }
4554
4555 /**
4556 * {@inheritDoc}
4557 */
4558 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
Adam Powellf93bb6d2011-12-12 15:21:57 -08004559 // It doesn't make a whole lot of sense to call this on a view that isn't attached,
4560 // but for some simple tests it can be useful. If we don't have attach info this
4561 // will allocate memory.
4562 final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF();
Gilles Debunnecea45132011-11-24 02:19:27 +01004563 rect.set(r);
4564
4565 if (!child.hasIdentityMatrix()) {
4566 child.getMatrix().mapRect(rect);
4567 }
4568
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004569 int dx = child.mLeft - mScrollX;
4570 int dy = child.mTop - mScrollY;
Gilles Debunnecea45132011-11-24 02:19:27 +01004571
4572 rect.offset(dx, dy);
4573
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004574 if (offset != null) {
Gilles Debunnecea45132011-11-24 02:19:27 +01004575 if (!child.hasIdentityMatrix()) {
Adam Powellf93bb6d2011-12-12 15:21:57 -08004576 float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation
4577 : new float[2];
Gilles Debunnecea45132011-11-24 02:19:27 +01004578 position[0] = offset.x;
4579 position[1] = offset.y;
4580 child.getMatrix().mapPoints(position);
4581 offset.x = (int) (position[0] + 0.5f);
4582 offset.y = (int) (position[1] + 0.5f);
4583 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004584 offset.x += dx;
4585 offset.y += dy;
4586 }
Gilles Debunnecea45132011-11-24 02:19:27 +01004587
4588 if (rect.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
4589 if (mParent == null) return true;
4590 r.set((int) (rect.left + 0.5f), (int) (rect.top + 0.5f),
4591 (int) (rect.right + 0.5f), (int) (rect.bottom + 0.5f));
4592 return mParent.getChildVisibleRect(this, r, offset);
4593 }
4594
4595 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004596 }
4597
4598 /**
4599 * {@inheritDoc}
4600 */
4601 @Override
Chet Haase9c087442011-01-12 16:20:16 -08004602 public final void layout(int l, int t, int r, int b) {
Chet Haase430742f2013-04-12 11:18:36 -07004603 if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
Chet Haase7dd4a532012-04-16 13:35:09 -07004604 if (mTransition != null) {
4605 mTransition.layoutChange(this);
4606 }
Chet Haase9c087442011-01-12 16:20:16 -08004607 super.layout(l, t, r, b);
4608 } else {
4609 // record the fact that we noop'd it; request layout when transition finishes
Chet Haaseb9895022013-04-02 15:10:58 -07004610 mLayoutCalledWhileSuppressed = true;
Chet Haase9c087442011-01-12 16:20:16 -08004611 }
4612 }
4613
4614 /**
4615 * {@inheritDoc}
4616 */
4617 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004618 protected abstract void onLayout(boolean changed,
4619 int l, int t, int r, int b);
4620
4621 /**
4622 * Indicates whether the view group has the ability to animate its children
4623 * after the first layout.
4624 *
4625 * @return true if the children can be animated, false otherwise
4626 */
4627 protected boolean canAnimate() {
4628 return mLayoutAnimationController != null;
4629 }
4630
4631 /**
4632 * Runs the layout animation. Calling this method triggers a relayout of
4633 * this view group.
4634 */
4635 public void startLayoutAnimation() {
4636 if (mLayoutAnimationController != null) {
4637 mGroupFlags |= FLAG_RUN_ANIMATION;
4638 requestLayout();
4639 }
4640 }
4641
4642 /**
4643 * Schedules the layout animation to be played after the next layout pass
4644 * of this view group. This can be used to restart the layout animation
4645 * when the content of the view group changes or when the activity is
4646 * paused and resumed.
4647 */
4648 public void scheduleLayoutAnimation() {
4649 mGroupFlags |= FLAG_RUN_ANIMATION;
4650 }
4651
4652 /**
4653 * Sets the layout animation controller used to animate the group's
4654 * children after the first layout.
4655 *
4656 * @param controller the animation controller
4657 */
4658 public void setLayoutAnimation(LayoutAnimationController controller) {
4659 mLayoutAnimationController = controller;
4660 if (mLayoutAnimationController != null) {
4661 mGroupFlags |= FLAG_RUN_ANIMATION;
4662 }
4663 }
4664
4665 /**
4666 * Returns the layout animation controller used to animate the group's
4667 * children.
4668 *
4669 * @return the current animation controller
4670 */
4671 public LayoutAnimationController getLayoutAnimation() {
4672 return mLayoutAnimationController;
4673 }
4674
4675 /**
4676 * Indicates whether the children's drawing cache is used during a layout
4677 * animation. By default, the drawing cache is enabled but this will prevent
4678 * nested layout animations from working. To nest animations, you must disable
4679 * the cache.
4680 *
4681 * @return true if the animation cache is enabled, false otherwise
4682 *
4683 * @see #setAnimationCacheEnabled(boolean)
4684 * @see View#setDrawingCacheEnabled(boolean)
4685 */
4686 @ViewDebug.ExportedProperty
4687 public boolean isAnimationCacheEnabled() {
4688 return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
4689 }
4690
4691 /**
4692 * Enables or disables the children's drawing cache during a layout animation.
4693 * By default, the drawing cache is enabled but this will prevent nested
4694 * layout animations from working. To nest animations, you must disable the
4695 * cache.
4696 *
4697 * @param enabled true to enable the animation cache, false otherwise
4698 *
4699 * @see #isAnimationCacheEnabled()
4700 * @see View#setDrawingCacheEnabled(boolean)
4701 */
4702 public void setAnimationCacheEnabled(boolean enabled) {
4703 setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
4704 }
4705
4706 /**
4707 * Indicates whether this ViewGroup will always try to draw its children using their
4708 * drawing cache. By default this property is enabled.
4709 *
4710 * @return true if the animation cache is enabled, false otherwise
4711 *
4712 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4713 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4714 * @see View#setDrawingCacheEnabled(boolean)
4715 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004716 @ViewDebug.ExportedProperty(category = "drawing")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004717 public boolean isAlwaysDrawnWithCacheEnabled() {
4718 return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
4719 }
4720
4721 /**
4722 * Indicates whether this ViewGroup will always try to draw its children using their
4723 * drawing cache. This property can be set to true when the cache rendering is
4724 * slightly different from the children's normal rendering. Renderings can be different,
4725 * for instance, when the cache's quality is set to low.
4726 *
4727 * When this property is disabled, the ViewGroup will use the drawing cache of its
4728 * children only when asked to. It's usually the task of subclasses to tell ViewGroup
4729 * when to start using the drawing cache and when to stop using it.
4730 *
4731 * @param always true to always draw with the drawing cache, false otherwise
4732 *
4733 * @see #isAlwaysDrawnWithCacheEnabled()
4734 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4735 * @see View#setDrawingCacheEnabled(boolean)
4736 * @see View#setDrawingCacheQuality(int)
4737 */
4738 public void setAlwaysDrawnWithCacheEnabled(boolean always) {
4739 setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
4740 }
4741
4742 /**
4743 * Indicates whether the ViewGroup is currently drawing its children using
4744 * their drawing cache.
4745 *
4746 * @return true if children should be drawn with their cache, false otherwise
4747 *
4748 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4749 * @see #setChildrenDrawnWithCacheEnabled(boolean)
4750 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004751 @ViewDebug.ExportedProperty(category = "drawing")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004752 protected boolean isChildrenDrawnWithCacheEnabled() {
4753 return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
4754 }
4755
4756 /**
4757 * Tells the ViewGroup to draw its children using their drawing cache. This property
4758 * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
4759 * will be used only if it has been enabled.
4760 *
4761 * Subclasses should call this method to start and stop using the drawing cache when
4762 * they perform performance sensitive operations, like scrolling or animating.
4763 *
4764 * @param enabled true if children should be drawn with their cache, false otherwise
4765 *
4766 * @see #setAlwaysDrawnWithCacheEnabled(boolean)
4767 * @see #isChildrenDrawnWithCacheEnabled()
4768 */
4769 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
4770 setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
4771 }
4772
Romain Guy293451e2009-11-04 13:59:48 -08004773 /**
4774 * Indicates whether the ViewGroup is drawing its children in the order defined by
4775 * {@link #getChildDrawingOrder(int, int)}.
4776 *
4777 * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
4778 * false otherwise
4779 *
4780 * @see #setChildrenDrawingOrderEnabled(boolean)
4781 * @see #getChildDrawingOrder(int, int)
4782 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004783 @ViewDebug.ExportedProperty(category = "drawing")
Romain Guy293451e2009-11-04 13:59:48 -08004784 protected boolean isChildrenDrawingOrderEnabled() {
4785 return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
4786 }
4787
4788 /**
4789 * Tells the ViewGroup whether to draw its children in the order defined by the method
4790 * {@link #getChildDrawingOrder(int, int)}.
4791 *
4792 * @param enabled true if the order of the children when drawing is determined by
4793 * {@link #getChildDrawingOrder(int, int)}, false otherwise
4794 *
4795 * @see #isChildrenDrawingOrderEnabled()
4796 * @see #getChildDrawingOrder(int, int)
4797 */
4798 protected void setChildrenDrawingOrderEnabled(boolean enabled) {
4799 setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
4800 }
4801
Svetoslav6254f482013-06-04 17:22:14 -07004802 private boolean hasBooleanFlag(int flag) {
Philip Milnef091b662013-02-27 11:15:21 -08004803 return (mGroupFlags & flag) == flag;
4804 }
4805
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004806 private void setBooleanFlag(int flag, boolean value) {
4807 if (value) {
4808 mGroupFlags |= flag;
4809 } else {
4810 mGroupFlags &= ~flag;
4811 }
4812 }
4813
4814 /**
4815 * Returns an integer indicating what types of drawing caches are kept in memory.
4816 *
4817 * @see #setPersistentDrawingCache(int)
4818 * @see #setAnimationCacheEnabled(boolean)
4819 *
4820 * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
4821 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
4822 * and {@link #PERSISTENT_ALL_CACHES}
4823 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07004824 @ViewDebug.ExportedProperty(category = "drawing", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004825 @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
Romain Guy203688c2010-05-12 15:41:32 -07004826 @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004827 @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
4828 @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL")
4829 })
4830 public int getPersistentDrawingCache() {
4831 return mPersistentDrawingCache;
4832 }
4833
4834 /**
4835 * Indicates what types of drawing caches should be kept in memory after
4836 * they have been created.
4837 *
4838 * @see #getPersistentDrawingCache()
4839 * @see #setAnimationCacheEnabled(boolean)
4840 *
4841 * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
4842 * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
4843 * and {@link #PERSISTENT_ALL_CACHES}
4844 */
4845 public void setPersistentDrawingCache(int drawingCacheToKeep) {
4846 mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
4847 }
4848
Philip Milnef091b662013-02-27 11:15:21 -08004849 private void setLayoutMode(int layoutMode, boolean explicitly) {
4850 mLayoutMode = layoutMode;
4851 setBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET, explicitly);
4852 }
4853
4854 /**
4855 * Recursively traverse the view hierarchy, resetting the layoutMode of any
4856 * descendants that had inherited a different layoutMode from a previous parent.
4857 * Recursion terminates when a descendant's mode is:
4858 * <ul>
4859 * <li>Undefined</li>
4860 * <li>The same as the root node's</li>
4861 * <li>A mode that had been explicitly set</li>
4862 * <ul/>
4863 * The first two clauses are optimizations.
4864 * @param layoutModeOfRoot
4865 */
4866 @Override
4867 void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
4868 if (mLayoutMode == LAYOUT_MODE_UNDEFINED ||
4869 mLayoutMode == layoutModeOfRoot ||
Svetoslav6254f482013-06-04 17:22:14 -07004870 hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
Philip Milnef091b662013-02-27 11:15:21 -08004871 return;
4872 }
4873 setLayoutMode(LAYOUT_MODE_UNDEFINED, false);
4874
4875 // apply recursively
4876 for (int i = 0, N = getChildCount(); i < N; i++) {
4877 getChildAt(i).invalidateInheritedLayoutMode(layoutModeOfRoot);
4878 }
4879 }
4880
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004881 /**
Philip Milnecfb631b2012-10-26 10:51:46 -07004882 * Returns the basis of alignment during layout operations on this ViewGroup:
Philip Milne7b757812012-09-19 18:13:44 -07004883 * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
Philip Milnecfb631b2012-10-26 10:51:46 -07004884 * <p>
4885 * If no layoutMode was explicitly set, either programmatically or in an XML resource,
4886 * the method returns the layoutMode of the view's parent ViewGroup if such a parent exists,
4887 * otherwise the method returns a default value of {@link #LAYOUT_MODE_CLIP_BOUNDS}.
Philip Milne1557fd72012-04-04 23:41:34 -07004888 *
Philip Milnefcc6a0f2012-04-16 16:12:19 -07004889 * @return the layout mode to use during layout operations
Philip Milne1557fd72012-04-04 23:41:34 -07004890 *
4891 * @see #setLayoutMode(int)
4892 */
4893 public int getLayoutMode() {
Philip Milnecfb631b2012-10-26 10:51:46 -07004894 if (mLayoutMode == LAYOUT_MODE_UNDEFINED) {
Philip Milnef091b662013-02-27 11:15:21 -08004895 int inheritedLayoutMode = (mParent instanceof ViewGroup) ?
4896 ((ViewGroup) mParent).getLayoutMode() : LAYOUT_MODE_DEFAULT;
4897 setLayoutMode(inheritedLayoutMode, false);
Philip Milnecfb631b2012-10-26 10:51:46 -07004898 }
Philip Milne1557fd72012-04-04 23:41:34 -07004899 return mLayoutMode;
4900 }
4901
4902 /**
Philip Milnecfb631b2012-10-26 10:51:46 -07004903 * Sets the basis of alignment during the layout of this ViewGroup.
Philip Milne7b757812012-09-19 18:13:44 -07004904 * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or
4905 * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
Philip Milne1557fd72012-04-04 23:41:34 -07004906 *
Philip Milnefcc6a0f2012-04-16 16:12:19 -07004907 * @param layoutMode the layout mode to use during layout operations
Philip Milne1557fd72012-04-04 23:41:34 -07004908 *
4909 * @see #getLayoutMode()
Scott Main27a85082013-06-10 10:39:48 -07004910 * @attr ref android.R.styleable#ViewGroup_layoutMode
Philip Milne1557fd72012-04-04 23:41:34 -07004911 */
4912 public void setLayoutMode(int layoutMode) {
4913 if (mLayoutMode != layoutMode) {
Philip Milnef091b662013-02-27 11:15:21 -08004914 invalidateInheritedLayoutMode(layoutMode);
4915 setLayoutMode(layoutMode, layoutMode != LAYOUT_MODE_UNDEFINED);
Philip Milne1557fd72012-04-04 23:41:34 -07004916 requestLayout();
4917 }
4918 }
4919
4920 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004921 * Returns a new set of layout parameters based on the supplied attributes set.
4922 *
4923 * @param attrs the attributes to build the layout parameters from
4924 *
4925 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
4926 * of its descendants
4927 */
4928 public LayoutParams generateLayoutParams(AttributeSet attrs) {
4929 return new LayoutParams(getContext(), attrs);
4930 }
4931
4932 /**
4933 * Returns a safe set of layout parameters based on the supplied layout params.
4934 * When a ViewGroup is passed a View whose layout params do not pass the test of
4935 * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
4936 * is invoked. This method should return a new set of layout params suitable for
4937 * this ViewGroup, possibly by copying the appropriate attributes from the
4938 * specified set of layout params.
4939 *
4940 * @param p The layout parameters to convert into a suitable set of layout parameters
4941 * for this ViewGroup.
4942 *
4943 * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
4944 * of its descendants
4945 */
4946 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
4947 return p;
4948 }
4949
4950 /**
4951 * Returns a set of default layout parameters. These parameters are requested
4952 * when the View passed to {@link #addView(View)} has no layout parameters
4953 * already set. If null is returned, an exception is thrown from addView.
4954 *
4955 * @return a set of default layout parameters or null
4956 */
4957 protected LayoutParams generateDefaultLayoutParams() {
4958 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
4959 }
4960
4961 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004962 * {@inheritDoc}
4963 */
4964 @Override
4965 protected void debug(int depth) {
4966 super.debug(depth);
4967 String output;
4968
4969 if (mFocused != null) {
4970 output = debugIndent(depth);
4971 output += "mFocused";
4972 Log.d(VIEW_LOG_TAG, output);
4973 }
4974 if (mChildrenCount != 0) {
4975 output = debugIndent(depth);
4976 output += "{";
4977 Log.d(VIEW_LOG_TAG, output);
4978 }
4979 int count = mChildrenCount;
4980 for (int i = 0; i < count; i++) {
4981 View child = mChildren[i];
4982 child.debug(depth + 1);
4983 }
4984
4985 if (mChildrenCount != 0) {
4986 output = debugIndent(depth);
4987 output += "}";
4988 Log.d(VIEW_LOG_TAG, output);
4989 }
4990 }
4991
4992 /**
4993 * Returns the position in the group of the specified child view.
4994 *
4995 * @param child the view for which to get the position
4996 * @return a positive integer representing the position of the view in the
4997 * group, or -1 if the view does not exist in the group
4998 */
4999 public int indexOfChild(View child) {
5000 final int count = mChildrenCount;
5001 final View[] children = mChildren;
5002 for (int i = 0; i < count; i++) {
5003 if (children[i] == child) {
5004 return i;
5005 }
5006 }
5007 return -1;
5008 }
5009
5010 /**
5011 * Returns the number of children in the group.
5012 *
5013 * @return a positive integer representing the number of children in
5014 * the group
5015 */
5016 public int getChildCount() {
5017 return mChildrenCount;
5018 }
5019
5020 /**
5021 * Returns the view at the specified position in the group.
5022 *
5023 * @param index the position at which to get the view from
5024 * @return the view at the specified position or null if the position
5025 * does not exist within the group
5026 */
5027 public View getChildAt(int index) {
Adam Powell3ba8f5d62011-03-07 15:36:33 -08005028 if (index < 0 || index >= mChildrenCount) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005029 return null;
5030 }
Adam Powell3ba8f5d62011-03-07 15:36:33 -08005031 return mChildren[index];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005032 }
5033
5034 /**
5035 * Ask all of the children of this view to measure themselves, taking into
5036 * account both the MeasureSpec requirements for this view and its padding.
5037 * We skip children that are in the GONE state The heavy lifting is done in
5038 * getChildMeasureSpec.
5039 *
5040 * @param widthMeasureSpec The width requirements for this view
5041 * @param heightMeasureSpec The height requirements for this view
5042 */
5043 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
5044 final int size = mChildrenCount;
5045 final View[] children = mChildren;
5046 for (int i = 0; i < size; ++i) {
5047 final View child = children[i];
5048 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
5049 measureChild(child, widthMeasureSpec, heightMeasureSpec);
5050 }
5051 }
5052 }
5053
5054 /**
5055 * Ask one of the children of this view to measure itself, taking into
5056 * account both the MeasureSpec requirements for this view and its padding.
5057 * The heavy lifting is done in getChildMeasureSpec.
5058 *
5059 * @param child The child to measure
5060 * @param parentWidthMeasureSpec The width requirements for this view
5061 * @param parentHeightMeasureSpec The height requirements for this view
5062 */
5063 protected void measureChild(View child, int parentWidthMeasureSpec,
5064 int parentHeightMeasureSpec) {
5065 final LayoutParams lp = child.getLayoutParams();
5066
5067 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
5068 mPaddingLeft + mPaddingRight, lp.width);
5069 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
5070 mPaddingTop + mPaddingBottom, lp.height);
5071
5072 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5073 }
5074
5075 /**
5076 * Ask one of the children of this view to measure itself, taking into
5077 * account both the MeasureSpec requirements for this view and its padding
5078 * and margins. The child must have MarginLayoutParams The heavy lifting is
5079 * done in getChildMeasureSpec.
5080 *
5081 * @param child The child to measure
5082 * @param parentWidthMeasureSpec The width requirements for this view
5083 * @param widthUsed Extra space that has been used up by the parent
5084 * horizontally (possibly by other children of the parent)
5085 * @param parentHeightMeasureSpec The height requirements for this view
5086 * @param heightUsed Extra space that has been used up by the parent
5087 * vertically (possibly by other children of the parent)
5088 */
5089 protected void measureChildWithMargins(View child,
5090 int parentWidthMeasureSpec, int widthUsed,
5091 int parentHeightMeasureSpec, int heightUsed) {
5092 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
5093
5094 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
5095 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
5096 + widthUsed, lp.width);
5097 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
5098 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
5099 + heightUsed, lp.height);
5100
5101 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5102 }
5103
5104 /**
5105 * Does the hard part of measureChildren: figuring out the MeasureSpec to
5106 * pass to a particular child. This method figures out the right MeasureSpec
5107 * for one dimension (height or width) of one child view.
5108 *
5109 * The goal is to combine information from our MeasureSpec with the
5110 * LayoutParams of the child to get the best possible results. For example,
5111 * if the this view knows its size (because its MeasureSpec has a mode of
5112 * EXACTLY), and the child has indicated in its LayoutParams that it wants
5113 * to be the same size as the parent, the parent should ask the child to
5114 * layout given an exact size.
5115 *
5116 * @param spec The requirements for this view
5117 * @param padding The padding of this view for the current dimension and
5118 * margins, if applicable
5119 * @param childDimension How big the child wants to be in the current
5120 * dimension
5121 * @return a MeasureSpec integer for the child
5122 */
5123 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
5124 int specMode = MeasureSpec.getMode(spec);
5125 int specSize = MeasureSpec.getSize(spec);
5126
5127 int size = Math.max(0, specSize - padding);
5128
5129 int resultSize = 0;
5130 int resultMode = 0;
5131
5132 switch (specMode) {
5133 // Parent has imposed an exact size on us
5134 case MeasureSpec.EXACTLY:
5135 if (childDimension >= 0) {
5136 resultSize = childDimension;
5137 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08005138 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005139 // Child wants to be our size. So be it.
5140 resultSize = size;
5141 resultMode = MeasureSpec.EXACTLY;
5142 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
5143 // Child wants to determine its own size. It can't be
5144 // bigger than us.
5145 resultSize = size;
5146 resultMode = MeasureSpec.AT_MOST;
5147 }
5148 break;
5149
5150 // Parent has imposed a maximum size on us
5151 case MeasureSpec.AT_MOST:
5152 if (childDimension >= 0) {
5153 // Child wants a specific size... so be it
5154 resultSize = childDimension;
5155 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08005156 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005157 // Child wants to be our size, but our size is not fixed.
5158 // Constrain child to not be bigger than us.
5159 resultSize = size;
5160 resultMode = MeasureSpec.AT_MOST;
5161 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
5162 // Child wants to determine its own size. It can't be
5163 // bigger than us.
5164 resultSize = size;
5165 resultMode = MeasureSpec.AT_MOST;
5166 }
5167 break;
5168
5169 // Parent asked to see how big we want to be
5170 case MeasureSpec.UNSPECIFIED:
5171 if (childDimension >= 0) {
5172 // Child wants a specific size... let him have it
5173 resultSize = childDimension;
5174 resultMode = MeasureSpec.EXACTLY;
Romain Guy980a9382010-01-08 15:06:28 -08005175 } else if (childDimension == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005176 // Child wants to be our size... find out how big it should
5177 // be
5178 resultSize = 0;
5179 resultMode = MeasureSpec.UNSPECIFIED;
5180 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
5181 // Child wants to determine its own size.... find out how
5182 // big it should be
5183 resultSize = 0;
5184 resultMode = MeasureSpec.UNSPECIFIED;
5185 }
5186 break;
5187 }
5188 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
5189 }
5190
5191
5192 /**
5193 * Removes any pending animations for views that have been removed. Call
5194 * this if you don't want animations for exiting views to stack up.
5195 */
5196 public void clearDisappearingChildren() {
5197 if (mDisappearingChildren != null) {
5198 mDisappearingChildren.clear();
Chet Haaseb85967b2012-03-26 14:37:51 -07005199 invalidate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005200 }
5201 }
5202
5203 /**
5204 * Add a view which is removed from mChildren but still needs animation
5205 *
5206 * @param v View to add
5207 */
5208 private void addDisappearingView(View v) {
5209 ArrayList<View> disappearingChildren = mDisappearingChildren;
5210
5211 if (disappearingChildren == null) {
5212 disappearingChildren = mDisappearingChildren = new ArrayList<View>();
5213 }
5214
5215 disappearingChildren.add(v);
5216 }
5217
5218 /**
5219 * Cleanup a view when its animation is done. This may mean removing it from
5220 * the list of disappearing views.
5221 *
5222 * @param view The view whose animation has finished
5223 * @param animation The animation, cannot be null
5224 */
Chet Haase64a48c12012-02-13 16:33:29 -08005225 void finishAnimatingView(final View view, Animation animation) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005226 final ArrayList<View> disappearingChildren = mDisappearingChildren;
5227 if (disappearingChildren != null) {
5228 if (disappearingChildren.contains(view)) {
5229 disappearingChildren.remove(view);
5230
5231 if (view.mAttachInfo != null) {
5232 view.dispatchDetachedFromWindow();
5233 }
5234
5235 view.clearAnimation();
5236 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
5237 }
5238 }
5239
5240 if (animation != null && !animation.getFillAfter()) {
5241 view.clearAnimation();
5242 }
5243
Dianne Hackborn4702a852012-08-17 15:18:29 -07005244 if ((view.mPrivateFlags & PFLAG_ANIMATION_STARTED) == PFLAG_ANIMATION_STARTED) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005245 view.onAnimationEnd();
5246 // Should be performed by onAnimationEnd() but this avoid an infinite loop,
5247 // so we'd rather be safe than sorry
Dianne Hackborn4702a852012-08-17 15:18:29 -07005248 view.mPrivateFlags &= ~PFLAG_ANIMATION_STARTED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005249 // Draw one more frame after the animation is done
5250 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
5251 }
5252 }
5253
Chet Haaseb20db3e2010-09-10 13:07:30 -07005254 /**
Chet Haaseaceafe62011-08-26 15:44:33 -07005255 * Utility function called by View during invalidation to determine whether a view that
5256 * is invisible or gone should still be invalidated because it is being transitioned (and
5257 * therefore still needs to be drawn).
5258 */
5259 boolean isViewTransitioning(View view) {
5260 return (mTransitioningViews != null && mTransitioningViews.contains(view));
5261 }
5262
5263 /**
Chet Haaseb20db3e2010-09-10 13:07:30 -07005264 * This method tells the ViewGroup that the given View object, which should have this
5265 * ViewGroup as its parent,
5266 * should be kept around (re-displayed when the ViewGroup draws its children) even if it
5267 * is removed from its parent. This allows animations, such as those used by
5268 * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
5269 * the removal of views. A call to this method should always be accompanied by a later call
5270 * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
5271 * so that the View finally gets removed.
5272 *
5273 * @param view The View object to be kept visible even if it gets removed from its parent.
5274 */
5275 public void startViewTransition(View view) {
5276 if (view.mParent == this) {
5277 if (mTransitioningViews == null) {
5278 mTransitioningViews = new ArrayList<View>();
5279 }
5280 mTransitioningViews.add(view);
5281 }
5282 }
5283
5284 /**
5285 * This method should always be called following an earlier call to
5286 * {@link #startViewTransition(View)}. The given View is finally removed from its parent
5287 * and will no longer be displayed. Note that this method does not perform the functionality
5288 * of removing a view from its parent; it just discontinues the display of a View that
5289 * has previously been removed.
5290 *
5291 * @return view The View object that has been removed but is being kept around in the visible
5292 * hierarchy by an earlier call to {@link #startViewTransition(View)}.
5293 */
5294 public void endViewTransition(View view) {
5295 if (mTransitioningViews != null) {
5296 mTransitioningViews.remove(view);
5297 final ArrayList<View> disappearingChildren = mDisappearingChildren;
5298 if (disappearingChildren != null && disappearingChildren.contains(view)) {
5299 disappearingChildren.remove(view);
Chet Haase5e25c2c2010-09-16 11:15:56 -07005300 if (mVisibilityChangingChildren != null &&
5301 mVisibilityChangingChildren.contains(view)) {
5302 mVisibilityChangingChildren.remove(view);
5303 } else {
5304 if (view.mAttachInfo != null) {
5305 view.dispatchDetachedFromWindow();
5306 }
5307 if (view.mParent != null) {
5308 view.mParent = null;
5309 }
Chet Haaseb20db3e2010-09-10 13:07:30 -07005310 }
Chet Haaseb85967b2012-03-26 14:37:51 -07005311 invalidate();
Chet Haaseb20db3e2010-09-10 13:07:30 -07005312 }
5313 }
5314 }
5315
Chet Haase21cd1382010-09-01 17:42:29 -07005316 private LayoutTransition.TransitionListener mLayoutTransitionListener =
5317 new LayoutTransition.TransitionListener() {
5318 @Override
5319 public void startTransition(LayoutTransition transition, ViewGroup container,
5320 View view, int transitionType) {
5321 // We only care about disappearing items, since we need special logic to keep
5322 // those items visible after they've been 'removed'
5323 if (transitionType == LayoutTransition.DISAPPEARING) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07005324 startViewTransition(view);
Chet Haase21cd1382010-09-01 17:42:29 -07005325 }
5326 }
5327
5328 @Override
5329 public void endTransition(LayoutTransition transition, ViewGroup container,
5330 View view, int transitionType) {
Chet Haaseb9895022013-04-02 15:10:58 -07005331 if (mLayoutCalledWhileSuppressed && !transition.isChangingLayout()) {
Chet Haase9c087442011-01-12 16:20:16 -08005332 requestLayout();
Chet Haaseb9895022013-04-02 15:10:58 -07005333 mLayoutCalledWhileSuppressed = false;
Chet Haase9c087442011-01-12 16:20:16 -08005334 }
Chet Haase21cd1382010-09-01 17:42:29 -07005335 if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
Chet Haaseb20db3e2010-09-10 13:07:30 -07005336 endViewTransition(view);
Chet Haase21cd1382010-09-01 17:42:29 -07005337 }
5338 }
5339 };
5340
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005341 /**
Chet Haaseb9895022013-04-02 15:10:58 -07005342 * Tells this ViewGroup to suppress all layout() calls until layout
5343 * suppression is disabled with a later call to suppressLayout(false).
5344 * When layout suppression is disabled, a requestLayout() call is sent
5345 * if layout() was attempted while layout was being suppressed.
5346 *
5347 * @hide
5348 */
5349 public void suppressLayout(boolean suppress) {
5350 mSuppressLayout = suppress;
5351 if (!suppress) {
5352 if (mLayoutCalledWhileSuppressed) {
5353 requestLayout();
5354 mLayoutCalledWhileSuppressed = false;
5355 }
5356 }
5357 }
5358
5359 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005360 * {@inheritDoc}
5361 */
5362 @Override
5363 public boolean gatherTransparentRegion(Region region) {
5364 // If no transparent regions requested, we are always opaque.
Dianne Hackborn4702a852012-08-17 15:18:29 -07005365 final boolean meOpaque = (mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005366 if (meOpaque && region == null) {
5367 // The caller doesn't care about the region, so stop now.
5368 return true;
5369 }
5370 super.gatherTransparentRegion(region);
5371 final View[] children = mChildren;
5372 final int count = mChildrenCount;
5373 boolean noneOfTheChildrenAreTransparent = true;
5374 for (int i = 0; i < count; i++) {
5375 final View child = children[i];
Mathias Agopiane3381152010-12-02 15:19:36 -08005376 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005377 if (!child.gatherTransparentRegion(region)) {
5378 noneOfTheChildrenAreTransparent = false;
5379 }
5380 }
5381 }
5382 return meOpaque || noneOfTheChildrenAreTransparent;
5383 }
5384
5385 /**
5386 * {@inheritDoc}
5387 */
5388 public void requestTransparentRegion(View child) {
5389 if (child != null) {
Dianne Hackborn4702a852012-08-17 15:18:29 -07005390 child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005391 if (mParent != null) {
5392 mParent.requestTransparentRegion(this);
5393 }
5394 }
5395 }
Romain Guy8506ab42009-06-11 17:35:47 -07005396
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005397
5398 @Override
5399 protected boolean fitSystemWindows(Rect insets) {
5400 boolean done = super.fitSystemWindows(insets);
5401 if (!done) {
5402 final int count = mChildrenCount;
5403 final View[] children = mChildren;
5404 for (int i = 0; i < count; i++) {
5405 done = children[i].fitSystemWindows(insets);
5406 if (done) {
5407 break;
5408 }
5409 }
5410 }
5411 return done;
5412 }
5413
5414 /**
5415 * Returns the animation listener to which layout animation events are
5416 * sent.
5417 *
5418 * @return an {@link android.view.animation.Animation.AnimationListener}
5419 */
5420 public Animation.AnimationListener getLayoutAnimationListener() {
5421 return mAnimationListener;
5422 }
5423
5424 @Override
5425 protected void drawableStateChanged() {
5426 super.drawableStateChanged();
5427
5428 if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
5429 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
5430 throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
5431 + " child has duplicateParentState set to true");
5432 }
5433
5434 final View[] children = mChildren;
5435 final int count = mChildrenCount;
5436
5437 for (int i = 0; i < count; i++) {
5438 final View child = children[i];
5439 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
5440 child.refreshDrawableState();
5441 }
5442 }
5443 }
5444 }
5445
5446 @Override
Dianne Hackborne2136772010-11-04 15:08:59 -07005447 public void jumpDrawablesToCurrentState() {
5448 super.jumpDrawablesToCurrentState();
5449 final View[] children = mChildren;
5450 final int count = mChildrenCount;
5451 for (int i = 0; i < count; i++) {
5452 children[i].jumpDrawablesToCurrentState();
5453 }
5454 }
5455
5456 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005457 protected int[] onCreateDrawableState(int extraSpace) {
5458 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
5459 return super.onCreateDrawableState(extraSpace);
5460 }
5461
5462 int need = 0;
5463 int n = getChildCount();
5464 for (int i = 0; i < n; i++) {
5465 int[] childState = getChildAt(i).getDrawableState();
5466
5467 if (childState != null) {
5468 need += childState.length;
5469 }
5470 }
5471
5472 int[] state = super.onCreateDrawableState(extraSpace + need);
5473
5474 for (int i = 0; i < n; i++) {
5475 int[] childState = getChildAt(i).getDrawableState();
5476
5477 if (childState != null) {
5478 state = mergeDrawableStates(state, childState);
5479 }
5480 }
5481
5482 return state;
5483 }
5484
5485 /**
5486 * Sets whether this ViewGroup's drawable states also include
5487 * its children's drawable states. This is used, for example, to
5488 * make a group appear to be focused when its child EditText or button
5489 * is focused.
5490 */
5491 public void setAddStatesFromChildren(boolean addsStates) {
5492 if (addsStates) {
5493 mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
5494 } else {
5495 mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
5496 }
5497
5498 refreshDrawableState();
5499 }
5500
5501 /**
5502 * Returns whether this ViewGroup's drawable states also include
5503 * its children's drawable states. This is used, for example, to
5504 * make a group appear to be focused when its child EditText or button
5505 * is focused.
5506 */
5507 public boolean addStatesFromChildren() {
5508 return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
5509 }
5510
5511 /**
Jeff Smitha45746e2012-07-19 14:19:24 -05005512 * If {@link #addStatesFromChildren} is true, refreshes this group's
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005513 * drawable state (to include the states from its children).
5514 */
5515 public void childDrawableStateChanged(View child) {
5516 if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
5517 refreshDrawableState();
5518 }
5519 }
5520
5521 /**
5522 * Specifies the animation listener to which layout animation events must
5523 * be sent. Only
5524 * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
5525 * and
5526 * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
5527 * are invoked.
5528 *
5529 * @param animationListener the layout animation listener
5530 */
5531 public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
5532 mAnimationListener = animationListener;
5533 }
5534
5535 /**
Chet Haasecca2c982011-05-20 14:34:18 -07005536 * This method is called by LayoutTransition when there are 'changing' animations that need
5537 * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
5538 * starts all pending transitions prior to the drawing phase in the current traversal.
5539 *
5540 * @param transition The LayoutTransition to be started on the next traversal.
5541 *
5542 * @hide
5543 */
5544 public void requestTransitionStart(LayoutTransition transition) {
Dianne Hackborn6dd005b2011-07-18 13:22:50 -07005545 ViewRootImpl viewAncestor = getViewRootImpl();
Chet Haase1abf7fa2011-08-17 18:31:56 -07005546 if (viewAncestor != null) {
5547 viewAncestor.requestTransitionStart(transition);
5548 }
Chet Haasecca2c982011-05-20 14:34:18 -07005549 }
5550
Fabrice Di Meglio4457e852012-09-18 19:23:12 -07005551 /**
5552 * @hide
5553 */
Fabrice Di Meglio80dc53d2011-06-21 18:36:33 -07005554 @Override
Fabrice Di Meglio09ecb252013-05-03 16:51:55 -07005555 public boolean resolveRtlPropertiesIfNeeded() {
5556 final boolean result = super.resolveRtlPropertiesIfNeeded();
5557 // We dont need to resolve the children RTL properties if nothing has changed for the parent
5558 if (result) {
5559 int count = getChildCount();
5560 for (int i = 0; i < count; i++) {
5561 final View child = getChildAt(i);
5562 if (child.isLayoutDirectionInherited()) {
5563 child.resolveRtlPropertiesIfNeeded();
5564 }
Fabrice Di Meglio84ebb352012-10-11 16:27:37 -07005565 }
5566 }
Fabrice Di Meglio09ecb252013-05-03 16:51:55 -07005567 return result;
Fabrice Di Meglio84ebb352012-10-11 16:27:37 -07005568 }
5569
5570 /**
5571 * @hide
5572 */
5573 @Override
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07005574 public boolean resolveLayoutDirection() {
5575 final boolean result = super.resolveLayoutDirection();
5576 if (result) {
5577 int count = getChildCount();
5578 for (int i = 0; i < count; i++) {
5579 final View child = getChildAt(i);
5580 if (child.isLayoutDirectionInherited()) {
5581 child.resolveLayoutDirection();
5582 }
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005583 }
5584 }
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07005585 return result;
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005586 }
5587
5588 /**
5589 * @hide
5590 */
5591 @Override
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07005592 public boolean resolveTextDirection() {
5593 final boolean result = super.resolveTextDirection();
5594 if (result) {
5595 int count = getChildCount();
5596 for (int i = 0; i < count; i++) {
5597 final View child = getChildAt(i);
5598 if (child.isTextDirectionInherited()) {
5599 child.resolveTextDirection();
5600 }
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005601 }
5602 }
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07005603 return result;
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005604 }
5605
5606 /**
5607 * @hide
5608 */
5609 @Override
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07005610 public boolean resolveTextAlignment() {
5611 final boolean result = super.resolveTextAlignment();
5612 if (result) {
5613 int count = getChildCount();
5614 for (int i = 0; i < count; i++) {
5615 final View child = getChildAt(i);
5616 if (child.isTextAlignmentInherited()) {
5617 child.resolveTextAlignment();
5618 }
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005619 }
5620 }
Fabrice Di Meglio9a048562012-09-26 14:55:56 -07005621 return result;
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005622 }
5623
5624 /**
5625 * @hide
5626 */
5627 @Override
Fabrice Di Meglio84ebb352012-10-11 16:27:37 -07005628 public void resolvePadding() {
5629 super.resolvePadding();
5630 int count = getChildCount();
5631 for (int i = 0; i < count; i++) {
5632 final View child = getChildAt(i);
5633 if (child.isLayoutDirectionInherited()) {
5634 child.resolvePadding();
5635 }
5636 }
5637 }
5638
5639 /**
5640 * @hide
5641 */
5642 @Override
5643 protected void resolveDrawables() {
5644 super.resolveDrawables();
5645 int count = getChildCount();
5646 for (int i = 0; i < count; i++) {
5647 final View child = getChildAt(i);
5648 if (child.isLayoutDirectionInherited()) {
5649 child.resolveDrawables();
5650 }
5651 }
5652 }
5653
Fabrice Di Meglio1e0ed6b2012-10-18 16:06:52 -07005654 /**
5655 * @hide
5656 */
Fabrice Di Megliofcc33482012-10-18 11:11:51 -07005657 @Override
5658 public void resolveLayoutParams() {
5659 super.resolveLayoutParams();
5660 int count = getChildCount();
5661 for (int i = 0; i < count; i++) {
5662 final View child = getChildAt(i);
5663 child.resolveLayoutParams();
5664 }
5665 }
5666
Fabrice Di Meglio84ebb352012-10-11 16:27:37 -07005667 /**
5668 * @hide
5669 */
5670 @Override
Fabrice Di Meglio4457e852012-09-18 19:23:12 -07005671 public void resetResolvedLayoutDirection() {
5672 super.resetResolvedLayoutDirection();
5673
Fabrice Di Meglio4457e852012-09-18 19:23:12 -07005674 int count = getChildCount();
Fabrice Di Meglio80dc53d2011-06-21 18:36:33 -07005675 for (int i = 0; i < count; i++) {
5676 final View child = getChildAt(i);
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -07005677 if (child.isLayoutDirectionInherited()) {
Fabrice Di Meglio7f86c802011-07-01 15:09:24 -07005678 child.resetResolvedLayoutDirection();
Fabrice Di Meglio80dc53d2011-06-21 18:36:33 -07005679 }
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005680 }
5681 }
5682
5683 /**
5684 * @hide
5685 */
5686 @Override
5687 public void resetResolvedTextDirection() {
5688 super.resetResolvedTextDirection();
5689
5690 int count = getChildCount();
5691 for (int i = 0; i < count; i++) {
5692 final View child = getChildAt(i);
Fabrice Di Meglio97e146c2012-09-23 15:45:16 -07005693 if (child.isTextDirectionInherited()) {
Fabrice Di Meglio22268862011-06-27 18:13:18 -07005694 child.resetResolvedTextDirection();
5695 }
Fabrice Di Meglio1f88ba82012-09-24 14:56:49 -07005696 }
5697 }
5698
5699 /**
5700 * @hide
5701 */
5702 @Override
5703 public void resetResolvedTextAlignment() {
5704 super.resetResolvedTextAlignment();
5705
5706 int count = getChildCount();
5707 for (int i = 0; i < count; i++) {
5708 final View child = getChildAt(i);
Fabrice Di Meglio1a7d4872012-09-23 16:19:58 -07005709 if (child.isTextAlignmentInherited()) {
Fabrice Di Meglio9da0f8a2012-03-13 19:37:57 -07005710 child.resetResolvedTextAlignment();
5711 }
5712 }
5713 }
5714
Fabrice Di Meglio22268862011-06-27 18:13:18 -07005715 /**
Fabrice Di Meglio84ebb352012-10-11 16:27:37 -07005716 * @hide
5717 */
5718 @Override
5719 public void resetResolvedPadding() {
5720 super.resetResolvedPadding();
5721
5722 int count = getChildCount();
5723 for (int i = 0; i < count; i++) {
5724 final View child = getChildAt(i);
5725 if (child.isLayoutDirectionInherited()) {
5726 child.resetResolvedPadding();
5727 }
5728 }
5729 }
5730
5731 /**
5732 * @hide
5733 */
5734 @Override
5735 protected void resetResolvedDrawables() {
5736 super.resetResolvedDrawables();
5737
5738 int count = getChildCount();
5739 for (int i = 0; i < count; i++) {
5740 final View child = getChildAt(i);
5741 if (child.isLayoutDirectionInherited()) {
5742 child.resetResolvedDrawables();
5743 }
5744 }
5745 }
5746
5747 /**
Patrick Dubroye0a799a2011-05-04 16:19:22 -07005748 * Return true if the pressed state should be delayed for children or descendants of this
5749 * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
5750 * This prevents the pressed state from appearing when the user is actually trying to scroll
5751 * the content.
5752 *
5753 * The default implementation returns true for compatibility reasons. Subclasses that do
5754 * not scroll should generally override this method and return false.
5755 */
5756 public boolean shouldDelayChildPressedState() {
5757 return true;
5758 }
5759
Philip Milned7dd8902012-01-26 16:55:30 -08005760 /** @hide */
5761 protected void onSetLayoutParams(View child, LayoutParams layoutParams) {
5762 }
5763
Patrick Dubroye0a799a2011-05-04 16:19:22 -07005764 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005765 * LayoutParams are used by views to tell their parents how they want to be
5766 * laid out. See
5767 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
5768 * for a list of all child view attributes that this class supports.
Romain Guy8506ab42009-06-11 17:35:47 -07005769 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005770 * <p>
5771 * The base LayoutParams class just describes how big the view wants to be
5772 * for both width and height. For each dimension, it can specify one of:
5773 * <ul>
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005774 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
5775 * means that the view wants to be as big as its parent (minus padding)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005776 * <li> WRAP_CONTENT, which means that the view wants to be just big enough
5777 * to enclose its content (plus padding)
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005778 * <li> an exact number
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005779 * </ul>
5780 * There are subclasses of LayoutParams for different subclasses of
5781 * ViewGroup. For example, AbsoluteLayout has its own subclass of
Joe Fernandez558459f2011-10-13 16:47:36 -07005782 * LayoutParams which adds an X and Y value.</p>
5783 *
5784 * <div class="special reference">
5785 * <h3>Developer Guides</h3>
5786 * <p>For more information about creating user interface layouts, read the
5787 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
5788 * guide.</p></div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005789 *
5790 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
5791 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
5792 */
5793 public static class LayoutParams {
5794 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005795 * Special value for the height or width requested by a View.
5796 * FILL_PARENT means that the view wants to be as big as its parent,
5797 * minus the parent's padding, if any. This value is deprecated
5798 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005799 */
Romain Guy980a9382010-01-08 15:06:28 -08005800 @SuppressWarnings({"UnusedDeclaration"})
5801 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005802 public static final int FILL_PARENT = -1;
5803
5804 /**
5805 * Special value for the height or width requested by a View.
Gilles Debunnef5c6eff2010-02-09 19:08:36 -08005806 * MATCH_PARENT means that the view wants to be as big as its parent,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005807 * minus the parent's padding, if any. Introduced in API Level 8.
Romain Guy980a9382010-01-08 15:06:28 -08005808 */
5809 public static final int MATCH_PARENT = -1;
5810
5811 /**
5812 * Special value for the height or width requested by a View.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005813 * WRAP_CONTENT means that the view wants to be just large enough to fit
5814 * its own internal content, taking its own padding into account.
5815 */
5816 public static final int WRAP_CONTENT = -2;
5817
5818 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005819 * Information about how wide the view wants to be. Can be one of the
5820 * constants FILL_PARENT (replaced by MATCH_PARENT ,
5821 * in API Level 8) or WRAP_CONTENT. or an exact size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005822 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005823 @ViewDebug.ExportedProperty(category = "layout", mapping = {
Romain Guy980a9382010-01-08 15:06:28 -08005824 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005825 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
5826 })
5827 public int width;
5828
5829 /**
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005830 * Information about how tall the view wants to be. Can be one of the
5831 * constants FILL_PARENT (replaced by MATCH_PARENT ,
5832 * in API Level 8) or WRAP_CONTENT. or an exact size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005833 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005834 @ViewDebug.ExportedProperty(category = "layout", mapping = {
Romain Guy980a9382010-01-08 15:06:28 -08005835 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005836 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
5837 })
5838 public int height;
5839
5840 /**
5841 * Used to animate layouts.
5842 */
5843 public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
5844
5845 /**
5846 * Creates a new set of layout parameters. The values are extracted from
5847 * the supplied attributes set and context. The XML attributes mapped
5848 * to this set of layout parameters are:
5849 *
5850 * <ul>
5851 * <li><code>layout_width</code>: the width, either an exact value,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005852 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
5853 * {@link #MATCH_PARENT} in API Level 8)</li>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005854 * <li><code>layout_height</code>: the height, either an exact value,
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005855 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
5856 * {@link #MATCH_PARENT} in API Level 8)</li>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005857 * </ul>
5858 *
5859 * @param c the application environment
5860 * @param attrs the set of attributes from which to extract the layout
5861 * parameters' values
5862 */
5863 public LayoutParams(Context c, AttributeSet attrs) {
5864 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
5865 setBaseAttributes(a,
5866 R.styleable.ViewGroup_Layout_layout_width,
5867 R.styleable.ViewGroup_Layout_layout_height);
5868 a.recycle();
5869 }
5870
5871 /**
5872 * Creates a new set of layout parameters with the specified width
5873 * and height.
5874 *
Dirk Dougherty75c66da2010-03-25 16:33:33 -07005875 * @param width the width, either {@link #WRAP_CONTENT},
5876 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
5877 * API Level 8), or a fixed size in pixels
5878 * @param height the height, either {@link #WRAP_CONTENT},
5879 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
5880 * API Level 8), or a fixed size in pixels
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005881 */
5882 public LayoutParams(int width, int height) {
5883 this.width = width;
5884 this.height = height;
5885 }
5886
5887 /**
5888 * Copy constructor. Clones the width and height values of the source.
5889 *
5890 * @param source The layout params to copy from.
5891 */
5892 public LayoutParams(LayoutParams source) {
5893 this.width = source.width;
5894 this.height = source.height;
5895 }
5896
5897 /**
5898 * Used internally by MarginLayoutParams.
5899 * @hide
5900 */
5901 LayoutParams() {
5902 }
5903
5904 /**
Dave Burke579e1402012-10-18 20:41:55 -07005905 * Extracts the layout parameters from the supplied attributes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005906 *
5907 * @param a the style attributes to extract the parameters from
5908 * @param widthAttr the identifier of the width attribute
5909 * @param heightAttr the identifier of the height attribute
5910 */
5911 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
Dave Burke579e1402012-10-18 20:41:55 -07005912 width = a.getLayoutDimension(widthAttr, "layout_width");
5913 height = a.getLayoutDimension(heightAttr, "layout_height");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005914 }
5915
5916 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005917 * Resolve layout parameters depending on the layout direction. Subclasses that care about
5918 * layoutDirection changes should override this method. The default implementation does
5919 * nothing.
5920 *
5921 * @param layoutDirection the direction of the layout
5922 *
5923 * {@link View#LAYOUT_DIRECTION_LTR}
5924 * {@link View#LAYOUT_DIRECTION_RTL}
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005925 */
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07005926 public void resolveLayoutDirection(int layoutDirection) {
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07005927 }
5928
5929 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005930 * Returns a String representation of this set of layout parameters.
5931 *
5932 * @param output the String to prepend to the internal representation
5933 * @return a String with the following format: output +
5934 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
Romain Guy8506ab42009-06-11 17:35:47 -07005935 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005936 * @hide
5937 */
5938 public String debug(String output) {
5939 return output + "ViewGroup.LayoutParams={ width="
5940 + sizeToString(width) + ", height=" + sizeToString(height) + " }";
5941 }
5942
5943 /**
Philip Milne10ca24a2012-04-23 15:38:27 -07005944 * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
5945 *
5946 * @param view the view that contains these layout parameters
5947 * @param canvas the canvas on which to draw
5948 *
5949 * @hide
5950 */
Philip Milne7b757812012-09-19 18:13:44 -07005951 public void onDebugDraw(View view, Canvas canvas, Paint paint) {
Philip Milne10ca24a2012-04-23 15:38:27 -07005952 }
5953
5954 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005955 * Converts the specified size to a readable String.
5956 *
5957 * @param size the size to convert
5958 * @return a String instance representing the supplied size
Romain Guy8506ab42009-06-11 17:35:47 -07005959 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005960 * @hide
5961 */
5962 protected static String sizeToString(int size) {
5963 if (size == WRAP_CONTENT) {
5964 return "wrap-content";
5965 }
Romain Guy980a9382010-01-08 15:06:28 -08005966 if (size == MATCH_PARENT) {
5967 return "match-parent";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005968 }
5969 return String.valueOf(size);
5970 }
5971 }
5972
5973 /**
5974 * Per-child layout information for layouts that support margins.
5975 * See
5976 * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
5977 * for a list of all child view attributes that this class supports.
5978 */
5979 public static class MarginLayoutParams extends ViewGroup.LayoutParams {
5980 /**
Philip Milned7dd8902012-01-26 16:55:30 -08005981 * The left margin in pixels of the child.
5982 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
5983 * to this field.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005984 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005985 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005986 public int leftMargin;
5987
5988 /**
Philip Milned7dd8902012-01-26 16:55:30 -08005989 * The top margin in pixels of the child.
5990 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
5991 * to this field.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005992 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07005993 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005994 public int topMargin;
5995
5996 /**
Philip Milned7dd8902012-01-26 16:55:30 -08005997 * The right margin in pixels of the child.
5998 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
5999 * to this field.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006000 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07006001 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006002 public int rightMargin;
6003
6004 /**
Philip Milned7dd8902012-01-26 16:55:30 -08006005 * The bottom margin in pixels of the child.
6006 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6007 * to this field.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006008 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07006009 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006010 public int bottomMargin;
6011
6012 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006013 * The start margin in pixels of the child.
Fabrice Di Meglio54546f22012-02-14 16:26:16 -08006014 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6015 * to this field.
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006016 */
6017 @ViewDebug.ExportedProperty(category = "layout")
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006018 private int startMargin = DEFAULT_MARGIN_RELATIVE;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006019
6020 /**
6021 * The end margin in pixels of the child.
Fabrice Di Meglio54546f22012-02-14 16:26:16 -08006022 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6023 * to this field.
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006024 */
6025 @ViewDebug.ExportedProperty(category = "layout")
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006026 private int endMargin = DEFAULT_MARGIN_RELATIVE;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006027
6028 /**
6029 * The default start and end margin.
Fabrice Di Megliof443f982012-07-13 20:24:03 -07006030 * @hide
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006031 */
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006032 public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006033
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006034 /**
6035 * Bit 0: layout direction
6036 * Bit 1: layout direction
6037 * Bit 2: left margin undefined
6038 * Bit 3: right margin undefined
6039 * Bit 4: is RTL compatibility mode
6040 * Bit 5: need resolution
6041 *
6042 * Bit 6 to 7 not used
6043 *
6044 * @hide
6045 */
6046 @ViewDebug.ExportedProperty(category = "layout", flagMapping = {
6047 @ViewDebug.FlagToString(mask = LAYOUT_DIRECTION_MASK,
6048 equals = LAYOUT_DIRECTION_MASK, name = "LAYOUT_DIRECTION"),
6049 @ViewDebug.FlagToString(mask = LEFT_MARGIN_UNDEFINED_MASK,
6050 equals = LEFT_MARGIN_UNDEFINED_MASK, name = "LEFT_MARGIN_UNDEFINED_MASK"),
6051 @ViewDebug.FlagToString(mask = RIGHT_MARGIN_UNDEFINED_MASK,
6052 equals = RIGHT_MARGIN_UNDEFINED_MASK, name = "RIGHT_MARGIN_UNDEFINED_MASK"),
6053 @ViewDebug.FlagToString(mask = RTL_COMPATIBILITY_MODE_MASK,
6054 equals = RTL_COMPATIBILITY_MODE_MASK, name = "RTL_COMPATIBILITY_MODE_MASK"),
6055 @ViewDebug.FlagToString(mask = NEED_RESOLUTION_MASK,
6056 equals = NEED_RESOLUTION_MASK, name = "NEED_RESOLUTION_MASK")
6057 })
6058 byte mMarginFlags;
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07006059
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006060 private static final int LAYOUT_DIRECTION_MASK = 0x00000003;
6061 private static final int LEFT_MARGIN_UNDEFINED_MASK = 0x00000004;
6062 private static final int RIGHT_MARGIN_UNDEFINED_MASK = 0x00000008;
6063 private static final int RTL_COMPATIBILITY_MODE_MASK = 0x00000010;
6064 private static final int NEED_RESOLUTION_MASK = 0x00000020;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07006065
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006066 private static final int DEFAULT_MARGIN_RESOLVED = 0;
6067 private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE;
Fabrice Di Meglio02a7d562013-03-27 12:41:22 -07006068
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006069 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006070 * Creates a new set of layout parameters. The values are extracted from
6071 * the supplied attributes set and context.
6072 *
6073 * @param c the application environment
6074 * @param attrs the set of attributes from which to extract the layout
6075 * parameters' values
6076 */
6077 public MarginLayoutParams(Context c, AttributeSet attrs) {
6078 super();
6079
6080 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
6081 setBaseAttributes(a,
6082 R.styleable.ViewGroup_MarginLayout_layout_width,
6083 R.styleable.ViewGroup_MarginLayout_layout_height);
6084
6085 int margin = a.getDimensionPixelSize(
6086 com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
6087 if (margin >= 0) {
6088 leftMargin = margin;
6089 topMargin = margin;
6090 rightMargin= margin;
6091 bottomMargin = margin;
6092 } else {
6093 leftMargin = a.getDimensionPixelSize(
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006094 R.styleable.ViewGroup_MarginLayout_layout_marginLeft,
Fabrice Di Meglio02a7d562013-03-27 12:41:22 -07006095 UNDEFINED_MARGIN);
6096 if (leftMargin == UNDEFINED_MARGIN) {
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006097 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
Fabrice Di Meglio02a7d562013-03-27 12:41:22 -07006098 leftMargin = DEFAULT_MARGIN_RESOLVED;
6099 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006100 rightMargin = a.getDimensionPixelSize(
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006101 R.styleable.ViewGroup_MarginLayout_layout_marginRight,
Fabrice Di Meglio02a7d562013-03-27 12:41:22 -07006102 UNDEFINED_MARGIN);
6103 if (rightMargin == UNDEFINED_MARGIN) {
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006104 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
Fabrice Di Meglio02a7d562013-03-27 12:41:22 -07006105 rightMargin = DEFAULT_MARGIN_RESOLVED;
6106 }
6107
6108 topMargin = a.getDimensionPixelSize(
6109 R.styleable.ViewGroup_MarginLayout_layout_marginTop,
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006110 DEFAULT_MARGIN_RESOLVED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006111 bottomMargin = a.getDimensionPixelSize(
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006112 R.styleable.ViewGroup_MarginLayout_layout_marginBottom,
6113 DEFAULT_MARGIN_RESOLVED);
Fabrice Di Meglio02a7d562013-03-27 12:41:22 -07006114
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006115 startMargin = a.getDimensionPixelSize(
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006116 R.styleable.ViewGroup_MarginLayout_layout_marginStart,
6117 DEFAULT_MARGIN_RELATIVE);
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006118 endMargin = a.getDimensionPixelSize(
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006119 R.styleable.ViewGroup_MarginLayout_layout_marginEnd,
6120 DEFAULT_MARGIN_RELATIVE);
6121
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006122 if (isMarginRelative()) {
6123 mMarginFlags |= NEED_RESOLUTION_MASK;
6124 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006125 }
6126
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006127 final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport();
6128 final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006129 if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) {
6130 mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK;
6131 }
6132
6133 // Layout direction is LTR by default
6134 mMarginFlags |= LAYOUT_DIRECTION_LTR;
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07006135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006136 a.recycle();
6137 }
6138
6139 /**
6140 * {@inheritDoc}
6141 */
6142 public MarginLayoutParams(int width, int height) {
6143 super(width, height);
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006144
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006145 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
6146 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
Fabrice Di Meglio02a7d562013-03-27 12:41:22 -07006147
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006148 mMarginFlags &= ~NEED_RESOLUTION_MASK;
6149 mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006150 }
6151
6152 /**
6153 * Copy constructor. Clones the width, height and margin values of the source.
6154 *
6155 * @param source The layout params to copy from.
6156 */
6157 public MarginLayoutParams(MarginLayoutParams source) {
6158 this.width = source.width;
6159 this.height = source.height;
6160
6161 this.leftMargin = source.leftMargin;
6162 this.topMargin = source.topMargin;
6163 this.rightMargin = source.rightMargin;
6164 this.bottomMargin = source.bottomMargin;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006165 this.startMargin = source.startMargin;
6166 this.endMargin = source.endMargin;
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07006167
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006168 this.mMarginFlags = source.mMarginFlags;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006169 }
6170
6171 /**
6172 * {@inheritDoc}
6173 */
6174 public MarginLayoutParams(LayoutParams source) {
6175 super(source);
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006176
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006177 mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
6178 mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
Fabrice Di Meglio02a7d562013-03-27 12:41:22 -07006179
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006180 mMarginFlags &= ~NEED_RESOLUTION_MASK;
6181 mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006182 }
6183
6184 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006185 * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
6186 * to be done so that the new margins are taken into account. Left and right margins may be
6187 * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006188 *
6189 * @param left the left margin size
6190 * @param top the top margin size
6191 * @param right the right margin size
6192 * @param bottom the bottom margin size
6193 *
6194 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
6195 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
6196 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
6197 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
6198 */
6199 public void setMargins(int left, int top, int right, int bottom) {
6200 leftMargin = left;
6201 topMargin = top;
6202 rightMargin = right;
6203 bottomMargin = bottom;
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006204 mMarginFlags &= ~LEFT_MARGIN_UNDEFINED_MASK;
6205 mMarginFlags &= ~RIGHT_MARGIN_UNDEFINED_MASK;
6206 if (isMarginRelative()) {
6207 mMarginFlags |= NEED_RESOLUTION_MASK;
6208 } else {
6209 mMarginFlags &= ~NEED_RESOLUTION_MASK;
6210 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006211 }
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006212
6213 /**
6214 * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
6215 * needs to be done so that the new relative margins are taken into account. Left and right
6216 * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout
6217 * direction.
6218 *
6219 * @param start the start margin size
6220 * @param top the top margin size
6221 * @param end the right margin size
6222 * @param bottom the bottom margin size
6223 *
6224 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
6225 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
6226 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
6227 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
6228 *
6229 * @hide
6230 */
6231 public void setMarginsRelative(int start, int top, int end, int bottom) {
6232 startMargin = start;
6233 topMargin = top;
6234 endMargin = end;
6235 bottomMargin = bottom;
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006236 mMarginFlags |= NEED_RESOLUTION_MASK;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006237 }
6238
6239 /**
Fabrice Di Meglioa40627d2012-09-11 16:47:21 -07006240 * Sets the relative start margin.
6241 *
Fabrice Di Meglio61a21772012-09-12 16:33:13 -07006242 * @param start the start margin size
Fabrice Di Meglioa40627d2012-09-11 16:47:21 -07006243 *
6244 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
6245 */
6246 public void setMarginStart(int start) {
6247 startMargin = start;
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006248 mMarginFlags |= NEED_RESOLUTION_MASK;
Fabrice Di Meglioa40627d2012-09-11 16:47:21 -07006249 }
6250
6251 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006252 * Returns the start margin in pixels.
6253 *
6254 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
6255 *
6256 * @return the start margin in pixels.
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006257 */
6258 public int getMarginStart() {
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006259 if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin;
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006260 if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006261 doResolveMargins();
6262 }
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006263 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07006264 case View.LAYOUT_DIRECTION_RTL:
6265 return rightMargin;
6266 case View.LAYOUT_DIRECTION_LTR:
6267 default:
6268 return leftMargin;
6269 }
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006270 }
6271
6272 /**
Fabrice Di Meglioa40627d2012-09-11 16:47:21 -07006273 * Sets the relative end margin.
6274 *
Fabrice Di Meglio61a21772012-09-12 16:33:13 -07006275 * @param end the end margin size
Fabrice Di Meglioa40627d2012-09-11 16:47:21 -07006276 *
6277 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
6278 */
6279 public void setMarginEnd(int end) {
6280 endMargin = end;
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006281 mMarginFlags |= NEED_RESOLUTION_MASK;
Fabrice Di Meglioa40627d2012-09-11 16:47:21 -07006282 }
6283
6284 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006285 * Returns the end margin in pixels.
6286 *
6287 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
6288 *
6289 * @return the end margin in pixels.
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006290 */
6291 public int getMarginEnd() {
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006292 if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin;
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006293 if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006294 doResolveMargins();
6295 }
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006296 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07006297 case View.LAYOUT_DIRECTION_RTL:
6298 return leftMargin;
6299 case View.LAYOUT_DIRECTION_LTR:
6300 default:
6301 return rightMargin;
6302 }
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006303 }
6304
6305 /**
6306 * Check if margins are relative.
6307 *
6308 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
6309 * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
6310 *
Fabrice Di Megliof443f982012-07-13 20:24:03 -07006311 * @return true if either marginStart or marginEnd has been set.
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006312 */
6313 public boolean isMarginRelative() {
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006314 return (startMargin != DEFAULT_MARGIN_RELATIVE || endMargin != DEFAULT_MARGIN_RELATIVE);
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006315 }
6316
6317 /**
Fabrice Di Megliof443f982012-07-13 20:24:03 -07006318 * Set the layout direction
6319 * @param layoutDirection the layout direction.
6320 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
6321 * or {@link View#LAYOUT_DIRECTION_RTL}.
6322 */
6323 public void setLayoutDirection(int layoutDirection) {
6324 if (layoutDirection != View.LAYOUT_DIRECTION_LTR &&
6325 layoutDirection != View.LAYOUT_DIRECTION_RTL) return;
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006326 if (layoutDirection != (mMarginFlags & LAYOUT_DIRECTION_MASK)) {
6327 mMarginFlags &= ~LAYOUT_DIRECTION_MASK;
6328 mMarginFlags |= (layoutDirection & LAYOUT_DIRECTION_MASK);
6329 if (isMarginRelative()) {
6330 mMarginFlags |= NEED_RESOLUTION_MASK;
6331 } else {
6332 mMarginFlags &= ~NEED_RESOLUTION_MASK;
6333 }
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006334 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -07006335 }
6336
6337 /**
6338 * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or
6339 * {@link View#LAYOUT_DIRECTION_RTL}.
6340 *
6341 * @return the layout direction.
6342 */
6343 public int getLayoutDirection() {
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006344 return (mMarginFlags & LAYOUT_DIRECTION_MASK);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07006345 }
6346
6347 /**
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006348 * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
Fabrice Di Meglio98aec1c2012-02-13 16:54:05 -08006349 * may be overridden depending on layout direction.
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006350 */
6351 @Override
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07006352 public void resolveLayoutDirection(int layoutDirection) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -07006353 setLayoutDirection(layoutDirection);
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07006354
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006355 // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything
6356 // Will use the left and right margins if no relative margin is defined.
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006357 if (!isMarginRelative() ||
6358 (mMarginFlags & NEED_RESOLUTION_MASK) != NEED_RESOLUTION_MASK) return;
Fabrice Di Meglio69bd5582012-07-02 13:17:24 -07006359
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07006360 // Proceed with resolution
6361 doResolveMargins();
6362 }
6363
6364 private void doResolveMargins() {
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006365 if ((mMarginFlags & RTL_COMPATIBILITY_MODE_MASK) == RTL_COMPATIBILITY_MODE_MASK) {
Fabrice Di Meglio02a7d562013-03-27 12:41:22 -07006366 // if left or right margins are not defined and if we have some start or end margin
6367 // defined then use those start and end margins.
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006368 if ((mMarginFlags & LEFT_MARGIN_UNDEFINED_MASK) == LEFT_MARGIN_UNDEFINED_MASK
6369 && startMargin > DEFAULT_MARGIN_RELATIVE) {
Fabrice Di Meglio02a7d562013-03-27 12:41:22 -07006370 leftMargin = startMargin;
6371 }
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006372 if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK
6373 && endMargin > DEFAULT_MARGIN_RELATIVE) {
Fabrice Di Meglio02a7d562013-03-27 12:41:22 -07006374 rightMargin = endMargin;
6375 }
6376 } else {
6377 // We have some relative margins (either the start one or the end one or both). So use
6378 // them and override what has been defined for left and right margins. If either start
6379 // or end margin is not defined, just set it to default "0".
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006380 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
Fabrice Di Meglio02a7d562013-03-27 12:41:22 -07006381 case View.LAYOUT_DIRECTION_RTL:
6382 leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
6383 endMargin : DEFAULT_MARGIN_RESOLVED;
6384 rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
6385 startMargin : DEFAULT_MARGIN_RESOLVED;
6386 break;
6387 case View.LAYOUT_DIRECTION_LTR:
6388 default:
6389 leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
6390 startMargin : DEFAULT_MARGIN_RESOLVED;
6391 rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
6392 endMargin : DEFAULT_MARGIN_RESOLVED;
6393 break;
6394 }
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006395 }
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006396 mMarginFlags &= ~NEED_RESOLUTION_MASK;
Fabrice Di Megliob76023a2011-06-20 17:41:21 -07006397 }
Philip Milne10ca24a2012-04-23 15:38:27 -07006398
Fabrice Di Meglio03b8d3a2012-09-27 17:05:27 -07006399 /**
6400 * @hide
6401 */
6402 public boolean isLayoutRtl() {
Fabrice Di Megliob365f912013-03-27 16:36:21 -07006403 return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07006404 }
6405
Philip Milne10ca24a2012-04-23 15:38:27 -07006406 /**
6407 * @hide
6408 */
6409 @Override
Philip Milne7b757812012-09-19 18:13:44 -07006410 public void onDebugDraw(View view, Canvas canvas, Paint paint) {
6411 Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE;
6412
6413 fillDifference(canvas,
6414 view.getLeft() + oi.left,
6415 view.getTop() + oi.top,
6416 view.getRight() - oi.right,
6417 view.getBottom() - oi.bottom,
6418 leftMargin,
6419 topMargin,
6420 rightMargin,
6421 bottomMargin,
6422 paint);
Philip Milne10ca24a2012-04-23 15:38:27 -07006423 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006424 }
Adam Powell2b342f02010-08-18 18:14:13 -07006425
Jeff Brown20e987b2010-08-23 12:01:02 -07006426 /* Describes a touched view and the ids of the pointers that it has captured.
6427 *
6428 * This code assumes that pointer ids are always in the range 0..31 such that
6429 * it can use a bitfield to track which pointer ids are present.
6430 * As it happens, the lower layers of the input dispatch pipeline also use the
6431 * same trick so the assumption should be safe here...
6432 */
6433 private static final class TouchTarget {
6434 private static final int MAX_RECYCLED = 32;
Romain Guy6410c0a2013-06-17 11:21:58 -07006435 private static final Object sRecycleLock = new Object[0];
Jeff Brown20e987b2010-08-23 12:01:02 -07006436 private static TouchTarget sRecycleBin;
6437 private static int sRecycledCount;
Adam Powell2b342f02010-08-18 18:14:13 -07006438
Jeff Brown20e987b2010-08-23 12:01:02 -07006439 public static final int ALL_POINTER_IDS = -1; // all ones
Adam Powell2b342f02010-08-18 18:14:13 -07006440
Jeff Brown20e987b2010-08-23 12:01:02 -07006441 // The touched child view.
6442 public View child;
6443
6444 // The combined bit mask of pointer ids for all pointers captured by the target.
6445 public int pointerIdBits;
6446
6447 // The next target in the target list.
6448 public TouchTarget next;
6449
6450 private TouchTarget() {
Adam Powell2b342f02010-08-18 18:14:13 -07006451 }
6452
Jeff Brown20e987b2010-08-23 12:01:02 -07006453 public static TouchTarget obtain(View child, int pointerIdBits) {
6454 final TouchTarget target;
6455 synchronized (sRecycleLock) {
Adam Powell816c3be2010-08-23 18:00:05 -07006456 if (sRecycleBin == null) {
Jeff Brown20e987b2010-08-23 12:01:02 -07006457 target = new TouchTarget();
Adam Powell816c3be2010-08-23 18:00:05 -07006458 } else {
Jeff Brown20e987b2010-08-23 12:01:02 -07006459 target = sRecycleBin;
6460 sRecycleBin = target.next;
6461 sRecycledCount--;
6462 target.next = null;
Adam Powell816c3be2010-08-23 18:00:05 -07006463 }
Adam Powell816c3be2010-08-23 18:00:05 -07006464 }
Jeff Brown20e987b2010-08-23 12:01:02 -07006465 target.child = child;
6466 target.pointerIdBits = pointerIdBits;
6467 return target;
6468 }
Adam Powell816c3be2010-08-23 18:00:05 -07006469
Jeff Brown20e987b2010-08-23 12:01:02 -07006470 public void recycle() {
6471 synchronized (sRecycleLock) {
6472 if (sRecycledCount < MAX_RECYCLED) {
6473 next = sRecycleBin;
6474 sRecycleBin = this;
6475 sRecycledCount += 1;
Patrick Dubroyfb0547d22010-10-19 17:36:18 -07006476 } else {
6477 next = null;
Adam Powell816c3be2010-08-23 18:00:05 -07006478 }
Patrick Dubroyfb0547d22010-10-19 17:36:18 -07006479 child = null;
Adam Powell816c3be2010-08-23 18:00:05 -07006480 }
6481 }
Adam Powell2b342f02010-08-18 18:14:13 -07006482 }
Jeff Brown87b7f802011-06-21 18:35:45 -07006483
6484 /* Describes a hovered view. */
6485 private static final class HoverTarget {
6486 private static final int MAX_RECYCLED = 32;
Romain Guy6410c0a2013-06-17 11:21:58 -07006487 private static final Object sRecycleLock = new Object[0];
Jeff Brown87b7f802011-06-21 18:35:45 -07006488 private static HoverTarget sRecycleBin;
6489 private static int sRecycledCount;
6490
6491 // The hovered child view.
6492 public View child;
6493
6494 // The next target in the target list.
6495 public HoverTarget next;
6496
6497 private HoverTarget() {
6498 }
6499
6500 public static HoverTarget obtain(View child) {
6501 final HoverTarget target;
6502 synchronized (sRecycleLock) {
6503 if (sRecycleBin == null) {
6504 target = new HoverTarget();
6505 } else {
6506 target = sRecycleBin;
6507 sRecycleBin = target.next;
6508 sRecycledCount--;
6509 target.next = null;
6510 }
6511 }
6512 target.child = child;
6513 return target;
6514 }
6515
6516 public void recycle() {
6517 synchronized (sRecycleLock) {
6518 if (sRecycledCount < MAX_RECYCLED) {
6519 next = sRecycleBin;
6520 sRecycleBin = this;
6521 sRecycledCount += 1;
6522 } else {
6523 next = null;
6524 }
6525 child = null;
6526 }
6527 }
6528 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07006529
6530 /**
6531 * Pooled class that orderes the children of a ViewGroup from start
6532 * to end based on how they are laid out and the layout direction.
6533 */
6534 static class ChildListForAccessibility {
6535
6536 private static final int MAX_POOL_SIZE = 32;
6537
Svetoslav Ganovbe922dc2012-11-30 16:46:26 -08006538 private static final SynchronizedPool<ChildListForAccessibility> sPool =
6539 new SynchronizedPool<ChildListForAccessibility>(MAX_POOL_SIZE);
Svetoslav Ganov42138042012-03-20 11:51:39 -07006540
6541 private final ArrayList<View> mChildren = new ArrayList<View>();
6542
6543 private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>();
6544
6545 public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) {
Svetoslav Ganovbe922dc2012-11-30 16:46:26 -08006546 ChildListForAccessibility list = sPool.acquire();
6547 if (list == null) {
6548 list = new ChildListForAccessibility();
Svetoslav Ganov42138042012-03-20 11:51:39 -07006549 }
Svetoslav Ganovbe922dc2012-11-30 16:46:26 -08006550 list.init(parent, sort);
6551 return list;
Svetoslav Ganov42138042012-03-20 11:51:39 -07006552 }
6553
6554 public void recycle() {
Svetoslav Ganov42138042012-03-20 11:51:39 -07006555 clear();
Svetoslav Ganovbe922dc2012-11-30 16:46:26 -08006556 sPool.release(this);
Svetoslav Ganov42138042012-03-20 11:51:39 -07006557 }
6558
6559 public int getChildCount() {
6560 return mChildren.size();
6561 }
6562
6563 public View getChildAt(int index) {
6564 return mChildren.get(index);
6565 }
6566
6567 public int getChildIndex(View child) {
6568 return mChildren.indexOf(child);
6569 }
6570
6571 private void init(ViewGroup parent, boolean sort) {
6572 ArrayList<View> children = mChildren;
6573 final int childCount = parent.getChildCount();
6574 for (int i = 0; i < childCount; i++) {
6575 View child = parent.getChildAt(i);
6576 children.add(child);
6577 }
6578 if (sort) {
6579 ArrayList<ViewLocationHolder> holders = mHolders;
6580 for (int i = 0; i < childCount; i++) {
6581 View child = children.get(i);
6582 ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child);
6583 holders.add(holder);
6584 }
6585 Collections.sort(holders);
6586 for (int i = 0; i < childCount; i++) {
6587 ViewLocationHolder holder = holders.get(i);
6588 children.set(i, holder.mView);
6589 holder.recycle();
6590 }
6591 holders.clear();
6592 }
6593 }
6594
6595 private void clear() {
6596 mChildren.clear();
6597 }
6598 }
6599
6600 /**
6601 * Pooled class that holds a View and its location with respect to
6602 * a specified root. This enables sorting of views based on their
6603 * coordinates without recomputing the position relative to the root
6604 * on every comparison.
6605 */
6606 static class ViewLocationHolder implements Comparable<ViewLocationHolder> {
6607
6608 private static final int MAX_POOL_SIZE = 32;
6609
Svetoslav Ganovbe922dc2012-11-30 16:46:26 -08006610 private static final SynchronizedPool<ViewLocationHolder> sPool =
6611 new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE);
Svetoslav Ganov42138042012-03-20 11:51:39 -07006612
6613 private final Rect mLocation = new Rect();
6614
6615 public View mView;
6616
6617 private int mLayoutDirection;
6618
6619 public static ViewLocationHolder obtain(ViewGroup root, View view) {
Svetoslav Ganovbe922dc2012-11-30 16:46:26 -08006620 ViewLocationHolder holder = sPool.acquire();
6621 if (holder == null) {
6622 holder = new ViewLocationHolder();
Svetoslav Ganov42138042012-03-20 11:51:39 -07006623 }
Svetoslav Ganovbe922dc2012-11-30 16:46:26 -08006624 holder.init(root, view);
6625 return holder;
Svetoslav Ganov42138042012-03-20 11:51:39 -07006626 }
6627
6628 public void recycle() {
Svetoslav Ganov42138042012-03-20 11:51:39 -07006629 clear();
Svetoslav Ganovbe922dc2012-11-30 16:46:26 -08006630 sPool.release(this);
Svetoslav Ganov42138042012-03-20 11:51:39 -07006631 }
6632
6633 @Override
6634 public int compareTo(ViewLocationHolder another) {
6635 // This instance is greater than an invalid argument.
6636 if (another == null) {
6637 return 1;
6638 }
6639 if (getClass() != another.getClass()) {
6640 return 1;
6641 }
6642 // First is above second.
6643 if (mLocation.bottom - another.mLocation.top <= 0) {
6644 return -1;
6645 }
6646 // First is below second.
6647 if (mLocation.top - another.mLocation.bottom >= 0) {
6648 return 1;
6649 }
6650 // LTR
6651 if (mLayoutDirection == LAYOUT_DIRECTION_LTR) {
6652 final int leftDifference = mLocation.left - another.mLocation.left;
6653 // First more to the left than second.
6654 if (leftDifference != 0) {
6655 return leftDifference;
6656 }
6657 } else { // RTL
6658 final int rightDifference = mLocation.right - another.mLocation.right;
6659 // First more to the right than second.
6660 if (rightDifference != 0) {
6661 return -rightDifference;
6662 }
6663 }
6664 // Break tie by top.
6665 final int topDiference = mLocation.top - another.mLocation.top;
6666 if (topDiference != 0) {
6667 return topDiference;
6668 }
6669 // Break tie by height.
6670 final int heightDiference = mLocation.height() - another.mLocation.height();
6671 if (heightDiference != 0) {
6672 return -heightDiference;
6673 }
6674 // Break tie by width.
6675 final int widthDiference = mLocation.width() - another.mLocation.width();
6676 if (widthDiference != 0) {
6677 return -widthDiference;
6678 }
Svetoslav Ganov005b83b2012-04-16 18:17:17 -07006679 // Just break the tie somehow. The accessibliity ids are unique
6680 // and stable, hence this is deterministic tie breaking.
6681 return mView.getAccessibilityViewId() - another.mView.getAccessibilityViewId();
Svetoslav Ganov42138042012-03-20 11:51:39 -07006682 }
6683
6684 private void init(ViewGroup root, View view) {
6685 Rect viewLocation = mLocation;
6686 view.getDrawingRect(viewLocation);
6687 root.offsetDescendantRectToMyCoords(view, viewLocation);
6688 mView = view;
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -07006689 mLayoutDirection = root.getLayoutDirection();
Svetoslav Ganov42138042012-03-20 11:51:39 -07006690 }
6691
6692 private void clear() {
6693 mView = null;
6694 mLocation.set(0, 0, 0, 0);
6695 }
6696 }
Romain Guycbc67742012-04-27 16:12:57 -07006697
6698 private static Paint getDebugPaint() {
6699 if (sDebugPaint == null) {
6700 sDebugPaint = new Paint();
6701 sDebugPaint.setAntiAlias(false);
6702 }
6703 return sDebugPaint;
6704 }
6705
Romain Guy6410c0a2013-06-17 11:21:58 -07006706 private static void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
Romain Guycbc67742012-04-27 16:12:57 -07006707 if (sDebugLines== null) {
Romain Guy6410c0a2013-06-17 11:21:58 -07006708 // TODO: This won't work with multiple UI threads in a single process
Romain Guycbc67742012-04-27 16:12:57 -07006709 sDebugLines = new float[16];
6710 }
6711
Romain Guycbc67742012-04-27 16:12:57 -07006712 sDebugLines[0] = x1;
6713 sDebugLines[1] = y1;
6714 sDebugLines[2] = x2;
6715 sDebugLines[3] = y1;
6716
6717 sDebugLines[4] = x2;
6718 sDebugLines[5] = y1;
6719 sDebugLines[6] = x2;
Philip Milne7b757812012-09-19 18:13:44 -07006720 sDebugLines[7] = y2;
Romain Guycbc67742012-04-27 16:12:57 -07006721
Philip Milne7b757812012-09-19 18:13:44 -07006722 sDebugLines[8] = x2;
Romain Guycbc67742012-04-27 16:12:57 -07006723 sDebugLines[9] = y2;
6724 sDebugLines[10] = x1;
6725 sDebugLines[11] = y2;
6726
Philip Milne7b757812012-09-19 18:13:44 -07006727 sDebugLines[12] = x1;
6728 sDebugLines[13] = y2;
Romain Guycbc67742012-04-27 16:12:57 -07006729 sDebugLines[14] = x1;
6730 sDebugLines[15] = y1;
6731
Philip Milne7b757812012-09-19 18:13:44 -07006732 canvas.drawLines(sDebugLines, paint);
Romain Guycbc67742012-04-27 16:12:57 -07006733 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006734}