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é?
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?
Considérez ce fil sur Twitter, où Nick Butcher explique comment implémenter la fonctionnalité:
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
.
Vous pouvez implémenter ceci sans avoir un cardview, et peut également avoir toutes les propriétés de cardview
Vous devez faire:
Copier les deux classes
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>
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:
En ajustant le rembourrage du bas, cela ressemblera à ceci:
[~ # ~] 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:
CardView
.