diff --git a/.jules/bolt.md b/.jules/bolt.md index 8513f7ad5..8e58efe19 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -27,3 +27,7 @@ ## 2024-05-18 - Optimize Regex pattern detection in DictionaryFacilitatorImpl **Learning:** `DictionaryFacilitatorImpl.isRegexPattern` used chained `String.contains()` and `String.startsWith()` calls, which internally execute full loops over the string (or delegate to regex). Replacing these chained calls with a single manual O(N) iteration reduces overhead and prevents repeatedly traversing the string. **Action:** Replace chained string detection methods (`contains`, `startsWith`, `endsWith`) with a single manual loop over the string's indices when checking multiple characters in performance hot paths. + +## 2024-05-18 - Cache Drawables in RecyclerView Adapters +**Learning:** `ClipboardAdapter` was allocating new `GradientDrawable` and `RippleDrawable` instances inside `onBindViewHolder` hot-paths (`HeaderViewHolder.bind` and `ClipViewHolder.setContent`). Since these methods are called frequently during scrolling, creating UI objects repeatedly causes unnecessary garbage collection pressure and scrolling jank. +**Action:** Always initialize and cache static UI components like backgrounds and drawables in the `ViewHolder`'s `init` block rather than recreating them during `onBindViewHolder`. diff --git a/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardAdapter.kt b/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardAdapter.kt index 9ff6a0783..266ac0f59 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardAdapter.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardAdapter.kt @@ -242,7 +242,7 @@ class ClipboardAdapter( } inner class HeaderViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView) { - fun bind(header: ClipboardDisplayItem.Header) { + init { textView.apply { typeface = itemTypeFace setTextColor(itemTextColor) @@ -250,6 +250,15 @@ class ClipboardAdapter( background = createCardBackground(this) clipToOutline = true + setOnClickListener { + isPinnedFolded = !isPinnedFolded + refresh() + } + } + } + + fun bind(header: ClipboardDisplayItem.Header) { + textView.apply { text = buildString { append(if (header.isFolded) "▶ " else "▼ ") append(context.getString(R.string.clipboard_pinned)) @@ -257,11 +266,6 @@ class ClipboardAdapter( append(header.count) append(")") } - - setOnClickListener { - isPinnedFolded = !isPinnedFolded - refresh() - } } } } @@ -289,6 +293,15 @@ class ClipboardAdapter( } selectIconView = view.findViewById(R.id.clipboard_entry_select_icon) selectedOverlay = view.findViewById(R.id.clipboard_entry_selected_overlay) + selectedOverlay?.background = GradientDrawable().apply { + val colors = Settings.getValues().mColors + val accentColor = colors.get(ColorType.ACTION_KEY_BACKGROUND) + val radiusDp = Settings.getValues().mKeyBorderRadius.takeIf { it >= 0f } ?: 8f + val radiusPx = radiusDp * view.resources.displayMetrics.density + shape = GradientDrawable.RECTANGLE + cornerRadius = radiusPx + setColor(ColorUtils.setAlphaComponent(accentColor, 0x26)) + } pinnedIconView = view.findViewById(R.id.clipboard_entry_pinned_icon).apply { visibility = View.GONE if (pinnedIconResId != 0) { @@ -325,15 +338,6 @@ class ClipboardAdapter( selectIconView.alpha = 1.0f colors.setColor(selectIconView, ColorType.ACTION_KEY_BACKGROUND) selectedOverlay?.visibility = View.VISIBLE - val accentColor = colors.get(ColorType.ACTION_KEY_BACKGROUND) - val radiusDp = Settings.getValues().mKeyBorderRadius.takeIf { it >= 0f } ?: 8f - val radiusPx = radiusDp * itemView.resources.displayMetrics.density - val overlayDrawable = GradientDrawable().apply { - shape = GradientDrawable.RECTANGLE - cornerRadius = radiusPx - setColor(ColorUtils.setAlphaComponent(accentColor, 0x26)) - } - selectedOverlay?.background = overlayDrawable } else { selectIconView.setImageResource(R.drawable.ic_check_circle_outline) selectIconView.alpha = 0.5f