feature(live-room-like-heart): 롱프레스 왕하트 애니메이션 변경
- 기존: 하트가 33.3dp 부터 커지는 애니메이션 - 변경: 하트가 133.3dp으로 고정되어 있고 물 채우기 애니메이션
This commit is contained in:
@@ -232,6 +232,8 @@ dependencies {
|
||||
|
||||
implementation 'io.github.glailton.expandabletextview:expandabletextview:1.0.4'
|
||||
|
||||
implementation 'com.github.orbitalsonic:Sonic-Water-Wave-Animation:2.0.1'
|
||||
|
||||
// ----- Test dependencies -----
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.mockito:mockito-core:5.20.0'
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.text.method.LinkMovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
@@ -38,6 +37,7 @@ import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@@ -49,6 +49,7 @@ import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.google.gson.Gson
|
||||
import com.orbitalsonic.waterwave.WaterWaveView
|
||||
import com.orhanobut.logger.Logger
|
||||
import io.agora.rtc2.ClientRoleOptions
|
||||
import io.agora.rtc2.IRtcEngineEventHandler
|
||||
@@ -1869,7 +1870,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
}
|
||||
}
|
||||
|
||||
private var longPressCenterHeart: ImageView? = null
|
||||
private var longPressCenterHeart: WaterWaveView? = null
|
||||
|
||||
// 롱프레스 상태/파라미터
|
||||
private var isLongPressTriggered = false
|
||||
@@ -1881,18 +1882,13 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
private val longPressDurationMs = 2000L
|
||||
private val centerHeartShowDelayMs = 500L
|
||||
|
||||
private val longPressScaleDurationMs get() = longPressDurationMs
|
||||
private val longPressScaleStart = 1.0f
|
||||
private val longPressScaleMax = 4.0f
|
||||
|
||||
// 0.5초 후 중앙 하트 표시 및 스케일 업데이트 시작을 위한 러너블
|
||||
// 0.5초 후 중앙 하트 표시 및 물 채우기(progress) 업데이트 시작을 위한 러너블
|
||||
private val showCenterHeartRunnable = Runnable {
|
||||
if (!isTrackingLongPress) return@Runnable
|
||||
if (isLongPressBlockedByAvailability) return@Runnable
|
||||
if (longPressCenterHeart != null) return@Runnable
|
||||
showCenterHeartForLongPress()
|
||||
longPressVisualStartTime = System.currentTimeMillis()
|
||||
updateCenterHeartScale(longPressScaleStart)
|
||||
handler.post(longPressScaleUpdateRunnable)
|
||||
}
|
||||
|
||||
@@ -1905,18 +1901,29 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
if (isLongPressBlockedByAvailability) return
|
||||
val elapsed = System.currentTimeMillis() - pressStartTime
|
||||
|
||||
// 스케일은 더 빠르게 커지도록 별도의 duration 기준으로 계산(최대치 도달 후 유지)
|
||||
val scaleFraction = (elapsed.coerceAtMost(longPressScaleDurationMs)
|
||||
.toFloat() / longPressScaleDurationMs.toFloat())
|
||||
val scale =
|
||||
longPressScaleStart + (longPressScaleMax - longPressScaleStart) * scaleFraction
|
||||
updateCenterHeartScale(scale)
|
||||
// 경과 시간 기준으로 물 채우기(progress) 계산 (0..100)
|
||||
val progressFraction = (elapsed.coerceAtMost(longPressDurationMs)
|
||||
.toFloat() / longPressDurationMs.toFloat())
|
||||
val progress = (progressFraction * 100f).toInt()
|
||||
longPressCenterHeart?.let { heartView ->
|
||||
try {
|
||||
heartView.progress = progress
|
||||
} catch (_: Throwable) {
|
||||
// 일부 버전에서 setter 명이 다를 수 있어 안전 처리
|
||||
}
|
||||
}
|
||||
|
||||
// 2초 유지 시 트리거 실행
|
||||
if (elapsed >= longPressDurationMs && !isLongPressTriggered) {
|
||||
isLongPressTriggered = true
|
||||
isTrackingLongPress = false
|
||||
removeCenterHeartForLongPress(withFade = true)
|
||||
// 최종 100% 보정
|
||||
longPressCenterHeart?.let { hv ->
|
||||
try {
|
||||
hv.progress = 100
|
||||
} catch (_: Throwable) {
|
||||
}
|
||||
}
|
||||
triggerBigHeartDonation()
|
||||
return
|
||||
}
|
||||
@@ -1994,26 +2001,22 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
|
||||
private fun showCenterHeartForLongPress() {
|
||||
if (longPressCenterHeart != null) return
|
||||
val size = 33.3f.dpToPx().toInt()
|
||||
val lp = FrameLayout.LayoutParams(size, size).apply { gravity = Gravity.CENTER }
|
||||
val heart = ImageView(this).apply {
|
||||
setImageResource(R.drawable.ic_heart_pink)
|
||||
layoutParams = lp
|
||||
scaleX = 1f
|
||||
scaleY = 1f
|
||||
alpha = 1f
|
||||
val heart = binding.heartWave
|
||||
heart.setBorderColor("#ff959a".toColorInt())
|
||||
heart.setFrontWaveColor("#ff959a".toColorInt())
|
||||
heart.setBehindWaveColor("#ff959a".toColorInt())
|
||||
val elapsed = System.currentTimeMillis() - pressStartTime
|
||||
val progress = ((elapsed.coerceAtMost(longPressDurationMs)
|
||||
.toFloat() / longPressDurationMs.toFloat()) * 100f).toInt()
|
||||
try {
|
||||
heart.progress = progress
|
||||
} catch (_: Throwable) {
|
||||
}
|
||||
binding.flRoot.addView(heart)
|
||||
heart.alpha = 1f
|
||||
heart.visibility = View.VISIBLE
|
||||
longPressCenterHeart = heart
|
||||
}
|
||||
|
||||
private fun updateCenterHeartScale(scale: Float) {
|
||||
longPressCenterHeart?.let {
|
||||
it.scaleX = scale
|
||||
it.scaleY = scale
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeCenterHeartForLongPress(withFade: Boolean = false) {
|
||||
val heart = longPressCenterHeart ?: return
|
||||
longPressCenterHeart = null
|
||||
@@ -2021,10 +2024,21 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
heart.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(150L)
|
||||
.withEndAction { binding.flRoot.removeView(heart) }
|
||||
.withEndAction {
|
||||
heart.visibility = View.GONE
|
||||
try {
|
||||
heart.progress = 0
|
||||
} catch (_: Throwable) {
|
||||
}
|
||||
}
|
||||
.start()
|
||||
} else {
|
||||
binding.flRoot.removeView(heart)
|
||||
heart.alpha = 1f
|
||||
heart.visibility = View.GONE
|
||||
try {
|
||||
heart.progress = 0
|
||||
} catch (_: Throwable) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -758,4 +758,17 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.orbitalsonic.waterwave.WaterWaveView
|
||||
android:id="@+id/heart_wave"
|
||||
android:layout_width="133.3dp"
|
||||
android:layout_height="133.3dp"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
app:max="100"
|
||||
app:progress="0"
|
||||
app:shapeType="heart"
|
||||
app:textHidden="true"
|
||||
app:textColor="@color/white" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
Reference in New Issue
Block a user