ShadowOverlayContainer.java revision ca48c80e6b6d90f6362144950c5f20a8f6839e9d
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14package android.support.v17.leanback.widget;
15
16import android.content.Context;
17import android.support.v17.leanback.R;
18import android.util.AttributeSet;
19import android.view.LayoutInflater;
20import android.view.View;
21import android.view.ViewGroup;
22import android.graphics.Rect;
23
24/**
25 * ShadowOverlayContainer Provides a SDK version independent wrapper container
26 * to take care of shadow and/or color overlay.
27 * <p>
28 * Shadow and color dimmer overlay are both optional.  When shadow is used,  it's
29 * user's responsibility to properly call setClipChildren(false) on parent views if
30 * the shadow can appear outside bounds of parent views.
31 * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container
32 * before using shadow.  Depending on sdk version, optical bounds might be applied
33 * to parent.
34 * </p>
35 * <p>
36 * {@link #initialize(boolean, boolean, boolean)} must be first called on the container
37 * to initialize shadows and/or color overlay.  Then call {@link #wrap(View)} to insert
38 * wrapped view into container.
39 * </p>
40 * <p>
41 * Call {@link #setShadowFocusLevel(float)} to control shadow alpha.
42 * </p>
43 * <p>
44 * Call {@link #setOverlayColor(int)} to control overlay color.
45 * </p>
46 */
47public class ShadowOverlayContainer extends ViewGroup {
48
49    private boolean mInitialized;
50    private View mColorDimOverlay;
51    private Object mShadowImpl;
52    private View mWrappedView;
53    private static final Rect sTempRect = new Rect();
54
55    public ShadowOverlayContainer(Context context) {
56        this(context, null, 0);
57    }
58
59    public ShadowOverlayContainer(Context context, AttributeSet attrs) {
60        this(context, attrs, 0);
61    }
62
63    public ShadowOverlayContainer(Context context, AttributeSet attrs, int defStyle) {
64        super(context, attrs, defStyle);
65    }
66
67    /**
68     * Return true if the platform sdk supports shadow.
69     */
70    public static boolean supportsShadow() {
71        return ShadowHelper.getInstance().supportsShadow();
72    }
73
74    /**
75     * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container
76     * before using shadow.  Depending on sdk version, optical bounds might be applied
77     * to parent.
78     */
79    public static void prepareParentForShadow(ViewGroup parent) {
80        ShadowHelper.getInstance().prepareParent(parent);
81    }
82
83    /**
84     * Initialize shadows, color overlay, and rounded corners.  All are optional.
85     */
86    public void initialize(boolean hasShadow, boolean hasColorDimOverlay, boolean roundedCorners) {
87        if (mInitialized) {
88            throw new IllegalStateException();
89        }
90        mInitialized = true;
91        if (hasShadow) {
92            mShadowImpl = ShadowHelper.getInstance().addShadow(this, roundedCorners);
93        } else if (roundedCorners) {
94            RoundedRectHelper.getInstance().setRoundedRectBackground(this,
95                    android.graphics.Color.TRANSPARENT);
96        }
97        if (hasColorDimOverlay) {
98            mColorDimOverlay = LayoutInflater.from(getContext())
99                    .inflate(R.layout.lb_card_color_overlay, this, false);
100            addView(mColorDimOverlay);
101        }
102    }
103
104    /**
105     * Set shadow focus level (0 to 1). 0 for unfocused, 1f for fully focused.
106     */
107    public void setShadowFocusLevel(float level) {
108        if (mShadowImpl != null) {
109            if (level < 0f) {
110                level = 0f;
111            } else if (level > 1f) {
112                level = 1f;
113            }
114            ShadowHelper.getInstance().setShadowFocusLevel(mShadowImpl, level);
115        }
116    }
117
118    /**
119     * Set color (with alpha) of the overlay.
120     */
121    public void setOverlayColor(int overlayColor) {
122        if (mColorDimOverlay != null) {
123            mColorDimOverlay.setBackgroundColor(overlayColor);
124        }
125    }
126
127    /**
128     * Inserts view into the wrapper.
129     */
130    public void wrap(View view) {
131        if (!mInitialized || mWrappedView != null) {
132            throw new IllegalStateException();
133        }
134        if (mColorDimOverlay != null) {
135            addView(view, indexOfChild(mColorDimOverlay));
136        } else {
137            addView(view);
138        }
139        mWrappedView = view;
140    }
141
142    @Override
143    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
144        if (mWrappedView == null) {
145            throw new IllegalStateException();
146        }
147        // padding and child margin are not supported.
148        // first measure the wrapped view, then measure the shadow view and/or overlay view.
149        int childWidthMeasureSpec, childHeightMeasureSpec;
150        LayoutParams lp = mWrappedView.getLayoutParams();
151        if (lp.width == LayoutParams.MATCH_PARENT) {
152            childWidthMeasureSpec = MeasureSpec.makeMeasureSpec
153                    (MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY);
154        } else {
155            childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width);
156        }
157        if (lp.height == LayoutParams.MATCH_PARENT) {
158            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec
159                    (MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY);
160        } else {
161            childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height);
162        }
163        mWrappedView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
164
165        int measuredWidth = mWrappedView.getMeasuredWidth();
166        int measuredHeight = mWrappedView.getMeasuredHeight();
167
168        for (int i = 0; i < getChildCount(); i++) {
169            View child = getChildAt(i);
170            if (child == mWrappedView) {
171                continue;
172            }
173            lp = child.getLayoutParams();
174            if (lp.width == LayoutParams.MATCH_PARENT) {
175                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec
176                        (measuredWidth, MeasureSpec.EXACTLY);
177            } else {
178                childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width);
179            }
180
181            if (lp.height == LayoutParams.MATCH_PARENT) {
182                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec
183                        (measuredHeight, MeasureSpec.EXACTLY);
184            } else {
185                childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height);
186            }
187            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
188        }
189        setMeasuredDimension(measuredWidth, measuredHeight);
190    }
191
192    @Override
193    protected void onLayout(boolean changed, int l, int t, int r, int b) {
194        final int count = getChildCount();
195        for (int i = 0; i < count; i++) {
196            final View child = getChildAt(i);
197            if (child.getVisibility() != GONE) {
198                final int width = child.getMeasuredWidth();
199                final int height = child.getMeasuredHeight();
200                child.layout(0, 0, width, height);
201            }
202        }
203        if (mWrappedView != null) {
204            sTempRect.left = (int) mWrappedView.getPivotX();
205            sTempRect.top = (int) mWrappedView.getPivotY();
206            offsetDescendantRectToMyCoords(mWrappedView, sTempRect);
207            setPivotX(sTempRect.left);
208            setPivotY(sTempRect.top);
209        }
210    }
211
212}
213