Skip to content

Commit

Permalink
perf: speed up recycler view Adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
nopdan authored and WhiredPlanck committed Feb 21, 2024
1 parent c09f96c commit 6014451
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 162 deletions.
5 changes: 4 additions & 1 deletion app/src/main/java/com/osfans/trime/data/theme/FontManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ object FontManager {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val fontFamilies = mutableListOf<FontFamily>()
getFontFamilies(key).let {
if (it.isEmpty()) return@getTypeface Typeface.DEFAULT
if (it.isEmpty()) {
typefaceCache[key] = Typeface.DEFAULT
return@getTypeface Typeface.DEFAULT
}
fontFamilies.addAll(getFontFamilies("latin_font"))
fontFamilies.addAll(it)
fontFamilies.addAll(getFontFamilies("hanb_font"))
Expand Down
107 changes: 59 additions & 48 deletions app/src/main/java/com/osfans/trime/ime/symbol/CandidateAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import splitties.views.dsl.constraintlayout.centerHorizontally
import splitties.views.dsl.constraintlayout.topOfParent

// 显示长度不固定,字体大小正常的内容。用于类型 CANDIDATE, VAR_LENGTH
class CandidateAdapter(private val theme: Theme) : RecyclerView.Adapter<CandidateAdapter.ViewHolder>() {
class CandidateAdapter(theme: Theme) : RecyclerView.Adapter<CandidateAdapter.ViewHolder>() {
private val mCandidates = mutableListOf<CandidateListItem>()

internal enum class CommentPosition {
Expand All @@ -44,40 +44,69 @@ class CandidateAdapter(private val theme: Theme) : RecyclerView.Adapter<Candidat
return mCandidates.size
}

private val mCandidateTextSize = theme.style.getFloat("candidate_text_size").coerceAtLeast(1f)
private val mCandidateFont = FontManager.getTypeface("candidate_font")
private val mCandidateTextColor = ColorManager.getColor("candidate_text_color")
private val mHilitedCandidateTextColor = ColorManager.getColor("hilited_candidate_text_color")
private val mCommentPosition = theme.style.getInt("comment_position")
private val mCommentTextSize = theme.style.getFloat("comment_text_size").coerceAtLeast(1f)
private val mCommentFont = FontManager.getTypeface("comment_font")
private val mCommentTextColor = ColorManager.getColor("comment_text_color")
private val mBackgroud =
ColorManager.getDrawable(
"key_back_color",
"key_border",
"key_border_color",
"round_corner",
null,
)

override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int,
): ViewHolder {
val binding = LiquidEntryViewBinding.inflate(LayoutInflater.from(parent.context))
binding.candidate.apply {
textSize = mCandidateTextSize
typeface = mCandidateFont
mCandidateTextColor?.let { setTextColor(it) }
}
val isCommentHidden = Rime.getRimeOption("_hide_comment")
if (isCommentHidden) return ViewHolder(binding)

binding.comment.apply {
visibility = View.GONE
textSize = mCommentTextSize
typeface = mCommentFont
mCommentTextColor?.let { setTextColor(it) }
}
val candidate = binding.candidate
val comment = binding.comment
val isCommentHidden = Rime.getRimeOption("_hide_comment")
if (!isCommentHidden) {
when (CommentPosition.entries[theme.style.getInt("comment_position")]) {
CommentPosition.BOTTOM -> {
candidate.updateLayoutParams<ConstraintLayout.LayoutParams> {
centerHorizontally()
}
comment.updateLayoutParams<ConstraintLayout.LayoutParams> {
below(candidate)
centerHorizontally()
bottomOfParent()
}
when (CommentPosition.entries[mCommentPosition]) {
CommentPosition.BOTTOM -> {
candidate.updateLayoutParams<ConstraintLayout.LayoutParams> {
centerHorizontally()
}
CommentPosition.TOP -> {
candidate.updateLayoutParams<ConstraintLayout.LayoutParams> {
centerHorizontally()
}
comment.updateLayoutParams<ConstraintLayout.LayoutParams> {
topOfParent()
above(candidate)
centerHorizontally()
}
comment.updateLayoutParams<ConstraintLayout.LayoutParams> {
below(candidate)
centerHorizontally()
bottomOfParent()
}
CommentPosition.RIGHT, CommentPosition.UNKNOWN -> {}
}

CommentPosition.TOP -> {
candidate.updateLayoutParams<ConstraintLayout.LayoutParams> {
centerHorizontally()
}
comment.updateLayoutParams<ConstraintLayout.LayoutParams> {
topOfParent()
above(candidate)
centerHorizontally()
}
}

CommentPosition.RIGHT, CommentPosition.UNKNOWN -> {}
}
binding.comment.visibility = View.GONE
return ViewHolder(binding)
}

Expand All @@ -92,44 +121,26 @@ class CandidateAdapter(private val theme: Theme) : RecyclerView.Adapter<Candidat
position: Int,
) {
val (comment, text) = mCandidates[position]
holder.candidate.apply {
this.text = text
textSize = theme.style.getFloat("candidate_text_size").coerceAtLeast(1f)
typeface = FontManager.getTypeface("candidate_font")
ColorManager.getColor("candidate_text_color")?.let { setTextColor(it) }
}
holder.comment.apply {
this.text = comment
textSize = theme.style.getFloat("comment_text_size").coerceAtLeast(1f)
typeface = FontManager.getTypeface("comment_font")
ColorManager.getColor("comment_text_color")?.let { setTextColor(it) }
}
holder.candidate.text = text
holder.comment.text = comment

// 点击前后必须使用相同类型的背景,或者全部为背景图,或者都为背景色
// 点击前后必须使用相同类型的背景,或者全部为背景图,或者都为背景色
// 如果直接使用background,会造成滚动时部分内容的背景填充错误的问题
holder.itemView.background =
ColorManager.getDrawable(
"key_back_color",
"key_border",
"key_border_color",
"round_corner",
null,
)
holder.itemView.background = mBackgroud

// 如果设置了回调,则设置点击事件
holder.itemView.setOnClickListener { listener?.invoke(position) }

// 点击时产生背景变色效果
holder.itemView.setOnTouchListener { _, motionEvent: MotionEvent ->
val hilited = ColorManager.getColor("hilited_candidate_text_color")
if (motionEvent.action == MotionEvent.ACTION_DOWN) {
hilited?.let { holder.candidate.setTextColor(it) }
mHilitedCandidateTextColor?.let { holder.candidate.setTextColor(it) }
}
false
}
}

/** 添加 候选点击事件 Listener 回调 * */
/** 添加 候选点击事件 Listener 回调 */
private var listener: ((Int) -> Unit)? = null

/** @param listener position
Expand Down
172 changes: 86 additions & 86 deletions app/src/main/java/com/osfans/trime/ime/symbol/FlexibleAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,14 @@ import com.osfans.trime.ime.core.Trime
import com.osfans.trime.util.appContext
import kotlinx.coroutines.launch

class FlexibleAdapter(
private val theme: Theme,
) : RecyclerView.Adapter<FlexibleAdapter.ViewHolder>() {
class FlexibleAdapter(theme: Theme) : RecyclerView.Adapter<FlexibleAdapter.ViewHolder>() {
private val mBeans = mutableListOf<DatabaseBean>()

// 映射条目的 id 和其在视图中位置的关系
// 以应对增删条目时 id 和其位置的相对变化
// [<id, position>, ...]
private val mBeansId = mutableMapOf<Int, Int>()
val beans: List<DatabaseBean>
get() = mBeans
val beans get() = mBeans

fun updateBeans(beans: List<DatabaseBean>) {
val sorted =
Expand All @@ -57,12 +54,36 @@ class FlexibleAdapter(

override fun getItemCount(): Int = mBeans.size

private val mTypeface = FontManager.getTypeface("long_text_font")
private val mLongTextColor = ColorManager.getColor("long_text_color")
private val mKeyTextColor = ColorManager.getColor("key_text_color")
private val mKeyLongTextSize = theme.style.getFloat("key_long_text_size")
private val mLabelTextSize = theme.style.getFloat("label_text_size")

// 这里不用 get() 会导致快速滑动时背景填充错误
private val mBackground
get() =
ColorManager.getDrawable(
"long_text_back_color",
"key_border",
"key_long_text_border",
"round_corner",
null,
)

override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int,
): ViewHolder {
val binding = SimpleKeyItemBinding.inflate(LayoutInflater.from(parent.context))
return ViewHolder(binding)
val holder = ViewHolder(binding)
holder.simpleKeyText.apply {
typeface = mTypeface
(mLongTextColor ?: mKeyTextColor)?.let { setTextColor(it) }
(mKeyLongTextSize.takeIf { it > 0f } ?: mLabelTextSize.takeIf { it > 0f })
?.let { textSize = it }
}
return holder
}

inner class ViewHolder(binding: SimpleKeyItemBinding) : RecyclerView.ViewHolder(binding.root) {
Expand All @@ -76,108 +97,87 @@ class FlexibleAdapter(
) {
with(viewHolder) {
val bean = mBeans[position]
simpleKeyText.apply {
text = bean.text
typeface = FontManager.getTypeface("long_text_font")
(
ColorManager.getColor("long_text_color")
?: ColorManager.getColor("key_text_color")
)
?.let { setTextColor(it) }

theme.style.getFloat("key_long_text_size").takeIf { it > 0f }
?: theme.style.getFloat("label_text_size").takeIf { it > 0f }
?.let { textSize = it }
}
simpleKeyText.text = bean.text
simpleKeyPin.visibility = if (bean.pinned) View.VISIBLE else View.INVISIBLE

itemView.background =
ColorManager.getDrawable(
"long_text_back_color",
"key_border",
"key_long_text_border",
"round_corner",
null,
)

itemView.background = mBackground
if (!this@FlexibleAdapter::listener.isInitialized) return
// 如果设置了回调,则设置点击事件
if (this@FlexibleAdapter::listener.isInitialized) {
itemView.setOnClickListener {
listener.onPaste(bean)
}
itemView.setOnLongClickListener {
val menu = PopupMenu(it.context, it)
val scope = it.findViewTreeLifecycleOwner()!!.lifecycleScope
menu.menu.apply {
add(R.string.edit).apply {
setIcon(R.drawable.ic_baseline_edit_24)
itemView.setOnClickListener {
listener.onPaste(bean)
}
itemView.setOnLongClickListener {
val menu = PopupMenu(it.context, it)
val scope = it.findViewTreeLifecycleOwner()!!.lifecycleScope
menu.menu.apply {
add(R.string.edit).apply {
setIcon(R.drawable.ic_baseline_edit_24)
setOnMenuItemClickListener {
scope.launch {
listener.onEdit(bean)
}
true
}
}
if (bean.pinned) {
add(R.string.simple_key_unpin).apply {
setIcon(R.drawable.ic_outline_push_pin_24)
setOnMenuItemClickListener {
scope.launch {
listener.onEdit(bean)
listener.onUnpin(bean)
setPinStatus(bean.id, false)
}
true
}
}
if (bean.pinned) {
add(R.string.simple_key_unpin).apply {
setIcon(R.drawable.ic_outline_push_pin_24)
setOnMenuItemClickListener {
scope.launch {
listener.onUnpin(bean)
setPinStatus(bean.id, false)
}
true
} else {
add(R.string.simple_key_pin).apply {
setIcon(R.drawable.ic_baseline_push_pin_24)
setOnMenuItemClickListener {
scope.launch {
listener.onPin(bean)
setPinStatus(bean.id, true)
}
true
}
} else {
add(R.string.simple_key_pin).apply {
setIcon(R.drawable.ic_baseline_push_pin_24)
setOnMenuItemClickListener {
scope.launch {
listener.onPin(bean)
setPinStatus(bean.id, true)
}
true
}
}
}
if (listener.showCollectButton) {
add(R.string.collect).apply {
setIcon(R.drawable.ic_baseline_star_24)
setOnMenuItemClickListener {
scope.launch { CollectionHelper.insert(DatabaseBean(text = bean.text)) }
true
}
}
if (listener.showCollectButton) {
add(R.string.collect).apply {
setIcon(R.drawable.ic_baseline_star_24)
setOnMenuItemClickListener {
scope.launch { CollectionHelper.insert(DatabaseBean(text = bean.text)) }
true
}
}

add(R.string.delete).apply {
setIcon(R.drawable.ic_baseline_delete_24)
setOnMenuItemClickListener {
scope.launch {
listener.onDelete(bean)
delete(bean.id)
}
true
}
add(R.string.delete).apply {
setIcon(R.drawable.ic_baseline_delete_24)
}
if (beans.isNotEmpty()) {
add(R.string.delete_all).apply {
setIcon(R.drawable.ic_baseline_delete_sweep_24)
setOnMenuItemClickListener {
scope.launch {
listener.onDelete(bean)
delete(bean.id)
askToDeleteAll()
}
true
}
}
if (beans.isNotEmpty()) {
add(R.string.delete_all).apply {
setIcon(R.drawable.ic_baseline_delete_sweep_24)
setOnMenuItemClickListener {
scope.launch {
askToDeleteAll()
}
true
}
}
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
menu.setForceShowIcon(true)
}
menu.show()
true
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
menu.setForceShowIcon(true)
}
menu.show()
true
}
}
}
Expand Down
Loading

0 comments on commit 6014451

Please sign in to comment.