Skip to content

Commit feb60aa

Browse files
committed
Merge branch 'lib_setup'
2 parents 9180abd + 3f4c86f commit feb60aa

File tree

10 files changed

+85
-35
lines changed

10 files changed

+85
-35
lines changed

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,35 @@
33
Simple in usage Recycler Layout Manager with circular item positioning.
44

55
![](animation.gif)
6+
![](example.gif)
7+
![](example2.gif)
68

9+
## What's new
10+
11+
1.1.0 - Now you can configure some individual components like `anglePerItem`, `firstCircleRadius`, `angleStepForCircles`. Also horizontal scrolling works, it moves all elements by a circle.
712

813
## Usage
9-
Register configuration providers like this:
10-
```java
14+
Just add a `CircularRecyclerLayoutManager` as a layout manager for your recycler view.
15+
```kotlin
1116
class MainActivity : AppCompatActivity() {
1217

1318
override fun onCreate(savedInstanceState: Bundle?) {
1419
super.onCreate(savedInstanceState)
1520
setContentView(R.layout.activity_main)
16-
circularRecycler.layoutManager = CircularRecyclerLayoutManager()
21+
circularRecycler.layoutManager = CircularRecyclerLayoutManager(
22+
canScrollHorizontally = true,
23+
itemsPerCircle = 4,
24+
anglePerItem = 100.0,
25+
firstCircleRadius = 200.0,
26+
angleStepForCircles = 45.0
27+
)
1728
circularRecycler.adapter = Adapter()
1829
}
1930
}
2031
```
2132

2233
### Installation
23-
- Add this to your project level `build.gradle`:
34+
- Add this to your project level `build.gradle`:
2435
```
2536
allprojects {
2637
repositories {

app/build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ android {
2626
dependencies {
2727
implementation fileTree(dir: 'libs', include: ['*.jar'])
2828
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
29-
implementation 'androidx.appcompat:appcompat:1.0.2'
30-
implementation 'androidx.core:core-ktx:1.0.2'
29+
implementation 'androidx.appcompat:appcompat:1.1.0'
30+
implementation 'androidx.core:core-ktx:1.1.0'
3131
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
32-
implementation 'androidx.recyclerview:recyclerview:1.1.0-beta03'
33-
implementation 'com.github.leshchenko:CircularLayoutManager:1.0.1'
32+
implementation 'androidx.recyclerview:recyclerview:1.1.0-beta04'
33+
implementation 'com.github.leshchenko:CircularLayoutManager:1.1.0'
3434
testImplementation 'junit:junit:4.12'
3535
androidTestImplementation 'androidx.test:runner:1.2.0'
3636
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

app/src/main/java/com/leshchenko/circularlayoutmanager/MainActivity.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@ class MainActivity : AppCompatActivity() {
1414
override fun onCreate(savedInstanceState: Bundle?) {
1515
super.onCreate(savedInstanceState)
1616
setContentView(R.layout.activity_main)
17-
circularRecycler.layoutManager = CircularRecyclerLayoutManager()
17+
circularRecycler.layoutManager = CircularRecyclerLayoutManager(
18+
canScrollHorizontally = true,
19+
itemsPerCircle = 4,
20+
anglePerItem = 100.0,
21+
firstCircleRadius = 200.0,
22+
angleStepForCircles = 45.0
23+
)
1824
circularRecycler.adapter = Adapter()
1925
}
2026

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// Top-level build file where you can add configuration options common to all sub-projects/modules.
22

33
buildscript {
4-
ext.kotlin_version = '1.3.41'
4+
ext.kotlin_version = '1.3.50'
55
repositories {
66
google()
77
jcenter()
88

99
}
1010
dependencies {
11-
classpath 'com.android.tools.build:gradle:3.4.2'
11+
classpath 'com.android.tools.build:gradle:3.5.0'
1212
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1313
// NOTE: Do not place your application dependencies here; they belong
1414
// in the individual module build.gradle files

circularlayoutmanagerlib/build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ android {
2929
dependencies {
3030
implementation fileTree(dir: 'libs', include: ['*.jar'])
3131

32-
implementation 'androidx.appcompat:appcompat:1.0.2'
32+
implementation 'androidx.appcompat:appcompat:1.1.0'
3333
testImplementation 'junit:junit:4.12'
3434
androidTestImplementation 'androidx.test:runner:1.2.0'
3535
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
36-
implementation "androidx.core:core-ktx:1.0.2"
37-
implementation 'androidx.recyclerview:recyclerview:1.1.0-beta03'
36+
implementation "androidx.core:core-ktx:1.1.0"
37+
implementation 'androidx.recyclerview:recyclerview:1.1.0-beta04'
3838
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
3939
}
4040
repositories {

circularlayoutmanagerlib/src/main/java/com/leshchenko/circularlayoutmanagerlib/CircularRecyclerLayoutManager.kt

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,34 @@ import android.util.SparseArray
55
import android.view.View
66
import 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
}

example.gif

10.7 MB
Loading

example2.gif

14.5 MB
Loading
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
#Fri Aug 16 16:15:20 EEST 2019
1+
#Mon Sep 23 14:39:23 EEST 2019
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists
6-
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
6+
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

horizontal.gif

17.3 MB
Loading

0 commit comments

Comments
 (0)