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