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