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