Skip to content

Commit

Permalink
Return SizeOnlyPlaceholder when the placeholder queue is empty (#1733)
Browse files Browse the repository at this point in the history
Co-authored-by: Jesse Wilson <[email protected]>
  • Loading branch information
jingwei99 and squarejesse authored Dec 8, 2023
1 parent 5815ba4 commit 63ddcf0
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ package app.cash.redwood.lazylayout.compose
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import app.cash.redwood.Modifier
import app.cash.redwood.layout.api.Constraint
Expand All @@ -45,23 +43,14 @@ internal fun LazyList(
val itemCount = itemProvider.itemCount
val itemsBefore = (state.firstIndex - state.preloadBeforeItemCount).coerceAtLeast(0)
val itemsAfter = (itemCount - (state.lastIndex + state.preloadAfterItemCount).coerceAtMost(itemCount)).coerceAtLeast(0)
// TODO(jwilson): drop this down to 20 once this is fixed:
// https://github.com/cashapp/redwood/issues/1551
var placeholderPoolSize by remember { mutableStateOf(30) }
val placeholderPoolSize = 30
LazyList(
isVertical,
itemsBefore = itemsBefore,
itemsAfter = itemsAfter,
isVertical = isVertical,
onViewportChanged = { localFirstVisibleItemIndex, localLastVisibleItemIndex ->
state.onUserScroll(localFirstVisibleItemIndex, localLastVisibleItemIndex)

val visibleItemCount = localLastVisibleItemIndex - localFirstVisibleItemIndex
val proposedPlaceholderPoolSize = visibleItemCount + visibleItemCount / 2
// We only ever want to increase the pool size.
if (placeholderPoolSize < proposedPlaceholderPoolSize) {
placeholderPoolSize = proposedPlaceholderPoolSize
}
},
itemsBefore = itemsBefore,
itemsAfter = itemsAfter,
width = width,
height = height,
margin = margin,
Expand Down Expand Up @@ -98,20 +87,13 @@ internal fun RefreshableLazyList(
val itemCount = itemProvider.itemCount
val itemsBefore = (state.firstIndex - state.preloadBeforeItemCount).coerceAtLeast(0)
val itemsAfter = (itemCount - (state.lastIndex + state.preloadAfterItemCount).coerceAtMost(itemCount)).coerceAtLeast(0)
var placeholderPoolSize by remember { mutableStateOf(20) }
val placeholderPoolSize = 30
RefreshableLazyList(
isVertical,
itemsBefore = itemsBefore,
itemsAfter = itemsAfter,
onViewportChanged = { localFirstVisibleItemIndex, localLastVisibleItemIndex ->
state.onUserScroll(localFirstVisibleItemIndex, localLastVisibleItemIndex)

val visibleItemCount = localLastVisibleItemIndex - localFirstVisibleItemIndex
val proposedPlaceholderPoolSize = visibleItemCount + visibleItemCount / 2
// We only ever want to increase the pool size.
if (placeholderPoolSize < proposedPlaceholderPoolSize) {
placeholderPoolSize = proposedPlaceholderPoolSize
}
},
refreshing = refreshing,
onRefresh = onRefresh,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ internal open class ViewLazyList private constructor(
override val value: View get() = recyclerView

private val processor = object : LazyListUpdateProcessor<ViewHolder, View>() {
override fun createPlaceholder(original: View): View {
return object : View(value.context) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
setMeasuredDimension(original.width, original.height)
}
}
}

override fun insertRows(index: Int, count: Int) {
adapter.notifyItemRangeInserted(index, count)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package app.cash.redwood.lazylayout.widget

import app.cash.redwood.Modifier
import app.cash.redwood.widget.Widget

/**
Expand Down Expand Up @@ -43,6 +44,12 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
/** Pool of placeholder widgets. */
private val placeholdersQueue = ArrayDeque<Widget<W>>()

/**
* The first placeholder ever returned. We use it to choose measured dimensions for created
* placeholders if the pool ever runs out.
*/
private var firstPlaceholder: Widget<W>? = null

/** Loaded items that may or may not have a view bound. */
private var loadedItems = mutableListOf<Binding<V, W>>()

Expand All @@ -57,6 +64,7 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
/** We expect placeholders to be added early and to never change. */
public val placeholder: Widget.Children<W> = object : Widget.Children<W> {
override fun insert(index: Int, widget: Widget<W>) {
if (firstPlaceholder == null) firstPlaceholder = widget
placeholdersQueue += widget
}

Expand Down Expand Up @@ -253,7 +261,7 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {

// We have a binding. Give it loaded content.
require(placeholder.isPlaceholder)
placeholdersQueue += placeholder.content!!
recyclePlaceholder(placeholder.content!!)
placeholder.isPlaceholder = false
placeholder.content = loadedContent
return placeholder
Expand Down Expand Up @@ -313,10 +321,35 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
}

private fun takePlaceholder(): Widget<W> {
return placeholdersQueue.removeFirstOrNull()
val result = placeholdersQueue.removeFirstOrNull()
if (result != null) return result

val created = createPlaceholder(firstPlaceholder!!.value)
?: throw IllegalStateException("no more placeholders!")

return SizeOnlyPlaceholder(created)
}

private fun recyclePlaceholder(placeholder: Widget<W>) {
if (placeholder !is SizeOnlyPlaceholder) {
placeholdersQueue += placeholder
}
}

private class SizeOnlyPlaceholder<W : Any>(
override val value: W,
) : Widget<W> {
override var modifier: Modifier = Modifier
}

/**
* Returns an empty widget with the same dimensions as [original].
*
* @param original a placeholder provided by the guest code. It is a live view that is currently
* in a layout.
*/
protected open fun createPlaceholder(original: W): W? = null

protected abstract fun insertRows(index: Int, count: Int)

protected abstract fun deleteRows(index: Int, count: Int)
Expand Down Expand Up @@ -383,7 +416,7 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
if (itemsAfterIndex != -1) processor.itemsAfter.set(itemsAfterIndex, null)

// When a placeholder is reused, recycle its widget.
processor.placeholdersQueue += content!!
processor.recyclePlaceholder(content!!)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ class EmojiSearchActivity : ComponentActivity() {
private var success = true
private var snackbar: Snackbar? = null

override fun uncaughtException(exception: Throwable) {
Log.e("Treehouse", "uncaughtException", exception)
}

override fun codeLoadFailed(exception: Exception, startValue: Any?) {
Log.w("Treehouse", "codeLoadFailed", exception)
if (success) {
Expand Down

0 comments on commit 63ddcf0

Please sign in to comment.