字体选择器 回调更新
This commit is contained in:
parent
8dc7d2072f
commit
ce45b71448
|
|
@ -14,17 +14,18 @@ class FontSetView @JvmOverloads constructor(
|
|||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
companion object {
|
||||
private var GAP = 2
|
||||
private const val GAP = 2
|
||||
private const val MIN_VALUE = 12
|
||||
private const val MAX_VALUE = 20
|
||||
}
|
||||
|
||||
private var mBinding: LayoutFontSetViewBinding
|
||||
private var mBinding: LayoutFontSetViewBinding =
|
||||
LayoutFontSetViewBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
|
||||
private var mFontValue = 16 // 12, 14, 16, 18, 20
|
||||
private val mFontMinValue = 16
|
||||
private val mFontMaxValue = 20
|
||||
|
||||
private var mFontValue = MIN_VALUE + GAP * 2
|
||||
|
||||
init {
|
||||
mBinding = LayoutFontSetViewBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
setupClickListeners()
|
||||
}
|
||||
|
||||
|
|
@ -33,48 +34,33 @@ class FontSetView @JvmOverloads constructor(
|
|||
|
||||
private fun setupClickListeners() {
|
||||
with (mBinding) {
|
||||
ivFontPlus.setOnClickListener {
|
||||
if (mFontValue > mFontMinValue) {
|
||||
ivFontMinus.setOnClickListener {
|
||||
if (mFontValue > MIN_VALUE) {
|
||||
mFontValue -= GAP
|
||||
}
|
||||
tvFontValue.text = mFontValue.toString()
|
||||
levelSeekbar.setLevel((mFontValue - 16)/GAP)
|
||||
levelSeekbar.setLevel((mFontValue - MIN_VALUE)/GAP)
|
||||
}
|
||||
|
||||
ivFontAdd.setOnClickListener {
|
||||
if (mFontValue < mFontMinValue) {
|
||||
if (mFontValue < MAX_VALUE) {
|
||||
mFontValue += GAP
|
||||
}
|
||||
tvFontValue.text = mFontValue.toString()
|
||||
levelSeekbar.setLevel((mFontValue - 16)/GAP)
|
||||
levelSeekbar.setLevel((mFontValue - MIN_VALUE)/GAP)
|
||||
}
|
||||
|
||||
levelSeekbar.setOnLevelChangeListener(object : LevelSeekBar.OnLevelChangeListener {
|
||||
override fun onLevelChanged(
|
||||
seekBar: LevelSeekBar,
|
||||
level: Int,
|
||||
fromUser: Boolean
|
||||
) {
|
||||
levelSeekbar.setOnLevelChangeListener { level->
|
||||
mFontValue = 16 + level * GAP
|
||||
if (mFontValue > mFontMaxValue) {
|
||||
mFontValue = mFontMaxValue
|
||||
if (mFontValue > MAX_VALUE) {
|
||||
mFontValue = MAX_VALUE
|
||||
}
|
||||
if (mFontValue < mFontMinValue) {
|
||||
mFontValue = mFontMinValue
|
||||
if (mFontValue < MIN_VALUE) {
|
||||
mFontValue = MIN_VALUE
|
||||
}
|
||||
tvFontValue.text = mFontValue.toString()
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: LevelSeekBar) {
|
||||
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: LevelSeekBar) {
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,14 +12,15 @@ import com.remax.visualnovel.utils.spannablex.utils.dp
|
|||
import androidx.core.content.withStyledAttributes
|
||||
|
||||
|
||||
|
||||
class LevelSeekBar @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : View(context, attrs, defStyleAttr) {
|
||||
|
||||
private var totalLevels = 5
|
||||
private var currentLevel = 2
|
||||
private var mTotalLevel = 5
|
||||
private var mCurLevel = 2
|
||||
|
||||
// 尺寸
|
||||
private var trackHeight = ResUtil.getPixelSize(R.dimen.dp_5).toFloat()
|
||||
|
|
@ -46,32 +47,25 @@ class LevelSeekBar @JvmOverloads constructor(
|
|||
|
||||
// 监听器
|
||||
private var onLevelChangeListener: OnLevelChangeListener? = null
|
||||
private var isDragging = false
|
||||
|
||||
|
||||
// 触摸相关
|
||||
private var lastTouchX = 0f
|
||||
|
||||
|
||||
|
||||
interface OnLevelChangeListener {
|
||||
fun onLevelChanged(seekBar: LevelSeekBar, level: Int, fromUser: Boolean)
|
||||
fun onStartTrackingTouch(seekBar: LevelSeekBar)
|
||||
fun onStopTrackingTouch(seekBar: LevelSeekBar)
|
||||
fun onLevelChanged(level: Int)
|
||||
}
|
||||
|
||||
init {
|
||||
setupAttributes(attrs)
|
||||
setupPaints()
|
||||
setBackgroundResource(R.color.red_ff3b30)
|
||||
}
|
||||
|
||||
private fun setupAttributes(attrs: AttributeSet?) {
|
||||
attrs?.let {
|
||||
context.withStyledAttributes(it, R.styleable.CustomLevelSeekBar) {
|
||||
totalLevels = getInt(R.styleable.CustomLevelSeekBar_totalLevels, totalLevels)
|
||||
currentLevel = getInt(R.styleable.CustomLevelSeekBar_currentLevel, currentLevel)
|
||||
.coerceIn(0, totalLevels - 1)
|
||||
mTotalLevel = getInt(R.styleable.CustomLevelSeekBar_totalLevels, mTotalLevel)
|
||||
mCurLevel = getInt(R.styleable.CustomLevelSeekBar_currentLevel, mCurLevel)
|
||||
.coerceIn(0, mTotalLevel - 1)
|
||||
|
||||
trackColor = getColor(R.styleable.CustomLevelSeekBar_trackColor, trackColor)
|
||||
activeTrackColor =
|
||||
|
|
@ -118,124 +112,94 @@ class LevelSeekBar @JvmOverloads constructor(
|
|||
}
|
||||
|
||||
private fun drawNodes(canvas: Canvas) {
|
||||
if (totalLevels <= 1) return
|
||||
if (mTotalLevel <= 1) return
|
||||
|
||||
val centerY = height / 2f
|
||||
|
||||
for (i in 0 until totalLevels) {
|
||||
for (i in 0 until mTotalLevel) {
|
||||
val x = calculatePositionForLevel(i)
|
||||
|
||||
nodePaint.color = if (i <= currentLevel) activeNodeColor else nodeColor
|
||||
//canvas.drawCircle(x, centerY, nodeRadius, nodePaint)
|
||||
val trackRect = RectF(x - nodeWidth/2, centerY - nodeHeight/2 + 6, x + nodeWidth/2, centerY + nodeHeight/2)
|
||||
nodePaint.color = if (i <= mCurLevel) activeNodeColor else nodeColor
|
||||
val trackRect = RectF(x - nodeWidth/2, centerY - nodeHeight/2, x + nodeWidth/2, centerY + nodeHeight/2)
|
||||
canvas.drawRoundRect(trackRect, trackEndRadius, trackEndRadius, nodePaint)
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawThumb(canvas: Canvas) {
|
||||
if (totalLevels <= 1) return
|
||||
if (mTotalLevel <= 1) return
|
||||
|
||||
val centerY = height / 2f
|
||||
val thumbX = calculatePositionForLevel(currentLevel)
|
||||
val thumbX = calculatePositionForLevel(mCurLevel)
|
||||
|
||||
canvas.drawCircle(thumbX, centerY, thumbRadius - 1F.dp.toFloat(), thumbPaint)
|
||||
}
|
||||
|
||||
private fun calculatePositionForLevel(level: Int): Float {
|
||||
if (totalLevels <= 1) return width / 2f
|
||||
if (mTotalLevel <= 1) return width / 2f
|
||||
|
||||
val availableWidth = width - 2 * thumbRadius
|
||||
return thumbRadius + (availableWidth * level.toFloat() / (totalLevels - 1))
|
||||
return thumbRadius + (availableWidth * level.toFloat() / (mTotalLevel - 1))
|
||||
}
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
if (isPointInThumb(event.x, event.y) || isPointInTrack(event.x, event.y)) {
|
||||
isDragging = true
|
||||
lastTouchX = event.x
|
||||
onLevelChangeListener?.onStartTrackingTouch(this)
|
||||
handleTouch(event.x)
|
||||
parent?.requestDisallowInterceptTouchEvent(true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (isDragging) {
|
||||
lastTouchX = event.x
|
||||
handleTouch(event.x)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
if (isDragging) {
|
||||
isDragging = false
|
||||
snapToNearestLevel(lastTouchX)
|
||||
onLevelChangeListener?.onStopTrackingTouch(this)
|
||||
parent?.requestDisallowInterceptTouchEvent(false)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onTouchEvent(event)
|
||||
}
|
||||
|
||||
private fun isPointInThumb(x: Float, y: Float): Boolean {
|
||||
val thumbX = calculatePositionForLevel(currentLevel)
|
||||
val centerY = height / 2f
|
||||
val distance = Math.sqrt(
|
||||
(x - thumbX) * (x - thumbX) + (y - centerY) * (y - centerY).toDouble()
|
||||
)
|
||||
return distance <= thumbRadius
|
||||
}
|
||||
|
||||
private fun isPointInTrack(x: Float, y: Float): Boolean {
|
||||
val centerY = height / 2f
|
||||
val trackTop = centerY - trackHeight / 2 - thumbRadius // 扩大触摸区域
|
||||
val trackBottom = centerY + trackHeight / 2 + thumbRadius
|
||||
return x in 0f..width.toFloat() && y in trackTop..trackBottom
|
||||
}
|
||||
|
||||
private fun handleTouch(x: Float) {
|
||||
if (totalLevels <= 1) return
|
||||
if (mTotalLevel <= 1) return
|
||||
|
||||
val newLevel = calculateLevelForPosition(x)
|
||||
if (newLevel != currentLevel) {
|
||||
currentLevel = newLevel
|
||||
if (newLevel != mCurLevel) {
|
||||
mCurLevel = newLevel
|
||||
invalidate()
|
||||
onLevelChangeListener?.onLevelChanged(this, currentLevel, true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun snapToNearestLevel(x: Float) {
|
||||
if (totalLevels <= 1) return
|
||||
if (mTotalLevel <= 1) return
|
||||
|
||||
val exactLevel = calculateExactLevelForPosition(x)
|
||||
val newLevel = (exactLevel + 0.5f).toInt().coerceIn(0, totalLevels - 1)
|
||||
val newLevel = (exactLevel + 0.5f).toInt().coerceIn(0, mTotalLevel - 1)
|
||||
|
||||
if (newLevel != currentLevel) {
|
||||
currentLevel = newLevel
|
||||
mCurLevel = newLevel
|
||||
invalidate()
|
||||
onLevelChangeListener?.onLevelChanged(this, currentLevel, true)
|
||||
}
|
||||
onLevelChangeListener?.onLevelChanged(mCurLevel)
|
||||
}
|
||||
|
||||
private fun calculateLevelForPosition(x: Float): Int {
|
||||
if (totalLevels <= 1) return 0
|
||||
if (mTotalLevel <= 1) return 0
|
||||
|
||||
val availableWidth = width - 2 * thumbRadius
|
||||
val progress = ((x - thumbRadius) / availableWidth).coerceIn(0f, 1f)
|
||||
return (progress * (totalLevels - 1)).toInt().coerceIn(0, totalLevels - 1)
|
||||
return (progress * (mTotalLevel - 1)).toInt().coerceIn(0, mTotalLevel - 1)
|
||||
}
|
||||
|
||||
private fun calculateExactLevelForPosition(x: Float): Float {
|
||||
if (totalLevels <= 1) return 0f
|
||||
if (mTotalLevel <= 1) return 0f
|
||||
|
||||
val availableWidth = width - 2 * thumbRadius
|
||||
val progress = ((x - thumbRadius) / availableWidth).coerceIn(0f, 1f)
|
||||
return progress * (totalLevels - 1)
|
||||
return progress * (mTotalLevel - 1)
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -243,45 +207,32 @@ class LevelSeekBar @JvmOverloads constructor(
|
|||
|
||||
//---------------------------- public 设置方法 ---------------------------------//
|
||||
fun setLevel(level: Int, fromUser: Boolean = false) {
|
||||
val newLevel = level.coerceIn(0, totalLevels - 1)
|
||||
if (newLevel != currentLevel) {
|
||||
currentLevel = newLevel
|
||||
val newLevel = level.coerceIn(0, mTotalLevel - 1)
|
||||
if (newLevel != mCurLevel) {
|
||||
mCurLevel = newLevel
|
||||
invalidate()
|
||||
onLevelChangeListener?.onLevelChanged(this, currentLevel, fromUser)
|
||||
}
|
||||
}
|
||||
|
||||
fun getLevel(): Int = currentLevel
|
||||
fun getLevel(): Int = mCurLevel
|
||||
|
||||
fun setTotalLevels(levels: Int) {
|
||||
if (levels > 0 && levels != totalLevels) {
|
||||
totalLevels = levels
|
||||
currentLevel = currentLevel.coerceIn(0, totalLevels - 1)
|
||||
if (levels > 0 && levels != mTotalLevel) {
|
||||
mTotalLevel = levels
|
||||
mCurLevel = mCurLevel.coerceIn(0, mTotalLevel - 1)
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
fun getTotalLevels(): Int = totalLevels
|
||||
|
||||
fun setOnLevelChangeListener(listener: OnLevelChangeListener) {
|
||||
this.onLevelChangeListener = listener
|
||||
}
|
||||
fun getTotalLevels(): Int = mTotalLevel
|
||||
|
||||
|
||||
fun setOnLevelChangeListener(
|
||||
onLevelChanged: (LevelSeekBar, Int, Boolean) -> Unit = { _, _, _ -> },
|
||||
onStartTrackingTouch: (LevelSeekBar) -> Unit = {},
|
||||
onStopTrackingTouch: (LevelSeekBar) -> Unit = {}
|
||||
onLevelChanged: (Int) -> Unit
|
||||
) {
|
||||
this.onLevelChangeListener = object : OnLevelChangeListener {
|
||||
override fun onLevelChanged(seekBar: LevelSeekBar, level: Int, fromUser: Boolean) {
|
||||
onLevelChanged(seekBar, level, fromUser)
|
||||
}
|
||||
override fun onStartTrackingTouch(seekBar: LevelSeekBar) {
|
||||
onStartTrackingTouch(seekBar)
|
||||
}
|
||||
override fun onStopTrackingTouch(seekBar: LevelSeekBar) {
|
||||
onStopTrackingTouch(seekBar)
|
||||
override fun onLevelChanged(level: Int) {
|
||||
onLevelChanged(level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,14 +38,14 @@ class MaxNumView @JvmOverloads constructor(
|
|||
private fun setupClickListeners() {
|
||||
with (mBinding) {
|
||||
ivLeftIcon.setOnClickListener {
|
||||
mCurIndex = mCurIndex.takeUnless { it > 0 }?.minus(1) ?: mCurIndex
|
||||
mCurIndex = mCurIndex.takeIf { it > 0 }?.minus(1) ?: mCurIndex
|
||||
mCurValue = mFixedValueList[mCurIndex]
|
||||
tvCenter.text = mCurValue.toString()
|
||||
mEventListener?.onValueChanged(mCurValue)
|
||||
}
|
||||
|
||||
ivRightIcon.setOnClickListener {
|
||||
mCurIndex = mCurIndex.takeUnless { it < mFixedValueList.size }?.plus(1) ?: mCurIndex
|
||||
mCurIndex = mCurIndex.takeIf { it < mFixedValueList.size - 1 }?.plus(1) ?: mCurIndex
|
||||
mCurValue = mFixedValueList[mCurIndex]
|
||||
tvCenter.text = mCurValue.toString()
|
||||
mEventListener?.onValueChanged(mCurValue)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
android:layout_toEndOf="@+id/left_container"
|
||||
android:layout_marginTop="@dimen/dp_10">
|
||||
<com.remax.visualnovel.widget.uitoken.view.UITokenImageView
|
||||
android:id="@+id/iv_font_plus"
|
||||
android:id="@+id/iv_font_minus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_20"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@id/iv_font_plus"
|
||||
android:layout_toEndOf="@id/iv_font_minus"
|
||||
android:layout_toStartOf="@+id/iv_font_add"
|
||||
android:layout_marginHorizontal="@dimen/dp_10"
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in New Issue