web-dev-qa-db-fra.com

Comment utiliser Android canvas pour dessiner un rectangle avec uniquement des coins supérieurs et droits arrondis?

J'ai trouvé une fonction pour les rectangles avec les 4 coins étant ronds, mais je veux avoir juste les 2 premiers coins ronds. Que puis-je faire?

canvas.drawRoundRect(new RectF(0, 100, 100, 300), 6, 6, Paint);
43
virsir

Vous pouvez dessiner ce morceau par morceau en utilisant les fonctions drawLine() et drawArc() à partir de Canvas.

2
Zelimir

Utilisez un chemin. Il a l'avantage de fonctionner pour des API inférieures à 21 (Arc est donc aussi limité, c'est pourquoi je quadruple). 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 l'arc sur API 1, mais vous ne pourrez alors pas utiliser un statique (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();

En tant que 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 voudriez aligner tout le long de ces bits de coin, plutôt que de les quadriller. C'est ce que fait la valeur true à conformToOriginalPost. Il vous suffit de vous aligner jusqu'au point de contrôle.

Si vous voulez tout faire mais ne vous souciez pas des trucs pré-Lollipop, et insistez de toute urgence que si votre rx et ry sont suffisamment élevés, il 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;
}

Donc, conformToOriginalPost dessine en fait un rect arrondi sans arrondir les deux bits du bas.

arcquadimage

50
Tatarize

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, vous les superposez juste pour que les coins supérieurs soient ronds. De préférence, vous devez écrire une méthode pour cela

43
Durza007

Fonction d'assistance simple écrite en Kotlin.

private fun Canvas.drawTopRoundRect(rect: RectF, Paint: Paint, radius: Float) {
    // Step 1. Draw rect with rounded corners.
    drawRoundRect(rect, radius, radius, Paint)

    // Step 2. Draw simple rect with reduced height,
    // so it wont cover top rounded corners.
    drawRect(
            rect.left,
            rect.top + radius,
            rect.right,
            rect.bottom,
            Paint
    )
}

tilisation:

canvas.drawTopRoundRect(rect, Paint, radius)
11
Vadim Akhmerov
public static Path composeRoundedRectPath(RectF rect, float topLeftDiameter, float topRightDiameter,float bottomRightDiameter, float bottomLeftDiameter){
    Path path = new Path();
    topLeftDiameter = topLeftDiameter < 0 ? 0 : topLeftDiameter;
    topRightDiameter = topRightDiameter < 0 ? 0 : topRightDiameter;
    bottomLeftDiameter = bottomLeftDiameter < 0 ? 0 : bottomLeftDiameter;
    bottomRightDiameter = bottomRightDiameter < 0 ? 0 : bottomRightDiameter;

    path.moveTo(rect.left + topLeftDiameter/2 ,rect.top);
    path.lineTo(rect.right - topRightDiameter/2,rect.top);
    path.quadTo(rect.right, rect.top, rect.right, rect.top + topRightDiameter/2);
    path.lineTo(rect.right ,rect.bottom - bottomRightDiameter/2);
    path.quadTo(rect.right ,rect.bottom, rect.right - bottomRightDiameter/2, rect.bottom);
    path.lineTo(rect.left + bottomLeftDiameter/2,rect.bottom);
    path.quadTo(rect.left,rect.bottom,rect.left, rect.bottom - bottomLeftDiameter/2);
    path.lineTo(rect.left,rect.top + topLeftDiameter/2);
    path.quadTo(rect.left,rect.top, rect.left + topLeftDiameter/2, rect.top);
    path.close();

    return path;
}
6
Kvant

Pour l'API 21 et au-dessus, 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);

à 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)
6
Saran Sankaran

J'ai atteint cet objectif en suivant les étapes ci-dessous.

Ce sont les pré-requis pour que le rectangle arrondi soit net

  • Le rayon des bords doit être égal à la (hauteur du rectangle/2). En effet, si sa valeur est différente, l'endroit où la courbe rencontre la ligne droite du rectangle ne sera pas

Viennent ensuite les étapes pour dessiner le rectangle arrondi.

  • D'abord, nous dessinons 2 cercles à gauche et à droite, avec le rayon = hauteur de rectange/2

  • Ensuite, nous dessinons un rectangle entre ces cercles pour obtenir le rectangle arrondi souhaité.

Je poste le code ci-dessous

private void drawRoundedRect(Canvas canvas, float left, float top, float right, float bottom) {
    float radius = getHeight() / 2;
    canvas.drawCircle(radius, radius, radius, mainPaint);
    canvas.drawCircle(right - radius, radius, radius, mainPaint);
    canvas.drawRect(left + radius, top, right - radius, bottom, mainPaint);
}

Maintenant, cela donne un très joli rectangle arrondi comme celui ci-dessous enter image description here

5
Bhargav

Un moyen simple et efficace de dessiner un côté solide consiste à utiliser l'écrêtage - l'écrêtage rectiligne est essentiellement gratuit et beaucoup moins de code à écrire qu'un chemin personnalisé.

Si je veux un rectangle 300x300, avec les coins supérieur gauche et droit arrondis de 50 pixels, vous pouvez faire:

canvas.save();
canvas.clipRect(0, 0, 300, 300);
canvas.drawRoundRect(new RectF(0, 0, 300, 350), 50, 50, Paint);
canvas.restore();

Cette approche ne fonctionnera que pour l'arrondi sur 2 ou 3 coins adjacents, elle est donc un peu moins configurable qu'une approche basée sur le chemin, mais l'utilisation des arrondis est plus efficace, car drawRoundRect () est entièrement accéléré par le matériel (c'est-à-dire tessellé en triangles) tandis que drawPath () revient toujours au rendu logiciel (logiciel-dessine un bitmap de chemin, et le télécharge pour le mettre en cache sur le GPU).

Ce n'est pas un problème de performances énorme pour les petits dessins peu fréquents, mais si vous animez des chemins, le coût du dessin logiciel peut allonger la durée de votre image et augmenter vos chances de supprimer des images. Le masque de chemin coûte également de la mémoire.

Si vous souhaitez utiliser une approche basée sur le chemin, je vous recommande d'utiliser GradientDrawable pour simplifier les lignes de code (en supposant que vous n'avez pas besoin de définir un shader personnalisé, par exemple pour dessiner un bitmap).

mGradient.setBounds(0, 0, 300, 300);
mGradient.setCornerRadii(new int[] {50,50, 50,50, 0,0, 0,0});

Avec GradientDrawable # setCornerRadii () , vous pouvez définir n'importe quel coin pour qu'il soit arrondi et animer raisonnablement entre les états.

4
Chris Craik

dessiner un rectangle rond avec des coins arrondis à gauche

  private void drawRoundRect(float left, float top, float right, float bottom, Paint paint, Canvas canvas) {
        Path path = new Path();
        path.moveTo(left, top);
        path.lineTo(right, top);
        path.lineTo(right, bottom);
        path.lineTo(left + radius, bottom);
        path.quadTo(left, bottom, left, bottom - radius);
        path.lineTo(left, top + radius);
        path.quadTo(left, top, left + radius, top);
        canvas.drawPath(path, onlinePaint);
    }
2
georgehardcore

Utiliser PaintDrawable pourrait être mieux:

    val topLeftRadius = 10
    val topRightRadius = 10
    val bottomLeftRadius = 0
    val bottomRightRadius = 0
    val rect = Rect(0, 0, 100, 100)
    val paintDrawable = PaintDrawable(Color.RED)
    val outter = floatArrayOf(topLeftRadius, topLeftRadius, topRightRadius, topRightRadius,
            bottomLeftRadius, bottomLeftRadius, bottomRightRadius, bottomRightRadius)
    paintDrawable.setCornerRadii(outter)
    paintDrawable.bounds = rect
    paintDrawable.draw(canvas)
1
GreatC

C'est une vieille question, mais je voulais ajouter ma solution car elle utilise le SDK natif sans beaucoup de code personnalisé ou de dessin hacky. Cette solution est prise en charge de nouveau à l'API 1.

La façon de le faire correctement est de créer un chemin (comme mentionné dans d'autres réponses) mais les réponses précédentes semblent ignorer l'appel de fonction addRoundedRect qui prend des rayons pour chaque coin.

Variables

private val path = Path()
private val Paint = Paint()

Configurer la peinture

Paint.color = Color.RED
Paint.style = Paint.Style.FILL

Chemin de mise à jour avec changements de taille

Appelez cela quelque part qui n'est pas onDraw, comme onMeasure pour une vue ou onBoundChange pour un dessinable. Si cela ne change pas (comme cet exemple), vous pouvez placer ce code là où vous avez configuré votre Paint.

val radii = floatArrayOf(
    25f, 25f, //Top left corner
    25f, 25f, //Top right corner
    0f, 0f,   //Bottom right corner
    0f, 0f,   //Bottom left corner
)

path.reset() //Clears the previously set path
path.addRoundedRect(0f, 0f, 100f, 100f, radii, Path.Direction.CW)

Ce code crée un rectangle arrondi 100x100 avec les coins supérieurs arrondis avec un rayon de 25.

Tracer le chemin

Appelez cela dans onDraw pour une vue ou draw pour un dessinable.

canvas.drawPath(path, Paint)
0
drspaceboo