【问题标题】:How to draw a triangle, a star, a square or a heart on the canvas?如何在画布上绘制三角形、星形、正方形或心形?
【发布时间】:2011-08-10 07:46:02
【问题描述】:

我可以使用
path.addCircle()在画布上绘制一个圆形和一个矩形

path.addRect().

现在我想知道如何画三角形、星形、正方形或心形?

【问题讨论】:

    标签: android draw shapes


    【解决方案1】:

    对于未来的直接答案寻求者,我使用画布绘制了一个几乎对称的星形,如图所示:

    主要工具是使用路径。

    假设您已设置:

    Paint paint = new Paint();
    paint.setColor(Color.WHITE);
    paint.setAntiAlias(true);
    paint.setStyle(Paint.Style.STROKE);
    
    Path path = new Path();
    

    然后在您的 onDraw 中,您可以像我在下面那样使用路径。它将正确缩放到任何尺寸的画布

    @Override
    protected void onDraw(Canvas canvas) {
    
        float mid = getWidth() / 2;
        float min = Math.min(getWidth(), getHeight());
        float fat = min / 17;
        float half = min / 2;
        float rad = half - fat;
        mid = mid - half;
    
        paint.setStrokeWidth(fat);
        paint.setStyle(Paint.Style.STROKE);
    
        canvas.drawCircle(mid + half, half, rad, paint);
    
        path.reset();
    
        paint.setStyle(Paint.Style.FILL);
    
    
            // top left
            path.moveTo(mid + half * 0.5f, half * 0.84f);
            // top right
            path.lineTo(mid + half * 1.5f, half * 0.84f);
            // bottom left
            path.lineTo(mid + half * 0.68f, half * 1.45f);
            // top tip
            path.lineTo(mid + half * 1.0f, half * 0.5f);
            // bottom right
            path.lineTo(mid + half * 1.32f, half * 1.45f);
            // top left
            path.lineTo(mid + half * 0.5f, half * 0.84f);
    
            path.close();
            canvas.drawPath(path, paint);
    
        super.onDraw(canvas);
    
    }
    

    【讨论】:

      【解决方案2】:

      适合所有需要心形的人:

          import android.content.Context;
          import android.graphics.Canvas;
          import android.graphics.Color;
          import android.graphics.Paint;
          import android.graphics.Paint.Style;
          import android.graphics.Path;
          import android.view.View;
      
          public class Heart extends View {
      
              private Path path;
      
              private Paint paint;
      
              public Heart(Context context) {
                  super(context);
      
                  path = new Path();
                  paint = new Paint(Paint.ANTI_ALIAS_FLAG);
              }
      
                  @Override
                  protected void onDraw(Canvas canvas) {
                      super.onDraw(canvas);
      
                      // Fill the canvas with background color
                      canvas.drawColor(Color.WHITE);
                      paint.setShader(null);
      
                      float width = getContext().getResources().getDimension(R.dimen.heart_width);
                      float height = getContext().getResources().getDimension(R.dimen.heart_height);
      
                      // Starting point
                      path.moveTo(width / 2, height / 5); 
      
                      // Upper left path
                      path.cubicTo(5 * width / 14, 0,
                              0, height / 15,
                              width / 28, 2 * height / 5);
      
                      // Lower left path
                      path.cubicTo(width / 14, 2 * height / 3,
                              3 * width / 7, 5 * height / 6,
                              width / 2, height);
      
                      // Lower right path
                      path.cubicTo(4 * width / 7, 5 * height / 6,
                              13 * width / 14, 2 * height / 3,
                              27 * width / 28, 2 * height / 5);
      
                      // Upper right path
                      path.cubicTo(width, height / 15,
                              9 * width / 14, 0,
                              width / 2, height / 5);
      
                      paint.setColor(Color.RED);
                      paint.setStyle(Style.FILL);
                      canvas.drawPath(path, paint);
      
                  }
          }
      

      抱歉所有数字,但这些数字对我来说效果最好:) 结果如下所示:

      【讨论】:

        【解决方案3】:

        您必须找出这些数字背后的数学原理。三角形和星星很容易画。以下是绘制心形的方法:http://www.mathematische-basteleien.de/heart.htm

        要绘制特殊路径,您应该通过添加点、椭圆等来创建它们。画布支持指定路径的剪贴蒙版,因此您可以设置心形的剪贴蒙版,将路径推送到矩阵,绘制内容心脏,然后再次弹出。

        这就是我在安卓上实现模拟 2D 页面卷曲效果所做的工作:http://code.google.com/p/android-page-curl/

        希望这会有所帮助!

        【讨论】:

          【解决方案4】:

          此方法将返回一个路径,该路径具有给定宽度的正方形内的角数。添加更多参数来处理小半径之类的事情。

              private Path createStarBySize(float width, int steps) {
              float halfWidth = width / 2.0F;
              float bigRadius = halfWidth;
              float radius = halfWidth / 2.0F;
              float degreesPerStep = (float) Math.toRadians(360.0F / (float) steps);
              float halfDegreesPerStep = degreesPerStep / 2.0F;
              Path ret = new Path();
              ret.setFillType(FillType.EVEN_ODD);
              float max = (float) (2.0F* Math.PI);
              ret.moveTo(width, halfWidth);
              for (double step = 0; step < max; step += degreesPerStep) {
                  ret.lineTo((float)(halfWidth + bigRadius * Math.cos(step)), (float)(halfWidth + bigRadius * Math.sin(step)));
                  ret.lineTo((float)(halfWidth + radius * Math.cos(step + halfDegreesPerStep)), (float)(halfWidth + radius * Math.sin(step + halfDegreesPerStep)));
              }
              ret.close();
              return ret;
          }
          

          【讨论】:

          • 回答了我自己的问题,设置星指向北: 1. 设置 FillType.WINDING。 2. 将最大值设置为 (2 * PI - PI / 2)。 3. 设置 ret.moveTo(halfWidth, 0)。
          【解决方案5】:

          如果你需要在一个正方形内画一个星星,你可以使用下面的代码。

          posXposY 是包围星形尖端的正方形左上角的坐标(实际上并未绘制正方形)。

          size是正方形的宽度和高度。

          a 是星星的顶端。路径是顺时针创建的。

          这绝不是一个完美的解决方案,但它可以很快完成工作。

           public void drawStar(float posX, float posY, int size, Canvas canvas){
          
                      int hMargin = size/9;
                      int vMargin = size/4;
          
                      Point a = new Point((int) (posX + size/2), (int) posY);
                      Point b = new Point((int) (posX + size/2 + hMargin), (int) (posY + vMargin));
                      Point c = new Point((int) (posX + size), (int) (posY + vMargin));
                      Point d = new Point((int) (posX + size/2 + 2*hMargin), (int) (posY + size/2 + vMargin/2));
                      Point e = new Point((int) (posX + size/2 + 3*hMargin), (int) (posY + size));
                      Point f = new Point((int) (posX + size/2), (int) (posY + size - vMargin));
                      Point g = new Point((int) (posX + size/2 - 3*hMargin), (int) (posY + size));
                      Point h = new Point((int) (posX + size/2 - 2*hMargin), (int) (posY + size/2 + vMargin/2));
                      Point i = new Point((int) posX, (int) (posY + vMargin));
                      Point j = new Point((int) (posX + size/2 - hMargin), (int) (posY + vMargin));
          
                      Path path = new Path();
                      path.moveTo(a.x, a.y);
                      path.lineTo(b.x, b.y);
                      path.lineTo(c.x, c.y);
                      path.lineTo(d.x, d.y);
                      path.lineTo(e.x, e.y);
                      path.lineTo(f.x, f.y);
                      path.lineTo(f.x, f.y);
                      path.lineTo(g.x, g.y);
                      path.lineTo(h.x, h.y);
                      path.lineTo(i.x, i.y);
                      path.lineTo(j.x, j.y);
                      path.lineTo(a.x, a.y);
          
                      path.close();
          
                      canvas.drawPath(path, paint);
          }
          

          【讨论】:

            【解决方案6】:

            【讨论】:

            • 您需要将复杂形状划分为原始形状。例如,您可以使用两个半圆和两条线绘制心形。三角形 - 选择 3 个点并用 drawLine() 连接它们,或者更好地将数组 pf 点传递给 drawLines();
            【解决方案7】:

            使用Shape类的实例非常好))

             @Override
                public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
            
                HeartShape shape = new HeartShape();
                ShapeDrawable shapeDrawable = new ShapeDrawable(shape);
                shapeDrawable.setColorFilter(new LightingColorFilter(0, Color.BLUE));
            
                LinearLayout linearLayout = new LinearLayout(this);
                linearLayout.setLayoutParams(new LinearLayout.LayoutParams(350 * 3, 350 * 3));
                linearLayout.setBackground(shapeDrawable);
            
                setContentView(linearLayout);
              }
            

            创建一个可以渲染漂亮心脏的形状类

             public class HeartShape extends Shape {
            
              private final int INVALID_SIZE = -1;
            
              private Path mPath = new Path();
              private RectF mRectF = new RectF();
            
              private float mOldWidth = INVALID_SIZE;
              private float mOldHeight = INVALID_SIZE;
            
              private float mScaleX, mScaleY;
            
              public HeartShape() {
            
              }
            
              @Override
              public void draw(Canvas canvas, Paint paint) {
                canvas.save();
                canvas.scale(mScaleX, mScaleY);
            
                float width = mRectF.width();
                float height = mRectF.height();
            
                float halfWidth = width/2;
                float halfHeight = height/2;
            
                float stdDestX = 5 * width / 14;
                float stdDestY = 2 * height / 3;
            
                PointF point1 = new PointF(stdDestX, 0);
                PointF point2 = new PointF(0, height / 15);
                PointF point3 = new PointF(stdDestX / 5, stdDestY);
                PointF point4 = new PointF(stdDestX, stdDestY);
            
                // Starting point
                mPath.moveTo(halfWidth, height / 5);
            
                mPath.cubicTo(point1.x, point1.y, point2.x, point2.y, width / 28, 2 * height / 5);
                mPath.cubicTo(point3.x, point3.y, point4.x, point4.y, halfWidth, height);
            
                canvas.drawPath(mPath, paint);
            
                canvas.scale(-mScaleX, mScaleY, halfWidth, halfHeight);
                canvas.drawPath(mPath, paint);
            
                canvas.restore();
              }
            
              @Override
              protected void onResize(float width, float height) {
                mOldWidth = mOldWidth == INVALID_SIZE ? width : Math.max(1, mOldWidth);
                mOldHeight = mOldHeight == INVALID_SIZE ? height : Math.max(1, mOldHeight);
            
                width = Math.max(1, width);
                height = Math.max(1, height);
            
                mScaleX = width / mOldWidth;
                mScaleY = height / mOldHeight;
            
                mOldWidth = width;
                mOldHeight = height;
            
            
                mRectF.set(0, 0, width, height);
              }
            
              @Override
              public void getOutline(@NonNull Outline outline) {
                // HeartShape not supported outlines
              }
            
              @Override
              public HeartShape clone() throws CloneNotSupportedException {
                HeartShape shape = (HeartShape) super.clone();
                shape.mPath = new Path(mPath);
                return shape;
              }
            
            }
            

            【讨论】:

              【解决方案8】:

              如果你只想绘制你指定的形状,我建议首先为每个形状创建一个类,并让每个形状实现 draw() 方法,你可以在其中传递画布和绘画对象。

              例如要创建多个星星,首先创建一个“Star”类并在其中实现逻辑。

              class Star(
                      var cx: Float = 0f,
                      var cy: Float = 0f,
                      var radius: Float = 0f,
                      var angle: Float = 0f,
                      var color: Int = Color.WHITE,
                      var numberOfSpikes: Int = 5,
                      var depth: Float = 0.4f
                  ) {
              
                      val path: Path = Path()
                      val point: PointF = PointF()
              
                      fun init() {
                          path.rewind()
              
                          var totalAngle = 0f
                          for (i in 0 until numberOfSpikes * 2) {
              
                              val x = cx
                              var y = cy
                              y -= if (i % 2 != 0) (radius * depth)
                              else (radius * 1f)
              
                              StaticMethods.rotate(cx, cy, x, y, totalAngle, false, point)
                              totalAngle += 360f / (numberOfSpikes * 2)
                              if (i == 0) {
                                  path.moveTo(point.x, point.y)
                              } else {
                                  path.lineTo(point.x, point.y)
                              }
                          }
                      }
              
                      fun draw(canvas: Canvas, paint: Paint) {
              
                          paint.apply {
                              style = Paint.Style.FILL
                              color = this@Star.color
                          }
                          canvas.drawPath(path, paint)
                      }
                  }
              
              
              object StaticMethods {
                  
               /**
                   * Rotate a point around a center with given angle
                   * @param cx rotary center point x coordinate
                   * @param cy rotary center point y coordinate
                   * @param x x coordinate of the point that will be rotated
                   * @param y y coordinate of the point that will be rotated
                   * @param angle angle of rotation in degrees
                   * @param anticlockWise rotate clockwise or anti-clockwise
                   * @param resultPoint object where the result rotational point will be stored
                   */
                  fun rotate(cx: Float, cy: Float, x: Float, y: Float, angle: Float, anticlockWise: Boolean = false, resultPoint: PointF = PointF()): PointF {
              
                      if (angle == 0f) {
                          resultPoint.x = x
                          resultPoint.y = y
                          return resultPoint
                      }
              
                      val radians = if (anticlockWise) {
                          (Math.PI / 180) * angle
                      } else {
                          (Math.PI / -180) * angle
                      }
              
                      val cos = Math.cos(radians)
                      val sin = Math.sin(radians)
                      val nx = (cos * (x - cx)) + (sin * (y - cy)) + cx
                      val ny = (cos * (y - cy)) - (sin * (x - cx)) + cy
              
                      resultPoint.x = nx.toFloat()
                      resultPoint.y = ny.toFloat()
                      return resultPoint
                  }
                  /**
                   * Inline function that is called, when the final measurement is made and
                   * the view is about to be draw.
                   */
                  inline fun View.afterMeasured(crossinline function: View.() -> Unit) {
                      viewTreeObserver.addOnGlobalLayoutListener(object :
                          ViewTreeObserver.OnGlobalLayoutListener {
                          override fun onGlobalLayout() {
                              if (measuredWidth > 0 && measuredHeight > 0) {
                                  viewTreeObserver.removeOnGlobalLayoutListener(this)
                                  function()
                              }
                          }
                      })
                   }
              }
              

              然后创建一个自定义视图,您可以在其中绘制形状,下面是创建 100 个随机星并在画布上绘制它们的代码。

              class StarView : View {
                  constructor(context: Context) : super(context)
                  constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
                  constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
              
                  val stars: ArrayList<Helper.Star> = arrayListOf()
              
                  val paint: Paint = Paint().apply {
                      isAntiAlias = true
                  }
              
                  init {
              
                      // after the view is measured and we have the width and height
                      afterMeasured {
              
                          // create 100 stars
                          for (i in 0 until 100) {
              
                              val star = Helper.Star(
                                  cx = width * Math.random().toFloat(),
                                  cy = height * Math.random().toFloat(),
                                  radius = width * 0.1f * Math.random().toFloat(),
                              )
                              star.init()
                              stars.add(star)
                          }
                      }
              
                  }
              
              
                  override fun onDraw(canvas: Canvas) {
                      stars.forEach {
                          it.draw(canvas, paint)
                      }
                  }
              }
              

              结果如下:


              如果您想创建许多不同的复杂形状,可以使用library。您可以在其中使用路径数据作为字符串传递 svg 形状。首先使用任何 SVG 矢量编辑器创建复杂的形状,例如

              • 微软表达式设计
              • Adobe Illustrator
              • Inkscape

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2011-03-30
                • 1970-01-01
                • 2019-02-27
                • 1970-01-01
                • 2016-04-12
                • 1970-01-01
                • 2011-03-11
                相关资源
                最近更新 更多