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