FileDocCategorySizeDatePackage
SizeAdaptiveLayout.javaAPI DocAndroid 5.1 API16604Thu Mar 12 22:22:10 GMT 2015com.android.internal.widget

SizeAdaptiveLayout

public class SizeAdaptiveLayout extends android.view.ViewGroup
A layout that switches between its children based on the requested layout height. Each child specifies its minimum and maximum valid height. Results are undefined if children specify overlapping ranges. A child may specify the maximum height as 'unbounded' to indicate that it is willing to be displayed arbitrarily tall.

See {@link SizeAdaptiveLayout.LayoutParams} for a full description of the layout parameters used by SizeAdaptiveLayout.

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private static final boolean
REPORT_BAD_BOUNDS
private static final long
CROSSFADE_TIME
private static final int
MIN_VALID_HEIGHT
private static final int
MAX_VALID_HEIGHT
private android.view.View
mActiveChild
private android.view.View
mLastActive
private android.animation.AnimatorSet
mTransitionAnimation
private android.animation.Animator.AnimatorListener
mAnimatorListener
private android.animation.ObjectAnimator
mFadePanel
private android.animation.ObjectAnimator
mFadeView
private int
mCanceledAnimationCount
private android.view.View
mEnteringView
private android.view.View
mLeavingView
private android.view.View
mModestyPanel
private int
mModestyPanelTop
Constructors Summary
public SizeAdaptiveLayout(android.content.Context context)


       
        this(context, null);
    
public SizeAdaptiveLayout(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, 0);
    
public SizeAdaptiveLayout(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)

        this(context, attrs, defStyleAttr, 0);
    
public SizeAdaptiveLayout(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes)

        super(context, attrs, defStyleAttr, defStyleRes);
        initialize();
    
Methods Summary
protected booleancheckLayoutParams(ViewGroup.LayoutParams p)

        return p instanceof SizeAdaptiveLayout.LayoutParams;
    
private intclampSizeToBounds(int measuredHeight, android.view.View child)

        SizeAdaptiveLayout.LayoutParams lp =
                (SizeAdaptiveLayout.LayoutParams) child.getLayoutParams();
        int heightIn = View.MEASURED_SIZE_MASK & measuredHeight;
        int height = Math.max(heightIn, lp.minHeight);
        if (lp.maxHeight != SizeAdaptiveLayout.LayoutParams.UNBOUNDED) {
            height = Math.min(height, lp.maxHeight);
        }

        if (REPORT_BAD_BOUNDS && heightIn != height) {
            Log.d(TAG, this + "child view " + child + " " +
                  "measured out of bounds at " + heightIn +"px " +
                  "clamped to " + height + "px");
        }

        return height;
    
protected com.android.internal.widget.SizeAdaptiveLayout$LayoutParamsgenerateDefaultLayoutParams()

        if (DEBUG) Log.d(TAG, "generate default layout from null");
        return new SizeAdaptiveLayout.LayoutParams();
    
public com.android.internal.widget.SizeAdaptiveLayout$LayoutParamsgenerateLayoutParams(android.util.AttributeSet attrs)

        if (DEBUG) Log.d(TAG, "generate layout from attrs");
        return new SizeAdaptiveLayout.LayoutParams(getContext(), attrs);
    
protected com.android.internal.widget.SizeAdaptiveLayout$LayoutParamsgenerateLayoutParams(ViewGroup.LayoutParams p)

        if (DEBUG) Log.d(TAG, "generate default layout from viewgroup");
        return new SizeAdaptiveLayout.LayoutParams(p);
    
public android.view.ViewgetModestyPanel()
Visible for testing

hide

        return mModestyPanel;
    
public android.animation.AnimatorgetTransitionAnimation()
Visible for testing

hide

        return mTransitionAnimation;
    
private voidinitialize()

        mModestyPanel = new View(getContext());
        // If the SizeAdaptiveLayout has a solid background, use it as a transition hint.
        Drawable background = getBackground();
        if (background instanceof StateListDrawable) {
            StateListDrawable sld = (StateListDrawable) background;
            sld.setState(StateSet.WILD_CARD);
            background = sld.getCurrent();
        }
        if (background instanceof ColorDrawable) {
            mModestyPanel.setBackgroundDrawable(background);
        }
        SizeAdaptiveLayout.LayoutParams layout =
                new SizeAdaptiveLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                                    ViewGroup.LayoutParams.MATCH_PARENT);
        mModestyPanel.setLayoutParams(layout);
        addView(mModestyPanel);
        mFadePanel = ObjectAnimator.ofFloat(mModestyPanel, "alpha", 0f);
        mFadeView = ObjectAnimator.ofFloat(null, "alpha", 0f);
        mAnimatorListener = new BringToFrontOnEnd();
        mTransitionAnimation = new AnimatorSet();
        mTransitionAnimation.play(mFadeView).with(mFadePanel);
        mTransitionAnimation.setDuration(CROSSFADE_TIME);
        mTransitionAnimation.addListener(mAnimatorListener);
    
public voidonAttachedToWindow()

        mLastActive = null;
        // make sure all views start off invisible.
        for (int i = 0; i < getChildCount(); i++) {
            getChildAt(i).setVisibility(View.GONE);
        }
    
protected voidonLayout(boolean changed, int left, int top, int right, int bottom)

        if (DEBUG) Log.d(TAG, this + " onlayout height: " + (bottom - top));
        mLastActive = mActiveChild;
        int measureSpec = View.MeasureSpec.makeMeasureSpec(bottom - top,
                                                           View.MeasureSpec.EXACTLY);
        mActiveChild = selectActiveChild(measureSpec);
        if (mActiveChild == null) return;

        mActiveChild.setVisibility(View.VISIBLE);

        if (mLastActive != mActiveChild && mLastActive != null) {
            if (DEBUG) Log.d(TAG, this + " changed children from: " + mLastActive +
                    " to: " + mActiveChild);

            mEnteringView = mActiveChild;
            mLeavingView = mLastActive;

            mEnteringView.setAlpha(1f);

            mModestyPanel.setAlpha(1f);
            mModestyPanel.bringToFront();
            mModestyPanelTop = mLeavingView.getHeight();
            mModestyPanel.setVisibility(View.VISIBLE);
            // TODO: mModestyPanel background should be compatible with mLeavingView

            mLeavingView.bringToFront();

            if (mTransitionAnimation.isRunning()) {
                mTransitionAnimation.cancel();
            }
            mFadeView.setTarget(mLeavingView);
            mFadeView.setFloatValues(0f);
            mFadePanel.setFloatValues(0f);
            mTransitionAnimation.setupStartValues();
            mTransitionAnimation.start();
        }
        final int childWidth = mActiveChild.getMeasuredWidth();
        final int childHeight = mActiveChild.getMeasuredHeight();
        // TODO investigate setting LAYER_TYPE_HARDWARE on mLastActive
        mActiveChild.layout(0, 0, childWidth, childHeight);

        if (DEBUG) Log.d(TAG, "got modesty offset of " + mModestyPanelTop);
        mModestyPanel.layout(0, mModestyPanelTop, childWidth, mModestyPanelTop + childHeight);
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        if (DEBUG) Log.d(TAG, this + " measure spec: " +
                         MeasureSpec.toString(heightMeasureSpec));
        View model = selectActiveChild(heightMeasureSpec);
        if (model == null) {
            setMeasuredDimension(0, 0);
            return;
        }
        SizeAdaptiveLayout.LayoutParams lp =
          (SizeAdaptiveLayout.LayoutParams) model.getLayoutParams();
        if (DEBUG) Log.d(TAG, "active min: " + lp.minHeight + " max: " + lp.maxHeight);
        measureChild(model, widthMeasureSpec, heightMeasureSpec);
        int childHeight = model.getMeasuredHeight();
        int childWidth = model.getMeasuredHeight();
        int childState = combineMeasuredStates(0, model.getMeasuredState());
        if (DEBUG) Log.d(TAG, "measured child at: " + childHeight);
        int resolvedWidth = resolveSizeAndState(childWidth, widthMeasureSpec, childState);
        int resolvedHeight = resolveSizeAndState(childHeight, heightMeasureSpec, childState);
        if (DEBUG) Log.d(TAG, "resolved to: " + resolvedHeight);
        int boundedHeight = clampSizeToBounds(resolvedHeight, model);
        if (DEBUG) Log.d(TAG, "bounded to: " + boundedHeight);
        setMeasuredDimension(resolvedWidth, boundedHeight);
    
private android.view.ViewselectActiveChild(int heightMeasureSpec)

        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        View unboundedView = null;
        View tallestView = null;
        int tallestViewSize = 0;
        View smallestView = null;
        int smallestViewSize = Integer.MAX_VALUE;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            if (child != mModestyPanel) {
                SizeAdaptiveLayout.LayoutParams lp =
                    (SizeAdaptiveLayout.LayoutParams) child.getLayoutParams();
                if (DEBUG) Log.d(TAG, "looking at " + i +
                                 " with min: " + lp.minHeight +
                                 " max: " +  lp.maxHeight);
                if (lp.maxHeight == SizeAdaptiveLayout.LayoutParams.UNBOUNDED &&
                    unboundedView == null) {
                    unboundedView = child;
                }
                if (lp.maxHeight > tallestViewSize) {
                    tallestViewSize = lp.maxHeight;
                    tallestView = child;
                }
                if (lp.minHeight < smallestViewSize) {
                    smallestViewSize = lp.minHeight;
                    smallestView = child;
                }
                if (heightMode != MeasureSpec.UNSPECIFIED &&
                    heightSize >= lp.minHeight && heightSize <= lp.maxHeight) {
                    if (DEBUG) Log.d(TAG, "  found exact match, finishing early");
                    return child;
                }
            }
        }
        if (unboundedView != null) {
            tallestView = unboundedView;
        }
        if (heightMode == MeasureSpec.UNSPECIFIED || heightSize > tallestViewSize) {
            return tallestView;
        } else {
            return smallestView;
        }