web-dev-qa-db-fra.com

Comment définir l'attribut d'avant-plan sur une autre vue que FrameLayout

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!!

12
cesards

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 FRelativeLayouthttps://Gist.github.com/shakalaca/6199283

19
Antonio

Checkout ForegroundView bibliothèque avec intégration Gradle. Il supporte les vues suivantes

  • Avant-planImageView
  • Premier plan
  • ForegroundTextView
  • ForegroundImageButton
  • ForegroundEditText
  • Avant-plan WebView
  • Avant-planLinearLayout
  • Avant-planRelativeLayout
  • ForegroundGridLayout
  • ForegroundGridView
  • Premier planHorizontalScrollView
  • ForegroundListView
  • ForegroundScrollViewForegroundImageView
2
Bao Le

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>
1
android developer