web-dev-qa-db-fra.com

Comment mettre en œuvre l'élévation de conception matérielle pour pré-sucette

Google a montré de belles façons de montrer l'effet d'élévation sur Lollipop ici .

Android:elevation="2dp"

pour les boutons,

Android:stateListAnimator="@anim/button_state_list_animator"

Comment puis-je imiter l'effet d'altitude sur les versions antérieures à Lollipop sans bibliothèque tierce?

40
AndroidDev

Vous pouvez imiter l'élévation avant Lollipop avec une méthode officielle.

J'obtiens le même effet en utilisant,

  Android:background="@Android:drawable/dialog_holo_light_frame"

Ma sortie testée:

enter image description here

référence - https://stackoverflow.com/a/25683148/3879847

Merci à l'utilisateur @Repo ..

Mise à jour: Si vous souhaitez modifier la couleur de cet dessin, essayez la réponse @Irfan ci-dessous

https://stackoverflow.com/a/40815944/3879847

72
Ranjith Kumar

Vous ne pouvez pas imiter l'élévation sur pré-Lollipop avec une méthode officielle.

Vous pouvez utiliser certains éléments dessinables pour créer l’ombre dans votre composant. Google utilise cette manière dans CardView par exemple.

La ViewCompat.setElevation(View, int) crée actuellement l'ombre uniquement sur API21 +. Si vous vérifiez le code derrière, cette méthode appelle:

API 21 +:

  @Override
    public void setElevation(View view, float elevation) {
        ViewCompatLollipop.setElevation(view, elevation);
    }

API <21

@Override
public void setElevation(View view, float elevation) {

}
22
Gabriele Mariotti

Vous pouvez soit le pirater en utilisant une vue de carte:

<Android.support.v7.widget.CardView
    xmlns:card_view="http://schemas.Android.com/apk/res-auto"
    Android:id="@+id/btnGetStuff"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    card_view:cardCornerRadius="4dp"
    card_view:cardBackgroundColor="@color/accent"
    >
    <!-- you could also add image view here for icon etc. -->
    <TextView
        Android:id="@+id/txtGetStuff"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:textSize="@dimen/textSize_small"
        Android:textColor="@color/primary_light"
        Android:freezesText="true"
        Android:text="Get Stuff"
        Android:maxWidth="120dp"
        Android:singleLine="true"
        Android:ellipsize="end"
        Android:maxLines="1"
        /></Android.support.v7.widget.CardView>

Ou utilisez cette bibliothèque tierce: https://github.com/rey5137/Material (voir l'article du wiki sur le bouton https://github.com/rey5137/Material/wiki/Bouton )

13
Steven Mark Ford

Pour créer des ombres dynamiques et animées sur les appareils pré-Lollipop, vous devez:

  1. Dessine une forme noire d'une vue en bitmap
  2. Estompez cette forme en utilisant l'altitude comme rayon. Vous pouvez le faire en utilisant RenderScript. Ce n'est pas exactement la méthode utilisée par Lollipop, mais elle donne de bons résultats et est facile à ajouter aux vues existantes.
  3. Dessinez cette forme floue sous la vue. Le meilleur endroit est probablement la méthode drawChild. Vous devez également redéfinir setElevation et setTranslationZ, redéfinir le dessin de la vue enfant dans les mises en forme, désactiver les animateurs de clip-to-padding et implémenter les états.

enter image description here

C'est beaucoup de travail, mais cela donne les meilleurs ombres dynamiques avec des animations de réponse. Je ne sais pas pourquoi vous voudriez y parvenir sans bibliothèques tierces. Si vous le souhaitez, vous pouvez analyser les sources de carbone et transférer les pièces que vous souhaitez inclure dans votre application:

génération des ombres

private static void blurRenderScript(Bitmap bitmap, float radius) {
    Allocation inAllocation = Allocation.createFromBitmap(renderScript, bitmap,
            Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
    Allocation outAllocation = Allocation.createTyped(renderScript, inAllocation.getType());

    blurShader.setRadius(radius);
    blurShader.setInput(inAllocation);
    blurShader.forEach(outAllocation);

    outAllocation.copyTo(bitmap);
}

public static Shadow generateShadow(View view, float elevation) {
    if (!software && renderScript == null) {
        try {
            renderScript = RenderScript.create(view.getContext());
            blurShader = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
        } catch (RSRuntimeException ignore) {
            software = true;
        }
    }

    ShadowView shadowView = (ShadowView) view;
    CornerView cornerView = (CornerView) view;
    boolean isRect = shadowView.getShadowShape() == ShadowShape.RECT ||
            shadowView.getShadowShape() == ShadowShape.ROUND_RECT && cornerView.getCornerRadius() < view.getContext().getResources().getDimension(R.dimen.carbon_1dip) * 2.5;

    int e = (int) Math.ceil(elevation);
    Bitmap bitmap;
    if (isRect) {
        bitmap = Bitmap.createBitmap(e * 4 + 1, e * 4 + 1, Bitmap.Config.ARGB_8888);

        Canvas shadowCanvas = new Canvas(bitmap);
        Paint.setStyle(Paint.Style.FILL);
        Paint.setColor(0xff000000);

        shadowCanvas.drawRect(e, e, e * 3 + 1, e * 3 + 1, Paint);

        blur(bitmap, elevation);

        return new NinePatchShadow(bitmap, elevation);
    } else {
        bitmap = Bitmap.createBitmap((int) (view.getWidth() / SHADOW_SCALE + e * 2), (int) (view.getHeight() / SHADOW_SCALE + e * 2), Bitmap.Config.ARGB_8888);

        Canvas shadowCanvas = new Canvas(bitmap);
        Paint.setStyle(Paint.Style.FILL);
        Paint.setColor(0xff000000);

        if (shadowView.getShadowShape() == ShadowShape.ROUND_RECT) {
            roundRect.set(e, e, (int) (view.getWidth() / SHADOW_SCALE - e), (int) (view.getHeight() / SHADOW_SCALE - e));
            shadowCanvas.drawRoundRect(roundRect, e, e, Paint);
        } else {
            int r = (int) (view.getWidth() / 2 / SHADOW_SCALE);
            shadowCanvas.drawCircle(r + e, r + e, r, Paint);
        }

        blur(bitmap, elevation);

        return new Shadow(bitmap, elevation);
    }
}

Dessin d'une vue avec une ombre

@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    if (!child.isShown())
        return super.drawChild(canvas, child, drawingTime);

    if (!isInEditMode() && child instanceof ShadowView && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KitKat_WATCH) {
        ShadowView shadowView = (ShadowView) child;
        Shadow shadow = shadowView.getShadow();
        if (shadow != null) {
            Paint.setAlpha((int) (ShadowGenerator.ALPHA * ViewHelper.getAlpha(child)));

            float childElevation = shadowView.getElevation() + shadowView.getTranslationZ();

            float[] childLocation = new float[]{(child.getLeft() + child.getRight()) / 2, (child.getTop() + child.getBottom()) / 2};
            Matrix matrix = carbon.internal.ViewHelper.getMatrix(child);
            matrix.mapPoints(childLocation);

            int[] location = new int[2];
            getLocationOnScreen(location);
            float x = childLocation[0] + location[0];
            float y = childLocation[1] + location[1];
            x -= getRootView().getWidth() / 2;
            y += getRootView().getHeight() / 2;   // looks Nice
            float length = (float) Math.sqrt(x * x + y * y);

            int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
            canvas.translate(
                    x / length * childElevation / 2,
                    y / length * childElevation / 2);
            canvas.translate(
                    child.getLeft(),
                    child.getTop());

            canvas.concat(matrix);
            canvas.scale(ShadowGenerator.SHADOW_SCALE, ShadowGenerator.SHADOW_SCALE);
            shadow.draw(canvas, child, Paint);
            canvas.restoreToCount(saveCount);
        }
    }

    if (child instanceof RippleView) {
        RippleView rippleView = (RippleView) child;
        RippleDrawable rippleDrawable = rippleView.getRippleDrawable();
        if (rippleDrawable != null && rippleDrawable.getStyle() == RippleDrawable.Style.Borderless) {
            int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
            canvas.translate(
                    child.getLeft(),
                    child.getTop());
            rippleDrawable.draw(canvas);
            canvas.restoreToCount(saveCount);
        }
    }

    return super.drawChild(canvas, child, drawingTime);
}

API d'élévation rétroportée à pré-Lollipop

private float elevation = 0;
private float translationZ = 0;
private Shadow shadow;

@Override
public float getElevation() {
    return elevation;
}

public synchronized void setElevation(float elevation) {
    if (elevation == this.elevation)
        return;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop)
        super.setElevation(elevation);
    this.elevation = elevation;
    if (getParent() != null)
        ((View) getParent()).postInvalidate();
}

@Override
public float getTranslationZ() {
    return translationZ;
}

public synchronized void setTranslationZ(float translationZ) {
    if (translationZ == this.translationZ)
        return;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop)
        super.setTranslationZ(translationZ);
    this.translationZ = translationZ;
    if (getParent() != null)
        ((View) getParent()).postInvalidate();
}

@Override
public ShadowShape getShadowShape() {
    if (cornerRadius == getWidth() / 2 && getWidth() == getHeight())
        return ShadowShape.CIRCLE;
    if (cornerRadius > 0)
        return ShadowShape.ROUND_RECT;
    return ShadowShape.RECT;
}

@Override
public void setEnabled(boolean enabled) {
    super.setEnabled(enabled);
    setTranslationZ(enabled ? 0 : -elevation);
}

@Override
public Shadow getShadow() {
    float elevation = getElevation() + getTranslationZ();
    if (elevation >= 0.01f && getWidth() > 0 && getHeight() > 0) {
        if (shadow == null || shadow.elevation != elevation)
            shadow = ShadowGenerator.generateShadow(this, elevation);
        return shadow;
    }
    return null;
}

@Override
public void invalidateShadow() {
    shadow = null;
    if (getParent() != null && getParent() instanceof View)
        ((View) getParent()).postInvalidate();
}
8
Zielony

Créez une image 9-patch avec des patchs extensibles définis sur une image entourée d’ombre.

enter image description here

Ajoutez cette image de 9 patchs en tant qu'arrière-plan de votre bouton avec un rembourrage pour que l'ombre soit visible.

Vous pouvez trouver des images prédéfinies de 9 correctifs (.9.png) ici ou ici à partir desquelles vous pouvez sélectionner, personnaliser et copier le dessin de votre projet.

8
random

ajouter @Ranjith Kumar answer

Pour ajouter une couleur d’arrière-plan au dessinable (exemple: couleur d’arrière-plan du bouton), vous devez le faire par programmation.

d'abord obtenir le drawable

Drawable drawable = getResources().getDrawable(Android.R.drawable.dialog_holo_light_frame);

définir la couleur

drawable.setColorFilter(new PorterDuffColorFilter(getResources().getColor(R.color.color_primary), PorterDuff.Mode.MULTIPLY));

puis définissez-le à la vue.

view.setBackgroundDrawable(drawable);

au cas où quelqu'un cherche.

6
Irfan

vous pouvez facilement simuler en déclarant un drawable comme celui-ci -

shadow.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:Android="http://schemas.Android.com/apk/res/Android"

    >

    <gradient Android:type="linear" Android:angle="270" Android:startColor="#b6b6b6" Android:endColor="#ffffff"/>


</shape>

et l'utiliser int ur principal xml comme -

 Android:background="@drawable/shadow"
1
Sniper