Created
February 19, 2014 13:16
-
-
Save chrisbanes/9091754 to your computer and use it in GitHub Desktop.
ForegroundLinearLayout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources> | |
<declare-styleable name="ForegroundLinearLayout"> | |
<attr name="android:foreground" /> | |
<attr name="android:foregroundInsidePadding" /> | |
<attr name="android:foregroundGravity" /> | |
</declare-styleable> | |
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2006 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 your.package; | |
import android.content.Context; | |
import android.content.res.TypedArray; | |
import android.graphics.Canvas; | |
import android.graphics.Rect; | |
import android.graphics.drawable.Drawable; | |
import android.util.AttributeSet; | |
import android.view.Gravity; | |
import android.widget.LinearLayout; | |
import your.package.R; | |
public class ForegroundLinearLayout extends LinearLayout { | |
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) { | |
super(context); | |
} | |
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_android_foregroundInsidePadding, true); | |
a.recycle(); | |
} | |
/** | |
* Describes how the foreground is positioned. | |
* | |
* @return foreground gravity. | |
* | |
* @see #setForegroundGravity(int) | |
*/ | |
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() | |
*/ | |
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); | |
} | |
@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. | |
*/ | |
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. | |
*/ | |
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(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); | |
} | |
} | |
} |
The simplest fix would probably be to give the ripple a mask so that it's clipped by the view's bounds.
minSdkVersion 17 it's getting crash and the log cat error "Caused by: java.lang.NoSuchMethodError: android.widget.LinearLayout.".
It might be not support for lower version ?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Using a RippleDrawable as the background causes it to bleed out of the layout bounds. Setting it to the foreground works fine, however.
Anyone know of a fix for this?