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