/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.design.internal; import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; import android.support.annotation.RestrictTo; import android.support.design.R; import android.support.v7.widget.LinearLayoutCompat; import android.util.AttributeSet; import android.view.Gravity; /** * @hide */ @RestrictTo(LIBRARY_GROUP) public class ForegroundLinearLayout extends LinearLayoutCompat { private Drawable mForeground; private final Rect mSelfBounds = new Rect(); private final Rect mOverlayBounds = new Rect(); private int mForegroundGravity = Gravity.FILL; protected boolean mForegroundInPadding = true; boolean mForegroundBoundsChanged = false; public ForegroundLinearLayout(Context context) { this(context, null); } public ForegroundLinearLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ForegroundLinearLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForegroundLinearLayout, defStyle, 0); mForegroundGravity = a.getInt( R.styleable.ForegroundLinearLayout_android_foregroundGravity, mForegroundGravity); final Drawable d = a.getDrawable(R.styleable.ForegroundLinearLayout_android_foreground); if (d != null) { setForeground(d); } mForegroundInPadding = a.getBoolean( R.styleable.ForegroundLinearLayout_foregroundInsidePadding, true); a.recycle(); } /** * Describes how the foreground is positioned. * * @return foreground gravity. * @see #setForegroundGravity(int) */ @Override public int getForegroundGravity() { return mForegroundGravity; } /** * Describes how the foreground is positioned. Defaults to START and TOP. * * @param foregroundGravity See {@link android.view.Gravity} * @see #getForegroundGravity() */ @Override public void setForegroundGravity(int foregroundGravity) { if (mForegroundGravity != foregroundGravity) { if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { foregroundGravity |= Gravity.START; } if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { foregroundGravity |= Gravity.TOP; } mForegroundGravity = foregroundGravity; if (mForegroundGravity == Gravity.FILL && mForeground != null) { Rect padding = new Rect(); mForeground.getPadding(padding); } requestLayout(); } } @Override protected boolean verifyDrawable(Drawable who) { return super.verifyDrawable(who) || (who == mForeground); } @RequiresApi(11) @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); if (mForeground != null) { mForeground.jumpToCurrentState(); } } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (mForeground != null && mForeground.isStateful()) { mForeground.setState(getDrawableState()); } } /** * Supply a Drawable that is to be rendered on top of all of the child * views in the frame layout. Any padding in the Drawable will be taken * into account by ensuring that the children are inset to be placed * inside of the padding area. * * @param drawable The Drawable to be drawn on top of the children. */ @Override public void setForeground(Drawable drawable) { if (mForeground != drawable) { if (mForeground != null) { mForeground.setCallback(null); unscheduleDrawable(mForeground); } mForeground = drawable; if (drawable != null) { setWillNotDraw(false); drawable.setCallback(this); if (drawable.isStateful()) { drawable.setState(getDrawableState()); } if (mForegroundGravity == Gravity.FILL) { Rect padding = new Rect(); drawable.getPadding(padding); } } else { setWillNotDraw(true); } requestLayout(); invalidate(); } } /** * Returns the drawable used as the foreground of this FrameLayout. The * foreground drawable, if non-null, is always drawn on top of the children. * * @return A Drawable or null if no foreground was set. */ @Override public Drawable getForeground() { return mForeground; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); mForegroundBoundsChanged |= changed; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mForegroundBoundsChanged = true; } @Override public void draw(@NonNull Canvas canvas) { super.draw(canvas); if (mForeground != null) { final Drawable foreground = mForeground; if (mForegroundBoundsChanged) { mForegroundBoundsChanged = false; final Rect selfBounds = mSelfBounds; final Rect overlayBounds = mOverlayBounds; final int w = getRight() - getLeft(); final int h = getBottom() - getTop(); if (mForegroundInPadding) { selfBounds.set(0, 0, w, h); } else { selfBounds.set(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(), h - getPaddingBottom()); } Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), foreground.getIntrinsicHeight(), selfBounds, overlayBounds); foreground.setBounds(overlayBounds); } foreground.draw(canvas); } } @RequiresApi(21) @Override public void drawableHotspotChanged(float x, float y) { super.drawableHotspotChanged(x, y); if (mForeground != null) { mForeground.setHotspot(x, y); } } }