165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/* 265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Copyright (C) 2014 The Android Open Source Project 365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Licensed under the Apache License, Version 2.0 (the "License"); 565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * you may not use this file except in compliance with the License. 665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * You may obtain a copy of the License at 765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * http://www.apache.org/licenses/LICENSE-2.0 965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 1065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Unless required by applicable law or agreed to in writing, software 1165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * distributed under the License is distributed on an "AS IS" BASIS, 1265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * See the License for the specific language governing permissions and 1465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * limitations under the License. 1565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 1665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 1765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepackage com.android.tv.settings.widget; 1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.Context; 2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.res.TypedArray; 2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.Canvas; 2265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.Matrix; 2365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.Rect; 2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.RectF; 2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.drawable.Drawable; 2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.drawable.ShapeDrawable; 2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.drawable.shapes.RectShape; 2865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.AttributeSet; 2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.View; 306e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantlerimport android.view.ViewDebug.ExportedProperty; 3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.ViewGroup; 3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.ViewParent; 3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.FrameLayout; 3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.ImageView; 3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.R; 3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.util.ArrayList; 3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/** 4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Allows a drawable to be added for shadowing views in this layout. The shadows 4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * will automatically be sized to wrap their corresponding view. The default 4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * drawable to use can be set in xml by defining the namespace and then using 4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * defaultShadow="@drawable/reference" 4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * <p> 4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * In code views can then have Shadows added to them via 4765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link #addShadowView(View)} to use the default drawable or with 4865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link #addShadowView(View, Drawable)}. 4965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 5065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic class FrameLayoutWithShadows extends FrameLayout { 5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int MAX_RECYCLE = 12; 5365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 5465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane static class ShadowView extends View { 5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private View shadowedView; 5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private Drawable mDrawableBottom; 5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private float mAlpha = 1f; 5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ShadowView(Context context) { 6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane super(context); 6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane setWillNotDraw(false); 6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane void init() { 6665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadowedView = null; 6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDrawableBottom = null; 6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 7165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void setBackground(Drawable background) { 7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane super.setBackground(background); 7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (background != null) { 7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // framework adds a callback on background to trigger a repaint 7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // when call Drawable.setAlpha(), this is not desired when we override 7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // setAlpha(); if we call Drawable.setAlpha() in the overriden 7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // setAlpha(), it will trigger another repaint event thus cause system 7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // never stop rendering. 7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane background.setCallback(null); 8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane background.setAlpha((int)(255 * mAlpha)); 8165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void setAlpha(float alpha) { 8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mAlpha != alpha) { 8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mAlpha = alpha; 8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Drawable d = getBackground(); 8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int alphaMulitplied = (int)(alpha * 255); 9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (d != null) { 9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane d.setAlpha(alphaMulitplied); 9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mDrawableBottom != null) { 9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDrawableBottom.setAlpha(alphaMulitplied); 9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane invalidate(); 9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @ExportedProperty(category = "drawing") 10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public float getAlpha() { 10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mAlpha; 10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane protected boolean onSetAlpha(int alpha) { 10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return true; 10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void setDrawableBottom(Drawable drawable) { 11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDrawableBottom = drawable; 11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mAlpha >= 0) { 11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDrawableBottom.setAlpha((int)(255 * mAlpha)); 11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane invalidate(); 11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane protected void onDraw(Canvas canvas) { 12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // draw background 9 patch 12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane super.onDraw(canvas); 12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // draw bottom 12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mDrawableBottom != null) { 12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDrawableBottom.setBounds(getPaddingLeft(), getHeight() - getPaddingBottom(), 12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane getWidth() - getPaddingRight(), getHeight() - getPaddingBottom() 12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane + mDrawableBottom.getIntrinsicHeight()); 12865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDrawableBottom.draw(canvas); 12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 1336e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler private final Rect rect = new Rect(); 1346e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler private final RectF rectf = new RectF(); 13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mShadowResourceId; 13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mBottomResourceId; 13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private float mShadowsAlpha = 1f; 1386e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler private final ArrayList<ShadowView> mRecycleBin = new ArrayList<>(MAX_RECYCLE); 13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public FrameLayoutWithShadows(Context context) { 14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this(context, null); 14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public FrameLayoutWithShadows(Context context, AttributeSet attrs) { 14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this(context, attrs, 0); 14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public FrameLayoutWithShadows(Context context, AttributeSet attrs, int defStyle) { 14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane super(context, attrs, defStyle); 15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane initFromAttributes(context, attrs); 15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane protected void onLayout(boolean changed, int l, int t, int r, int b) { 15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane super.onLayout(changed, l, t, r, b); 15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane layoutShadows(); 15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void initFromAttributes(Context context, AttributeSet attrs) { 16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (attrs == null) { 16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return; 16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FrameLayoutWithShadows); 16465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane setDefaultShadowResourceId(a.getResourceId( 16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.styleable.FrameLayoutWithShadows_defaultShadow, 0)); 16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane setDrawableBottomResourceId(a.getResourceId( 16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.styleable.FrameLayoutWithShadows_drawableBottom, 0)); 16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane a.recycle(); 17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void setDefaultShadowResourceId(int id) { 17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mShadowResourceId = id; 17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public int getDefaultShadowResourceId() { 17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mShadowResourceId; 17965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 18065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 18165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void setDrawableBottomResourceId(int id) { 18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mBottomResourceId = id; 18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 18465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public int getDrawableBottomResourceId() { 18665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mBottomResourceId; 18765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 18865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 18965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void setShadowsAlpha(float alpha) { 19065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mShadowsAlpha = alpha; 19165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = getChildCount() - 1; i >= 0; i--) { 19265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane View shadow = getChildAt(i); 19365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (shadow instanceof ShadowView) { 19465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadow.setAlpha(alpha); 19565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 19665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 19765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 19865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 19965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 20065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * prune shadow views whose related view was detached from FrameLayoutWithShadows 20165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 20265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void prune() { 20365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (getWindowToken() ==null) { 20465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return; 20565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 20665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = getChildCount() - 1; i >= 0; i--) { 20765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane View shadow = getChildAt(i); 20865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (shadow instanceof ShadowView) { 20965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ShadowView shadowView = (ShadowView) shadow; 21065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane View view = shadowView.shadowedView; 21165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (this != findParentShadowsView(view)) { 21265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane view.setTag(R.id.ShadowView, null); 21365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadowView.shadowedView = null; 21465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane removeView(shadowView); 21565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane addToRecycleBin(shadowView); 21665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 21765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 21865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 21965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 22065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 22165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 22265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Perform a layout of the shadow views. This is done as part of the layout 22365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * pass for the view but may also be triggered manually if the borders of a 22465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * child view has changed. 22565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 22665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void layoutShadows() { 22765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane prune(); 22865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = getChildCount() - 1; i >= 0; i--) { 22965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane View shadow = getChildAt(i); 23065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!(shadow instanceof ShadowView)) { 23165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane continue; 23265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 23365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ShadowView shadowView = (ShadowView) shadow; 23465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane View view = shadowView.shadowedView; 23565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (view != null) { 23665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (this != findParentShadowsView(view)) { 23765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane continue; 23865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 23965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane boolean isImageMatrix = false; 24065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (view instanceof ImageView) { 24165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // For ImageView, we get the draw bounds of the image drawable, 24265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // which could be smaller than the imageView depending on ScaleType. 24365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Matrix matrix = ((ImageView) view).getImageMatrix(); 24465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Drawable drawable = ((ImageView) view).getDrawable(); 24565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (drawable != null) { 24665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane isImageMatrix = true; 24765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rect.set(drawable.getBounds()); 24865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rectf.set(rect); 24965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane matrix.mapRect(rectf); 25065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rectf.offset(view.getPaddingLeft(), view.getPaddingTop()); 25165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rectf.intersect(view.getPaddingLeft(), view.getPaddingTop(), 25265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane view.getWidth() - view.getPaddingLeft() - view.getPaddingRight(), 25365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane view.getHeight() - view.getPaddingTop() - view.getPaddingBottom()); 25465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rectf.left -= shadow.getPaddingLeft(); 25565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rectf.top -= shadow.getPaddingTop(); 25665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rectf.right += shadow.getPaddingRight(); 25765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rectf.bottom += shadow.getPaddingBottom(); 25865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rect.left = (int) (rectf.left + 0.5f); 25965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rect.top = (int) (rectf.top + 0.5f); 26065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rect.right = (int) (rectf.right + 0.5f); 26165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rect.bottom = (int) (rectf.bottom + 0.5f); 26265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 26365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 26465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!isImageMatrix){ 26565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rect.left = view.getPaddingLeft() - shadow.getPaddingLeft(); 26665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rect.top = view.getPaddingTop() - shadow.getPaddingTop(); 26765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rect.right = view.getWidth() + view.getPaddingRight() 26865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane + shadow.getPaddingRight(); 26965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane rect.bottom = view.getHeight() + view.getPaddingBottom() 27065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane + shadow.getPaddingBottom(); 27165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 27265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane offsetDescendantRectToMyCoords(view, rect); 27365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadow.layout(rect.left, rect.top, rect.right, rect.bottom); 27465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 27565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 27665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 27765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 27865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 27965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Add a shadow view to FrameLayoutWithShadows. This will use the drawable 28065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * specified for the shadow view and will also handle clean-up of any 28165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * previous shadow set for this view. 28265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 28365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public View addShadowView(View view, Drawable shadow) { 28465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ShadowView shadowView = (ShadowView) view.getTag(R.id.ShadowView); 28565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (shadowView == null) { 28665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadowView = getFromRecycleBin(); 28765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (shadowView == null) { 28865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadowView = new ShadowView(getContext()); 28965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadowView.setLayoutParams(new LayoutParams(0, 0)); 29065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 29165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane view.setTag(R.id.ShadowView, shadowView); 29265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadowView.shadowedView = view; 29365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane addView(shadowView, 0); 29465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 29565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadow.mutate(); 29665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadowView.setAlpha(mShadowsAlpha); 29765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadowView.setBackground(shadow); 29865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mBottomResourceId != 0) { 2996e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler Drawable d = getContext().getDrawable(mBottomResourceId); 30065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadowView.setDrawableBottom(d.mutate()); 30165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 30265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return shadowView; 30365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 30465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 30565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 30665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Add a shadow view using the default shadow. This will also handle 30765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * clean-up of any previous shadow set for this view. 30865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 30965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public View addShadowView(View view) { 3106e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler final Drawable shadow; 31165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mShadowResourceId != 0) { 3126e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler shadow = getContext().getDrawable(mShadowResourceId); 31365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 31465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return null; 31565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 31665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return addShadowView(view, shadow); 31765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 31865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 31965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 32065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Get the shadow associated with the given view. Returns null if the view 32165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * does not have a shadow. 32265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 32365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static View getShadowView(View view) { 32465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane View shadowView = (View) view.getTag(R.id.ShadowView); 32565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (shadowView != null) { 32665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return shadowView; 32765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 32865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return null; 32965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 33065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 33165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void setShadowViewUnderline(View shadowView, int underlineColor, int heightInPx) { 33265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ShapeDrawable drawable = new ShapeDrawable(); 33365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane drawable.setShape(new RectShape()); 33465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane drawable.setIntrinsicHeight(heightInPx); 33565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane drawable.getPaint().setColor(underlineColor); 33665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ((ShadowView) shadowView).setDrawableBottom(drawable); 33765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 33865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 33965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void setShadowViewUnderline(View shadowView, Drawable drawable) { 34065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ((ShadowView) shadowView).setDrawableBottom(drawable); 34165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 34265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 34365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 34465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Makes the shadow associated with the given view draw above other views. 34565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Subsequent calls to this or changes to the z-order may move the shadow 34665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * back down in the z-order. 34765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 34865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void bringViewShadowToTop(View view) { 34965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane View shadowView = (View) view.getTag(R.id.ShadowView); 35065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (shadowView == null) { 35165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return; 35265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 35365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int index = indexOfChild(shadowView); 35465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (index < 0) { 35565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // not found 35665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return; 35765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 35865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int lastIndex = getChildCount() - 1; 35965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (lastIndex == index) { 36065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // already last one 36165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return; 36265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 36365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane View lastShadowView = getChildAt(lastIndex); 36465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!(lastShadowView instanceof ShadowView)) { 36565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane removeView(shadowView); 36665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane addView(shadowView); 36765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 36865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane removeView(lastShadowView); 36965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane removeView(shadowView); 37065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane addView(lastShadowView, 0); 37165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane addView(shadowView); 37265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 37365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 37465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 37565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 37665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Utility function to remove the shadow associated with the given view. 37765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 37865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static void removeShadowView(View view) { 37965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ShadowView shadowView = (ShadowView) view.getTag(R.id.ShadowView); 38065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (shadowView != null) { 38165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane view.setTag(R.id.ShadowView, null); 38265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadowView.shadowedView = null; 38365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (shadowView.getRootView() != null) { 38465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ViewParent parent = shadowView.getParent(); 38565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (parent instanceof ViewGroup) { 38665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ((ViewGroup) parent).removeView(shadowView); 38765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (parent instanceof FrameLayoutWithShadows) { 38865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ((FrameLayoutWithShadows) parent).addToRecycleBin(shadowView); 38965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 39065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 39165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 39265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 39365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 39465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 39565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void addToRecycleBin(ShadowView shadowView) { 39665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mRecycleBin.size() < MAX_RECYCLE) { 39765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mRecycleBin.add(shadowView); 39865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 39965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 40065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 40165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public ShadowView getFromRecycleBin() { 40265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int size = mRecycleBin.size(); 40365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (size > 0) { 40465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ShadowView view = mRecycleBin.remove(size - 1); 40565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane view.init(); 40665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 40765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return null; 40865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 40965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 41065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 41165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Sets the visibility of the shadow associated with the given view. This 41265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * should be called when the view's visibility changes to keep the shadow's 41365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * visibility in sync. 41465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 41565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void setShadowVisibility(View view, int visibility) { 41665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane View shadowView = (View) view.getTag(R.id.ShadowView); 41765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (shadowView != null) { 41865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shadowView.setVisibility(visibility); 41965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return; 42065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 42165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 42265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 42365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 42465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Finds the first parent of this view that is a FrameLayoutWithShadows and 42565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * returns that or null if there is none. 42665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 42765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static FrameLayoutWithShadows findParentShadowsView(View view) { 42865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ViewParent nextView = view.getParent(); 42965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane while (nextView != null && !(nextView instanceof FrameLayoutWithShadows)) { 43065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane nextView = nextView.getParent(); 43165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 43265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return (FrameLayoutWithShadows) nextView; 43365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 43465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane} 435