@@ -5,29 +5,34 @@ import android.util.SparseArray
55import android.view.View
66import androidx.recyclerview.widget.RecyclerView
77
8- class CircularRecyclerLayoutManager (private val itemsPerCircle : Int = 6 ) : RecyclerView.LayoutManager() {
8+ class CircularRecyclerLayoutManager (
9+ private val itemsPerCircle : Int = 6 ,
10+ private var anglePerItem : Double = Double .NaN ,
11+ private var firstCircleRadius : Double = Double .NaN ,
12+ private var angleStepForCircles : Double = Double .NaN ,
13+ private val canScrollHorizontally : Boolean = false
14+ ) : RecyclerView.LayoutManager() {
915 override fun generateDefaultLayoutParams () =
10- RecyclerView .LayoutParams (RecyclerView .LayoutParams .MATCH_PARENT , RecyclerView .LayoutParams .MATCH_PARENT )
16+ RecyclerView .LayoutParams (
17+ RecyclerView .LayoutParams .MATCH_PARENT ,
18+ RecyclerView .LayoutParams .MATCH_PARENT
19+ )
1120
12- override fun canScrollHorizontally () = false
21+ override fun canScrollHorizontally () = canScrollHorizontally
1322
1423 override fun canScrollVertically () = true
1524
1625 override fun measureChildWithMargins (child : View , widthUsed : Int , heightUsed : Int ) {
1726 child.measure(
18- View .MeasureSpec .makeMeasureSpec(widthUsed, View .MeasureSpec .EXACTLY ),
19- View .MeasureSpec .makeMeasureSpec(heightUsed, View .MeasureSpec .EXACTLY )
27+ View .MeasureSpec .makeMeasureSpec(widthUsed, View .MeasureSpec .EXACTLY ),
28+ View .MeasureSpec .makeMeasureSpec(heightUsed, View .MeasureSpec .EXACTLY )
2029 )
2130 }
2231
2332 private lateinit var centerPoint: PointF
2433
25- private var firstCircleRadius = 0.0
26-
2734 private var itemWidth = 0
2835
29- private var anglePerItem = 0.0
30-
3136 private val viewCalculation = SparseArray <ItemData >(itemCount)
3237
3338 override fun onLayoutChildren (recycler : RecyclerView .Recycler ? , state : RecyclerView .State ? ) {
@@ -43,7 +48,9 @@ class CircularRecyclerLayoutManager(private val itemsPerCircle: Int = 6) : Recyc
4348
4449 val circleRadius = firstCircleRadius + (itemWidth * 1.5 * circleOrderPosition)
4550
46- val angle = (anglePerItem * position) + if (circleOrderPosition.isDivideByTwo()) 0.0 else anglePerItem.div(2 )
51+ val angleStep = if (angleStepForCircles.isNaN()) anglePerItem.div(2 ) else angleStepForCircles
52+
53+ val angle = (anglePerItem * position) + if (circleOrderPosition.isDivideByTwo()) 0.0 else angleStep
4754
4855 val positionData = calculatePosition(circleRadius, angle)
4956
@@ -79,7 +86,11 @@ class CircularRecyclerLayoutManager(private val itemsPerCircle: Int = 6) : Recyc
7986 }
8087 }
8188
82- override fun scrollVerticallyBy (dy : Int , recycler : RecyclerView .Recycler ? , state : RecyclerView .State ? ): Int {
89+ override fun scrollVerticallyBy (
90+ dy : Int ,
91+ recycler : RecyclerView .Recycler ? ,
92+ state : RecyclerView .State ?
93+ ): Int {
8394 updateCalculation(dy)
8495 updateViews(recycler)
8596 return dy
@@ -103,10 +114,10 @@ class CircularRecyclerLayoutManager(private val itemsPerCircle: Int = 6) : Recyc
103114 }
104115
105116 private fun layoutItemIfNeeded (
106- positionData : PositionData ,
107- data : ItemData ,
108- recycler : RecyclerView .Recycler ? ,
109- position : Int
117+ positionData : PositionData ,
118+ data : ItemData ,
119+ recycler : RecyclerView .Recycler ? ,
120+ position : Int
110121 ) {
111122 if (isViewVisible(positionData) && data.currentRadius > 0.0 ) {
112123 recycler?.getViewForPosition(position)?.let { viewForPosition ->
@@ -118,7 +129,10 @@ class CircularRecyclerLayoutManager(private val itemsPerCircle: Int = 6) : Recyc
118129 }
119130 }
120131
121- private fun updateAllChild (viewsForDetaching : MutableList <View >, updatedPositions : MutableList <Int >) {
132+ private fun updateAllChild (
133+ viewsForDetaching : MutableList <View >,
134+ updatedPositions : MutableList <Int >
135+ ) {
122136 for (position in 0 until childCount) {
123137 getChildAt(position)?.let { childAt ->
124138 val childPosition = getPosition(childAt)
@@ -165,12 +179,31 @@ class CircularRecyclerLayoutManager(private val itemsPerCircle: Int = 6) : Recyc
165179
166180 private fun calculateConstants () {
167181 centerPoint = PointF (width / 2f , height / 2f )
168- firstCircleRadius = width / 4.0
182+ if (firstCircleRadius.isNaN()) {
183+ firstCircleRadius = width / 4.0
184+ }
169185 val firstCircleLength = 2 * Math .PI * firstCircleRadius
170186 itemWidth = ((firstCircleLength / (itemsPerCircle)) * 0.4 ).toInt()
171- anglePerItem = 360 / if (itemCount > itemsPerCircle) itemsPerCircle.toDouble() else itemCount.toDouble()
187+ if (anglePerItem.isNaN()) {
188+ anglePerItem = 360 / if (itemCount > itemsPerCircle) itemsPerCircle.toDouble() else itemCount.toDouble()
189+ }
190+ }
191+
192+ override fun scrollHorizontallyBy (
193+ dx : Int ,
194+ recycler : RecyclerView .Recycler ? ,
195+ state : RecyclerView .State ?
196+ ): Int {
197+ for (position in 0 until viewCalculation.size()) {
198+ if (shouldItemMove(position).not ()) {
199+ continue
200+ }
201+ viewCalculation.get(position).angle + = dx * 0.1
202+ }
203+ updateViews(recycler)
204+ return dx
172205 }
173206
174- inner class ItemData (val initialRadius : Double , var currentRadius : Double , val angle : Double )
207+ inner class ItemData (val initialRadius : Double , var currentRadius : Double , var angle : Double )
175208 inner class PositionData (val left : Int , val top : Int , val right : Int , val bottom : Int )
176209}
0 commit comments