DEV Community

Cover image for Re-gaining orientation #4
Thomas Künneth
Thomas Künneth

Posted on

Re-gaining orientation #4

Welcome to the final episode of Re-gaining orientation. Please recall that in this mini series we focussed on changes to device orientation and how that is covered in Jetpack Compose. We also looked at foldables and investigated how opening and closing the device should change your composables hierarchy.

On traditional tablets (and smartphones in landscape mode) my sample app has a typical two-column-view where the left part is a list and therefore is narrower than the content part at the right hand side. On a foldable with a noticeable hinge it might make more sense to divide the screen into two equal-sized parts. We can use the Jetpack Window Manager component to check if the device has a so-called FoldingFeature. However, I concluded the previous post by saying

While both results look quite pretty we have a few thing to do.
Can you guess what?

The problem is that on both the stock Android emulator (acting as a foldable) and the Surface Duo Emulator parts of the screen content are not visible - because of the fold or hinge.

A simulated hinge covers a portion of the screen

Fortunately it is easy to determine both position and size of the covered area:

(feature as FoldingFeature).run {
  if (isSeparating) {
    weightModuleSelection = 0.5F
    weightModule = 0.5F
    width = bounds.width()
  }
}
Enter fullscreen mode Exit fullscreen mode

bounds is an ordinary android.graphics.Rect, so we can obtain its width with, well, width(). My width variable looks like this:

private var width by mutableStateOf(0)
Enter fullscreen mode Exit fullscreen mode

To make use of it in my sample code I need to add just one line. Can you spot it?

@Composable
fun Landscape(
    module: MutableState<Module?>,
    weightModuleSelection: Float,
    weightModule: Float
) {
    Row(modifier = Modifier.fillMaxSize()) {
        ModuleSelection(
            module = module,
            modifier = Modifier.weight(weight = weightModuleSelection)
        )
        Spacer(modifier = Modifier.width(LocalDensity.current.run { width.toDp() }))
        module.value?.let {
            Module(
                module = it,
                modifier = Modifier.weight(weightModule)
            )
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Spacer() is a composable...

that represents an empty space layout, whose size can
be defined using Modifier.width, Modifier.height and
Modifier.size modifiers.

Please note that we must convert the pixel values to dp. Here's how my sample app looks like after these changes:

Sample app honoring the hinge

You may be wondering why it takes just a Spacer() to make things work. Well, both the module selection (the list) and the module (the content) get their width through Modifier.weight. While computing these values, Compose takes into account the width of other composables that have not specified a weight - like the Spacer(). If the app is run an a device that has no FoldingFeature its size remains 0.

Clever, isn't it`? 😎 I will be briefly mentioning an alternative, nonetheless. That's because my solution assumes that the hinge or fold is in the middle of the screen. Please recall that this was the preliminary idea - to split the display into two equal-sized areas. But what if this is not the case? What if the designer wants a device that folds at a 30 : 70 ratio?

Please recall that the FoldingFeature gives us the bounds rectangle. So far we only used its width. But we can access its left attribute to compute how big the area at the left hand side of the fold is. If left is, say 800, we know that (for example) the module selection in my example should be this wide. Hence, instead of using Modifier.weight() we just use Modifier.width() like in the Spacer().

This concludes my musings about screen orientation and foldables. Anything else you would like to see covered? Please do share your thoughts in the comments.


Source

Top comments (0)