# Android View Collisions

## How to try to prevent view intersection

--

**Short aside***:* *I missed new article in the end of February, because of my honeymoon vacation. I’m sorry. Now I try to make an interesting and big article with lots of code snippets. But it takes some time. While it is preparing let’s dive into world of views … ;)*

There was time long ago, when I wanted to have a layout to be able to gravitate views to the top and to the bottom, but when this layout came too small, the views would not intersect with each other, but formulate a simple LinearLayout with ability to scroll.

Time passed, and my desire with forgotten and buried in my cemetery of chimerical ideas. Until one day my colleague ask for help. He needed next:

We have layout with 3 views, we want to show one at the top, one at the center and one at the bottom. But also we want to prevent their collision, so if it will be not possible, for example place second view at the center, we will just shift it little bit up or down depends on collided view.

“Sure” I’ve answered and started to think. Next thing, I remember, I came with that sketch of solution:

Which looked like (next here and in the rest of article I will use animation of collapsing and expanding layout to emulate different heights of devices):

He said, that this idea is very similar, but he also, in that solution the second view is not in the center of layout:

And also he wanted scrolling feature. And here I remembered my old forgotten wish. Now it is time to make that dream to come true :)

# Base Solution

Let’s create our custom `ViewGroup`

that can handle its children like in aforementioned gif. The main 2 methods that are important for new are `onMeasure`

and `onLayout`

.

## OnMeasure

The content of this method, will be similar to measuring `FrameLayout`

with difference that we not need to find maximum height through all children, but sum all heights to resulting one:

No difficulties here. We just iterating through all children of a parent view, measuring them and getting their height (including top and bottom margins); and also adjusting the result width and height of parent view. Next we need to layout children inside their parent view.

Here I want to **make a quick note**. *Next will be an ugly snipped of code, since I just wanted to layout children for cases of 1, 2 or 3 children*. After that we will talk about any amount of child views, but for now our layout process will be split into 3 cases:

**override fun **onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {

**val **fixedL = l + *paddingLeft*

**val **fixedT = t + *paddingTop*

**val **fixedR = r - *paddingRight*

**val **fixedB = b - *paddingBottom*

**when**(*childCount*) {

1 -> layoutOneChild(fixedL, fixedT, fixedR, fixedB)

2 -> layoutTwoChildren(fixedL, fixedT, fixedR, fixedB)

**else **-> layoutTreeChildren(fixedL, fixedT, fixedR, fixedB)

}

}

So next we will consider all three cases. In the first case we need to position our single child in the center of parent if we have a place for that. Otherwise, we will place it at the top of the parent. Our snippet for `layoutOneChild`

method is that:

This method is self-explaining, too. We take only one child, and if its height is more that height of parent, then we its top will be equal to `t`

else it will be at the center, which means that top will be equal to `t + (height — totalHeight) / 2`

. Next we need to see the snippet with `layoutTwoChildren`

method:

For two children we want to place one at the top of parent view and one at the bottom, if there is such possibility. Otherwise we will place views one under another, like in `LinearLayout`

. Next we need to write a method for three children. In it there will be little bit more logic. We will place first view at the top, last view at the bottom and second view at the center of parent. And we need to understand how to shift second view: to down or to top. So let’s look at the snippet of code:

Here we have slightly more difficulties. We check if sum of `measuredHeight`

s of all three children is more than height of a parent. If so, we just place all children as we would for `LinearLayout`

. In other case we try to assume, what `top`

and `bottom`

would have second and third views and then adjust their y coordinates appropriatly.

Whew. Ugly internal part is over. Now let’s check interesting part: **results**!!!

Our mission is over and we can exhale and drink some beer with friends…can we? May be, but I really don’t like that we have that disgusting choice for 1, 2 or 3 children and, moreover, we can not position, for instance, 10 children.

# 👍Boost 1️⃣: Extrapolate it for any child count

Now our task is to extrapolate our solution and invite new generic solution for any amount of children more than 1. To do that let’s think how we can approach the final algorithm.

Basically we have **n **children, that will be placed from screen top to bottom, and each child will be tried to place at special anchor. All those anchors will be at the same distances from neighbours and all views except the first one and the last one will placed at their middle at those anchors. Here is a sketch which visualise previous setnences:

We can think how we can combine their positions to achieve this equality. But I suggest you to go from another corner and rethink about this problem from a different angle.

## Concept of algorithm

The idea is to analyze distances between views when they already placed in case, when parent is really-really big, so children can be placed on their anchors correctly, without shifting. Each D_i consists of tree values:

- a half of height of View_i (for View_1 it will be full height);
- a half of height of View_(i+1) (for View_(n-1) it will be full height);
- remained height

First two values are known, so their sum is also known and determined. Let’s call it weight_i (next will be more pseudo-code):

`weight[i] = (view[i].height + view[i + 1].height) / 2`

weight[0] = view[0].height + view[1].height / 2

weight[n - 2] = view[n - 2].height / 2 + view[n - 1].height

Thus, we have `weights`

array. Now we can associate each weight with child and its position and remember that info. So right now we have an ability to sort `weight`

by ascending value with keeping original state in mind (let’s call newly created array `sortedWeights`

).

Now let’s remember that we have `height`

of parent view and also we know `totalHeight`

of children:

`totalHeight = children.sum { it.height }`

Let’s introduce `freeHeight`

which will be remain amount of height we need to distribute between views, to equalize distances between their centers.

How we can do that? You can image our `sortedWeight`

array as half of pit and a `freeHeight`

is a sand that we will pour into this pit. Firstly the bottom will be filled (means part of `freeHeight`

value will be distributed to first item of array), and then continuously all elements will start to fill simultaneously. Here is another sketch, which illustrate that process of distribution `freeHeight`

value between all elements of `sortedWeight`

:

As the result of such spreading `freeHeight`

between elements of `sortedWeights`

we have an array `distribution`

with elements `d_1, d_2, ..., d_(n — 2)`

.

And now we have all we need. We have info about children: height, width, x and y; and also we have info about distance between them (`distribution`

array).

## Implementation

Let’s start with implementing steps of our meta algoritm with appropriate methods and them we will little bit optimize them by memory. So let’s go step by step:

- Build array of weights:

2. Sort array of weights (with remembering origin positions):

3. Distribute `freeHeight`

between sorted weights:

4. Associate result distribution (distance between views) to origin positions

5. Combine all and layout children

That’s it! Before we will check results, let’s mention few words about optimization. As you notices in `createWeights`

, `sortWeights`

, `distributeFreeHeight`

, `buildDistanceArray`

every time, we call them, we create new instances of array. But in fact we use them only once in layout phase. So we can create a fields with that array and rewrite data in them each time, when layout phase is happened. Okay, let’s go to results.

# Results

Here will be shown some results for random child count with random colors and heights:

# 👍Boost 2️⃣: Add offsets

With aforementioned solution you can build layout from the example left, but can not do layout from the example right.

To achieve more flexibility of positioning children in our created layout we need to add a possibility to scale ranges between centers of each children. I remind you that for now we assume that all distances between child centers are equal to each other. So let’s enhance our solution with adding `scales`

.

## Concept

So we need to transform our task to the previous one. Suppose we now have `scales`

and `weights`

. If we will be able to scale down every `weight`

by its `scale`

we will be able to equalize every `weights`

as they were if every `scale`

would be equal to one (i.e in a aforementioned task). To do that we need to ensure that sum of all scales are equal to 1. In other words we need to normalize our `scales`

.

Next we need to (as it was in previous task) properly distribute `freeHeight`

between all `weights `

taking to account different `scales`

. For that we need to sort our transformed `weights`

and fill them with `freeHeight`

value as it was previously, but also we need to properly adapt that value according to different `scales`

.

Also, we want to provide our scales properly through children in xml layout. To do that we can use `LinearLayout.LayoutParams.weight`

for that. But we have `n`

views and `n-1`

`scales`

and `weights`

. So we need to properly transform view’s weight to `scales`

.

Now as summary we need to:

- Take all view layout param’s weights and transform them to
`scales`

- Normalize
`scales`

- Adapt all
`weights`

(as we counted them earlier) with given`scales`

- Distribute
`freeHeights`

between all`weights`

properly applying`scales`

- Build a distances
- Build a layout

## Implementation

As the implementation will be an enhancement of previous implementation we will see pretty much same lines of code, but beside that we also introduce a new structure `Bucket`

for convenient computing:

**private class **Bucket(

**val filled**: Float,

**val scales**: Float,

**val position**: Int

)

- Get view’s weight and create
`scales`

:

2. Normalize `scales`

:

3. Create and adapt all `weights`

by given normalized `scales`

:

4. Create buckets (gather `weight`

, `scale`

and original position together) and sort them by adapted `weights`

:

5. Distribute `freeHeights`

between all `weights`

properly applying `scales`

6. Building distances is pretty much the same as for previous section.

7. Gather all previous steps and layout children:

# Results

Now let’s check what we can create with our new layout:

# Afterwords

In this article we solved one interesting task. And you know what? It doesn’t matter, if such layout is not needed in ordinary app development. More interesting that we came to that solution by ourself. It means that we leveled up our brain and skills and this is good :)

If you interested in full source code of result `CollisionView`

, please, check this link.