Mastering Custom Graphics in Jetpack Compose: Building Unique Progress Indicators with Canvas

Joseph Howerton
7 min readMay 28, 2024

--

In this Article

Throughout this article, I will provide a step-by-step guide to building these custom components. I will start with a basic overview of the Canvas API, followed by a detailed exploration of how to create and customize custom components. Each section will include code explanations and practical examples to help you understand how to implement and tailor these components to your own projects. By the end of this article, you will have a solid foundation in creating custom Composable using Canvas in Jetpack Compose.

GitHub: https://github.com/CodingWithJoseph/ComposeCanvas

Jetpack Compose Icon

Introduction to Jetpack Compose

Jetpack Compose has significantly transformed Android UI development by providing a modern, declarative approach to building native UIs. This framework allows developers to create complex and beautiful user interfaces with less code and improved performance. One of the most powerful features of Jetpack Compose is its Canvas API, which enables the creation of custom graphics and animations directly within the Compose framework.

Introduction to Canvas API

The Canvas API in Jetpack Compose is a powerful tool that allows developers to draw custom graphics directly onto the screen. With Canvas, you can create anything from simple shapes to complex animations, offering a level of customization that standard UI components cannot provide.

Key Concepts of the Canvas API

Basics

  • Coordinate System: The Canvas coordinate system starts at the origin (0,0) located at the top-left corner. The x-axis extends to the right, while the y-axis extends downward. Understanding this system is crucial for accurate drawing.
  • Drawing Operations: Canvas provides a variety of methods to draw shapes, paths, text, and images. Common methods include:
  • drawRect: Draws a rectangle.
  • drawCircle: Draws a circle.
  • drawArc: Draws an arc or a part of a circle.
  • drawLine: Draws a line between two points.
  • drawOval: Draws an oval.
  • drawPath: Draws a complex shape by connecting a series of points.
  • drawPoints: Draws multiple points.
  • drawRoundRect: Draws a rectangle with rounded corners.
  • drawText: Draws text.
  • drawImage: Draws an image.
  • drawBitmap: Draws a bitmap image.
  • drawColor: Fills the canvas with a single color.

Drawing Shapes

  • Rectangles and Circles: Use drawRect and drawCircle to create basic shapes.
  • Arcs and Ovals: Use drawArc and drawOval for more complex rounded shapes.
  • Paths: For more complex shapes, use drawPath, which allows you to define a path by specifying a series of points and drawing lines or curves between them.
  • Lines and Points: Use drawLine and drawPoints to create lines and collections of points.

Styling

  • Paint: The Paint class defines the style of the drawing, including attributes like color, stroke width, and text size. Customizing the paint object enables various visual effects.
  • Stroke and Fill: Choose between drawing the outline of shapes (stroke) or filling them with color (fill).

Transforms

  • Translation, Rotation, and Scaling: You can transform the Canvas by translating (moving), rotating, or scaling the coordinate system. These transformations are useful for creating complex animations and dynamic graphics.
  • translate: Moves the coordinate system.
  • rotate: Rotates the coordinate system.
  • scale: Scales the coordinate system.
Circular Progress Component

Implementing a Basic Circular Progress Component

To start, let’s implement a basic circular progress bar using the Canvas API in Jetpack Compose. This will lay the foundation for more complex custom components later in the article.

Explanation

  • Canvas Basics: The Canvas composable allows you to draw custom graphics. In this case, we use it to draw arcs representing the progress and background track.
  • Drawing the Background Track: The first drawArc call draws a complete circle (360 degrees) as the background track. This provides a visual reference for the progress.
  • Drawing the Progress Arc: The second drawArc call draws the progress arc. The sweepAngle is calculated based on the progress value, which determines how much of the circle is filled.
  • Start Angle: The start angle is set to -90 degrees to make the progress start from the top of the circle, similar to the 12 o’clock position on a clock. This positioning is more intuitive for users, as progress typically fills from top to bottom and left to right.

Building a Circular Progress Indicator

Next, we will build upon our basic circular progress component to create a more comprehensive circular progress indicator. This new component will not only display the progress visually but also include a text percentage in the center.

Explanation:

  • Enhanced Component: The CircleProgressIndicator builds upon the CircularProgress component by adding a text element in the center to display the progress percentage.
  • Column and Box Layout: The component uses a Column to center align its content horizontally. Inside the column, a Box is used to overlay the text on top of the circular progress.
  • Text Element: The Text composable displays the progress percentage. Its position and alignment are controlled to ensure it is centered within the Box.
Water Progress Component

Implementing a Basic Water Progress Component

Next, let’s create a custom component that visualizes water intake progress with a wave effect. This component will use the Canvas API to draw the waves, providing a dynamic and visually appealing way to represent progress.

Explanation:

  • Canvas Basics: Similar to the circular progress component, we use the Canvas composable to draw custom graphics. In this case, we are drawing wave paths to represent water intake.
  • Wave Paths: The wavePath and progressWavePath are created using quadratic bezier curves to simulate wave motion. The amplitude and frequency of the waves are controlled by the waveAmplitude and waveFrequency variables.
  • Progress Wave: The progressWavePath represents the filled portion of the water intake based on the progress value.

Building a Water Indicator

Building on the WaterProgressComponent, we will now create a WaterIndicator component that combines the wave visualization with text to show the percentage of water intake.

Explanation:

  • Enhanced Component: The WaterIndicator builds upon the WaterProgressComponent by adding a text element that displays the percentage of water intake.
  • Card and Column Layout: The component uses a Card for the background and a Column to organize its content. Inside the column, a Row is used to align the "Water Intake" label and the percentage text.
  • Progress Visualization: The WaterProgressComponent is included to provide the wave visualization for water intake, making the component visually engaging.
Dashboard UI (Day, Night)

Putting it All Together

In this article, we have walked through the creation of several custom components using Jetpack Compose: CircularProgress, CircleProgressIndicator, WaterProgressComponent, and WaterIndicator. Each of these components leverages the power of Jetpack Compose to create a dynamic and visually appealing UI.

Building the Dashboard

By combining these components, we created a comprehensive dashboard that visualizes various health metrics, such as calorie intake, macronutrient distribution (carbs, fiber, protein, fat), and water intake. This dashboard serves as a practical example of how you can use custom Compose components to build complex, interactive UI screens.

Reusability and Customization

One of the key benefits of using Jetpack Compose is the reusability and customization of components. The components we created can be easily reused across different parts of your application or even in other projects. Here’s how:

  • Modularity: Each component is self-contained and modular, making it easy to integrate into different screens or projects. You can customize the appearance and behavior of each component by adjusting their parameters.
  • Theming: With Jetpack Compose’s theming capabilities, you can easily apply consistent styles across your components, ensuring a cohesive look and feel throughout your app.
  • Scalability: These components can scale with your project. As your application grows, you can extend the existing components or create new ones based on the same principles to meet new requirements.

Extending to Other Projects

The techniques and principles demonstrated in this article are not limited to health applications. You can apply them to various types of projects, such as:

  • Finance Apps: Visualize spending, saving goals, and investment progress with circular and wave progress indicators.
  • Education Apps: Track learning progress, quiz scores, and course completion rates.
  • Fitness Apps: Monitor exercise routines, workout progress, and hydration levels.
  • Productivity Apps: Display task completion, project milestones, and goal tracking.

Conclusion

By mastering the creation of custom components in Jetpack Compose, you unlock the potential to build highly interactive and visually rich UIs. The flexibility and power of Compose make it an excellent choice for modern Android app development. Whether you are working on a personal project or a large-scale application, the skills you’ve learned in this article will be invaluable in creating engaging user experiences.

--

--