Last active
March 17, 2021 04:21
-
-
Save OmarKRostom/bb1d063d2b297733162d0e73bcdad88a to your computer and use it in GitHub Desktop.
This is a layout manager to draw items on a cruve like design
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.omarkrostom.arclayoutmanager | |
import android.content.Context | |
import android.view.ViewGroup.LayoutParams.MATCH_PARENT | |
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT | |
import androidx.recyclerview.widget.RecyclerView | |
import kotlin.math.* | |
class ArcLayoutManager( | |
private val context: Context, | |
private var horizontalOffset: Int = 0 | |
) : RecyclerView.LayoutManager() { | |
override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams = | |
RecyclerView.LayoutParams(MATCH_PARENT, WRAP_CONTENT) | |
override fun canScrollHorizontally(): Boolean = true | |
override fun scrollHorizontallyBy( | |
dx: Int, | |
recycler: RecyclerView.Recycler?, | |
state: RecyclerView.State? | |
): Int { | |
horizontalOffset += dx | |
fill(recycler, state) | |
return dx | |
} | |
override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) { | |
super.onLayoutChildren(recycler, state) | |
fill(recycler, state) | |
} | |
private fun fill(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) { | |
detachAndScrapAttachedViews(recycler ?: return) | |
for (itemIndex in 0 until itemCount) { | |
val view = recycler.getViewForPosition(itemIndex) | |
addView(view) | |
val viewWidth = pxFromDp(context, 90f) | |
val viewHeight = pxFromDp(context, 90f) | |
val left = (itemIndex * viewWidth) - horizontalOffset | |
val right = left + viewWidth | |
val top = computeYComponent((left + right) / 2, viewHeight) | |
val bottom = top.first + viewHeight | |
val alpha = top.second | |
view.rotation = (alpha * (180 / PI)).toFloat() - 90f | |
measureChildWithMargins(view ?: return, viewWidth.toInt(), viewHeight.toInt()) | |
layoutDecoratedWithMargins( | |
view, | |
left.toInt(), | |
top.first.toInt(), | |
right.toInt(), | |
bottom.toInt() | |
) | |
} | |
recycler.scrapList.toList().forEach { | |
recycler.recycleView(it.itemView) | |
} | |
} | |
private fun computeYComponent(viewCenterX: Float, | |
h: Float): Pair<Int, Double> { | |
val screenWidth = context.resources.displayMetrics.widthPixels | |
val s = screenWidth.toDouble() / 2 | |
val radius = (h * h + s * s) / (h * 2) | |
val xScreenFraction = viewCenterX.toDouble() / screenWidth.toDouble() | |
val beta = acos(s / radius) | |
val alpha = beta + (xScreenFraction * (Math.PI - (2 * beta))) | |
val yComponent = radius - (radius * sin(alpha)) | |
return Pair(yComponent.toInt(), alpha) | |
} | |
fun pxFromDp(context: Context, dp: Float): Float { | |
return dp * context.resources.displayMetrics.density | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment