Skip to content

Commit ae964b4

Browse files
committed
Merge remote-tracking branch 'origin/release/1.3.0'
2 parents 20f51b6 + da9b29a commit ae964b4

6 files changed

Lines changed: 111 additions & 20 deletions

File tree

indicator-fast-scroll/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ buildscript {
1111
}
1212
}
1313

14-
def libraryVersionName = '1.2.1'
14+
def libraryVersionName = '1.3.0-beta01'
1515

1616
android {
1717
compileSdkVersion 29

indicator-fast-scroll/src/main/java/com/reddit/indicatorfastscroll/FastScrollerView.kt

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,31 @@ class FastScrollerView @JvmOverloads constructor(
4646
defStyleRes
4747
) {
4848

49-
var iconColor: ColorStateList? by onUpdate(::bindItemIndicatorViews)
50-
var textAppearanceRes: Int by onUpdate(::bindItemIndicatorViews)
51-
var textColor: ColorStateList? by onUpdate(::bindItemIndicatorViews)
52-
var textPadding: Float by onUpdate(::bindItemIndicatorViews)
49+
var iconColor: ColorStateList? = null
50+
set(value) {
51+
field = value
52+
pressedIconColor = value?.getColorForState(intArrayOf(android.R.attr.state_activated))
53+
bindItemIndicatorViews()
54+
}
55+
var textAppearanceRes: Int = 0
56+
set(value) {
57+
field = value
58+
bindItemIndicatorViews()
59+
}
60+
var textColor: ColorStateList? = null
61+
set(value) {
62+
field = value
63+
pressedTextColor = value?.getColorForState(intArrayOf(android.R.attr.state_activated))
64+
bindItemIndicatorViews()
65+
}
66+
var textPadding: Float = 0f
67+
set(value) {
68+
field = value
69+
bindItemIndicatorViews()
70+
}
71+
72+
private var pressedIconColor: Int? = null
73+
private var pressedTextColor: Int? = null
5374

5475
internal var itemIndicatorsBuilder: ItemIndicatorsBuilder = ItemIndicatorsBuilder()
5576

@@ -79,7 +100,7 @@ class FastScrollerView @JvmOverloads constructor(
79100
* The function will be called when building the list of indicators, which happens after the
80101
* RecyclerView's adapter's data changes. It will be called on the UI thread.
81102
*/
82-
var showIndicator: ((FastScrollItemIndicator, Int, Int) -> Boolean)? by onUpdate {
103+
var showIndicator: ((FastScrollItemIndicator, Int, Int) -> Boolean)? by onUpdate { _ ->
83104
postUpdateItemIndicators()
84105
}
85106

@@ -235,7 +256,7 @@ class FastScrollerView @JvmOverloads constructor(
235256
}
236257

237258
// Optimize the views by batching adjacent text indicators into a single TextView
238-
val viewCreators = ArrayList<() -> View>()
259+
val views = ArrayList<View>()
239260
itemIndicators.run {
240261
var index = 0
241262
while (index <= lastIndex) {
@@ -244,12 +265,12 @@ class FastScrollerView @JvmOverloads constructor(
244265
.takeWhile { it is FastScrollItemIndicator.Text }
245266
as List<FastScrollItemIndicator.Text>
246267
if (textIndicatorsBatch.isNotEmpty()) {
247-
viewCreators.add { createTextView(textIndicatorsBatch) }
268+
views.add(createTextView(textIndicatorsBatch))
248269
index += textIndicatorsBatch.size
249270
} else {
250271
when (val indicator = this[index]) {
251272
is FastScrollItemIndicator.Icon -> {
252-
viewCreators.add { createIconView(indicator) }
273+
views.add(createIconView(indicator))
253274
}
254275
is FastScrollItemIndicator.Text -> {
255276
throw IllegalStateException("Text indicator wasn't batched")
@@ -259,19 +280,20 @@ class FastScrollerView @JvmOverloads constructor(
259280
}
260281
}
261282
}
262-
viewCreators.forEach { createView ->
263-
addView(createView())
264-
}
283+
views.forEach(::addView)
265284
}
266285

267286
private fun selectItemIndicator(
268287
indicator: FastScrollItemIndicator,
269-
indicatorCenterY: Int
288+
indicatorCenterY: Int,
289+
touchedView: View,
290+
textLine: Int?
270291
) {
271292
val position = itemIndicatorsWithPositions
272293
.first { it.first == indicator }
273294
.let(ItemIndicatorWithPosition::second)
274295
if (position != lastSelectedPosition) {
296+
clearSelectedItemIndicator()
275297
lastSelectedPosition = position
276298
if (useDefaultScroller) {
277299
scrollToPosition(position)
@@ -284,12 +306,29 @@ class FastScrollerView @JvmOverloads constructor(
284306
HapticFeedbackConstants.KEYBOARD_TAP
285307
}
286308
)
309+
if (touchedView is ImageView) {
310+
touchedView.isActivated = true
311+
} else if (textLine != null) {
312+
pressedTextColor?.let { color ->
313+
TextColorUtil.highlightAtIndex(touchedView as TextView, textLine, color)
314+
}
315+
}
287316
itemIndicatorSelectedCallbacks.forEach {
288317
it.onItemIndicatorSelected(indicator, indicatorCenterY, position)
289318
}
290319
}
291320
}
292321

322+
private fun clearSelectedItemIndicator() {
323+
lastSelectedPosition = null
324+
if (pressedIconColor != null) {
325+
children.filterIsInstance<ImageView>().forEach { it.isActivated = false }
326+
}
327+
if (pressedTextColor != null) {
328+
children.filterIsInstance<TextView>().forEach(TextColorUtil::clearHighlight)
329+
}
330+
}
331+
293332
private fun scrollToPosition(position: Int) {
294333
recyclerView!!.apply {
295334
stopScroll()
@@ -301,9 +340,9 @@ class FastScrollerView @JvmOverloads constructor(
301340
override fun onTouchEvent(event: MotionEvent): Boolean {
302341
fun View.containsY(y: Int) = y in (top until bottom)
303342

304-
if (event.action in MOTIONEVENT_STOP_ACTIONS) {
343+
if (event.actionMasked in MOTIONEVENT_STOP_ACTIONS) {
305344
isPressed = false
306-
lastSelectedPosition = null
345+
clearSelectedItemIndicator()
307346
onItemIndicatorTouched?.invoke(false)
308347
return false
309348
}
@@ -316,7 +355,7 @@ class FastScrollerView @JvmOverloads constructor(
316355
is ImageView -> {
317356
val touchedIndicator = view.tag as FastScrollItemIndicator.Icon
318357
val centerY = view.y.toInt() + (view.height / 2)
319-
selectItemIndicator(touchedIndicator, centerY)
358+
selectItemIndicator(touchedIndicator, centerY, view, textLine = null)
320359
consumed = true
321360
}
322361
is TextView -> {
@@ -332,7 +371,7 @@ class FastScrollerView @JvmOverloads constructor(
332371

333372
val centerY = view.y.toInt() +
334373
(textLineHeight / 2) + (touchedIndicatorIndex * textLineHeight)
335-
selectItemIndicator(touchedIndicator, centerY)
374+
selectItemIndicator(touchedIndicator, centerY, view, textLine = touchedIndicatorIndex)
336375
consumed = true
337376
}
338377
}

indicator-fast-scroll/src/main/java/com/reddit/indicatorfastscroll/ResourcesUtil.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.reddit.indicatorfastscroll
22

3+
import android.content.res.ColorStateList
34
import android.view.View
5+
import androidx.annotation.ColorInt
46
import androidx.annotation.StyleRes
57

68
internal fun View.throwIfMissingAttrs(@StyleRes styleRes: Int, block: () -> Unit) {
@@ -15,3 +17,8 @@ internal fun View.throwIfMissingAttrs(@StyleRes styleRes: Int, block: () -> Unit
1517
)
1618
}
1719
}
20+
21+
@ColorInt
22+
internal fun ColorStateList.getColorForState(stateSet: IntArray): Int? {
23+
return getColorForState(stateSet, defaultColor).takeIf { it != defaultColor }
24+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.reddit.indicatorfastscroll
2+
3+
import android.text.SpannableString
4+
import android.text.Spanned
5+
import android.text.style.ForegroundColorSpan
6+
import android.widget.TextView
7+
import androidx.core.text.clearSpans
8+
9+
internal object TextColorUtil {
10+
11+
fun highlightAtIndex(textView: TextView, highlightedIndex: Int?, color: Int) {
12+
textView.apply {
13+
if (highlightedIndex == null) {
14+
clearHighlight(this)
15+
} else {
16+
text = SpannableString.valueOf(text).apply {
17+
clearSpans()
18+
val linesUpToHighlight = lineSequence().take(highlightedIndex + 1).toList() // inclusive
19+
val start = linesUpToHighlight
20+
.dropLast(1)
21+
.fold(0) { acc, line ->
22+
acc + line.length + 1
23+
}
24+
val highlightedLineSize = linesUpToHighlight.lastOrNull()?.length ?: 0
25+
26+
setSpan(ForegroundColorSpan(color), start, start + highlightedLineSize, 0)
27+
}
28+
}
29+
}
30+
}
31+
32+
fun clearHighlight(textView: TextView) {
33+
textView.apply {
34+
if (text is Spanned) {
35+
text = SpannableString.valueOf(text).apply {
36+
clearSpans()
37+
}
38+
}
39+
}
40+
}
41+
42+
}

indicator-fast-scroll/src/main/java/com/reddit/indicatorfastscroll/UpdateDelegate.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.reddit.indicatorfastscroll
33
import kotlin.properties.ReadWriteProperty
44
import kotlin.reflect.KProperty
55

6-
internal class UpdateDelegate<T>(val update: () -> Unit) : ReadWriteProperty<Any?, T> {
6+
internal class UpdateDelegate<T>(val update: (T) -> Unit) : ReadWriteProperty<Any?, T> {
77

88
var set = false
99
var value: T? = null
@@ -21,12 +21,14 @@ internal class UpdateDelegate<T>(val update: () -> Unit) : ReadWriteProperty<Any
2121
this.set = true
2222
this.value = value
2323
if (wasSet) {
24-
update()
24+
update(value)
2525
}
2626
}
2727
}
2828

2929
/**
3030
* A delegate that sets a backing value and calls [update] on every change after the first.
3131
*/
32-
internal fun <T> onUpdate(update: () -> Unit) = UpdateDelegate<T>(update)
32+
internal fun <T> onUpdate(update: (T) -> Unit) = UpdateDelegate(update)
33+
34+
internal fun <T> onUpdate(update: () -> Unit) = UpdateDelegate<T> { update() }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<selector xmlns:android="http://schemas.android.com/apk/res/android">
3+
<item android:color="@color/colorAccent" android:state_activated="true" />
34
<item android:color="@color/colorPrimary" android:state_pressed="true" />
45
<item android:color="@android:color/black" />
56
</selector>

0 commit comments

Comments
 (0)