77 votes

Comment utiliser le canevas Android pour dessiner un rectangle dont seuls les coins supérieur gauche et supérieur droit sont arrondis ?

J'ai trouvé une fonction pour les rectangles dont les 4 coins sont arrondis, mais je veux que seuls les 2 coins supérieurs soient arrondis. Que puis-je faire ?

canvas.drawRoundRect(new RectF(0, 100, 100, 300), 6, 6, paint);

1 votes

Ne pouvez-vous pas simplement dessiner un rectangle arrondi puis un rectangle ordinaire sur la moitié inférieure (en remplaçant les coins arrondis par des coins normaux) ?

70voto

Saran Sankaran Points 734

Pour les API 21 et supérieures, la classe Path a ajouté une nouvelle méthode addRoundRect() que vous pouvez utiliser comme ceci.

corners = new float[]{
    80, 80,        // Top left radius in px
    80, 80,        // Top right radius in px
    0, 0,          // Bottom right radius in px
    0, 0           // Bottom left radius in px
};

final Path path = new Path();
path.addRoundRect(rect, corners, Path.Direction.CW);
canvas.drawPath(path, mPaint);

en Kotlin

val corners = floatArrayOf(
    80f, 80f,   // Top left radius in px
    80f, 80f,   // Top right radius in px
    0f, 0f,     // Bottom right radius in px
    0f, 0f      // Bottom left radius in px
)

val path = Path()
path.addRoundRect(rect, corners, Path.Direction.CW)
canvas.drawPath(path, mPaint)

9 votes

C'EST LE SAINT-GRAAL QUE JE CHERCHAIS ! Merci à tous !

0 votes

Solution correcte

65voto

Tatarize Points 490

Utilisez un chemin. Il a l'avantage de fonctionner pour les API inférieures à 21 (l'Arc est également limité de cette manière, c'est pourquoi je suis quad). Ce qui est un problème car tout le monde n'a pas encore Lollipop. Vous pouvez cependant spécifier un RectF et définir les valeurs avec cela et utiliser Arc pour revenir à l'API 1, mais alors vous ne pourriez pas utiliser un static (sans déclarer un nouvel objet pour construire l'objet).

Dessiner un rectangle arrondi :

    path.moveTo(right, top + ry);
    path.rQuadTo(0, -ry, -rx, -ry);
    path.rLineTo(-(width - (2 * rx)), 0);
    path.rQuadTo(-rx, 0, -rx, ry);
    path.rLineTo(0, (height - (2 * ry)));
    path.rQuadTo(0, ry, rx, ry);
    path.rLineTo((width - (2 * rx)), 0);
    path.rQuadTo(rx, 0, rx, -ry);
    path.rLineTo(0, -(height - (2 * ry)));
    path.close();

Comme une fonction complète :

static public Path RoundedRect(float left, float top, float right, float bottom, float rx, float ry, boolean conformToOriginalPost) {
    Path path = new Path();
    if (rx < 0) rx = 0;
    if (ry < 0) ry = 0;
    float width = right - left;
    float height = bottom - top;
    if (rx > width/2) rx = width/2;
    if (ry > height/2) ry = height/2;
    float widthMinusCorners = (width - (2 * rx));
    float heightMinusCorners = (height - (2 * ry));

    path.moveTo(right, top + ry);
    path.rQuadTo(0, -ry, -rx, -ry);//top-right corner
    path.rLineTo(-widthMinusCorners, 0);
    path.rQuadTo(-rx, 0, -rx, ry); //top-left corner
    path.rLineTo(0, heightMinusCorners);

    if (conformToOriginalPost) {
        path.rLineTo(0, ry);
        path.rLineTo(width, 0);
        path.rLineTo(0, -ry);
    }
    else {
        path.rQuadTo(0, ry, rx, ry);//bottom-left corner
        path.rLineTo(widthMinusCorners, 0);
        path.rQuadTo(rx, 0, rx, -ry); //bottom-right corner
    }

    path.rLineTo(0, -heightMinusCorners);

    path.close();//Given close, last lineto can be removed.

    return path;
}

Vous devriez aligner jusqu'à ces coins, plutôt que de les traverser. C'est ce que fait le paramètre true de conformToOriginalPost. Il suffit d'aligner jusqu'au point de contrôle.

Si vous voulez faire tout cela mais que vous ne vous souciez pas des trucs pré-Lollipop, et que vous insistez pour que si votre rx et votre ry sont suffisamment élevés, cela devrait dessiner un cercle.

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
static public Path RoundedRect(float left, float top, float right, float bottom, float rx, float ry, boolean conformToOriginalPost) {
    Path path = new Path();
    if (rx < 0) rx = 0;
    if (ry < 0) ry = 0;
    float width = right - left;
    float height = bottom - top;
    if (rx > width/2) rx = width/2;
    if (ry > height/2) ry = height/2;
    float widthMinusCorners = (width - (2 * rx));
    float heightMinusCorners = (height - (2 * ry));

    path.moveTo(right, top + ry);
    path.arcTo(right - 2*rx, top, right, top + 2*ry, 0, -90, false); //top-right-corner
    path.rLineTo(-widthMinusCorners, 0);
    path.arcTo(left, top, left + 2*rx, top + 2*ry, 270, -90, false);//top-left corner.
    path.rLineTo(0, heightMinusCorners);
    if (conformToOriginalPost) {
        path.rLineTo(0, ry);
        path.rLineTo(width, 0);
        path.rLineTo(0, -ry);
    }
    else {
        path.arcTo(left, bottom - 2 * ry, left + 2 * rx, bottom, 180, -90, false); //bottom-left corner
        path.rLineTo(widthMinusCorners, 0);
        path.arcTo(right - 2 * rx, bottom - 2 * ry, right, bottom, 90, -90, false); //bottom-right corner
    }

    path.rLineTo(0, -heightMinusCorners);

    path.close();//Given close, last lineto can be removed.
    return path;
}

Ainsi , conformToOriginalPost dessine en fait un rectangle arrondi sans les deux bits inférieurs arrondis.

arcquadimage

0 votes

À mon avis, les fonctions de chemin relatif sont bien meilleures, car vous ne devez vous préoccuper que du seul point où le chemin sera tracé.

0 votes

@chx101 oui, ok, je l'ai refait en relatif et j'ai commencé sur une courbe pour que la close() puisse couvrir la ligne.

0 votes

@Tatrize Wow, tu as tout réécrit, hein ?

55voto

Durza007 Points 594

Je dessinerais deux rectangles :

canvas.drawRect(new RectF(0, 110, 100, 290), paint);
canvas.drawRoundRect(new RectF(0, 100, 100, 200), 6, 6, paint);

Ou quelque chose comme ça, il suffit de les superposer pour que les coins supérieurs soient ronds. De préférence, vous devriez écrire une méthode pour cela

13 votes

Et si j'ai besoin de définir l'alpha pour l'ensemble du rectangle, la section de chevauchement aura une valeur alpha différente des autres.

4 votes

@V.Kalyuzhnyu la signature de la méthode utilisée dans la réponse est disponible depuis API 1, vous devez parler de drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) qui est disponible auprès de l'API 21.

0 votes

@virsir paint.setColor(Color.argb(200, 0,0,0)); définit l'alpha de la peinture.

31voto

Mohammad Mahtabi Points 1490

J'ai changé este répondre afin de pouvoir définir le coin que vous voulez rond et celui que vous voulez pointu. fonctionne également sur pré-lolipop

Exemple d'utilisation :

seuls les coins en haut à droite et en bas à droite sont arrondis.

 Path path = RoundedRect(0, 0, fwidth , fheight , 5,5,
                     false, true, true, false);
 canvas.drawPath(path,myPaint);

RoundRect :

    public static Path RoundedRect(
            float left, float top, float right, float bottom, float rx, float ry,
               boolean tl, boolean tr, boolean br, boolean bl
    ){
        Path path = new Path();
        if (rx < 0) rx = 0;
        if (ry < 0) ry = 0;
        float width = right - left;
        float height = bottom - top;
        if (rx > width / 2) rx = width / 2;
        if (ry > height / 2) ry = height / 2;
        float widthMinusCorners = (width - (2 * rx));
        float heightMinusCorners = (height - (2 * ry));

        path.moveTo(right, top + ry);
        if (tr)
            path.rQuadTo(0, -ry, -rx, -ry);//top-right corner
        else{
            path.rLineTo(0, -ry);
            path.rLineTo(-rx,0);
        }
        path.rLineTo(-widthMinusCorners, 0);
        if (tl)
            path.rQuadTo(-rx, 0, -rx, ry); //top-left corner
        else{
            path.rLineTo(-rx, 0);
            path.rLineTo(0,ry);
        }
        path.rLineTo(0, heightMinusCorners);

        if (bl)
            path.rQuadTo(0, ry, rx, ry);//bottom-left corner
        else{
            path.rLineTo(0, ry);
            path.rLineTo(rx,0);
        }

        path.rLineTo(widthMinusCorners, 0);
        if (br)
            path.rQuadTo(rx, 0, rx, -ry); //bottom-right corner
        else{
            path.rLineTo(rx,0);
            path.rLineTo(0, -ry);
        }

        path.rLineTo(0, -heightMinusCorners);

        path.close();//Given close, last lineto can be removed.

        return path;
    }

1 votes

Vous devriez corriger ma réponse en ajoutant un arc de Bézier approprié pour l'approximation des angles. spencermortensen.com/articles/bezier-circle Et convertir en utilisant un truc bitwise de style C pour les coins plutôt qu'un tas de booléens. static final int SHARP_TOP_RIGHT = 0b0001, static final int SHARP_TOP_LEFT = 0b0010 ; -- alors vous pouvez l'appeler, getRoundRect(0, 0, fwidth , fheight , 5, 5, SHARP_TOP_LEFT | SHARP_BOTTOM_LEFT) ; avec les ifs étant (if ((SHARP_TOP_LEFT & cornerflag

1 votes

P

1 votes

I

13voto

Hashem Mousavi Points 150

Vous pouvez facilement y parvenir en utilisant Path :

val radiusArr = floatArrayOf(
    15f, 15f,
    15f, 15f,
    0f, 0f,
    0f, 0f
)
val myPath = Path()
myPath.addRoundRect(
    RectF(0f, 0f, 400f, 400f),
    radiusArr,
    Path.Direction.CW
)

canvas.drawPath(myPath, paint)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X