J'aimerais savoir comment appliquer ou émuler un effet de premier plan dans une vue différente de FrameLayout, telle que LinearLayout ou RelativeLayout
Voici ce que j'ai maintenant:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
Android:id="@+id/cardContent"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:background="@drawable/row_background"
Android:foreground="@drawable/foreground_row">
...
</FrameLayout>
Et je veux quelque chose comme:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
Android:id="@+id/cardContent"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:background="@drawable/row_background"
app:foreground="@drawable/foreground_row">
...
</RelativeLayout>
Merci d'avance!!
L'idée est d'entourer votre mise en page d'un FrameLayout et de définir le sélecteur et l'événement onClick sur cette mise en page.
<FrameLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/selectableItem"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:foreground="@drawable/foreground_row"
>
<RelativeLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
Android:id="@+id/cardContent"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:background="@drawable/row_background">
...
</RelativeLayout>
</FrameLayout>
Vous pouvez trouver une explication complète sur mon blog:
http://antonioleiva.com/unveiling-bandhook-foreground-any-layout/
Ou vous pouvez prolonger rhis FRelativeLayout
https://Gist.github.com/shakalaca/6199283
Checkout ForegroundView bibliothèque avec intégration Gradle. Il supporte les vues suivantes
Voici une implémentation possible, qui prend également en charge la vérification d’une mise en page.
Une solution presque identique peut être appliquée à n'importe quelle vue de mise en page que vous souhaitez (seul le CTOR est différent).
public class CheckableLinearLayout extends LinearLayout implements Checkable {
private boolean mChecked;
private static final String TAG = CheckableLinearLayout.class.getCanonicalName();
private static final int[] CHECKED_STATE_SET = { Android.R.attr.state_checked };
private Drawable mForeground;
public CheckableLinearLayout(final Context context) {
this(context, null, 0);
}
public CheckableLinearLayout(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public CheckableLinearLayout(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
final TypedArray a = context
.obtainStyledAttributes(attrs, R.styleable.CheckableLinearLayout, defStyle, 0);
setForegroundEx(a.getDrawable(R.styleable.CheckableLinearLayout_foreground));
a.recycle();
}
public void setForegroundEx(final Drawable drawable) {
this.mForeground = drawable;
}
@Override
protected int[] onCreateDrawableState(final int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
final Drawable drawable = getBackground();
boolean needRedraw = false;
final int[] myDrawableState = getDrawableState();
if (drawable != null) {
drawable.setState(myDrawableState);
needRedraw = true;
}
if (mForeground != null) {
mForeground.setState(myDrawableState);
needRedraw = true;
}
if (needRedraw)
invalidate();
}
@Override
protected void onSizeChanged(final int width, final int height, final int oldwidth, final int oldheight) {
super.onSizeChanged(width, height, oldwidth, oldheight);
if (mForeground != null)
mForeground.setBounds(0, 0, width, height);
}
@Override
protected void dispatchDraw(final Canvas canvas) {
super.dispatchDraw(canvas);
if (mForeground != null)
mForeground.draw(canvas);
}
@Override
public boolean isChecked() {
return mChecked;
}
@Override
public void setChecked(final boolean checked) {
mChecked = checked;
refreshDrawableState();
//TODO think if you wish to also check inner views, maybe even recursively
}
@Override
public void toggle() {
setChecked(!mChecked);
}
@Override
public Parcelable onSaveInstanceState() {
// Force our ancestor class to save its state
final Parcelable superState = super.onSaveInstanceState();
final SavedState savedState = new SavedState(superState);
savedState.checked = isChecked();
return savedState;
}
@Override
public void onRestoreInstanceState(final Parcelable state) {
final SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
setChecked(savedState.checked);
requestLayout();
}
@SuppressLint("ClickableViewAccessibility")
@TargetApi(Build.VERSION_CODES.Lollipop)
@Override
public boolean onTouchEvent(final MotionEvent e) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop && //
e.getActionMasked() == MotionEvent.ACTION_DOWN && //
mForeground != null)
mForeground.setHotspot(e.getX(), e.getY());
return super.onTouchEvent(e);
}
// /////////////
// SavedState //
// /////////////
private static class SavedState extends BaseSavedState {
boolean checked;
SavedState(final Parcelable superState) {
super(superState);
}
private SavedState(final Parcel in) {
super(in);
checked = (Boolean) in.readValue(null);
}
@Override
public void writeToParcel(final Parcel out, final int flags) {
super.writeToParcel(out, flags);
out.writeValue(checked);
}
@Override
public String toString() {
return TAG + ".SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " checked=" + checked
+ "}";
}
@SuppressWarnings("unused")
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(final Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(final int size) {
return new SavedState[size];
}
};
}
}
attr.xml
<declare-styleable name="CheckableLinearLayout">
<attr name="foreground"/>
</declare-styleable>
Voici un moyen plus simple de le faire, en utilisant Kotlin, et sans être vérifiable:
class LinearLayoutEx @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : LinearLayout(context, attrs, defStyle) {
var foregroundDrawable: Drawable? = null
set(value) {
field = value
invalidate()
}
init {
val a = context
.obtainStyledAttributes(attrs, R.styleable.LinearLayoutEx, defStyle, 0)
foregroundDrawable = a.getDrawable(R.styleable.LinearLayoutEx_foreground)
a.recycle()
}
override fun drawableStateChanged() {
super.drawableStateChanged()
val drawable = background
var needRedraw = false
val myDrawableState = drawableState
if (drawable != null) {
drawable.state = myDrawableState
needRedraw = true
}
if (foregroundDrawable != null) {
foregroundDrawable!!.state = myDrawableState
needRedraw = true
}
if (needRedraw)
invalidate()
}
override fun onSizeChanged(width: Int, height: Int, oldwidth: Int, oldheight: Int) {
super.onSizeChanged(width, height, oldwidth, oldheight)
foregroundDrawable?.setBounds(0, 0, width, height)
}
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
foregroundDrawable?.draw(canvas)
}
@SuppressLint("ClickableViewAccessibility")
@TargetApi(Build.VERSION_CODES.Lollipop)
override fun onTouchEvent(e: MotionEvent): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop && e.actionMasked == MotionEvent.ACTION_DOWN)
foregroundDrawable?.setHotspot(e.x, e.y)
return super.onTouchEvent(e)
}
}
attr.xml
<declare-styleable name="LinearLayoutEx" tools:ignore="MissingDefaultResource">
<attr name="foreground"/>
</declare-styleable>