Arturo Cuya

Join the weekly newsletter

Not a subscriber?

Every week I'll send you cutting edge advice and actionable tips about modern Roku development. State-of-the-art knowledge that you won't find anywhere else — For free.

April 2, 2024 | 6 min read

Three UI Challenges That Keep Roku Developers Frustrated (And How To Overcome Them)

By Arturo Cuya

@arturo__cuya

Today, I want to share with you three challenges Roku developers encounter when implementing UI designs and the different ways to fix them.

Some other platforms have easier ways to solve these problems, leveraging native components or a better markup language in general. But on Roku, getting things to pixel-perfect quality gets tricky, since the number of tools is limited.

Problem #1: Using Rounded Corners

This is the most common problem I see new Roku developers asking about. The platform simply does not support rounded corners, even in their plain Rectangle component implementation.

Rounded corners are a basic requirement for modern designs, being present in buttons, modals, and all other kinds of containers. So why wouldn’t the Roku platform support them?

Simply put, they are not interested in providing extra features for the specific design requirements of each application. Roku pushes the usage of their own SceneGraph components, which are straightforward and functional (but also a bit ugly) to ensure universally compatible layouts.

The solution

We have no other way here than to use an image as a background for the elements where we want to have rounded corners.

This is significantly more memory expensive than if Roku had a native implementation for rounded corners, but we can reduce this cost using a special image format: The 9-patch image.

A 9-patch image is a special kind of image used in software development, that can be stretched and resized without losing its quality or looking distorted. Imagine it like a rubber band with some solid parts that don’t stretch and some flexible parts that can stretch. The image is divided into nine sections:

Anatomy of a 9 patch image.
  • The four corners stay the same size no matter how you stretch the image. They anchor the image visually to its corners.

  • The four edges can be stretched horizontally or vertically but not both, making them ideal for borders or elongating elements without altering their thickness.

  • The center part can be stretched both ways, filling the space between the corners and edges as the image expands, which is great for backgrounds or larger areas that need to fill variable sizes.

This special image format allows developers to create elements of the user interface that look good on screens of different sizes and resolutions without needing multiple images for each specific size.

I personally use this tool to transform my regular PNGs into 9-patch images.

Pro tip: If the 9-patch image is not scaling the way you expect it, try resizing it to be smaller or bigger than it currently is.

Problem #2: Implementing Shadows and Gradients

Same as rounded corners, shadows and gradients are a cornerstone of modern application design.

However, they are also not supported directly. You’d also need to use images (9-patch if possible) to implement them. There’s also another way that’s more performant, but with a catch.

The Limited But Performant Alternative Solution

You can also use MaskGroup nodes to natively apply an alpha bitmap (transparent image) to the rendering of a component.

Mask Group example

Original image + Alpha bitmap = Image masked with a gradient

As performant as it is, MaskGroups present a couple of important limitations:

  1. Unlike other bitmaps, the MaskGroup node class does not work with 9-patch images. If the bitmap size does not match the group bounding rectangle, the edge rows of the mask are repeated as needed.

  2. MaskGroup nodes do not work on all Roku players (specifically, MaskGroup nodes only work on players that support OpenGL). On players whose graphics do not support OpenGL, a MaskGroup node just renders its children without applying the extra alpha mask.

So, basically, MaskGroups are a good option only if you’re sure that your app won’t be used by old devices. So use them with caution! (and only for decorative purposes).

Problem #3: Scaling Issues

Roku applications are designed and implemented from the start with a target resolution in mind — usually FHD / 1920x1080 — which is defined in the application’s manifest file.

However, not many developers know the mechanism that the Roku OS uses to scale up or down your UI implementation when it needs to be presented in a lower (HD) or greater (4K) resolution. Most people see that things look weird or a bit off when testing their app on a different resolution but are not sure why.

The Rule Of Three (And Its Consequences)

The rule that Roku uses to scale up or down your UI is to divide or multiply every position and size by three.

Meaning that if your Rectangle element was at (600, 300) in FHD and had a width of 30px, in HD (1280x720) it would look as if it was relatively placed in (200, 100) with a width of 10px.

Of course, it should look relatively the same. You should only see the difference if you place both screens next to each other.

Except that it doesn’t.

See, when Roku scales up our UI there’s no problem: Any integer multiplied by three is also an integer. But when it scales down, we can end up with floating values for positions and sizes.

The consequence of this is that, since floating values are floored down to the nearest integer, we are left with less pixels than expected:

  • Labels that were centered inside a container can end up 1 pixel off from the center.

  • Rectangles used as separators with 1px width will end up as 0.3px = 0px, seemingly disappearing.

Solving this is not easy, but possible in two different ways:

  • Consider talking with your design team to include the Rule of Three in their plans and make critical UIs divisible by 3.

  • Use roDeviceInfo’s GetUiResolution() to programmatically set different dimensions according to the current screen size.

BONUS: How To Create Complex Animations With One Line Of Code

Animations are really complex to define in the Roku platform.

For example, if we were to:

Translate the node with id rectId +200px (to the right) while scaling it to x1.5 and rotating it 120 degrees, for 1 second.

Here’s how that would usually look:

<Animation
    id="myAnimation"
    duration="1"
>
    <Vector2DFieldInterpolator
        fieldToInterp="rectId.translation",
        key="[0,1]"
        keyValue="[[200, 200],[400, 200]]"
    />
    <Vector2DFieldInterpolator
        fieldToInterp="rectId.scale",
        key="[0,1]"
        keyValue="[[1,1], [1.5,1.5]]"
    />
    <FloatFieldInterpolator
        fieldToInterp="rectId.rotation",
        key="[0,1]"
        keyValue="[0, 2.09439]"
    />
</Animation>

It’s a headache, just too much code for such a simple set of actions.

Lucky for you, there’s an open source library called animate that allows you to do the same through a simple configuration object:

m.animation = animate.create({
    targets: "rectId",
    translateX: 200,
    duration: 1,
    scale: 1.5,
    rotation: "120deg",
    autoplay: true
})

This is much more readable, and easier to debug.

In Summary

  • Use images as backgrounds for elements to implement rounded corners on Roku.
  • Leverage 9-patch images to maintain image quality when stretched.
  • Implement shadows and gradients using 9-patch images (or MaskGroup nodes with caution).
  • Address scaling issues by planning designs with the Rule of Three in mind and using roDeviceInfo for manual screen size adjustments.
  • Utilize the animate library for simpler, more readable code when creating complex animations.

Some other ways I can help you:

1. Course: A Modern Introduction to Roku Development (Coming April 2024)

For absolute beginners, this course is meant to set you in the right path to make use of modern tools and best practices from day one. Subscribe to the newsletter to get notified when it's available.

2. Open Source tools to boost your productivity:

Use these tools to 10x your workflows. Free and open source forever.

  • create-roku-app: A CLI tool to quickly scaffold Roku apps using the best practices.
  • SceneGraph Flex: A flexbox component for Roku Scenegraph applications.
  • Roku Animate: Define complex Roku Scenegraph animations in one line of code.