web-dev-qa-db-ja.com

Android canvasを使用して、左上隅と右上隅のみが丸い四角形を描画する方法は?

4つの角すべてが丸い長方形の関数を見つけましたが、上の2つの角だけを丸くしたいです。私に何ができる?

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

CanvasdrawLine()およびdrawArc()関数を使用して、そのピースを1つずつ描画できます。

2
Zelimir

パスを使用します。 21未満のAPIで動作するという利点があります(したがって、Arcも制限されているため、クワッドです)。誰もがまだロリポップを持っているわけではないので、これは問題です。ただし、RectFを指定して値を設定し、アークをAPI 1に戻すことはできますが、静的オブジェクトを使用することはできません(新しいオブジェクトを宣言してオブジェクトを構築することはできません)。

角丸長方形の描画:

    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();

完全な機能として:

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;
}

四角ではなく、それらの隅の部分までずっと並べたいと思うでしょう。これは、conformToOriginalPostにtrueを設定することです。そこにあるコントロールポイントに向かってください。

ロリポップ以前のものを気にせずにすべてを行いたい場合は、rxとryが十分に高い場合は円を描く必要があることを緊急に主張します。

@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;
}

したがって、conformToOriginalPostは実際には、下の2ビットが丸められずに、丸みのある長方形を描画します。

arcquadimage

50
Tatarize

2つの四角形を描画します。

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

またはそのようなもの、それらを単にオーバーラップして、上部の角が丸くなるようにします。できれば、このためのメソッドを書く必要があります

43
Durza007

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
    )
}

使用法:

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

API 21以降では、Pathクラスに新しいメソッドaddRoundRect()が追加され、このように使用できます。

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);

コトリンで

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

以下の手順に従ってこれを達成しました。

これらは、丸みを帯びた長方形がきれいに見えるための前提条件です

  • エッジの半径は(長方形の高さ/ 2)と等しくなければなりません。これは、その値が異なる場合、曲線が長方形の直線と交わる場所は、

次は、角丸四角形を描画する手順です。

  • 最初に、半径=長方形の高さ/ 2で、左右に2つの円を描きます

  • 次に、これらの円の間に長方形を描き、目的の丸い長方形を取得します。

私は以下のコードを投稿しています

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);
}

これにより、以下に示すような本当にすてきな丸みのある長方形になります enter image description here

5
Bhargav

ソリッドサイドを描画する簡単で効率的な方法の1つは、クリッピングを使用することです。長方形のクリッピングは基本的に無料で、カスタムパスよりも記述するコードがはるかに少なくなります。

左上と右を50ピクセルで丸めた300x300の四角形が必要な場合は、次の操作を実行できます。

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

このアプローチは、隣接する2つまたは3つのコーナーでのラウンドでのみ機能するため、Pathベースのアプローチよりも設定が少し少なくなりますが、drawRoundRect()は完全にハードウェアアクセラレーションされる(つまり、三角形にテッセレーションされる)ため、ラウンド四角形を使用する方が効率的です一方、drawPath()は常にソフトウェアレンダリングにフォールバックします(ソフトウェアでパスビットマップを描画し、GPUにキャッシュするためにアップロードします)。

小さい頻度の描画では大きなパフォーマンスの問題ではありませんが、パスをアニメーション化する場合、ソフトウェア描画のコストによりフレーム時間が長くなり、フレームをドロップする可能性が高くなります。パスマスクもメモリを消費します。

パスベースのアプローチを使用する場合は、GradientDrawableを使用してコード行を簡略化することをお勧めします(カスタムシェーダーを設定する必要がない場合、たとえばビットマップを描画する場合)。

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

GradientDrawable#setCornerRadii() を使用すると、任意の角を任意の丸みに設定し、状態間を合理的にアニメーション化できます。

4
Chris Craik

左の角が丸い長方形を描く

  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

PaintDrawable を使用することをお勧めします。

    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

これは古い質問ですが、多くのカスタムコードやハッキーな描画なしでネイティブSDKを使用するため、ソリューションを追加したかったのです。このソリューションは、API 1でサポートされます。

これを適切に行う方法は、パスを作成することです(他の回答で述べたように)が、以前の回答では、各コーナーの半径を取るaddRoundedRect関数呼び出しを見落としているようです。

変数

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

塗料の設定

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

サイズ変更でパスを更新

これは、onDrawではない場所、たとえばビューの場合はonMeasure、ドロアブルの場合はonBoundChangeのように呼び出します。 (この例のように)変わらない場合は、ペイントを設定した場所にこのコードを配置できます。

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)

このコードは、角が半径25で丸みを帯びた100x100の角丸長方形を作成します。

パスの描画

ビューの場合はonDrawで、またはドロアブルの場合はdrawでこれを呼び出します。

canvas.drawPath(path, Paint)
0
drspaceboo