ShadowOverlayContainer.java revision a00bada00bff4a58436a39472ab14ccb7a8f619d
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 * Provides an SDK version-independent wrapper to support shadows, color overlays, and rounded
26 * corners.
27 * <p>
28 * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container
29 * before using shadow.  Depending on sdk version, optical bounds might be applied
30 * to parent.
31 * </p>
32 * <p>
33 * If shadows can appear outside the bounds of the parent view, setClipChildren(false) must
34 * be called on the grandparent view.
35 * </p>
36 * <p>
37 * {@link #initialize(boolean, boolean, boolean)} must be first called on the container.
38 * Then call {@link #wrap(View)} to insert the wrapped view into the container.
39 * </p>
40 * <p>
41 * Call {@link #setShadowFocusLevel(float)} to control the strength of the shadow (focused shadows
42 * cast stronger shadows).
43 * </p>
44 * <p>
45 * Call {@link #setOverlayColor(int)} to control overlay color.
46 * </p>
47 */
48public class ShadowOverlayContainer extends ViewGroup {
49
50    private boolean mInitialized;
51    private View mColorDimOverlay;
52    private Object mShadowImpl;
53    private View mWrappedView;
54    private static final Rect sTempRect = new Rect();
55
56    public ShadowOverlayContainer(Context context) {
57        this(context, null, 0);
58    }
59
60    public ShadowOverlayContainer(Context context, AttributeSet attrs) {
61        this(context, attrs, 0);
62    }
63
64    public ShadowOverlayContainer(Context context, AttributeSet attrs, int defStyle) {
65        super(context, attrs, defStyle);
66    }
67
68    /**
69     * Return true if the platform sdk supports shadow.
70     */
71    public static boolean supportsShadow() {
72        return ShadowHelper.getInstance().supportsShadow();
73    }
74
75    /**
76     * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container
77     * before using shadow.  Depending on sdk version, optical bounds might be applied
78     * to parent.
79     */
80    public static void prepareParentForShadow(ViewGroup parent) {
81        ShadowHelper.getInstance().prepareParent(parent);
82    }
83
84    /**
85     * Initialize shadows, color overlay.
86     * @deprecated use {@link #initialize(boolean, boolean, boolean)} instead.
87     */
88    @Deprecated
89    public void initialize(boolean hasShadow, boolean hasColorDimOverlay) {
90        initialize(hasShadow, hasColorDimOverlay, true);
91    }
92
93    /**
94     * Initialize shadows, color overlay, and rounded corners.  All are optional.
95     */
96    public void initialize(boolean hasShadow, boolean hasColorDimOverlay, boolean roundedCorners) {
97        if (mInitialized) {
98            throw new IllegalStateException();
99        }
100        mInitialized = true;
101        if (hasShadow) {
102            mShadowImpl = ShadowHelper.getInstance().addShadow(this, roundedCorners);
103        } else if (roundedCorners) {
104            RoundedRectHelper.getInstance().setRoundedRectBackground(this,
105                    android.graphics.Color.TRANSPARENT);
106        }
107        if (hasColorDimOverlay) {
108            mColorDimOverlay = LayoutInflater.from(getContext())
109                    .inflate(R.layout.lb_card_color_overlay, this, false);
110            addView(mColorDimOverlay);
111        }
112    }
113
114    /**
115     * Set shadow focus level (0 to 1). 0 for unfocused, 1f for fully focused.
116     */
117    public void setShadowFocusLevel(float level) {
118        if (mShadowImpl != null) {
119            if (level < 0f) {
120                level = 0f;
121            } else if (level > 1f) {
122                level = 1f;
123            }
124            ShadowHelper.getInstance().setShadowFocusLevel(mShadowImpl, level);
125        }
126    }
127
128    /**
129     * Set color (with alpha) of the overlay.
130     */
131    public void setOverlayColor(int overlayColor) {
132        if (mColorDimOverlay != null) {
133            mColorDimOverlay.setBackgroundColor(overlayColor);
134        }
135    }
136
137    /**
138     * Inserts view into the wrapper.
139     */
140    public void wrap(View view) {
141        if (!mInitialized || mWrappedView != null) {
142            throw new IllegalStateException();
143        }
144        if (mColorDimOverlay != null) {
145            addView(view, indexOfChild(mColorDimOverlay));
146        } else {
147            addView(view);
148        }
149        mWrappedView = view;
150    }
151
152    @Override
153    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
154        if (mWrappedView == null) {
155            throw new IllegalStateException();
156        }
157        // padding and child margin are not supported.
158        // first measure the wrapped view, then measure the shadow view and/or overlay view.
159        int childWidthMeasureSpec, childHeightMeasureSpec;
160        LayoutParams lp = mWrappedView.getLayoutParams();
161        if (lp.width == LayoutParams.MATCH_PARENT) {
162            childWidthMeasureSpec = MeasureSpec.makeMeasureSpec
163                    (MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY);
164        } else {
165            childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width);
166        }
167        if (lp.height == LayoutParams.MATCH_PARENT) {
168            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec
169                    (MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY);
170        } else {
171            childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height);
172        }
173        mWrappedView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
174
175        int measuredWidth = mWrappedView.getMeasuredWidth();
176        int measuredHeight = mWrappedView.getMeasuredHeight();
177
178        for (int i = 0; i < getChildCount(); i++) {
179            View child = getChildAt(i);
180            if (child == mWrappedView) {
181                continue;
182            }
183            lp = child.getLayoutParams();
184            if (lp.width == LayoutParams.MATCH_PARENT) {
185                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec
186                        (measuredWidth, MeasureSpec.EXACTLY);
187            } else {
188                childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width);
189            }
190
191            if (lp.height == LayoutParams.MATCH_PARENT) {
192                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec
193                        (measuredHeight, MeasureSpec.EXACTLY);
194            } else {
195                childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height);
196            }
197            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
198        }
199        setMeasuredDimension(measuredWidth, measuredHeight);
200    }
201
202    @Override
203    protected void onLayout(boolean changed, int l, int t, int r, int b) {
204        final int count = getChildCount();
205        for (int i = 0; i < count; i++) {
206            final View child = getChildAt(i);
207            if (child.getVisibility() != GONE) {
208                final int width = child.getMeasuredWidth();
209                final int height = child.getMeasuredHeight();
210                child.layout(0, 0, width, height);
211            }
212        }
213        if (mWrappedView != null) {
214            sTempRect.left = (int) mWrappedView.getPivotX();
215            sTempRect.top = (int) mWrappedView.getPivotY();
216            offsetDescendantRectToMyCoords(mWrappedView, sTempRect);
217            setPivotX(sTempRect.left);
218            setPivotY(sTempRect.top);
219        }
220    }
221
222}
223