GridLayout+Decoration spacing problem

As is known to all, there are two common ways to increase the spacing of items in RecyclerView:

  • Add spacing directly to the layout of item
  • Add ItemDecoration to RecyclerView to process spacing

The first approach is straightforward: there is no actual spacing between items. It is done by manually adding white space to display content and boundaries within each item so that it looks like there is spacing. And the second way, the surface seems to be very simple: how much space directly fill in how much, there is nothing difficult. But in practice, there are strange problems.

Take a chestnut

A simple requirement, to give a N row and 3 columns of RecyclerView spacing, the spacing between each Item is 10, and then the edge distance of the screen is 0, that is, close to the screen, write like this:


addItemDecoration(object : RecyclerView.ItemDecoration() {
               override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
                     val pos = parent.getChildAdapterPosition(view)
                   val column = (pos % span)
                   
                   outRect.left = 5
                   outRect.right = 5
                   outRect.top = 5
                   outRect.bottom = 5
                   
                   if (column == 0){
                       outRect.left = 0
                   }
                   if (column == 2){
                       outRect.right = 0}}})Copy the code

Looks like it’s okay? The left margin of the first column is 0, the right margin of the third column is 0, and the left margin of all items is exactly 5+5=10. Is that right? See the effect

Obviously, the item in the middle becomes smaller, which is caused by the drawing principle of Item decoration. Because items are evenly distributed at first, and the contents displayed inside Item are not completely centered, the process of drawing calculation is calculated from left to right. So the further you go, the bigger the distance is. It looks like the spacing is normal, but because the width of the displayable content is squeezed, it looks like the displayable area in the middle is smaller.

How do you solve it? In this example, for each row, there is only a space between the Item in the first column and the Item in the second column, and a space between the Item in the second column and the Item in the third column. The left and right sides of the line are 0 away from the screen, so the remaining width of the line, except for the viewable content, is 10+10=20. And then, because there are three columns in a row, for each Item, he gets 20/3 of the blank space. So for the first column Item, its distance to the left is 0, so the distance to the right is 20/3-0=20/3. The second column, what is his distance to the left? Now that we know the distance to the right of the first Item, the distance to the left of the second Item is 10-20/3=10/3, so the remaining blank distance of the Item is 20/3-10/3. So that’s 10/3. The left margin of the third column Item is 10-10/3=20/3, and the right margin is 20/3-20/3, which is just 0. The code is as follows:


addItemDecoration(object : RecyclerView.ItemDecoration() {
               override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
                   val pos = parent.getChildAdapterPosition(view)
                   val column = (pos % span)
                   
                   if (column == 0) { / / the first column
                       outRect.left = 0
                       outRect.right = 20/3
                   } else if (column == 1) { / / the second column
                       outRect.left = 10/3
                       outRect.right = 10/3
                   } else { / / the third column
                       outRect.left = 20/3
                       outRect.right = 0
                   }

                   outRect.top = 5
                   outRect.bottom = 5}})Copy the code

The result is perfect: