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