Vistas personalizadas y dibujo en el desarrollo móvil: concepto, qué API y cómo funciona

Autor: IT Sectr Publicado: 2026-08-01 Tiempo de lectura: 11 min

Las vistas personalizadas son la base de la interfaz única de cualquier aplicación móvil. Las vistas personalizadas en aplicaciones móviles permiten crear gráficos, animaciones y elementos propios que no están disponibles en los componentes estándar. Según Apple Developer, 2025, drawRect sigue siendo el método principal de dibujo personalizado en el desarrollo móvil para iOS. En Android, el mismo rol lo cumple onDraw con Canvas, y en Flutter — CustomPainter.

Lo más importante

  • Vistas personalizadas — componentes definidos por el usuario con lógica propia de dibujo, tamaño y comportamiento en aplicaciones móviles.
  • drawRect — método de UIView en iOS para dibujo personalizado mediante Core Graphics y CGContext.
  • onDraw — método de View en Android para dibujo mediante Canvas y Paint con el sistema de medición onMeasure.
  • CustomPainter — clase de Flutter para dibujar en Canvas con los métodos paint y shouldRepaint.
  • Medición — proceso de determinar las dimensiones de la View mediante sizeThatFits en iOS, onMeasure en Android y computeDryLayout en Flutter.

Qué son las vistas personalizadas y por qué se necesitan en aplicaciones móviles

Las vistas personalizadas son componentes de interfaz definidos por el usuario que sobrescriben los métodos estándar de dibujo y medición para crear gráficos únicos. Gráficos, animaciones, diagramas, elementos de chat personalizados y máscaras AR — son escenarios típicos de uso de vistas personalizadas en aplicaciones móviles. Los componentes de UI estándar (Button, TextView, Label) cubren el 80% de las tareas, pero el 20% restante requiere control total sobre el dibujo en el desarrollo móvil. El dibujo en el desarrollo móvil es una habilidad que diferencia a un desarrollador junior de un senior.

Cuándo están justificadas las vistas personalizadas

Las vistas personalizadas en aplicaciones móviles son necesarias para gráficos 2D únicos, animaciones complejas, gráficos estadísticos y visualización de datos. Si el UIKit estándar no proporciona el elemento necesario — se crea una vista personalizada. En aplicaciones móviles, las vistas personalizadas se usan para barras de progreso, botones personalizados, rellenos degradados, iconos animados y lienzos para dibujar con el dedo. Es importante no abusar — para estilos simples basta con configurar los componentes estándar mediante la API de apariencia.

Vistas personalizadas en iOS: drawRect y Core Graphics

En iOS, las vistas personalizadas se crean heredando de UIView y sobrescribiendo el método drawRect. drawRect se llama en el primer renderizado y después de cada llamada a setNeedsDisplay(). Dentro de drawRect, el desarrollador obtiene un CGContext mediante UIGraphicsGetCurrentContext() — un lienzo para líneas, formas, degradados y texto. Core Graphics (Quartz 2D) es un motor de renderizado de bajo nivel en CPU que ofrece control total sobre cada píxel. UIBezierPath es un envoltorio conveniente para crear rutas con sintaxis concisa.

swift
class CircleView: UIView {
    private var fillColor: UIColor = .systemBlue

    override func drawRect(_ rect: CGRect) {
        guard let ctx = UIGraphicsGetCurrentContext() else { return }
        ctx.setFillColor(fillColor.cgColor)
        ctx.addEllipse(in: bounds.insetBy(dx: 4, dy: 4))
        ctx.drawPath(using: .fill)
    }

    func updateColor(_ newColor: UIColor) {
        fillColor = newColor
        setNeedsDisplay()
    }
}

Core Graphics: CGContext, CGGradient y transformaciones

Core Graphics proporciona CGContext — un lienzo para todas las operaciones de dibujo. CGGradient crea degradados lineales y radiales con soporte de CGColorSpace (sRGB, Display P3). Las transformaciones se realizan mediante CGAffineTransform — una matriz 3x3 para rotación, escalado y desplazamiento. CGContextTranslateCTM, CGContextRotateCTM y CGContextScaleCTM modifican el sistema de coordenadas. Core Graphics funciona en CPU, por lo que los gráficos complejos pueden cargar el procesador — use Metal para aceleración GPU.

UIBezierPath para crear formas

UIBezierPath es un envoltorio orientado a objetos sobre CGPath. El método addArc(withCenter:radius:startAngle:endAngle:clockwise:) crea arcos y círculos, addCurve crea curvas Bézier. Las rutas admiten operaciones de unión, intersección y sustracción mediante addClip. UIBezierPath es conveniente para máscaras personalizadas (maskView), contornos de recorte y formas complejas — estrellas, corazones, iconos personalizados.

Vistas personalizadas en Android: onDraw y Canvas

En Android, una vista personalizada hereda de View con onDraw(Canvas) sobrescrito. Canvas contiene todos los métodos de dibujo en el desarrollo móvil: drawCircle, drawRect, drawLine, drawText, drawBitmap, drawPath y drawRoundRect. Paint configura los parámetros — color, grosor de línea, estilo (FILL, STROKE, FILL_AND_STROKE), suavizado (isAntiAlias) y tamaño de texto. onDraw se llama cada vez en invalidate() y no debe contener operaciones pesadas — todos los cálculos se almacenan en caché.

kotlin
class CustomCircleView(context: Context, attrs: AttributeSet?)
    : View(context, attrs) {

    private val paint = Paint().apply {
        style = Paint.Style.FILL
        color = Color.BLUE
        isAntiAlias = true
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val cx = width / 2f
        val cy = height / 2f
        val radius = minOf(cx, cy) - 4f
        canvas.drawCircle(cx, cy, radius, paint)
    }
}

Path y animaciones en Android

Path en Android es el equivalente de UIBezierPath: addCircle, addRect, addOval, lineTo, cubicTo y quadTo. PathMeasure calcula la longitud de la ruta y las coordenadas en cualquier punto — útil para animar el movimiento de un objeto a lo largo de una ruta. Para animaciones se usa ValueAnimator con invalidate() en el callback. El animador puede cambiar color, radio, posición — cualquier propiedad que afecte al dibujo. Las vistas personalizadas en Android también admiten StateListDrawable y animated-vector para animación mediante XML.

Shader y degradados en Canvas

Los degradados en Android se definen mediante Shader — LinearGradient, RadialGradient y SweepGradient. Shader se asigna a Paint mediante paint.setShader(shader). LinearGradient genera un degradado lineal entre dos puntos con posiciones de color opcionales. RadialGradient — radial desde el centro hacia los bordes. SweepGradient — cónico alrededor de un punto central. Shader.TileMode (CLAMP, REPEAT, MIRROR) define el comportamiento fuera de los límites del área.

CustomPainter en Flutter: dibujo en Canvas

Flutter usa CustomPainter — una clase con los métodos paint(Canvas canvas, Size size) y shouldRepaint. Canvas en Flutter proporciona los mismos métodos que en Android: drawCircle, drawRect, drawLine, drawPath, drawParagraph y drawImage. Paint en Flutter configura color, grosor, estilo y suavizado. Flutter no usa drawRect ni onDraw — todo el dibujo se realiza mediante CustomPainter, integrado en el widget CustomPaint.

dart
class CirclePainter extends CustomPainter {
  final Color color;

  CirclePainter({required this.color});

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = color
      ..style = PaintingStyle.fill
      ..isAntiAlias = true;
    final radius = size.shortestSide / 2 - 4;
    canvas.drawCircle(size.center(Offset.zero), radius, paint);
  }

  @override
  bool shouldRepaint(CirclePainter oldDelegate) =>
    oldDelegate.color != color;
}

shouldRepaint y RepaintBoundary

shouldRepaint determina si es necesario volver a dibujar el CustomPainter. Devuelva true solo cuando cambien los datos que afectan al renderizado — color, tamaño, datos del gráfico. RepaintBoundary aísla el redibujado en una capa separada — úselo para widgets que cambian con poca frecuencia. En Flutter no hay setNeedsDisplay ni invalidate — el redibujado se activa mediante setState o ValueNotifier con escucha de cambios.

Gradientes y transformaciones en Flutter

Flutter admite LinearGradient, RadialGradient y SweepGradient — los mismos tipos que en Android. Gradient crea un Shader para Paint mediante createShader(Rect bounds). Las transformaciones se realizan con canvas.rotate, canvas.scale, canvas.translate y canvas.skew. Flutter usa una matriz de transformación 4x4 (Matrix4) para el widget Transform. Las animaciones de dibujo en Flutter se hacen con AnimationController junto con setState para redibujar CustomPainter.

Medición de vistas personalizadas: métodos sizeThatFits, onMeasure y computeDryLayout

La medición correcta de las vistas personalizadas es la base del comportamiento predecible de la interfaz. En iOS, el tamaño de la View se determina mediante sizeThatFits e intrinsicContentSize. sizeThatFits devuelve el tamaño óptimo para las restricciones dadas. intrinsicContentSize — el tamaño natural del contenido sin restricciones externas. En Android, la medición se implementa en onMeasure con parámetros MeasureSpec (UNSPECIFIED, EXACTLY, AT_MOST). En Flutter, el tamaño se pasa a paint(Canvas, Size) y se puede calcular mediante computeDryLayout o LayoutBuilder.

sizeThatFits e intrinsicContentSize en iOS

intrinsicContentSize devuelve un CGSize — el tamaño natural de la View basado en su contenido. setContentHuggingPriority y setContentCompressionResistancePriority controlan el comportamiento durante el estiramiento y la compresión. sizeThatFits es llamado por el sistema para calcular el tamaño en Auto Layout. Para vistas personalizadas, sobrescriba intrinsicContentSize si su componente tiene un tamaño natural — por ejemplo, un círculo siempre debe ser 100x100.

onMeasure y MeasureSpec en Android

onMeasure acepta widthMeasureSpec y heightMeasureSpec. MeasureSpec contiene modo y tamaño: UNSPECIFIED (sin restricciones, la View elige su tamaño), EXACTLY (tamaño exacto del padre), AT_MOST (tamaño máximo). setMeasuredDimension(width, height) guarda el resultado de la medición. Al cambiar los datos, llame a requestLayout() para volver a medir. Los ViewGroup anidados requieren sobrescribir onLayout para la disposición de los elementos hijos.

PlataformaMétodo de mediciónLlamada al cambiarRetorno
iOSsizeThatFits / intrinsicContentSizesetNeedsLayout / invalidateIntrinsicContentSizeCGSize
AndroidonMeasure / setMeasuredDimensionrequestLayout / invalidatevoid (guarda tamaño)
FluttercomputeDryLayoutsetState / LayoutBuilderSize

computeDryLayout en Flutter

computeDryLayout es un método de RenderObject para calcular el tamaño sin un ciclo completo de diseño. LayoutBuilder.getSize — una alternativa para obtener el espacio disponible en el método build. En Flutter, el tamaño del lienzo se pasa a paint como Size y no requiere medición separada — CustomPainter dibuja dentro de los límites pasados. Sin embargo, para RenderObject personalizados, es necesario sobrescribir performLayout y computeDryLayout.

Preguntas frecuentes

Qué es drawRect en iOS y cuándo se llama?

drawRect es un método de UIView para dibujo personalizado mediante Core Graphics. Se llama en el primer renderizado de la View y después de cada llamada a setNeedsDisplay(). Use CGContext dentro de drawRect para líneas, formas y texto.

Cómo dibujar en Android usando Canvas?

Cree una clase que herede de View, sobrescriba onDraw(Canvas canvas) y use la API de Canvas: drawCircle, drawRect, drawPath. Paint configura color, grosor y estilo. Para actualizar, llame a invalidate().

Qué es CustomPainter en Flutter?

CustomPainter es una clase de Flutter para dibujo personalizado en Canvas. Implemente los métodos paint(Canvas, Size) y shouldRepaint. Se coloca mediante el widget CustomPaint. Flutter no tiene drawRect — todo el dibujo se realiza mediante CustomPainter.

Cómo medir el tamaño de una vista personalizada?

En iOS use sizeThatFits e intrinsicContentSize. En Android — onMeasure con MeasureSpec. En Flutter, el tamaño se pasa a paint(Canvas, Size) o se calcula mediante computeDryLayout. Cada plataforma usa su propio método de medición.

Cuándo usar vistas personalizadas en lugar de componentes estándar?

Las vistas personalizadas son necesarias para gráficos únicos, animaciones, gráficos estadísticos y elementos que no se pueden implementar con UIKit o View estándar. Para estilos simples, basta con configurar los componentes estándar — no complique la arquitectura innecesariamente.

Resumen

  • Vistas personalizadas — base de interfaces únicas para gráficos, animaciones y elementos no estándar en aplicaciones móviles.
  • drawRect — método de iOS para dibujo mediante Core Graphics con CGContext y UIBezierPath.
  • onDraw — método de Android para dibujo mediante Canvas y Paint con soporte de Path y Shader.
  • CustomPainter — clase de Flutter para dibujo en Canvas con shouldRepaint y RepaintBoundary.
  • Medición — validación de tamaños mediante sizeThatFits (iOS), onMeasure (Android) y computeDryLayout (Flutter).
  • El dibujo en el desarrollo móvil sigue un patrón unificado en todas las plataformas: obtener un contexto, configurar estilos y dibujar primitivas.
  • Almacene en caché los objetos de dibujo (Paint, Path) — no los cree dentro de drawRect, onDraw o paint para un rendimiento estable.

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto