Skip to content

Commit 20790b5

Browse files
authored
prepare 1.2.0 (#2)
* prepare 1.2.0 * fix issue where adapter data will be called twice on initial refresh * adapter item public methods also have [set] in addition to [with] * better documentation * updated readme * misc updated * update readme Co-authored-by: Marcel Vojtkovszky <[email protected]>
1 parent f223c69 commit 20790b5

File tree

10 files changed

+437
-104
lines changed

10 files changed

+437
-104
lines changed

.idea/misc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ class ImageViewRecyclerItem(private val drawable: Drawable?): AdapterItem<ImageV
1616
}
1717
}
1818
```
19-
20-
Or inflate your own view.
19+
or inflate your own view.
2120
``` kotlin
2221
class TextRowRecyclerItem(private val text: String): AdapterItem<View>() {
2322
override fun getNewView(parent: ViewGroup): View {
@@ -29,15 +28,31 @@ class TextRowRecyclerItem(private val text: String): AdapterItem<View>() {
2928
}
3029
```
3130

32-
AdapterItem has other methods you can override to fine-tune behaviour based on the adapter's callbacks.
31+
AdapterItem subscribes to callbacks you can override to fine-tune behaviour in different states.
32+
``` kotlin
33+
override fun onItemViewAttached(view: YourView): Unit
34+
35+
override fun onItemViewDetached(view: YourView): Unit
36+
37+
override fun onItemViewRecycled(view: YourView): Unit
38+
39+
override fun onItemViewFailedToRecycle(view: YourView): Unit
40+
```
41+
42+
and has a few public methods to help you manage it.
3343
``` kotlin
34-
override fun onItemViewAttached(view: YourView) { }
35-
override fun onItemViewDetached(view: YourView) { }
36-
override fun onItemViewRecycled(view: YourView) { }
37-
override fun onItemViewFailedToRecycle(view: YourView) { }
44+
fun getView(): YourView
45+
46+
fun setAnimation(@AnimRes animation: Int): Unit
47+
48+
fun setClickListener(clickListener: View.OnClickListener?): Unit
49+
50+
fun setMargins(startMargin: Int = 0, topMargin: Int = 0, endMargin: Int = 0, bottomMargin: Int = 0): Unit
51+
52+
fun setViewTag(viewTag: Any?): Unit
3853
```
3954

40-
2. Have your Activity or Fragment implement ProperBaseAdapterImplementation .
55+
<br/>2. Have your Activity, Fragment or View implement BaseRecyclerViewImplementation .
4156
``` kotlin
4257
class MainActivity : AppCompatActivity(), ProperBaseAdapterImplementation {
4358
...
@@ -72,7 +87,7 @@ class MainActivity : AppCompatActivity(), ProperBaseAdapterImplementation {
7287
...
7388
```
7489

75-
3. Simply calling refreshRecyclerView method will do the rest and populate RecyclerView with provided data.
90+
<br/>3. Simply calling refreshRecyclerView method will do the rest and populate recycler view with provided data.
7691
``` kotlin
7792
override fun onCreate(savedInstanceState: Bundle?) {
7893
super.onCreate(savedInstanceState)
@@ -81,6 +96,30 @@ override fun onCreate(savedInstanceState: Bundle?) {
8196
}
8297
```
8398

99+
<br/>You're of course not required use a provided BaseRecyclerViewImplementation and simply construct ProperBaseAdapter yourself and set it to your RecyclerView.\
100+
<br/>Make use of multiple adapter's public methods based on your needs:
101+
``` kotlin
102+
fun addItems(dataObjects: List<AdapterItem<*>>?, notifyItemRangeChanged: Boolean = true): Unit
103+
104+
fun getItemAt(position: Int): AdapterItem<*>?
105+
106+
fun getItemByViewTag(viewTag: Any): AdapterItem<*>?
107+
108+
fun getItemTypeAt(position: Int): KClass<*>
109+
110+
fun getPositionForItemWithViewTag(viewTag: Any): Int?
111+
112+
fun notifyItemWithViewTagChanged(viewTag: Any): Unit
113+
114+
fun updateItems(newItems: List<AdapterItem<*>>): Unit
115+
116+
fun removeAllItems(notifyDataSetChanged: Boolean = true): Unit
117+
118+
fun removeItems(fromPosition: Int, itemCount: Int = 1, notifyDataSetChanged: Boolean = true): Unit
119+
120+
fun setItems(newData: MutableList<AdapterItem<*>>, notifyDataSetChanged: Boolean = true): Unit
121+
```
122+
84123
## Nice! How do I get started?
85124
Add it in your root build.gradle at the end of repositories:
86125
``` gradle

app/src/main/kotlin/com/vojtkovszky/properbaseadapter/example/MainActivity.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,37 @@ class MainActivity : AppCompatActivity(), ProperBaseAdapterImplementation {
1414
override fun onCreate(savedInstanceState: Bundle?) {
1515
super.onCreate(savedInstanceState)
1616
setContentView(R.layout.activity_main)
17-
refreshRecyclerView()
17+
18+
// let's start
19+
refreshRecyclerView(
20+
refreshType = DataDispatchMethod.SET_DATA_AND_REFRESH,
21+
delayMillis = 1000)
1822
}
1923

2024
override fun getAdapterData(data: MutableList<AdapterItem<*>>): MutableList<AdapterItem<*>> {
25+
// let's put an image on top
2126
data.add(ImageViewRecyclerItem(ContextCompat.getDrawable(this, android.R.drawable.btn_radio))
2227
.withMargins(
2328
topMargin = resources.getDimensionPixelSize(R.dimen.dp16),
2429
bottomMargin = resources.getDimensionPixelSize(R.dimen.dp16)))
30+
31+
// then 10 text items
2532
for (i in 1..10) {
2633
data.add(TextViewRecyclerItem("Text item $i")
2734
.withMargins(
2835
startMargin = resources.getDimensionPixelSize(R.dimen.dp16),
2936
topMargin = resources.getDimensionPixelSize(R.dimen.dp8),
3037
endMargin = resources.getDimensionPixelSize(R.dimen.dp16),
3138
bottomMargin = resources.getDimensionPixelSize(R.dimen.dp8))
32-
//.withAnimation(android.R.anim.fade_in)
39+
.withAnimation(R.anim.item_fall_down)
3340
.withClickListener(View.OnClickListener {
3441
Toast.makeText(this, "Clicked item $i", Toast.LENGTH_SHORT).show()
3542
}))
3643
}
37-
data.add(ImageViewRecyclerItem(ContextCompat.getDrawable(this, android.R.drawable.ic_btn_speak_now)))
44+
45+
// and another image for the last row
46+
data.add(ImageViewRecyclerItem(ContextCompat.getDrawable(this, android.R.drawable.ic_btn_speak_now))
47+
.withViewTag("BOTTOM_IMAGE"))
3848

3949
return data
4050
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<set xmlns:android="http://schemas.android.com/apk/res/android"
4+
android:duration="@android:integer/config_mediumAnimTime">
5+
6+
<translate
7+
android:fromYDelta="-20%"
8+
android:toYDelta="0"
9+
android:interpolator="@android:anim/decelerate_interpolator" />
10+
11+
<alpha
12+
android:fromAlpha="0"
13+
android:toAlpha="1"
14+
android:interpolator="@android:anim/decelerate_interpolator" />
15+
16+
<scale
17+
android:fromXScale="105%"
18+
android:fromYScale="105%"
19+
android:toXScale="100%"
20+
android:toYScale="100%"
21+
android:pivotX="50%"
22+
android:pivotY="50%"
23+
android:interpolator="@android:anim/decelerate_interpolator" />
24+
25+
</set>

properbaseadapter/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ android {
99
defaultConfig {
1010
minSdkVersion 16
1111
targetSdkVersion 29
12-
versionCode 2
13-
versionName "1.1.0"
12+
versionCode 3
13+
versionName "1.2.0"
1414

1515
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1616
consumerProguardFiles 'consumer-rules.pro'

properbaseadapter/src/main/kotlin/com/vojtkovszky/properbaseadapter/AdapterItem.kt

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -88,20 +88,7 @@ abstract class AdapterItem<AIV: View> : AdapterViewHolder.OnCallbackListener<AIV
8888
*/
8989
override fun onItemViewFailedToRecycle(view: AIV) {}
9090

91-
// called when recycler view calls onBindViewHolder on this item.
92-
internal fun bind(viewHolder: AdapterViewHolder<View>, position: Int) {
93-
this.viewHolder = viewHolder as AdapterViewHolder<AIV>
94-
this.boundPosition = position
95-
this.viewHolder?.setCallbackListener(this)
96-
onViewBound(viewHolder.itemView as AIV)
97-
}
98-
99-
// Calculate and return view type whenever adapter asks for it.
100-
// Mind that adapter caches view type ids and associates it with data to minimize recalculations.
101-
internal fun getViewTypeId(): Int {
102-
return getViewTypeIdForClass(this::class)
103-
}
104-
91+
//region PUBLIC METHODS
10592
/**
10693
* @return Returns view bound to this adapter item, with correct casting.
10794
* Mind that this will be null if called before view is bound by adapter.
@@ -111,6 +98,34 @@ abstract class AdapterItem<AIV: View> : AdapterViewHolder.OnCallbackListener<AIV
11198
else null
11299
}
113100

101+
/**
102+
* See [withAnimation]
103+
*/
104+
fun setAnimation(@AnimRes animation: Int) {
105+
withAnimation(animation)
106+
}
107+
108+
/**
109+
* See [withClickListener]
110+
*/
111+
fun setClickListener(clickListener: View.OnClickListener?) {
112+
withClickListener(clickListener)
113+
}
114+
115+
/**
116+
* See [withMargins]
117+
*/
118+
fun setMargins(startMargin: Int = 0, topMargin: Int = 0, endMargin: Int = 0, bottomMargin: Int = 0) {
119+
withMargins(startMargin, topMargin, endMargin, bottomMargin)
120+
}
121+
122+
/**
123+
* See [withViewTag]
124+
*/
125+
fun setViewTag(viewTag: Any?) {
126+
withViewTag(viewTag)
127+
}
128+
114129
/**
115130
* Define a custom animation for view of this item to be used when visibility changes.
116131
* If none defined, BaseAdapter will use it's own default animation for it (if defined).
@@ -120,6 +135,15 @@ abstract class AdapterItem<AIV: View> : AdapterViewHolder.OnCallbackListener<AIV
120135
return this
121136
}
122137

138+
/**
139+
* Define a generic view click listener to be set to view bound to this item.
140+
* This gets useful if we just want to have a simple click listener on an item.
141+
*/
142+
fun withClickListener(clickListener: View.OnClickListener?): AdapterItem<AIV> {
143+
this.clickListener = clickListener
144+
return this
145+
}
146+
123147
/**
124148
* Define custom margins for this item to be applied when view gets bound.
125149
*/
@@ -132,15 +156,6 @@ abstract class AdapterItem<AIV: View> : AdapterViewHolder.OnCallbackListener<AIV
132156
return this
133157
}
134158

135-
/**
136-
* Define a generic view click listener to be set to view bound to this item.
137-
* This gets useful if we just want to have a simple click listener on an item.
138-
*/
139-
fun withClickListener(clickListener: View.OnClickListener?): AdapterItem<AIV> {
140-
this.clickListener = clickListener
141-
return this
142-
}
143-
144159
/**
145160
* Define a tag to be set to view bound to this item.
146161
* This gets useful if we want to retrieve an item from adapter by using
@@ -150,4 +165,19 @@ abstract class AdapterItem<AIV: View> : AdapterViewHolder.OnCallbackListener<AIV
150165
this.viewTag = viewTag
151166
return this
152167
}
168+
//endregion
169+
170+
// called when recycler view calls onBindViewHolder on this item.
171+
internal fun bind(viewHolder: AdapterViewHolder<View>, position: Int) {
172+
this.viewHolder = viewHolder as AdapterViewHolder<AIV>
173+
this.boundPosition = position
174+
this.viewHolder?.setCallbackListener(this)
175+
onViewBound(viewHolder.itemView as AIV)
176+
}
177+
178+
// Calculate and return view type whenever adapter asks for it.
179+
// Mind that adapter caches view type ids and associates it with data to minimize recalculations.
180+
internal fun getViewTypeId(): Int {
181+
return getViewTypeIdForClass(this::class)
182+
}
153183
}

properbaseadapter/src/main/kotlin/com/vojtkovszky/properbaseadapter/ProperBaseAdapter.kt

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,6 @@ class ProperBaseAdapter constructor(data: MutableList<AdapterItem<*>> = mutableL
4949
else data[position]
5050
}
5151

52-
/**
53-
* Retrieve type of [AdapterItem] at a given position.
54-
* If position is out of bounds, [Nothing.annotationClass] will be returned
55-
*/
56-
fun getItemTypeAt(position: Int): KClass<*> {
57-
return if (position > data.size || position < 0) Nothing::class
58-
else data[position]::class
59-
}
60-
6152
/**
6253
* Retrieve instance of [AdapterItem] by a given tag or null if no such item is found
6354
*/
@@ -70,6 +61,15 @@ class ProperBaseAdapter constructor(data: MutableList<AdapterItem<*>> = mutableL
7061
return null
7162
}
7263

64+
/**
65+
* Retrieve type of [AdapterItem] at a given position.
66+
* If position is out of bounds, [Nothing.annotationClass] will be returned
67+
*/
68+
fun getItemTypeAt(position: Int): KClass<*> {
69+
return if (position > data.size || position < 0) Nothing::class
70+
else data[position]::class
71+
}
72+
7373
/**
7474
* Retrieve an indexed position for an [AdapterItem] with a given tag or null if no such item
7575
* is found
@@ -83,6 +83,28 @@ class ProperBaseAdapter constructor(data: MutableList<AdapterItem<*>> = mutableL
8383
return null
8484
}
8585

86+
87+
/**
88+
* Add new items at the end of existing data set and define whether
89+
* [RecyclerView.Adapter.notifyDataSetChanged] should be called afterwards.
90+
*
91+
* TODO: Insert should be possible too, just need to look into modifying
92+
* [addDefaultToDataViewTypeIds] method
93+
*/
94+
fun addItems(dataObjects: List<AdapterItem<*>>?, notifyItemRangeChanged: Boolean = true) {
95+
if (dataObjects == null || dataObjects.isEmpty()) {
96+
return
97+
}
98+
99+
val addStartPosition = if (data.isEmpty()) 0 else data.size
100+
data.addAll(dataObjects)
101+
addDefaultToDataViewTypeIds(addStartPosition)
102+
103+
if (notifyItemRangeChanged) {
104+
notifyItemRangeChanged(addStartPosition, dataObjects.size)
105+
}
106+
}
107+
86108
/**
87109
* Set items to the the adapter and define whether [RecyclerView.Adapter.notifyDataSetChanged]
88110
* should be called afterwards.
@@ -118,27 +140,6 @@ class ProperBaseAdapter constructor(data: MutableList<AdapterItem<*>> = mutableL
118140
}
119141
}
120142

121-
/**
122-
* Add new items at the end of existing data set and define whether
123-
* [RecyclerView.Adapter.notifyDataSetChanged] should be called afterwards.
124-
*
125-
* TODO: Insert should be possible too, just need to look into modifying
126-
* [addDefaultToDataViewTypeIds] method
127-
*/
128-
fun addItems(dataObjects: List<AdapterItem<*>>?, notifyItemRangeChanged: Boolean = true) {
129-
if (dataObjects == null || dataObjects.isEmpty()) {
130-
return
131-
}
132-
133-
val addStartPosition = if (data.isEmpty()) 0 else data.size
134-
data.addAll(dataObjects)
135-
addDefaultToDataViewTypeIds(addStartPosition)
136-
137-
if (notifyItemRangeChanged) {
138-
notifyItemRangeChanged(addStartPosition, dataObjects.size)
139-
}
140-
}
141-
142143
/**
143144
* Remove all items from adapter and define whether [RecyclerView.Adapter.notifyDataSetChanged]
144145
* should be called afterwards.

0 commit comments

Comments
 (0)