web-dev-qa-db-fra.com

Modification de la couleur de l'ombre de CardView

Cette question a été posée plusieurs fois sur SO), mais je n’ai toujours pas trouvé de solution satisfaisante à ce problème.

Pourquoi ai-je besoin de ça? Bien parce que projet moi et mon équipe développe a style iOS.

Qu'est-ce que j'ai essayé?

  1. générateur d'ombre 9.pathch mais 9.pathes sont essentiellement des pngs et cela ne me donne aucune flexibilité et si j'utilise cette approche, je devrais éditer les marges partout.
  2. bibliothèque Carbon il prend en charge les ombres personnalisées et elles sont dessinées en dehors des limites de la vue, mais il existe problème en ce qui concerne les rectangles arrondis, lorsque la bibliothèque ne dessine pas d'ombre pour les coins arrondis.
  3. en utilisant l'ancienne implémentation de CardView et en remplaçant sa couleur d'ombre, mais il est tracé à l'intérieur des limites de la carte, il n'est donc pas une option.

Existe-t-il donc un moyen de modifier la couleur de l'ombre de CardView avec des modifications minimales de tous les fichiers de présentation et de dessiner une ombre en dehors de la vue, comme le fait CardView d'origine?

20
Mike Herasimov

Considérez ce fil sur Twitter, où Nick Butcher explique comment implémenter la fonctionnalité:

enter image description here

Voir outlineAmbientShadowColor , outlineSpotShadowColor , spotShadowAlpha et ambientShadowAlpha attributs pour plus de détails. Malheureusement, c'est possible à partir de l'API 28.

Pour les API inférieures, Nick a partagé un Gist . Voici le résultat:

Fonctionnant sur l'API 21

Cette technique n'est pas directement connectée à CardView, elle peut être appliquée à n'importe quel View.

20
azizbekian

Vous pouvez implémenter ceci sans avoir un cardview, et peut également avoir toutes les propriétés de cardview

Vous devez faire:

  1. Copier les deux classes

  2. Enveloppez votre vue requise avec la vue personnalisée comme dans l'exemple, vous n'avez pas à faire beaucoup de changements dans votre mise en page ou ailleurs!

La classe ci-dessous créera une vue personnalisée qui encapsulera votre mise en page/vue pour qu'elle s'affiche dans la vue de la carte avec une couleur d'ombre personnalisée.

Créer une classe:

import Android.content.Context;
import Android.support.annotation.Nullable;
import Android.support.v4.content.ContextCompat;
import Android.util.AttributeSet;
import Android.view.Gravity;
import Android.widget.LinearLayout;

import com.qzion.nfscrew.R;


public class RoundLinerLayoutNormal extends LinearLayout {
    public RoundLinerLayoutNormal(Context context) {
        super(context);
        initBackground();
    }

    public RoundLinerLayoutNormal(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initBackground();
    }

    public RoundLinerLayoutNormal(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initBackground();
    }

    private void initBackground() {
        setBackground(ViewUtils.generateBackgroundWithShadow(this,R.color.white,
                R.dimen.radius_corner,R.color.colorPrimaryDark,R.dimen.elevation, Gravity.BOTTOM));
    }
}

Créez également la classe pour les paramètres de l’ombre, ViewUtils.Java.

import Android.graphics.Paint;
import Android.graphics.Rect;
import Android.graphics.drawable.Drawable;
import Android.graphics.drawable.LayerDrawable;
import Android.graphics.drawable.ShapeDrawable;
import Android.graphics.drawable.shapes.RoundRectShape;
import Android.support.annotation.ColorRes;
import Android.support.annotation.DimenRes;
import Android.support.v4.content.ContextCompat;
import Android.view.Gravity;
import Android.view.View;

import static Android.support.v4.view.ViewCompat.LAYER_TYPE_SOFTWARE;


public class ViewUtils {

    public static Drawable generateBackgroundWithShadow(View view, @ColorRes int backgroundColor,
                                                        @DimenRes int cornerRadius,
                                                        @ColorRes int shadowColor,
                                                        @DimenRes int elevation,
                                                        int shadowGravity) {
        float cornerRadiusValue = view.getContext().getResources().getDimension(cornerRadius);
        int elevationValue = (int) view.getContext().getResources().getDimension(elevation);
        int shadowColorValue = ContextCompat.getColor(view.getContext(),shadowColor);
        int backgroundColorValue = ContextCompat.getColor(view.getContext(),backgroundColor);

        float[] outerRadius = {cornerRadiusValue, cornerRadiusValue, cornerRadiusValue,
                cornerRadiusValue, cornerRadiusValue, cornerRadiusValue, cornerRadiusValue,
                cornerRadiusValue};

        Paint backgroundPaint = new Paint();
        backgroundPaint.setStyle(Paint.Style.FILL);
        backgroundPaint.setShadowLayer(cornerRadiusValue, 0, 0, 0);

        Rect shapeDrawablePadding = new Rect();
        shapeDrawablePadding.left = elevationValue;
        shapeDrawablePadding.right = elevationValue;

        int DY;
        switch (shadowGravity) {
            case Gravity.CENTER:
                shapeDrawablePadding.top = elevationValue;
                shapeDrawablePadding.bottom = elevationValue;
                DY = 0;
                break;
            case Gravity.TOP:
                shapeDrawablePadding.top = elevationValue*2;
                shapeDrawablePadding.bottom = elevationValue;
                DY = -1*elevationValue/3;
                break;
            default:
            case Gravity.BOTTOM:
                shapeDrawablePadding.top = elevationValue;
                shapeDrawablePadding.bottom = elevationValue*2;
                DY = elevationValue/3;
                break;
        }

        ShapeDrawable shapeDrawable = new ShapeDrawable();
        shapeDrawable.setPadding(shapeDrawablePadding);

        shapeDrawable.getPaint().setColor(backgroundColorValue);
        shapeDrawable.getPaint().setShadowLayer(cornerRadiusValue/3, 0, DY, shadowColorValue);

        view.setLayerType(LAYER_TYPE_SOFTWARE, shapeDrawable.getPaint());

        shapeDrawable.setShape(new RoundRectShape(outerRadius, null, null));

        LayerDrawable drawable = new LayerDrawable(new Drawable[]{shapeDrawable});
        drawable.setLayerInset(0, elevationValue, elevationValue*2, elevationValue, elevationValue*2);

        return drawable;

    }
}

et enfin votre XML, où vous avez les vues requises pour avoir une ombre.

<com.qzion.nfscrew.utils.RoundLinerLayoutNormal
                Android:layout_width="match_parent"
                Android:layout_height="wrap_content"
                Android:layout_margin="10dp">

                <TextView
                    Android:layout_width="wrap_content"
                    Android:layout_height="wrap_content"
                    Android:text="This view will have shadow"/>

            </com.qzion.nfscrew.utils.RoundLinerLayoutNormal>
4
Blue_Alien

Eh bien, je pense à une solution facile sans utiliser un Java ou certaines bibliothèques. Vous devez créer une forme Drawable et la placer dans le dossier drawable, puis ajuster le dégradé de la manière suivante: une ombre.

Par exemple, dans ma solution, j'ai ajouté deux couleurs:

<color name="yellow_middle">#ffee58</color>
<color name="yellow_end">#7ae7de83</color>

Ensuite, j'ai créé un fichier et l'ai placé dans un dossier pouvant être dessiné drawable\card_view_shape.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:shape="rectangle">
  <size
        Android:width="10dp"
        Android:height="10dp" />
  <corners Android:radius="6dp" />
  <stroke
        Android:width="2dp"
        Android:color="@color/yellow_end" />
  <gradient
       Android:angle="-90"
       Android:centerColor="@color/yellow_middle"
       Android:endColor="@color/yellow_end"
       Android:startColor="#fff" />
</shape>

Ensuite, à partir de là, vous devez envelopper une vue ( qui aurait été à l'intérieur de CardView) dans un conteneur tel que LinearLayout, puis appliquer celle-ci en tant qu'arrière-plan du conteneur que vous souhaitez utiliser. vu comme un cardview. Pour le résoudre, ajoutez un peu de remplissage (C’est votre ombre) au conteneur lui-même. Par exemple, vérifiez le mien:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    tools:context="com.xenolion.ritetrends.MainActivity">

    <LinearLayout
        Android:layout_width="200dp"
        Android:layout_height="200dp"
        Android:layout_gravity="center"
        Android:background="@drawable/card_view_shape"
        Android:orientation="vertical"
        Android:paddingBottom="10dp"
        Android:paddingLeft="3dp"
        Android:paddingRight="3dp"
        Android:paddingTop="3dp">

        <TextView
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            Android:background="#fff"
            Android:gravity="center"
            Android:text="I love StackOverflow"
            Android:textColor="#000"
            Android:textSize="18sp" />

    </LinearLayout>


</FrameLayout>

Ensuite, les résultats ressemblent à ceci:
Testing Results

En ajustant le rembourrage du bas, cela ressemblera à ceci:

Testing results

[~ # ~] commentaire [~ # ~]
Puisque je ne suis pas un artiste, mais si vous jouez avec, vous pouvez faire en sorte que le tout ressemble exactement à CardView vérifiez quelques astuces:

  • Mettre plusieurs dégradés dans la forme
  • Ajustez les couleurs de fin des dégradés pour qu'ils apparaissent plus grisâtres
  • Les couleurs de fin doivent aussi être un peu transparentes
  • Ajustez le rembourrage de votre vue pour qu’elle apparaisse comme une ombre et colorée mais grisâtre
  • L'arrière-plan de la vue principale a également son importance pour faire ressortir la réalité. À partir de là, redessinez la forme pour qu'elle ressemble encore plus à un CardView.
4
Xenolion