ForegroundLinearLayout.java revision c39d9c75590eca86a5e7e32a8824ba04a0d42e9b
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.design.internal; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.graphics.Canvas; 22import android.graphics.Rect; 23import android.graphics.drawable.Drawable; 24import android.support.annotation.NonNull; 25import android.support.annotation.RestrictTo; 26import android.support.design.R; 27import android.support.v7.widget.LinearLayoutCompat; 28import android.util.AttributeSet; 29import android.view.Gravity; 30 31import static android.support.annotation.RestrictTo.Scope.GROUP_ID; 32 33/** 34 * @hide 35 */ 36@RestrictTo(GROUP_ID) 37public class ForegroundLinearLayout extends LinearLayoutCompat { 38 39 private Drawable mForeground; 40 41 private final Rect mSelfBounds = new Rect(); 42 43 private final Rect mOverlayBounds = new Rect(); 44 45 private int mForegroundGravity = Gravity.FILL; 46 47 protected boolean mForegroundInPadding = true; 48 49 boolean mForegroundBoundsChanged = false; 50 51 public ForegroundLinearLayout(Context context) { 52 this(context, null); 53 } 54 55 public ForegroundLinearLayout(Context context, AttributeSet attrs) { 56 this(context, attrs, 0); 57 } 58 59 public ForegroundLinearLayout(Context context, AttributeSet attrs, int defStyle) { 60 super(context, attrs, defStyle); 61 62 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForegroundLinearLayout, 63 defStyle, 0); 64 65 mForegroundGravity = a.getInt( 66 R.styleable.ForegroundLinearLayout_android_foregroundGravity, mForegroundGravity); 67 68 final Drawable d = a.getDrawable(R.styleable.ForegroundLinearLayout_android_foreground); 69 if (d != null) { 70 setForeground(d); 71 } 72 73 mForegroundInPadding = a.getBoolean( 74 R.styleable.ForegroundLinearLayout_foregroundInsidePadding, true); 75 76 a.recycle(); 77 } 78 79 /** 80 * Describes how the foreground is positioned. 81 * 82 * @return foreground gravity. 83 * @see #setForegroundGravity(int) 84 */ 85 public int getForegroundGravity() { 86 return mForegroundGravity; 87 } 88 89 /** 90 * Describes how the foreground is positioned. Defaults to START and TOP. 91 * 92 * @param foregroundGravity See {@link android.view.Gravity} 93 * @see #getForegroundGravity() 94 */ 95 public void setForegroundGravity(int foregroundGravity) { 96 if (mForegroundGravity != foregroundGravity) { 97 if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { 98 foregroundGravity |= Gravity.START; 99 } 100 101 if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { 102 foregroundGravity |= Gravity.TOP; 103 } 104 105 mForegroundGravity = foregroundGravity; 106 107 if (mForegroundGravity == Gravity.FILL && mForeground != null) { 108 Rect padding = new Rect(); 109 mForeground.getPadding(padding); 110 } 111 112 requestLayout(); 113 } 114 } 115 116 @Override 117 protected boolean verifyDrawable(Drawable who) { 118 return super.verifyDrawable(who) || (who == mForeground); 119 } 120 121 @Override 122 public void jumpDrawablesToCurrentState() { 123 super.jumpDrawablesToCurrentState(); 124 if (mForeground != null) { 125 mForeground.jumpToCurrentState(); 126 } 127 } 128 129 @Override 130 protected void drawableStateChanged() { 131 super.drawableStateChanged(); 132 if (mForeground != null && mForeground.isStateful()) { 133 mForeground.setState(getDrawableState()); 134 } 135 } 136 137 /** 138 * Supply a Drawable that is to be rendered on top of all of the child 139 * views in the frame layout. Any padding in the Drawable will be taken 140 * into account by ensuring that the children are inset to be placed 141 * inside of the padding area. 142 * 143 * @param drawable The Drawable to be drawn on top of the children. 144 */ 145 public void setForeground(Drawable drawable) { 146 if (mForeground != drawable) { 147 if (mForeground != null) { 148 mForeground.setCallback(null); 149 unscheduleDrawable(mForeground); 150 } 151 152 mForeground = drawable; 153 154 if (drawable != null) { 155 setWillNotDraw(false); 156 drawable.setCallback(this); 157 if (drawable.isStateful()) { 158 drawable.setState(getDrawableState()); 159 } 160 if (mForegroundGravity == Gravity.FILL) { 161 Rect padding = new Rect(); 162 drawable.getPadding(padding); 163 } 164 } else { 165 setWillNotDraw(true); 166 } 167 requestLayout(); 168 invalidate(); 169 } 170 } 171 172 /** 173 * Returns the drawable used as the foreground of this FrameLayout. The 174 * foreground drawable, if non-null, is always drawn on top of the children. 175 * 176 * @return A Drawable or null if no foreground was set. 177 */ 178 public Drawable getForeground() { 179 return mForeground; 180 } 181 182 @Override 183 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 184 super.onLayout(changed, left, top, right, bottom); 185 mForegroundBoundsChanged |= changed; 186 } 187 188 @Override 189 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 190 super.onSizeChanged(w, h, oldw, oldh); 191 mForegroundBoundsChanged = true; 192 } 193 194 @Override 195 public void draw(@NonNull Canvas canvas) { 196 super.draw(canvas); 197 198 if (mForeground != null) { 199 final Drawable foreground = mForeground; 200 201 if (mForegroundBoundsChanged) { 202 mForegroundBoundsChanged = false; 203 final Rect selfBounds = mSelfBounds; 204 final Rect overlayBounds = mOverlayBounds; 205 206 final int w = getRight() - getLeft(); 207 final int h = getBottom() - getTop(); 208 209 if (mForegroundInPadding) { 210 selfBounds.set(0, 0, w, h); 211 } else { 212 selfBounds.set(getPaddingLeft(), getPaddingTop(), 213 w - getPaddingRight(), h - getPaddingBottom()); 214 } 215 216 Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), 217 foreground.getIntrinsicHeight(), selfBounds, overlayBounds); 218 foreground.setBounds(overlayBounds); 219 } 220 221 foreground.draw(canvas); 222 } 223 } 224 225 @Override 226 public void drawableHotspotChanged(float x, float y) { 227 super.drawableHotspotChanged(x, y); 228 if (mForeground != null) { 229 mForeground.setHotspot(x, y); 230 } 231 } 232 233} 234