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