/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import com.android.internal.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Region; import android.os.Parcelable; import android.util.AttributeSet; import android.util.EventLog; import android.util.Log; import android.util.SparseArray; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.LayoutAnimationController; import android.view.animation.Transformation; import java.util.ArrayList; /** *

* A ViewGroup is a special view that can contain other views * (called children.) The view group is the base class for layouts and views * containers. This class also defines the * {@link android.view.ViewGroup.LayoutParams} class which serves as the base * class for layouts parameters. *

* *

* Also see {@link LayoutParams} for layout attributes. *

*/ public abstract class ViewGroup extends View implements ViewParent, ViewManager { private static final boolean DBG = false; /** * Views which have been hidden or removed which need to be animated on * their way out. * This field should be made private, so it is hidden from the SDK. * {@hide} */ protected ArrayList mDisappearingChildren; /** * Listener used to propagate events indicating when children are added * and/or removed from a view group. * This field should be made private, so it is hidden from the SDK. * {@hide} */ protected OnHierarchyChangeListener mOnHierarchyChangeListener; // The view contained within this ViewGroup that has or contains focus. private View mFocused; // The current transformation to apply on the child being drawn private final Transformation mChildTransformation = new Transformation(); // Target of Motion events private View mMotionTarget; private final Rect mTempRect = new Rect(); // Layout animation private LayoutAnimationController mLayoutAnimationController; private Animation.AnimationListener mAnimationListener; /** * Internal flags. * * This field should be made private, so it is hidden from the SDK. * {@hide} */ protected int mGroupFlags; // When set, ViewGroup invalidates only the child's rectangle // Set by default private static final int FLAG_CLIP_CHILDREN = 0x1; // When set, ViewGroup excludes the padding area from the invalidate rectangle // Set by default private static final int FLAG_CLIP_TO_PADDING = 0x2; // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set private static final int FLAG_INVALIDATE_REQUIRED = 0x4; // When set, dispatchDraw() will run the layout animation and unset the flag private static final int FLAG_RUN_ANIMATION = 0x8; // When set, there is either no layout animation on the ViewGroup or the layout // animation is over // Set by default private static final int FLAG_ANIMATION_DONE = 0x10; // If set, this ViewGroup has padding; if unset there is no padding and we don't need // to clip it, even if FLAG_CLIP_TO_PADDING is set private static final int FLAG_PADDING_NOT_NULL = 0x20; // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation // Set by default private static final int FLAG_ANIMATION_CACHE = 0x40; // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a // layout animation; this avoid clobbering the hierarchy // Automatically set when the layout animation starts, depending on the animation's // characteristics private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80; // When set, the next call to drawChild() will clear mChildTransformation's matrix private static final int FLAG_CLEAR_TRANSFORMATION = 0x100; // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes // the children's Bitmap caches if necessary // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set) private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200; /** * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)} * to get the index of the child to draw for that iteration. */ protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400; /** * When set, this ViewGroup supports static transformations on children; this causes * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be * invoked when a child is drawn. * * Any subclass overriding * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should * set this flags in {@link #mGroupFlags}. * * This flag needs to be removed until we can add a setter for it. People * can't be directly stuffing values in to mGroupFlags!!! * * {@hide} */ protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800; // When the previous drawChild() invocation used an alpha value that was lower than // 1.0 and set it in mCachePaint private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000; /** * When set, this ViewGroup's drawable states also include those * of its children. */ private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000; /** * When set, this ViewGroup tries to always draw its children using their drawing cache. */ private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000; /** * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to * draw its children with their drawing cache. */ private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000; /** * When set, this group will go through its list of children to notify them of * any drawable state change. */ private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000; private static final int FLAG_MASK_FOCUSABILITY = 0x60000; /** * This view will get focus before any of its descendants. */ public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000; /** * This view will get focus only if none of its descendants want it. */ public static final int FOCUS_AFTER_DESCENDANTS = 0x40000; /** * This view will block any of its descendants from getting focus, even * if they are focusable. */ public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000; /** * Used to map between enum in attrubutes and flag values. */ private static final int[] DESCENDANT_FOCUSABILITY_FLAGS = {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS}; /** * When set, this ViewGroup should not intercept touch events. */ private static final int FLAG_DISALLOW_INTERCEPT = 0x80000; /** * Indicates which types of drawing caches are to be kept in memory. * This field should be made private, so it is hidden from the SDK. * {@hide} */ protected int mPersistentDrawingCache; /** * Used to indicate that no drawing cache should be kept in memory. */ public static final int PERSISTENT_NO_CACHE = 0x0; /** * Used to indicate that the animation drawing cache should be kept in memory. */ public static final int PERSISTENT_ANIMATION_CACHE = 0x1; /** * Used to indicate that the scrolling drawing cache should be kept in memory. */ public static final int PERSISTENT_SCROLLING_CACHE = 0x2; /** * Used to indicate that all drawing caches should be kept in memory. */ public static final int PERSISTENT_ALL_CACHES = 0x3; /** * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL * are set at the same time. */ protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL; // Index of the child's left position in the mLocation array private static final int CHILD_LEFT_INDEX = 0; // Index of the child's top position in the mLocation array private static final int CHILD_TOP_INDEX = 1; // Child views of this ViewGroup private View[] mChildren; // Number of valid children in the mChildren array, the rest should be null or not // considered as children private int mChildrenCount; private static final int ARRAY_INITIAL_CAPACITY = 12; private static final int ARRAY_CAPACITY_INCREMENT = 12; // Used to draw cached views private final Paint mCachePaint = new Paint(); public ViewGroup(Context context) { super(context); initViewGroup(); } public ViewGroup(Context context, AttributeSet attrs) { super(context, attrs); initViewGroup(); initFromAttributes(context, attrs); } public ViewGroup(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initViewGroup(); initFromAttributes(context, attrs); } private void initViewGroup() { // ViewGroup doesn't draw by default setFlags(WILL_NOT_DRAW, DRAW_MASK); mGroupFlags |= FLAG_CLIP_CHILDREN; mGroupFlags |= FLAG_CLIP_TO_PADDING; mGroupFlags |= FLAG_ANIMATION_DONE; mGroupFlags |= FLAG_ANIMATION_CACHE; mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE; setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS); mChildren = new View[ARRAY_INITIAL_CAPACITY]; mChildrenCount = 0; mCachePaint.setDither(false); mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE; } private void initFromAttributes(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewGroup); final int N = a.getIndexCount(); for (int i = 0; i < N; i++) { int attr = a.getIndex(i); switch (attr) { case R.styleable.ViewGroup_clipChildren: setClipChildren(a.getBoolean(attr, true)); break; case R.styleable.ViewGroup_clipToPadding: setClipToPadding(a.getBoolean(attr, true)); break; case R.styleable.ViewGroup_animationCache: setAnimationCacheEnabled(a.getBoolean(attr, true)); break; case R.styleable.ViewGroup_persistentDrawingCache: setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE)); break; case R.styleable.ViewGroup_addStatesFromChildren: setAddStatesFromChildren(a.getBoolean(attr, false)); break; case R.styleable.ViewGroup_alwaysDrawnWithCache: setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true)); break; case R.styleable.ViewGroup_layoutAnimation: int id = a.getResourceId(attr, -1); if (id > 0) { setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id)); } break; case R.styleable.ViewGroup_descendantFocusability: setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]); break; } } a.recycle(); } /** * Gets the descendant focusability of this view group. The descendant * focusability defines the relationship between this view group and its * descendants when looking for a view to take focus in * {@link #requestFocus(int, android.graphics.Rect)}. * * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS}, * {@link #FOCUS_BLOCK_DESCENDANTS}. */ @ViewDebug.ExportedProperty(mapping = { @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"), @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"), @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS") }) public int getDescendantFocusability() { return mGroupFlags & FLAG_MASK_FOCUSABILITY; } /** * Set the descendant focusability of this view group. This defines the relationship * between this view group and its descendants when looking for a view to * take focus in {@link #requestFocus(int, android.graphics.Rect)}. * * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS}, * {@link #FOCUS_BLOCK_DESCENDANTS}. */ public void setDescendantFocusability(int focusability) { switch (focusability) { case FOCUS_BEFORE_DESCENDANTS: case FOCUS_AFTER_DESCENDANTS: case FOCUS_BLOCK_DESCENDANTS: break; default: throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, " + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS"); } mGroupFlags &= ~FLAG_MASK_FOCUSABILITY; mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY); } /** * {@inheritDoc} */ @Override void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) { if (mFocused != null) { mFocused.unFocus(); mFocused = null; } super.handleFocusGainInternal(direction, previouslyFocusedRect); } /** * {@inheritDoc} */ public void requestChildFocus(View child, View focused) { if (DBG) { System.out.println(this + " requestChildFocus()"); } if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) { return; } // Unfocus us, if necessary super.unFocus(); // We had a previous notion of who had focus. Clear it. if (mFocused != child) { if (mFocused != null) { mFocused.unFocus(); } mFocused = child; } if (mParent != null) { mParent.requestChildFocus(this, focused); } } /** * {@inheritDoc} */ public void focusableViewAvailable(View v) { if (mParent != null // shortcut: don't report a new focusable view if we block our descendants from // getting focus && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS) // shortcut: don't report a new focusable view if we already are focused // (and we don't prefer our descendants) // // note: knowing that mFocused is non-null is not a good enough reason // to break the traversal since in that case we'd actually have to find // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and // an ancestor of v; this will get checked for at ViewRoot && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) { mParent.focusableViewAvailable(v); } } /** * {@inheritDoc} */ public boolean showContextMenuForChild(View originalView) { return mParent != null && mParent.showContextMenuForChild(originalView); } /** * Find the nearest view in the specified direction that wants to take * focus. * * @param focused The view that currently has focus * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and * FOCUS_RIGHT, or 0 for not applicable. */ public View focusSearch(View focused, int direction) { if (isRootNamespace()) { // root namespace means we should consider ourselves the top of the // tree for focus searching; otherwise we could be focus searching // into other tabs. see LocalActivityManager and TabHost for more info return FocusFinder.getInstance().findNextFocus(this, focused, direction); } else if (mParent != null) { return mParent.focusSearch(focused, direction); } return null; } /** * Called when a child of this group wants a particular rectangle to be * positioned onto the screen. {@link ViewGroup}s overriding this can trust * that: * * *

{@link ViewGroup}s overriding this should uphold the contract:

*