絵文字ピッカーのレイアウト改善

This commit is contained in:
tateisu 2023-02-23 06:59:35 +09:00
parent 7dae622f70
commit 5e0a44a321
3 changed files with 109 additions and 43 deletions

View File

@ -10,8 +10,10 @@ import android.view.*
import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatButton
import androidx.appcompat.widget.AppCompatImageButton
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
@ -25,7 +27,6 @@ import jp.juggler.subwaytooter.databinding.EmojiPickerDialogBinding
import jp.juggler.subwaytooter.emoji.*
import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.subwaytooter.pref.PrefS
import jp.juggler.subwaytooter.span.EmojiImageSpan
import jp.juggler.subwaytooter.span.NetworkEmojiSpan
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.emojiSizeMode
@ -37,6 +38,7 @@ import jp.juggler.util.coroutine.launchAndShowError
import jp.juggler.util.data.*
import jp.juggler.util.log.*
import jp.juggler.util.ui.*
import org.jetbrains.anko.image
import org.jetbrains.anko.wrapContent
import kotlin.math.abs
import kotlin.math.sign
@ -72,11 +74,16 @@ private class EmojiPicker(
private open class PickerItemCategory(
var name: String,
val category: EmojiCategory,
val original: PickerItemCategory? = null,
var next: PickerItemCategory? = null,
) : PickerItem {
val items = ArrayList<PickerItem>()
open fun createFiltered(keywordLower: String?) =
PickerItemCategory(name = name, category = category).also { dst ->
PickerItemCategory(
name = name,
category = category,
original = this,
).also { dst ->
dst.items.addAll(
if (keywordLower.isNullOrEmpty()) {
items
@ -192,24 +199,63 @@ private class EmojiPicker(
abstract fun bind(item: PickerItem)
}
private var lastExpandCategory: PickerItemCategory? = null
private var canCollapse = true
private inner class VhCategory(
view: FrameLayout = FrameLayout(activity),
view: LinearLayout = LinearLayout(activity),
) : ViewHolderBase(view) {
var lastItem: PickerItemCategory? = null
val ibExpand = AppCompatImageButton(activity).apply {
layoutParams = LinearLayout.LayoutParams(gridSize, matchParent)
background = ContextCompat.getDrawable(
this@EmojiPicker.activity,
R.drawable.btn_bg_transparent_round6dp
)
setOnClickListener {
val orig = lastItem?.original
?: return@setOnClickListener
lastExpandCategory = if (lastExpandCategory != orig) orig else lastItem?.next
// 再表示
showFiltered(
lastSelectedCategory,
lastSelectedKeyword,
scrollCategoryTab = false,
scrollToCategory = true,
)
}
}
val tv = AppCompatTextView(activity).apply {
layoutParams = FrameLayout.LayoutParams(headerWidth, gridSize)
layoutParams = LinearLayout.LayoutParams(0, wrapContent).apply {
weight = 1f
}
minHeightCompat = (density * 48f + 0.5f).toInt()
gravity = Gravity.START or Gravity.CENTER_VERTICAL
includeFontPadding = false
}
init {
view.layoutParams = RecyclerView.LayoutParams(wrapContent, wrapContent)
view.layoutParams = RecyclerView.LayoutParams(headerWidth, wrapContent)
view.setPadding(cellMargin, cellMargin, cellMargin, cellMargin)
view.isBaselineAligned = false
view.gravity = Gravity.START or Gravity.CENTER_VERTICAL
view.addView(ibExpand)
view.addView(tv)
}
override fun bind(item: PickerItem) {
if (item is PickerItemCategory) {
lastItem = item
tv.text = item.name
ibExpand.vg(canCollapse)?.let {
val drawableId = when (lastExpandCategory == item.original) {
true -> R.drawable.ic_arrow_drop_down
else -> R.drawable.ic_arrow_drop_up
}
it.image = ContextCompat.getDrawable(activity, drawableId)
}
}
}
}
@ -564,27 +610,53 @@ private class EmojiPicker(
private fun showFiltered(
selectedCategory: EmojiCategory?,
selectedKeyword: String?,
scrollCategoryTab: Boolean = true,
scrollToCategory: Boolean = false,
) {
lastSelectedCategory = selectedCategory
lastSelectedKeyword = selectedKeyword
adapter.list = buildList {
val keywordLower = selectedKeyword?.lowercase()?.trim()
pickerCategries.filter {
this.canCollapse =
keywordLower.isNullOrEmpty() && (selectedCategory == null || selectedCategory == EmojiCategory.Custom)
val list = buildList {
val filteredCategories = pickerCategries.filter {
selectedCategory == null || it.category == selectedCategory
}.mapNotNull { category ->
category.createFiltered(keywordLower)
.takeIf { it.items.isNotEmpty() }
}.forEach {
if (it.category == EmojiCategory.Custom || selectedCategory == null) add(it)
}
for (i in filteredCategories.indices) {
filteredCategories[i].next =
filteredCategories.elementAtOrNull(i + 1)?.original
?: filteredCategories.elementAtOrNull(i - 1)?.original
}
if (lastExpandCategory == null ||
filteredCategories.none { it.original == lastExpandCategory }
) lastExpandCategory = filteredCategories.firstOrNull()?.original
filteredCategories.forEach {
if (selectedCategory == null || it.category == EmojiCategory.Custom) {
// 見出し付き表示の場合は折りたたむ可能性がある
add(it)
if (!canCollapse || lastExpandCategory == it.original) addAll(it.items)
} else {
// カスタム以外のカテゴリが選択されている場合、(ヘッダがなく解除できないので) 折りたたみはできない
addAll(it.items)
// val mod = it.items.size % gridCols
// if (mod > 0) {
// repeat(gridCols - mod) {
// add(PickerItemSpace)
// }
// }
}
}
}
adapter.list = list
if (scrollToCategory) {
val idx =
list.indexOfFirst { (it as? PickerItemCategory)?.original == lastExpandCategory }
if (idx != -1) {
views.rvGrid.smoothScrollToPosition(idx)
}
}
for (it in views.llCategories.children) {
val backgroundId = when (it.tag) {
selectedCategory -> R.drawable.bg_button_cw
@ -592,7 +664,7 @@ private class EmojiPicker(
}
it.background = ContextCompat.getDrawable(it.context, backgroundId)
if (it.tag == selectedCategory) {
if (it.tag == selectedCategory && scrollCategoryTab) {
val oldScrollX = views.svCategories.scrollX
val visibleWidth = views.svCategories.width
log.i("left=${it.left},r=${it.right},s=$oldScrollX")

View File

@ -1,7 +1,6 @@
package jp.juggler.subwaytooter.util
import android.graphics.RectF
import androidx.collection.LruCache
import jp.juggler.subwaytooter.api.entity.TootInstance
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.util.log.LogCategory
@ -35,8 +34,7 @@ class EmojiImageRect(
companion object {
private val log = LogCategory("EmojiImageRect")
// ImageWidthCache
val imageAspectCache = LruCache<String, Float>(1024)
val imageAspectCache = HashMap<String, Float>()
}
val rectDst = RectF()
@ -44,7 +42,7 @@ class EmojiImageRect(
var emojiHeight = 0f
var transY = 0f
private var lastWidth: Float? = null
var lastWidth: Float? = null
/**
* lastAspect に基づいて rectDst transY を更新する
@ -71,9 +69,7 @@ class EmojiImageRect(
) {
this.emojiHeight = h
val aspect = when (aspectArg) {
null -> {
imageAspectCache[url] ?: 1f
}
null -> imageAspectCache[url] ?: 1f
else -> {
imageAspectCache.put(url, aspectArg)
aspectArg

View File

@ -60,6 +60,7 @@ class NetworkEmojiView(
lp.width = w
lp.height = h
layoutParams = lp
requestLayout()
},
)
@ -72,20 +73,17 @@ class NetworkEmojiView(
this.url = url
mPaint.isFilterBitmap = true
invalidate()
if (url != null && initialAspect != null) {
emojiImageRect.lastWidth = null
emojiImageRect.updateRect(
url = url,
url = url ?: "",
aspectArg = initialAspect,
h = defaultHeight.toFloat(),
// may call layout callback
)
} else {
val lp = layoutParams
lp.width = defaultWidth
lp.height = defaultHeight
lp.width = (emojiImageRect.emojiWidth + 0.5f).toInt()
lp.height = (emojiImageRect.emojiHeight + 0.5f).toInt()
layoutParams = lp
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)