Skip to content

Instantly share code, notes, and snippets.

@wafer-li
Last active May 12, 2020 09:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wafer-li/8b0e6ebd98f799f21b9f9f90a69575a9 to your computer and use it in GitHub Desktop.
Save wafer-li/8b0e6ebd98f799f21b9f9f90a69575a9 to your computer and use it in GitHub Desktop.
基于公式法写的一个 GridSpaceItemDecoration, 实现 GridLayoutManager 居中元素空隙
import android.graphics.Rect
import android.view.View
import androidx.annotation.Dimension
import androidx.core.text.TextUtilsCompat
import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import li.wafer.nbnhhshandroid.common.extensions.dipToPx
import java.util.*
import kotlin.math.roundToInt
class GridSpaceItemDecoration(
@Dimension(unit = Dimension.DP) private val start: Double = 0.0,
@Dimension(unit = Dimension.DP) private val top: Double = 0.0,
@Dimension(unit = Dimension.DP) private val end: Double = 0.0,
@Dimension(unit = Dimension.DP) private val bottom: Double = 0.0,
@Dimension(unit = Dimension.DP) private val horizontal: Double = 0.0,
@Dimension(unit = Dimension.DP) private val vertical: Double = 0.0
) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
val gridLayoutManager = parent.layoutManager as? GridLayoutManager ?: return
val itemCount = parent.adapter?.itemCount ?: return
val spanCount = gridLayoutManager.spanCount
val position = parent.getChildLayoutPosition(view)
val spanSizeLookup = gridLayoutManager.spanSizeLookup
val spanSize = spanSizeLookup.getSpanSize(position)
val spanIndex = spanSizeLookup.getSpanIndex(position, spanCount)
val spanGroupIndex =
spanSizeLookup.getSpanGroupIndex(position, spanCount) // nMinusOne in vertical
val isRtl =
TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL
val verticalSpanCount = spanSizeLookup.getSpanGroupIndex(itemCount - 1, spanCount) + 1
val verticalSizeAvg =
(top + bottom + vertical * (verticalSpanCount - 1)) / verticalSpanCount
val horizontalSizeAvg =
(start + end + horizontal * (spanCount - 1)) / spanCount
val start = calculateStartOffsets(spanIndex, horizontalSizeAvg)
val end =
calculateEndOffset((spanCount - 1) - (spanIndex + spanSize - 1), horizontalSizeAvg)
val top = calculateTopOffsets(spanGroupIndex, verticalSizeAvg)
val bottom =
calculateBottomOffsets((verticalSpanCount - 1) - (spanGroupIndex), verticalSizeAvg)
val (left, right) = if (isRtl) {
end to start
} else {
start to end
}
val context = parent.context
outRect.set(
context.dipToPx(left),
context.dipToPx(top),
context.dipToPx(right),
context.dipToPx(bottom)
)
}
@Dimension(unit = Dimension.DP)
private fun calculateBottomOffsets(
nMinusOne: Int,
@Dimension(unit = Dimension.DP) verticalSizeAvg: Double
): Int {
return calculateOffset(bottom, nMinusOne, vertical - verticalSizeAvg)
}
@Dimension(unit = Dimension.DP)
private fun calculateTopOffsets(
nMinusOne: Int,
@Dimension(unit = Dimension.DP) verticalSizeAvg: Double
): Int {
return calculateOffset(top, nMinusOne, vertical - verticalSizeAvg)
}
@Dimension(unit = Dimension.DP)
private fun calculateStartOffsets(
nMinusOne: Int,
@Dimension(unit = Dimension.DP) horizontalSizeAvg: Double
): Int {
return calculateOffset(start, nMinusOne, horizontal - horizontalSizeAvg)
}
private fun calculateOffset(
initialValue: Double,
nMinusOne: Int,
commonDifference: Double
): Int {
return (initialValue + nMinusOne * commonDifference).roundToInt()
}
private fun calculateEndOffset(nMinusOne: Int, horizontalSizeAvg: Double): Int {
return calculateOffset(
end,
(nMinusOne),
horizontal - horizontalSizeAvg
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment