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