web-dev-qa-db-fra.com

Comment créer une vue sous Android avec des angles arrondis

J'essaie de créer une vue sous Android avec des bords arrondis. La solution que j'ai trouvée jusqu'à présent est de définir une forme avec des coins arrondis et de l'utiliser comme arrière-plan de cette vue.

Voici ce que j'ai fait, définir un dessin comme indiqué ci-dessous

 

<padding
Android:top="2dp"
Android:bottom="2dp"/>
<corners Android:bottomRightRadius="20dp"
Android:bottomLeftRadius="20dp"
Android:topLeftRadius="20dp"
Android:topRightRadius="20dp"/>

Maintenant, je l'ai utilisé comme arrière-plan pour ma mise en page comme ci-dessous

<LinearLayout
        Android:orientation="vertical"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_marginLeft="10dp"
        Android:layout_marginRight="10dp"
        Android:layout_marginBottom="10dp"
        Android:clipChildren="true"
        Android:background="@drawable/rounded_corner">

Cela fonctionne parfaitement, je peux voir que la vue a des bords arrondis. 

Mais ma mise en page contient de nombreuses autres vues enfants. Disons un ImageView ou un MapView. Lorsque je place un ImageView à l'intérieur de la disposition ci-dessus, les coins de l'image ne sont pas coupés/rognés, mais semblent pleins.

J'ai vu d'autres solutions de contournement pour le faire fonctionner comme celle expliquée ici

Mais existe-t-il une méthode pour définir des angles arrondis pour une vue et toutes ses les vues enfant sont contenues dans cette vue principale qui a été arrondie des coins?

Merci.

58
Zach

Une autre approche consiste à créer une classe de mise en page personnalisée comme celle ci-dessous. Cette mise en page dessine d'abord son contenu dans un bitmap hors écran, masque le bitmap hors écran avec un rectangle arrondi, puis dessine le bitmap hors écran sur le canevas.

Je l'ai essayé et cela semble fonctionner (au moins pour mon test simple). Cela affectera bien sûr les performances par rapport à une mise en page normale.

package com.example;

import Android.content.Context;
import Android.graphics.*;
import Android.util.AttributeSet;
import Android.util.DisplayMetrics;
import Android.util.TypedValue;
import Android.widget.FrameLayout;

public class RoundedCornerLayout extends FrameLayout {
    private final static float CORNER_RADIUS = 40.0f;

    private Bitmap maskBitmap;
    private Paint paint, maskPaint;
    private float cornerRadius;

    public RoundedCornerLayout(Context context) {
        super(context);
        init(context, null, 0);
    }

    public RoundedCornerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public RoundedCornerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    private void init(Context context, AttributeSet attrs, int defStyle) {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, metrics);

        Paint = new Paint(Paint.ANTI_ALIAS_FLAG);

        maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
        maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

        setWillNotDraw(false);
    }

    @Override
    public void draw(Canvas canvas) {
        Bitmap offscreenBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas offscreenCanvas = new Canvas(offscreenBitmap);

        super.draw(offscreenCanvas);

        if (maskBitmap == null) {
            maskBitmap = createMask(canvas.getWidth(), canvas.getHeight());
        }

        offscreenCanvas.drawBitmap(maskBitmap, 0f, 0f, maskPaint);
        canvas.drawBitmap(offscreenBitmap, 0f, 0f, Paint);
    }

    private Bitmap createMask(int width, int height) {
        Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
        Canvas canvas = new Canvas(mask);

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Paint.setColor(Color.WHITE);

        canvas.drawRect(0, 0, width, height, Paint);

        Paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        canvas.drawRoundRect(new RectF(0, 0, width, height), cornerRadius, cornerRadius, Paint);

        return mask;
    }
}

Utilisez ceci comme une mise en page normale:

<com.example.RoundedCornerLayout
    Android:layout_width="200dp"
    Android:layout_height="200dp">

    <ImageView
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:src="@drawable/test"/>

    <View
        Android:layout_width="match_parent"
        Android:layout_height="100dp"
        Android:background="#ff0000"
        />

</com.example.RoundedCornerLayout>
103

forme.xml

<shape xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:shape="rectangle">

    <solid Android:color="#f6eef1" />

    <stroke
        Android:width="2dp"
        Android:color="#000000" />

    <padding
        Android:bottom="5dp"
        Android:left="5dp"
        Android:right="5dp"
        Android:top="5dp" />

    <corners Android:radius="5dp" />

</shape>

et à l'intérieur de votre mise en page

<LinearLayout
        Android:orientation="vertical"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:layout_marginLeft="10dp"
        Android:layout_marginRight="10dp"
        Android:layout_marginBottom="10dp"
        Android:clipChildren="true"
        Android:background="@drawable/shape">

        <ImageView
             Android:layout_width="match_parent"
             Android:layout_height="match_parent"
             Android:src="@drawable/your image"
             Android:background="@drawable/shape">

</LinearLayout>
38

Ou vous pouvez utiliser un Android.support.v7.widget.CardView comme ceci:

<Android.support.v7.widget.CardView
    xmlns:card_view="http://schemas.Android.com/apk/res-auto"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    card_view:cardBackgroundColor="@color/white"
    card_view:cardCornerRadius="4dp">

    <!--YOUR CONTENT-->
</Android.support.v7.widget.CardView>
34
rocketspacer

Si vous rencontrez des problèmes lors de l'ajout d'auditeurs tactiles à la mise en page. Utilisez cette mise en page comme mise en page parent.

import Android.content.Context;
import Android.graphics.Bitmap;
import Android.graphics.Canvas;
import Android.graphics.Paint;
import Android.graphics.Path;
import Android.graphics.RectF;
import Android.graphics.Region;
import Android.util.AttributeSet;
import Android.util.DisplayMetrics;
import Android.util.TypedValue;
import Android.view.View;
import Android.widget.FrameLayout;

public class RoundedCornerLayout extends FrameLayout {
    private final static float CORNER_RADIUS = 6.0f;
    private float cornerRadius;

    public RoundedCornerLayout(Context context) {
        super(context);
        init(context, null, 0);
    }

    public RoundedCornerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public RoundedCornerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    private void init(Context context, AttributeSet attrs, int defStyle) {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, metrics);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }


    @Override
    protected void dispatchDraw(Canvas canvas) {
        int count = canvas.save();

        final Path path = new Path();
        path.addRoundRect(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), cornerRadius, cornerRadius, Path.Direction.CW);
        canvas.clipPath(path, Region.Op.REPLACE);

        canvas.clipPath(path);
        super.dispatchDraw(canvas);
        canvas.restoreToCount(count);
    }


}

comme 

<?xml version="1.0" encoding="utf-8"?>
<com.example.view.RoundedCornerLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <RelativeLayout
        Android:id="@+id/patentItem"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:paddingRight="20dp">
        ... your child goes here
    </RelativeLayout>
</com.example.view.RoundedCornerLayout>
16
Sushant

créer un fichier round.xml xml dans un dossier pouvant être dessiné

<solid Android:color="#FFFFFF" />
<stroke Android:width=".05dp" Android:color="#d2d2d2" />
<corners Android:topLeftRadius="5dp" Android:topRightRadius="5dp" Android:bottomLeftRadius="5dp" Android:bottomRightRadius="5dp" />

puis utilisez round.xml comme arrière-plan pour les éléments .. Ensuite, il donnera des coins arrondis

8
Ajay Venugopal

Dans Android L, vous pourrez simplement utiliser View.setClipToOutline pour obtenir cet effet. Dans les versions précédentes, il était impossible de couper le contenu d'un groupe de vues aléatoire dans une forme donnée.

Vous devrez penser à quelque chose qui vous donnerait un effet similaire:

  • Si vous n'avez besoin que d'angles arrondis dans ImageView, vous pouvez utiliser un shader pour "peindre" l'image sur la forme utilisée en tant qu'arrière-plan. Jetez un coup d'oeil à cette bibliothèque pour un exemple.

  • Si vous avez vraiment besoin que tous les enfants soient coupés, vous pourriez peut-être avoir une autre vue de votre mise en page? Un avec un fond de la couleur que vous utilisez et un "trou" rond au milieu? Vous pouvez en fait créer un groupe de vues personnalisé qui dessine cette forme sur tous les enfants redéfinissant la méthode onDraw.

7
ivagarz

La réponse de Jaap van Hengstum fonctionne très bien, mais je pense que cela coûte cher et que si nous appliquons cette méthode sur un bouton, l'effet tactile est perdu car la vue est rendue sous forme de bitmap.

Pour moi, la meilleure méthode, la plus simple, consiste à appliquer un masque sur la vue, comme ceci:

@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
    super.onSizeChanged(width, height, oldWidth, oldHeight);

    float cornerRadius = <whatever_you_want>;
    this.path = new Path();
    this.path.addRoundRect(new RectF(0, 0, width, height), cornerRadius, cornerRadius, Path.Direction.CW);
}

@Override
protected void dispatchDraw(Canvas canvas) {
    if (this.path != null) {
        canvas.clipPath(this.path);
    }
    super.dispatchDraw(canvas);
}
7
Donkey
public class RoundedCornerLayout extends FrameLayout {
    private double mCornerRadius;

    public RoundedCornerLayout(Context context) {
        this(context, null, 0);
    }

    public RoundedCornerLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RoundedCornerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    private void init(Context context, AttributeSet attrs, int defStyle) {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }

    public double getCornerRadius() {
        return mCornerRadius;
    }

    public void setCornerRadius(double cornerRadius) {
        mCornerRadius = cornerRadius;
    }

    @Override
    public void draw(Canvas canvas) {
        int count = canvas.save();

        final Path path = new Path();
        path.addRoundRect(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), (float) mCornerRadius, (float) mCornerRadius, Path.Direction.CW);
        canvas.clipPath(path, Region.Op.REPLACE);

        canvas.clipPath(path);
        super.draw(canvas);
        canvas.restoreToCount(count);
    }
}
2
Mahmoud Shahoud

suivez ce tutoriel et toutes les discussions qui le suivent - http://www.curious-creature.org/2012/12/11/Android-recipe-1-image-with-rounded-corners/

selon cet article écrit par Guy Romain, l'un des principaux développeurs de l'ensemble de la boîte à outils de l'interface utilisateur Android, il est possible de créer un conteneur (et toutes ses vues enfant) avec des angles arrondis, mais il a expliqué qu'il était trop coûteux problèmes de rendu).

Je vous recommanderai d'y aller en fonction de son post, et si vous voulez des coins arrondis, alors implémentez des angles arrondis ImageView selon ce post. Ensuite, vous pouvez le placer dans un conteneur avec n'importe quel fond et vous obtiendrez l'effet que vous souhaitez.

c'est ce que j'ai aussi fait éventuellement aussi.

2
Tal Kanel

Le lien de didacticiel que vous avez fourni semble suggérer que vous deviez définir les propriétés layout_width et layout_height de vos éléments enfants sur match_parent.

<ImageView
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">
1
danielcooperxyz

Différence avec la réponse de Jaap van Hengstum:

  1. Utilisez BitmapShader au lieu de masque bitmap.
  2. Créer un bitmap une seule fois.
public class RoundedFrameLayout extends FrameLayout {
    private Bitmap mOffscreenBitmap;
    private Canvas mOffscreenCanvas;
    private BitmapShader mBitmapShader;
    private Paint mPaint;
    private RectF mRectF;

    public RoundedFrameLayout(Context context) {
        super(context);
        init();
    }

    public RoundedFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RoundedFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setWillNotDraw(false);
    }

    @Override
    public void draw(Canvas canvas) {
        if (mOffscreenBitmap == null) {
            mOffscreenBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
            mOffscreenCanvas = new Canvas(mOffscreenBitmap);
            mBitmapShader = new BitmapShader(mOffscreenBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setShader(mBitmapShader);
            mRectF = new RectF(0f, 0f, canvas.getWidth(), canvas.getHeight());
        }
        super.draw(mOffscreenCanvas);

        canvas.drawRoundRect(mRectF, 8, 8, mPaint);
    }
}
1
Peter Zhao

La CardView a fonctionné pour moi dans l'API 27 d'Android Studio 3.0.1. La colorPrimary a été référencée dans le fichier res/values/colors.xml et n'est qu'un exemple. Pour le layout_width de 0dp, il s’étendra à la largeur du parent. Vous devrez configurer les contraintes et la largeur/hauteur selon vos besoins.

<Android.support.v7.widget.CardView
    Android:id="@+id/cardView"
    Android:layout_width="0dp"
    Android:layout_height="200dp"
    Android:layout_marginEnd="8dp"
    Android:layout_marginStart="8dp"
    Android:layout_marginTop="8dp"
    app:cardCornerRadius="4dp"
    app:cardBackgroundColor="@color/colorPrimary">

    <!-- put your content here -->

</Android.support.v7.widget.CardView>
1
Lloyd Rochester

essayez cette propriété avec votre disposition linéaire cela aidera
tools: context = ". votre activité"

1
user4050065
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) {

        Bitmap roundedBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap
                .getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(roundedBitmap);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);
        final float roundPx = pixels;

        Paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        Paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, Paint);

        Paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, Paint);

        return roundedBitmap;
    }
0

Créez un fichier XML sous votre dossier pouvant être dessiné avec le code suivant. (Le nom du fichier que j'ai créé est rounded_corner.xml)

rounded_corner.xml

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

    <!-- view background color -->
    <solid
        Android:color="#a9c5ac" >
    </solid>

    <!-- view border color and width -->
    <stroke
        Android:width="3dp"
        Android:color="#1c1b20" >
    </stroke>

    <!-- If you want to add some padding -->
    <padding
        Android:left="4dp"
        Android:top="4dp"
        Android:right="4dp"
        Android:bottom="4dp"    >
    </padding>

    <!-- Here is the corner radius -->
    <corners
        Android:radius="10dp"   >
    </corners>

Et conservez ce dessin comme arrière-plan pour la vue à laquelle vous souhaitez conserver une bordure de coin arrondie. Gardons-le pour un LinearLayout

<LinearLayout Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:background="@drawable/rounded_corner"
        Android:layout_centerInParent="true">

        <TextView Android:layout_width="fill_parent"
            Android:layout_height="wrap_content"
            Android:text="Hi, This layout has rounded corner borders ..."
            Android:gravity="center"
            Android:padding="5dp"/>

    </LinearLayout>
0
Rajneesh Shukla