ShadowOverlayContainer.java revision 0bc1aae85ac55af50c2debfe711505093fd3626a
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.
85     * @deprecated use {@link #initialize(boolean, boolean, boolean)} instead.
86     */
87    @Deprecated
88    public void initialize(boolean hasShadow, boolean hasColorDimOverlay) {
89        initialize(hasShadow, hasColorDimOverlay, true);
90    }
91
92    /**
93     * Initialize shadows, color overlay, and rounded corners.  All are optional.
94     */
95    public void initialize(boolean hasShadow, boolean hasColorDimOverlay, boolean roundedCorners) {
96        if (mInitialized) {
97            throw new IllegalStateException();
98        }
99        mInitialized = true;
100        if (hasShadow) {
101            mShadowImpl = ShadowHelper.getInstance().addShadow(this, roundedCorners);
102        } else if (roundedCorners) {
103            RoundedRectHelper.getInstance().setRoundedRectBackground(this,
104                    android.graphics.Color.TRANSPARENT);
105        }
106        if (hasColorDimOverlay) {
107            mColorDimOverlay = LayoutInflater.from(getContext())
108                    .inflate(R.layout.lb_card_color_overlay, this, false);
109            addView(mColorDimOverlay);
110        }
111    }
112
113    /**
114     * Set shadow focus level (0 to 1). 0 for unfocused, 1f for fully focused.
115     */
116    public void setShadowFocusLevel(float level) {
117        if (mShadowImpl != null) {
118            if (level < 0f) {
119                level = 0f;
120            } else if (level > 1f) {
121                level = 1f;
122            }
123            ShadowHelper.getInstance().setShadowFocusLevel(mShadowImpl, level);
124        }
125    }
126
127    /**
128     * Set color (with alpha) of the overlay.
129     */
130    public void setOverlayColor(int overlayColor) {
131        if (mColorDimOverlay != null) {
132            mColorDimOverlay.setBackgroundColor(overlayColor);
133        }
134    }
135
136    /**
137     * Inserts view into the wrapper.
138     */
139    public void wrap(View view) {
140        if (!mInitialized || mWrappedView != null) {
141            throw new IllegalStateException();
142        }
143        if (mColorDimOverlay != null) {
144            addView(view, indexOfChild(mColorDimOverlay));
145        } else {
146            addView(view);
147        }
148        mWrappedView = view;
149    }
150
151    @Override
152    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
153        if (mWrappedView == null) {
154            throw new IllegalStateException();
155        }
156        // padding and child margin are not supported.
157        // first measure the wrapped view, then measure the shadow view and/or overlay view.
158        int childWidthMeasureSpec, childHeightMeasureSpec;
159        LayoutParams lp = mWrappedView.getLayoutParams();
160        if (lp.width == LayoutParams.MATCH_PARENT) {
161            childWidthMeasureSpec = MeasureSpec.makeMeasureSpec
162                    (MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY);
163        } else {
164            childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width);
165        }
166        if (lp.height == LayoutParams.MATCH_PARENT) {
167            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec
168                    (MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY);
169        } else {
170            childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height);
171        }
172        mWrappedView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
173
174        int measuredWidth = mWrappedView.getMeasuredWidth();
175        int measuredHeight = mWrappedView.getMeasuredHeight();
176
177        for (int i = 0; i < getChildCount(); i++) {
178            View child = getChildAt(i);
179            if (child == mWrappedView) {
180                continue;
181            }
182            lp = child.getLayoutParams();
183            if (lp.width == LayoutParams.MATCH_PARENT) {
184                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec
185                        (measuredWidth, MeasureSpec.EXACTLY);
186            } else {
187                childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width);
188            }
189
190            if (lp.height == LayoutParams.MATCH_PARENT) {
191                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec
192                        (measuredHeight, MeasureSpec.EXACTLY);
193            } else {
194                childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height);
195            }
196            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
197        }
198        setMeasuredDimension(measuredWidth, measuredHeight);
199    }
200
201    @Override
202    protected void onLayout(boolean changed, int l, int t, int r, int b) {
203        final int count = getChildCount();
204        for (int i = 0; i < count; i++) {
205            final View child = getChildAt(i);
206            if (child.getVisibility() != GONE) {
207                final int width = child.getMeasuredWidth();
208                final int height = child.getMeasuredHeight();
209                child.layout(0, 0, width, height);
210            }
211        }
212        if (mWrappedView != null) {
213            sTempRect.left = (int) mWrappedView.getPivotX();
214            sTempRect.top = (int) mWrappedView.getPivotY();
215            offsetDescendantRectToMyCoords(mWrappedView, sTempRect);
216            setPivotX(sTempRect.left);
217            setPivotY(sTempRect.top);
218        }
219    }
220
221}
222