ShadowOverlayContainer.java revision 85886809580d605d44a1f45d8bb80742d6ca987e
1dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu/* 2dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Copyright (C) 2014 The Android Open Source Project 3dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * 4dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * in compliance with the License. You may obtain a copy of the License at 6dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * 7dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * http://www.apache.org/licenses/LICENSE-2.0 8dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * 9dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Unless required by applicable law or agreed to in writing, software distributed under the License 10dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * or implied. See the License for the specific language governing permissions and limitations under 12dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * the License. 13dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 14dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gupackage android.support.v17.leanback.widget; 15dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 16dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.content.Context; 17dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.support.v17.leanback.R; 18dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.util.AttributeSet; 19dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.view.LayoutInflater; 20dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.view.View; 21dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.view.ViewGroup; 22dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 23dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu/** 24dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * ShadowOverlayContainer Provides a SDK version independent wrapper container 25dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * to take care of shadow and/or color overlay. 26dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * <p> 27dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Shadow and color dimmer overlay are both optional. When shadow is used, it's 28dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * user's responsibility to properly call setClipChildren(false) on parent views if 29dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * the shadow can appear outside bounds of parent views. 30dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container 31dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * before using shadow. Depending on sdk version, optical bounds might be applied 32dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * to parent. 33dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * </p> 34dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * <p> 35dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * {@link #initialize(boolean, boolean)} must be first called on the container to initialize 36dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * shadows and/or color overlay. Then call {@link #wrap(View)} to insert wrapped view 37dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * into container. 38dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * </p> 39dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * <p> 40dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Call {@link #setShadowFocusLevel(float)} to control shadow alpha. 41dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * </p> 42dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * <p> 43f110e403308bae45a8016f462052d8c2dee870cfTim Kilbourn * Call {@link #setOverlayColor(int)} to control overlay color. 44dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * </p> 45dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 4685886809580d605d44a1f45d8bb80742d6ca987eDake Gupublic class ShadowOverlayContainer extends ViewGroup { 47dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 48dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu private boolean mInitialized; 49dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu private View mColorDimOverlay; 50dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu private Object mShadowImpl; 5185886809580d605d44a1f45d8bb80742d6ca987eDake Gu private View mWrappedView; 52dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 53dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public ShadowOverlayContainer(Context context) { 54dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu this(context, null, 0); 55dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 56dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 57dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public ShadowOverlayContainer(Context context, AttributeSet attrs) { 58dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu this(context, attrs, 0); 59dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 60dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 61dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public ShadowOverlayContainer(Context context, AttributeSet attrs, int defStyle) { 62dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu super(context, attrs, defStyle); 63dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 64dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 65dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu /** 66dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Return true if the platform sdk supports shadow. 67dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 68dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public static boolean supportsShadow() { 69dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu return ShadowHelper.getInstance().supportsShadow(); 70dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 71dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 72dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu /** 73dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container 74dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * before using shadow. Depending on sdk version, optical bounds might be applied 75dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * to parent. 76dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 77dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public static void prepareParentForShadow(ViewGroup parent) { 78dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu ShadowHelper.getInstance().prepareParent(parent); 79dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 80dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 81dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu /** 82dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Initialize shadows and/or color overlay. Both are optional. 83dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 84dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public void initialize(boolean hasShadow, boolean hasColorDimOverlay) { 85dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu if (mInitialized) { 86dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu throw new IllegalStateException(); 87dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 88dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu mInitialized = true; 89dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu if (hasShadow) { 90dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu mShadowImpl = ShadowHelper.getInstance().addShadow(this); 91dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 92dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu if (hasColorDimOverlay) { 93dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu mColorDimOverlay = LayoutInflater.from(getContext()) 94dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu .inflate(R.layout.lb_card_color_overlay, this, false); 95dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu addView(mColorDimOverlay); 96dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 97dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 98dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 99dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu /** 100dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Set shadow focus level (0 to 1). 0 for unfocused, 1f for fully focused. 101dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 102dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public void setShadowFocusLevel(float level) { 103dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu if (mShadowImpl != null) { 104dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu if (level < 0f) { 105dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu level = 0f; 106dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } else if (level > 1f) { 107dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu level = 1f; 108dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 109dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu ShadowHelper.getInstance().setShadowFocusLevel(mShadowImpl, level); 110dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 111dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 112dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 113dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu /** 114dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Set color (with alpha) of the overlay. 115dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 116dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public void setOverlayColor(int overlayColor) { 1179240e796bc63422c28f2707840bd99c48573279bDake Gu if (mColorDimOverlay != null) { 1189240e796bc63422c28f2707840bd99c48573279bDake Gu mColorDimOverlay.setBackgroundColor(overlayColor); 1199240e796bc63422c28f2707840bd99c48573279bDake Gu } 120dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 121dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 122dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu /** 123dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Inserts view into the wrapper. 124dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 125dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public void wrap(View view) { 12685886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (!mInitialized || mWrappedView != null) { 127dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu throw new IllegalStateException(); 128dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 129dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu if (mColorDimOverlay != null) { 130dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu addView(view, indexOfChild(mColorDimOverlay)); 131dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } else { 132dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu addView(view); 133dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 13485886809580d605d44a1f45d8bb80742d6ca987eDake Gu mWrappedView = view; 13585886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 13685886809580d605d44a1f45d8bb80742d6ca987eDake Gu 13785886809580d605d44a1f45d8bb80742d6ca987eDake Gu @Override 13885886809580d605d44a1f45d8bb80742d6ca987eDake Gu protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 13985886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (mWrappedView == null) { 14085886809580d605d44a1f45d8bb80742d6ca987eDake Gu throw new IllegalStateException(); 14185886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 14285886809580d605d44a1f45d8bb80742d6ca987eDake Gu // padding and child margin are not supported. 14385886809580d605d44a1f45d8bb80742d6ca987eDake Gu // first measure the wrapped view, then measure the shadow view and/or overlay view. 14485886809580d605d44a1f45d8bb80742d6ca987eDake Gu int childWidthMeasureSpec, childHeightMeasureSpec; 14585886809580d605d44a1f45d8bb80742d6ca987eDake Gu LayoutParams lp = mWrappedView.getLayoutParams(); 14685886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (lp.width == LayoutParams.MATCH_PARENT) { 14785886809580d605d44a1f45d8bb80742d6ca987eDake Gu childWidthMeasureSpec = MeasureSpec.makeMeasureSpec 14885886809580d605d44a1f45d8bb80742d6ca987eDake Gu (MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY); 14985886809580d605d44a1f45d8bb80742d6ca987eDake Gu } else { 15085886809580d605d44a1f45d8bb80742d6ca987eDake Gu childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width); 15185886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 15285886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (lp.height == LayoutParams.MATCH_PARENT) { 15385886809580d605d44a1f45d8bb80742d6ca987eDake Gu childHeightMeasureSpec = MeasureSpec.makeMeasureSpec 15485886809580d605d44a1f45d8bb80742d6ca987eDake Gu (MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY); 15585886809580d605d44a1f45d8bb80742d6ca987eDake Gu } else { 15685886809580d605d44a1f45d8bb80742d6ca987eDake Gu childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height); 15785886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 15885886809580d605d44a1f45d8bb80742d6ca987eDake Gu mWrappedView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 15985886809580d605d44a1f45d8bb80742d6ca987eDake Gu 16085886809580d605d44a1f45d8bb80742d6ca987eDake Gu int measuredWidth = mWrappedView.getMeasuredWidth(); 16185886809580d605d44a1f45d8bb80742d6ca987eDake Gu int measuredHeight = mWrappedView.getMeasuredHeight(); 16285886809580d605d44a1f45d8bb80742d6ca987eDake Gu 16385886809580d605d44a1f45d8bb80742d6ca987eDake Gu for (int i = 0; i < getChildCount(); i++) { 16485886809580d605d44a1f45d8bb80742d6ca987eDake Gu View child = getChildAt(i); 16585886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (child == mWrappedView) { 16685886809580d605d44a1f45d8bb80742d6ca987eDake Gu continue; 16785886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 16885886809580d605d44a1f45d8bb80742d6ca987eDake Gu lp = child.getLayoutParams(); 16985886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (lp.width == LayoutParams.MATCH_PARENT) { 17085886809580d605d44a1f45d8bb80742d6ca987eDake Gu childWidthMeasureSpec = MeasureSpec.makeMeasureSpec 17185886809580d605d44a1f45d8bb80742d6ca987eDake Gu (measuredWidth, MeasureSpec.EXACTLY); 17285886809580d605d44a1f45d8bb80742d6ca987eDake Gu } else { 17385886809580d605d44a1f45d8bb80742d6ca987eDake Gu childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width); 17485886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 17585886809580d605d44a1f45d8bb80742d6ca987eDake Gu 17685886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (lp.height == LayoutParams.MATCH_PARENT) { 17785886809580d605d44a1f45d8bb80742d6ca987eDake Gu childHeightMeasureSpec = MeasureSpec.makeMeasureSpec 17885886809580d605d44a1f45d8bb80742d6ca987eDake Gu (measuredHeight, MeasureSpec.EXACTLY); 17985886809580d605d44a1f45d8bb80742d6ca987eDake Gu } else { 18085886809580d605d44a1f45d8bb80742d6ca987eDake Gu childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height); 18185886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 18285886809580d605d44a1f45d8bb80742d6ca987eDake Gu child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 18385886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 18485886809580d605d44a1f45d8bb80742d6ca987eDake Gu setMeasuredDimension(measuredWidth, measuredHeight); 18585886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 18685886809580d605d44a1f45d8bb80742d6ca987eDake Gu 18785886809580d605d44a1f45d8bb80742d6ca987eDake Gu @Override 18885886809580d605d44a1f45d8bb80742d6ca987eDake Gu protected void onLayout(boolean changed, int l, int t, int r, int b) { 18985886809580d605d44a1f45d8bb80742d6ca987eDake Gu final int count = getChildCount(); 19085886809580d605d44a1f45d8bb80742d6ca987eDake Gu for (int i = 0; i < count; i++) { 19185886809580d605d44a1f45d8bb80742d6ca987eDake Gu final View child = getChildAt(i); 19285886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (child.getVisibility() != GONE) { 19385886809580d605d44a1f45d8bb80742d6ca987eDake Gu final int width = child.getMeasuredWidth(); 19485886809580d605d44a1f45d8bb80742d6ca987eDake Gu final int height = child.getMeasuredHeight(); 19585886809580d605d44a1f45d8bb80742d6ca987eDake Gu child.layout(0, 0, width, height); 19685886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 19785886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 198dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 199dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 200dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu} 201