【问题标题】:How to draw filled polygon?如何绘制填充多边形?
【发布时间】:2011-01-04 02:10:30
【问题描述】:

如何在Android中绘制填充多边形?

【问题讨论】:

    标签: android


    【解决方案1】:

    Android 没有像 Java 这样方便的 drawPolygon(x_array, y_array, numberofpoints) 操作。您必须逐点制作Path 对象。例如,要为 3D 地牢墙制作填充梯形,您可以将所有点放在 x 和 y 数组中,然后编码如下:

    Paint wallpaint = new Paint();
    wallpaint.setColor(Color.GRAY);
    wallpaint.setStyle(Style.FILL);
    
    Path wallpath = new Path();
    wallpath.reset(); // only needed when reusing this path for a new build
    wallpath.moveTo(x[0], y[0]); // used for first point
    wallpath.lineTo(x[1], y[1]);
    wallpath.lineTo(x[2], y[2]);
    wallpath.lineTo(x[3], y[3]);
    wallpath.lineTo(x[0], y[0]); // there is a setLastPoint action but i found it not to work as expected
    
    canvas.drawPath(wallpath, wallpaint);
    

    要为某个深度添加恒定的线性渐变,您可以编写如下代码。注意 y[0] 使用了两次来保持渐变水平:

     wallPaint.reset(); // precaution when resusing Paint object, here shader replaces solid GRAY anyway
     wallPaint.setShader(new LinearGradient(x[0], y[0], x[1], y[0], Color.GRAY, Color.DKGRAY,TileMode.CLAMP)); 
    
     canvas.drawPath(wallpath, wallpaint);
    

    请参阅 PaintPathCanvas 文档了解更多选项,例如数组定义的渐变、添加弧线以及在多边形上放置位图。

    【讨论】:

    • 不使用Path.lineTo(x0, y0),您只需调用Path.close() 即可自动添加结束线段。
    【解决方案2】:

    您需要将绘制对象设置为 FILL

    Paint paint = new Paint();
    paint.setStyle(Paint.Style.FILL);
    

    然后你可以画任何你想要的东西,它就会被填满。

    canvas.drawCircle(20, 20, 15, paint);
    canvas.drawRectangle(60, 20, 15, paint);
    

    等等

    对于更复杂的形状,您需要使用PATH object

    【讨论】:

    • 是否可以用图标(标记)填充这个多边形?图标之间有一些填充和指定的空间?
    【解决方案3】:

    我喜欢分三步完成...

    第 1 步。创建一个尖锐的类 ;-)

    /**
     * Simple point
     */
    private class Point {
    
        public float x = 0;
        public float y = 0;
    
        public Point(float x, float y) {
            this.x = x;
            this.y = y;
        }
    }
    

    第 2 步。添加绘图方法/函数

    /**
     * Draw polygon
     *
     * @param canvas The canvas to draw on
     * @param color  Integer representing a fill color (see http://developer.android.com/reference/android/graphics/Color.html)
     * @param points Polygon corner points
     */
    private void drawPoly(Canvas canvas, int color, Point[] points) {
        // line at minimum...
        if (points.length < 2) {
            return;
        }
    
        // paint
        Paint polyPaint = new Paint();
        polyPaint.setColor(color);
        polyPaint.setStyle(Style.FILL);
    
        // path
        Path polyPath = new Path();
        polyPath.moveTo(points[0].x, points[0].y);
        int i, len;
        len = points.length;
        for (i = 0; i < len; i++) {
            polyPath.lineTo(points[i].x, points[i].y);
        }
        polyPath.lineTo(points[0].x, points[0].y);
    
        // draw
        canvas.drawPath(polyPath, polyPaint);
    }
    

    第 3 步。绘制

        drawPoly(canvas, 0xFF5555ee,
                new Point[]{
                    new Point(10, 10),
                    new Point(15, 10),
                    new Point(15, 20)
                });
    

    是的,你可能会更有效地做到这一点,但可能不会更易读:-)。

    【讨论】:

    • 这段代码有一些严重的问题:首先,Android 中已经有两个PointPointF 类,所以你真的不需要重新发明自己的类。其次,您确实希望避免在 View.draw() 方法中分配对象,并且您提供的示例为单次绘制执行了大量分配。
    • 这取决于您要绘制多少个矩形。但我同意这不是最有效的方法。但高效通常意味着降低代码的可用性或可读性或两者兼而有之。
    【解决方案4】:

    绘制具有 x 边和自定义半径的多边形:

    private void drawPolygon(Canvas mCanvas, float x, float y, float radius, float sides, float startAngle, boolean anticlockwise, Paint paint) {
    
        if (sides < 3) { return; }
    
        float a = ((float) Math.PI *2) / sides * (anticlockwise ? -1 : 1);
        mCanvas.save();
        mCanvas.translate(x, y);
        mCanvas.rotate(startAngle);
        Path path = new Path();
        path.moveTo(radius, 0);
        for(int i = 1; i < sides; i++) {
            path.lineTo(radius * (float) Math.cos(a * i), radius * (float) Math.sin(a * i));
        }
        path.close();
        mCanvas.drawPath(path, paint);
        mCanvas.restore();
    }
    

    【讨论】:

    • 正是我想要的
    【解决方案5】:

    顺便说一句 - 我发现一旦您开始创建路径,路径中的任何 moveTo 命令都将意味着该形状未填充。

    仔细想想,Android/Java 会留下未填充的形状是有道理的,因为 moveTo 将代表多边形中的中断。

    不过我看过一些这样的教程How to draw a filled triangle in android canvas?

    在每个 lineTo 之后都有 moveTo。尽管这可能会导致多边形不中断,但 Android 仍假定 moveTo 表示多边形中的中断。

    【讨论】:

      【解决方案6】:

      老问题,但对任何发现这个问题的人来说都是一个技巧。如果您包含具有所需多边形的字体作为字形,则可以使用 drawText 函数来绘制多边形。

      缺点是你必须提前知道你需要什么形状。好处是如果你提前知道你可以包含一个漂亮的形状库。此代码假定您在项目的 assets/fonts 文件夹中有一个名为 shape 的字体。

                  TypeFace shapesTypeFace = Typeface.createFromAsset(getAssets(), "fonts/shapes.ttf");
                  Paint stopSignPaint = new Paint();
                  stopSignPaint.setColor(Color.RED);
                  //set anti-aliasing so it looks nice
                  stopSignPaint.setAntiAlias(true);
                  stopSignPaint.setTextSize(200);
                  stopSignPaint.setTypeface(shapesTypeFace);
      
                  //will show up as a box or question mark since 
                  //the current display font doesn't have this glyph. 
                  //open the shapes font in a tool like Character Map 
                  //to copy and paste the glyph into your IDE
                  //saving it as a variable makes it much easier to work with
                  String hexagonGlyph = ""
                  String triangleGlyph = ""
      
      
                  ....whatever code you got...
      
      
                  //arguments: text, x coordinate, y coordinate, paint
                   canvas.drawText(hexagonGlyph, 200, 100, stopSignPaint);
      
                  //make it into a go sign
                  stopSignPaint.setColor(Color.Green);
                   canvas.drawText(hexagonGlyph, 200, 100, stopSignPaint);
      
      
                  //make a tiny one
                  stopSignPaint.setTextSize(20);
                  stopSignPaint.setColor(Color.RED);
                   canvas.drawText(hexagonGlyph, 200, 100, stopSignPaint);
      
      
                   //make a triangle
                   canvas.drawText(triangleGlyph, 200, 100, stopSignPaint);
      

      【讨论】:

        【解决方案7】:

        试试这个,或者see the full demo

            Paint paint = new Paint();
            paint.setColor(Color.parseColor("#BAB399"));
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        

        【讨论】:

          【解决方案8】:

          这个类可以用来绘制任何类型的多边形。只需在 onDraw() 方法中调用drawPolygonPath()

          class PolygonView : View {
              constructor(context: Context?) : super(context) {
                  init()
              }
          
              constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
                  init()
              }
          
              constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
                  context,
                  attrs,
                  defStyleAttr
              ) {
                  init()
              }
          
              private lateinit var paint: Paint
          
              private fun init() {
                  paint = Paint().apply {
                      color = Color.RED
                      isAntiAlias = true
                      style = Paint.Style.FILL
                      strokeWidth = 10f
                  }
              }
          
          
              override fun onDraw(canvas: Canvas) {
                  canvas.drawPath(drawPolygonPath(8, 150f), paint)
                  canvas.drawPath(drawPolygonPath(5, 120f), paint)
          
              }
          
              /**
               * @param sides number of polygon sides
               * @param radius side length.
               * @param cx drawing x start point.
               * @param cy drawing y start point.
               * */
              private fun drawPolygonPath(
                  sides: Int,
                  radius: Float,
                  cx: Float = radius,
                  cy: Float = radius
              ): Path {
                  val path = Path()
                  val x0 = cx + (radius * cos(0.0).toFloat())
                  val y0 = cy + (radius * sin(0.0).toFloat())
                  //2.0 * Math.PI = 2π, which means one circle(360)
                  //The polygon total angles of the sides must equal 360 degree.
                  val angle = 2 * Math.PI / sides
          
                  path.moveTo(x0, y0)
          
                  for (s in 1 until sides) {
          
                      path.lineTo(
                          cx + (radius * cos(angle * s)).toFloat(),
                          cy + (radius * sin(angle * s)).toFloat()
                      )
                  }
          
                  path.close()
          
                  return path
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-04-21
            • 2011-11-29
            • 1970-01-01
            • 2014-10-14
            • 2015-01-12
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多