2011-12-07 25 views
5

En esta clase de borde personalizado, defino una forma RoundRectangle2D. Este objeto se usa para pintar el borde. Desafortunadamente, dado que el método paint de JComponent invoca paintComponent antes de paintBorder, establecer el clip Graphics en la forma RoundRectangle2D no tiene ningún efecto; incluso si publico un repaint. Por lo tanto, el componente pintará fuera de su borde, lo cual es comprensiblemente indeseable.Pintura de componentes fuera del borde personalizado

Entonces, me preguntaba: ¿cómo puedo obtener el componente para pintar exclusivamente dentro de un borde personalizado?

Un enfoque que consideré fue obtener el objeto Border del componente en el método paintComponent. Y luego lanzando este objeto a la clase apropiada, donde defino los parámetros que influirán en el clip. Pero esto no parecía un diseño "sano".


Editar -

import java.awt.BasicStroke; 
import java.awt.Color; 
import java.awt.Component; 
import java.awt.Dimension; 
import java.awt.FlowLayout; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Insets; 
import java.awt.RenderingHints; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.SwingUtilities; 
import javax.swing.border.AbstractBorder; 

class JRoundedCornerBorder extends AbstractBorder 
{ 
    private static final long serialVersionUID = 7644739936531926341L; 
    private static final int THICKNESS = 2; 

    JRoundedCornerBorder() 
    { 
     super(); 
    } 

    @Override 
    public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) 
    { 
     Graphics2D g2 = (Graphics2D)g.create(); 

     g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
     if(c.hasFocus()) 
     { 
      g2.setColor(Color.BLUE); 
     } 
     else 
     { 
      g2.setColor(Color.BLACK); 
     } 
     g2.setStroke(new BasicStroke(THICKNESS, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 
     g2.drawRoundRect(THICKNESS, THICKNESS, width - THICKNESS - 2, height - THICKNESS - 2, 20, 20); 

     g2.dispose(); 
    } 

    @Override 
    public Insets getBorderInsets(Component c) 
    { 
     return new Insets(THICKNESS, THICKNESS, THICKNESS, THICKNESS); 
    } 

    @Override 
    public Insets getBorderInsets(Component c, Insets insets) 
    { 
     insets.left = insets.top = insets.right = insets.bottom = THICKNESS; 
     return insets; 
    } 

    public boolean isBorderOpaque() { 
     return false; 
    } 

    public static void main(String[] args) 
    { 
     SwingUtilities.invokeLater(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       final JFrame frame = new JFrame(); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setLayout(new FlowLayout()); 

       // Add button with custom border 
       final JButton button = new JButton("Hello"); 
       button.setBorder(new JRoundedCornerBorder()); 
       button.setBackground(Color.YELLOW); 
       button.setPreferredSize(new Dimension(200, 200)); 
       frame.add(button); 

       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 
} 

enter image description here

Los círculos rojos ponen de manifiesto que el componente se extiende más allá de sus fronteras.

+0

debería suceder automáticamente, siempre que su borde informe correctamente sus inserciones (también conocido como: espacio en el que desea pintar) Lo mejor es mostrar su código :-) – kleopatra

+0

recién recordado (podría estar relacionado o no, difícil de ver sin ver el código): AbstractBorder solía tener un error en getBorderInsets - asegúrese de implementarlo correctamente. – kleopatra

+0

@kleopatra, creo que estoy informando sobre mis inserciones correctamente ... ¡por favor, mira mi edición! – mre

Respuesta

6

Ahh ... por fin lo conseguí (se perdió el roundBorder por alguna razón, mi culpa :-) El botón está simplemente cumpliendo con su contrato: indica que es opaco, por lo que debe llenar su área completa, incluida la del borde. Por lo que establecer un clip (que se podía hacer en paintComponent) haría violar el contrato:

// DO NOT - a opaque component violates its contract, as it will not fill 
// its complete area 
     @Override 
     protected void paintComponent(Graphics g) { 
      if (getBorder() instanceof JRoundedCornerBorder) { 
       Shape borderShape = ((JRoundedCornerBorder) getBorder()). 
        getBorderShape(getWidth(), getHeight()); 
       g.setClip(borderShape); 
      } 
      super.paintComponent(g); 
     } 

feo, pero seguro sería informar a sí misma como transparente y hacerse cargo del fondo de la pintura sí mismo:

 @Override 
     protected void paintComponent(Graphics g) { 
      if (getBorder() instanceof JRoundedCornerBorder) { 
       g.setColor(getBackground()); 
       Shape borderShape = ((JRoundedCornerBorder) getBorder()) 
        .getBorderShape(getWidth(), getHeight()); 
       ((Graphics2D) g).fill(borderShape); 
      } 
      super.paintComponent(g); 
     } 

     @Override 
     public boolean isContentAreaFilled() { 
      if (getBorder() instanceof JRoundedCornerBorder) return false; 
      return super.isContentAreaFilled(); 
     } 

     // using 
     button.setOpaque(false); 
3

¿Cómo pintas el borde? ¿Implementaste la interfaz Border? Hay 3 métodos para colocar toda su lógica de frontera no

void paintBorder(Component c, Graphics g, int x, int y, int width, int height); 
    Insets getBorderInsets(Component c); 
    boolean isBorderOpaque(); 
+0

He subclasificado 'AbstractBorder'. Cuando implementé la lógica en 'paintBorder', partes del componente fueron pintadas fuera de los límites de la frontera, que es lo que no quiero. Entonces, pensé que establecer el clip antes en esos límites evitaría esto y no sabía cómo implementarlo. – mre

5

no una respuesta a su pregunta, sólo otra idea de cómo hacerlo

enter image description here

de código muy larga

import java.awt.*; 
    import java.awt.geom.*; 
    import javax.swing.*; 
    import javax.swing.event.*; 

    public class Panel2Test { 

     public static void main(String[] args) { 
      SwingUtilities.invokeLater(new Runnable() { 

       @Override 
       public void run() { 
        ShadeOptionsPanel shadeOptions = new ShadeOptionsPanel(); 
        ShadowSelector shadowSelector = new ShadowSelector(shadeOptions); 
        //ComponentSource componentSource = new ComponentSource(shadeOptions); 
        JFrame f = new JFrame("Rounded Concept Demo with Shadows"); 
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        f.add(shadowSelector, "North"); 
        f.add(shadeOptions); 
        //f.add(componentSource, "South"); 
        f.setSize(300, 200); 
        f.setLocation(150, 150); 
        f.setVisible(true); 
       } 
      }); 
     } 

     private Panel2Test() { 
     } 
    } 

    class ShadeOptionsPanel extends JPanel { 

     private static final long serialVersionUID = 1L; 
     private final int PAD, DIA, BORDER; 
     private Color colorIn, colorOut; 
     private int xc, yc; 
     private Ellipse2D eIn, eOut; 
     private GradientPaint gradient; 
     private CustomPaint customPaint; 
     private Area arcBorder; 
     private int width, height; 
     private Point2D neOrigin, nwOrigin, swOrigin, seOrigin, neDiag, nwDiag, swDiag, seDiag; 
     private final static int NORTHEAST = 0, NORTHWEST = 1, SOUTHWEST = 2, SOUTHEAST = 3; 
     public int shadowVertex = 3; 

     public ShadeOptionsPanel() { 
      PAD = 25; 
      DIA = 75; 
      BORDER = 10; 
      colorIn = Color.black; 
      colorOut = getBackground(); 
     } 

     @Override 
     public void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      Graphics2D g2 = (Graphics2D) g; 
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
      width = getWidth(); 
      height = getHeight(); 
      g2.drawRoundRect(PAD, PAD, width - 2 * PAD, height - 2 * PAD, DIA, DIA); 
      calculateArcOrigins(); 
      calculateCardinalDiagonals(); 
      drawVertexArc(g2, shadowVertex); 
      switch (shadowVertex) { 
       case NORTHEAST: 
        drawNorthSide(g2); 
        drawEastSide(g2); 
        xc = PAD + DIA/2; // draw northwest arc 
        yc = PAD + DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(nwOrigin, nwDiag); 
        eOut = getOuterEllipse(nwOrigin, nwDiag); 
        arcBorder = getArcArea(eIn, eOut, 90.0); 
        g2.fill(arcBorder); 
        xc = width - PAD - DIA/2; // draw southeast arc 
        yc = height - PAD - DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(seOrigin, seDiag); 
        eOut = getOuterEllipse(seOrigin, seDiag); 
        arcBorder = getArcArea(eIn, eOut, 270.0); 
        g2.fill(arcBorder); 
        break; 
       case NORTHWEST: 
        drawNorthSide(g2); 
        drawWestSide(g2); 
        xc = width - PAD - DIA/2;// draw northeast arc 
        yc = PAD + DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(neOrigin, neDiag); 
        eOut = getOuterEllipse(neOrigin, neDiag); 
        arcBorder = getArcArea(eIn, eOut, 0.0); 
        g2.fill(arcBorder); 
        xc = PAD + DIA/2;// draw southwest arc 
        yc = height - PAD - DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(swOrigin, swDiag); 
        eOut = getOuterEllipse(swOrigin, swDiag); 
        arcBorder = getArcArea(eIn, eOut, 180.0); 
        g2.fill(arcBorder); 
        break; 
       case SOUTHWEST: 
        drawWestSide(g2); 
        drawSouthSide(g2); 
        xc = PAD + DIA/2; // draw northwest arc 
        yc = PAD + DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(nwOrigin, nwDiag); 
        eOut = getOuterEllipse(nwOrigin, nwDiag); 
        arcBorder = getArcArea(eIn, eOut, 90.0); 
        g2.fill(arcBorder); 
        xc = width - PAD - DIA/2; // draw the southeast arc 
        yc = height - PAD - DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(seOrigin, seDiag); 
        eOut = getOuterEllipse(seOrigin, seDiag); 
        arcBorder = getArcArea(eIn, eOut, 270.0); 
        g2.fill(arcBorder); 
        break; 
       case SOUTHEAST: 
        drawEastSide(g2); 
        drawSouthSide(g2); 
        xc = width - PAD - DIA/2; // draw northeast arc 
        yc = PAD + DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(neOrigin, neDiag); 
        eOut = getOuterEllipse(neOrigin, neDiag); 
        arcBorder = getArcArea(eIn, eOut, 0.0); 
        g2.fill(arcBorder); 
        xc = PAD + DIA/2; // draw southwest arc 
        yc = height - PAD - DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(swOrigin, swDiag); 
        eOut = getOuterEllipse(swOrigin, swDiag); 
        arcBorder = getArcArea(eIn, eOut, 180.0); 
        g2.fill(arcBorder); 
      } 
     } 

     private Ellipse2D getInnerEllipse(Point2D center, Point2D corner) { 
      return new Ellipse2D.Double(center.getX() - DIA/2, 
        center.getY() - DIA/2, DIA, DIA); 
     } 

     private Ellipse2D getOuterEllipse(Point2D center, Point2D corner) { 
      int w = DIA, h = DIA; 
      if (shadowVertex < 2) { 
       if (center.getY() > corner.getY()) { 
        h += 2 * BORDER; 
       } else { 
        w += 2 * BORDER; 
       } 
      } else if (center.getY() > corner.getY()) { 
       w += 2 * BORDER; 
      } else { 
       h += 2 * BORDER; 
      } 
      return new Ellipse2D.Double(center.getX() - w/2, center.getY() - h/2, w, h); 
     } 

     private Area getArcArea(Ellipse2D e1, Ellipse2D e2, double start) { 
      Arc2D arc1 = new Arc2D.Double(e1.getBounds2D(), start, 90.0, Arc2D.PIE); 
      Arc2D arc2 = new Arc2D.Double(e2.getBounds2D(), start, 90.0, Arc2D.PIE); 
      Area arc = new Area(arc2); 
      arc.subtract(new Area(arc1)); 
      return arc; 
     } 

     private void drawNorthSide(Graphics2D g2) { 
      gradient = new GradientPaint(width/2, PAD - BORDER, colorOut, 
        width/2, PAD, colorIn); 
      g2.setPaint(gradient); 
      g2.fill(new Rectangle2D.Double(PAD + DIA/2, PAD - BORDER, 
        width - 2 * (PAD + DIA/2) + 1, BORDER)); 
     } 

     private void drawWestSide(Graphics2D g2) { 
      gradient = new GradientPaint(PAD - BORDER, height/2, colorOut, 
        PAD, height/2, colorIn); 
      g2.setPaint(gradient); 
      g2.fill(new Rectangle2D.Double(PAD - BORDER, PAD + DIA/2, 
        BORDER, height - 2 * (PAD + DIA/2) + 1)); 
     } 

     private void drawSouthSide(Graphics2D g2) { 
      gradient = new GradientPaint(width/2, height - PAD, colorIn, 
        width/2, height - PAD + BORDER, colorOut); 
      g2.setPaint(gradient); 
      g2.fill(new Rectangle2D.Double(PAD + DIA/2, height - PAD, 
        width - 2 * (PAD + DIA/2) + 1, BORDER)); 
     } 

     private void drawEastSide(Graphics2D g2) { 
      gradient = new GradientPaint(width - PAD, height/2, colorIn, 
        width - PAD + BORDER, height/2, colorOut); 
      g2.setPaint(gradient); 
      g2.fill(new Rectangle2D.Double(width - PAD, PAD + DIA/2, 
        BORDER, height - 2 * (PAD + DIA/2) + 1)); 
     } 

     /** 
     * Draws the central, full-shaded arc (opposite of the unshaded arc). 
     */ 
     private void drawVertexArc(Graphics2D g2, int index) { 
      switch (index) { 
       case NORTHEAST: 
        xc = width - PAD - DIA/2; 
        yc = PAD + DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = new Ellipse2D.Double(width - PAD - DIA, PAD, DIA, DIA); 
        eOut = new Ellipse2D.Double(width - PAD - DIA - BORDER, PAD - BORDER, 
          DIA + 2 * BORDER, DIA + 2 * BORDER); 
        arcBorder = getArcArea(eIn, eOut, 0.0); 
        g2.fill(arcBorder); 
        break; 
       case NORTHWEST: 
        xc = PAD + DIA/2; 
        yc = PAD + DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = new Ellipse2D.Double(PAD, PAD, DIA, DIA); 
        eOut = new Ellipse2D.Double(PAD - BORDER, PAD - BORDER, 
          DIA + 2 * BORDER, DIA + 2 * BORDER); 
        arcBorder = getArcArea(eIn, eOut, 90.0); 
        g2.fill(arcBorder); 
        break; 
       case SOUTHWEST: 
        xc = PAD + DIA/2; 
        yc = height - PAD - DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = new Ellipse2D.Double(PAD, height - PAD - DIA, DIA, DIA); 
        eOut = new Ellipse2D.Double(PAD - BORDER, height - PAD - DIA - BORDER, 
          DIA + 2 * BORDER, DIA + 2 * BORDER); 
        arcBorder = getArcArea(eIn, eOut, 180.0); 
        g2.fill(arcBorder); 
        break; 
       case SOUTHEAST: 
        xc = width - PAD - DIA/2; 
        yc = height - PAD - DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = new Ellipse2D.Double(width - PAD - DIA, height - PAD - DIA, DIA, DIA); 
        eOut = new Ellipse2D.Double(width - PAD - DIA - BORDER, 
          height - PAD - DIA - BORDER, DIA + 2 * BORDER, DIA + 2 * BORDER); 
        arcBorder = getArcArea(eIn, eOut, 270.0); 
        g2.fill(arcBorder); 
      } 
     } 

     private void calculateArcOrigins() { 
      neOrigin = new Point2D.Double(width - PAD - DIA/2, PAD + DIA/2); 
      nwOrigin = new Point2D.Double(PAD + DIA/2, PAD + DIA/2); 
      swOrigin = new Point2D.Double(PAD + DIA/2, height - PAD - DIA/2); 
      seOrigin = new Point2D.Double(width - PAD - DIA/2, height - PAD - DIA/2); 
     } 

     private void calculateCardinalDiagonals() { 
      neDiag = new Point2D.Double(neOrigin.getX() 
        + DIA * Math.cos(Math.toRadians(45))/2, 
        neOrigin.getY() - DIA * Math.sin(Math.toRadians(45))/2); 
      nwDiag = new Point2D.Double(nwOrigin.getX() 
        + DIA * Math.cos(Math.toRadians(135))/2, 
        nwOrigin.getY() - DIA * Math.sin(Math.toRadians(135))/2); 
      swDiag = new Point2D.Double(swOrigin.getX() 
        + DIA * Math.cos(Math.toRadians(225))/2, 
        swOrigin.getY() - DIA * Math.sin(Math.toRadians(225))/2); 
      seDiag = new Point2D.Double(seOrigin.getX() 
        + DIA * Math.cos(Math.toRadians(315))/2, 
        seOrigin.getY() - DIA * Math.sin(Math.toRadians(315))/2); 
     } 

     public Dimension getInnerSize() { 
      return new Dimension((int) nwOrigin.distance(neOrigin), 
        (int) nwOrigin.distance(swOrigin)); 
     } 
    } 

    class ShadowSelector extends JPanel { 

     private static final long serialVersionUID = 1L; 
     private ShadeOptionsPanel soPanel; 
     private String[] directions = {"northeast", "northwest", "southwest", "southeast"}; 

     public ShadowSelector(ShadeOptionsPanel sop) { 
      soPanel = sop; 

      final SpinnerListModel model = new SpinnerListModel(directions); 
      model.setValue(directions[3]); 
      JSpinner spinner = new JSpinner(model); 
      spinner.setPreferredSize(new Dimension(90, spinner.getPreferredSize().height)); 
      spinner.addChangeListener(new ChangeListener() { 

       @Override 
       public void stateChanged(ChangeEvent e) { 
        String value = (String) model.getValue(); 
        soPanel.shadowVertex = model.getList().indexOf(value); 
        soPanel.repaint(); 
       } 
      }); 
      add(new JLabel("shadow vertex", JLabel.RIGHT)); 
      add(spinner); 
     } 
    } 

class CustomPaint implements Paint { 

    Point2D originP, radiusP; 
    int radius, border; 
    Color colorIn, colorOut; 

    public CustomPaint(int x, int y, Point2D radiusP, 
      int radius, int border, 
      Color colorIn, Color colorOut) { 
     originP = new Point2D.Double(x, y); 
     this.radiusP = radiusP; 
     this.radius = radius; 
     this.border = border; 
     this.colorIn = colorIn; 
     this.colorOut = colorOut; 
    } 

    @Override 
    public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { 
     Point2D xformOrigin = xform.transform(originP, null), xformRadius = xform.deltaTransform(radiusP, null); 
     return new CustomPaintContext(xformOrigin, xformRadius, radius, border, colorIn, colorOut); 
    } 

    @Override 
    public int getTransparency() { 
     int alphaIn = colorIn.getAlpha(); 
     int alphaOut = colorOut.getAlpha(); 
     return (((alphaIn & alphaOut) == 0xff) ? OPAQUE : TRANSLUCENT); 
    } 
} 

class CustomPaintContext implements PaintContext { 

    Point2D originP, radiusP; 
    Color colorIn, colorOut; 
    int radius, border; 

    public CustomPaintContext(Point2D originP, Point2D radiusP, int radius, int border, Color colorIn, Color colorOut) { 
     this.originP = originP; 
     this.radiusP = radiusP; 
     this.radius = radius; 
     this.border = border; 
     this.colorIn = colorIn; 
     this.colorOut = colorOut; 
    } 

    @Override 
    public void dispose() { 
    } 

    @Override 
    public ColorModel getColorModel() { 
     return ColorModel.getRGBdefault(); 
    } 

    @Override 
    public Raster getRaster(int x, int y, int w, int h) { 
     WritableRaster raster = getColorModel().createCompatibleWritableRaster(w, h); 
     int[] data = new int[w * h * 4]; 
     for (int j = 0; j < h; j++) { 
      for (int i = 0; i < w; i++) { 
       double distance = originP.distance(x + i, y + j); 
       double r = radiusP.distance(radius, radius); 
       double ratio = distance - r < 0 ? 0.0 : (distance - r)/border; 
       if (ratio > 1.0) { 
        ratio = 1.0; 
       } 
       int base = (j * w + i) * 4; 
       data[base + 0] = (int) (colorIn.getRed() + ratio * (colorOut.getRed() - colorIn.getRed())); 
       data[base + 1] = (int) (colorIn.getGreen() + ratio * (colorOut.getGreen() - colorIn.getGreen())); 
       data[base + 2] = (int) (colorIn.getBlue() + ratio * (colorOut.getBlue() - colorIn.getBlue())); 
       data[base + 3] = (int) (colorIn.getAlpha() + ratio * (colorOut.getAlpha() - colorIn.getAlpha())); 
      } 
     } 
     raster.setPixels(0, 0, w, h, data); 
     return raster; 
    } 
} 
+0

OMG. ¿Algún código para la implementación de CustomPaint? – ThomasRS

+1

@Thomas básicamente (excluyendo a mi persona) todos de [Top10 in All Time] (http: // stackoverflow.com/tags/swing/topusers) son muy buenos en Algebra :-) – mKorbel

+0

@Thomas, encontré ese código interesante, así que google un poco y encontré esto, http://www.pastemine.com/97q0. Allí puedes encontrar la implementación de CustomPaint. De todos modos, ese es otro ejemplo que no funciona, así que corregiré el código – albfan

Cuestiones relacionadas