feat: 标记待办为已完成后的彩带庆祝
This commit is contained in:
parent
70a1febce3
commit
0e54c94572
5 changed files with 124 additions and 4 deletions
|
|
@ -6,8 +6,11 @@ import androidx.activity.compose.setContent
|
|||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import cn.super12138.todo.ui.components.Konfetti
|
||||
import cn.super12138.todo.ui.navigation.ToDoNavigation
|
||||
import cn.super12138.todo.ui.theme.ToDoTheme
|
||||
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
@ -16,7 +19,10 @@ class MainActivity : ComponentActivity() {
|
|||
setContent {
|
||||
ToDoTheme {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
ToDoNavigation()
|
||||
val mainViewModel: MainViewModel = viewModel()
|
||||
val showConfetti = mainViewModel.showConfetti
|
||||
ToDoNavigation(viewModel = mainViewModel)
|
||||
Konfetti(state = showConfetti)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
106
app/src/main/kotlin/cn/super12138/todo/ui/components/Konfetti.kt
Normal file
106
app/src/main/kotlin/cn/super12138/todo/ui/components/Konfetti.kt
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
package cn.super12138.todo.ui.components
|
||||
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import nl.dionsegijn.konfetti.compose.KonfettiView
|
||||
import nl.dionsegijn.konfetti.compose.OnParticleSystemUpdateListener
|
||||
import nl.dionsegijn.konfetti.core.Angle
|
||||
import nl.dionsegijn.konfetti.core.Party
|
||||
import nl.dionsegijn.konfetti.core.PartySystem
|
||||
import nl.dionsegijn.konfetti.core.Position
|
||||
import nl.dionsegijn.konfetti.core.Spread
|
||||
import nl.dionsegijn.konfetti.core.emitter.Emitter
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* 参照于:https://github.com/hushenghao/AndroidEasterEggs/blob/main/app/src/main/java/com/dede/android_eggs/views/main/compose/Konfetti.kt
|
||||
*/
|
||||
|
||||
@Composable
|
||||
fun Konfetti(
|
||||
state: MutableState<Boolean>,
|
||||
primary: Color = MaterialTheme.colorScheme.primary,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
var visible by state
|
||||
if (!visible) {
|
||||
return
|
||||
}
|
||||
val listener = remember(state) {
|
||||
object : OnParticleSystemUpdateListener {
|
||||
override fun onParticleSystemEnded(system: PartySystem, activeSystems: Int) {
|
||||
if (activeSystems == 0)
|
||||
visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
KonfettiView(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(modifier),
|
||||
parties = remember { particles(primary.toArgb()) },
|
||||
updateListener = listener
|
||||
)
|
||||
}
|
||||
|
||||
private val defaultColors = listOf(
|
||||
0xFCE18A,
|
||||
0x009688,
|
||||
0xFF726D,
|
||||
0xF4306D,
|
||||
0xB48DEF,
|
||||
0x95FF82,
|
||||
0x82ECFF,
|
||||
0xFF9800,
|
||||
0x0E008A,
|
||||
)
|
||||
|
||||
private const val colorBlendFraction = 0.3f
|
||||
|
||||
private fun particles(primary: Int) = listOf(
|
||||
Party(
|
||||
speed = 0f,
|
||||
maxSpeed = 12f,
|
||||
damping = 0.9f,
|
||||
angle = Angle.BOTTOM,
|
||||
spread = Spread.ROUND,
|
||||
colors = defaultColors.map { it.blend(primary, colorBlendFraction) },
|
||||
emitter = Emitter(duration = 2, TimeUnit.SECONDS).perSecond(100),
|
||||
position = Position.Relative(0.0, 0.0).between(Position.Relative(1.0, 0.0)),
|
||||
),
|
||||
Party(
|
||||
speed = 10f,
|
||||
maxSpeed = 30f,
|
||||
damping = 0.9f,
|
||||
angle = Angle.RIGHT - 55,
|
||||
spread = 60,
|
||||
colors = defaultColors.map { it.blend(primary, colorBlendFraction) },
|
||||
emitter = Emitter(duration = 2, TimeUnit.SECONDS).perSecond(100),
|
||||
position = Position.Relative(0.0, 1.0)
|
||||
),
|
||||
Party(
|
||||
speed = 10f,
|
||||
maxSpeed = 30f,
|
||||
damping = 0.9f,
|
||||
angle = Angle.RIGHT - 125,
|
||||
spread = 60,
|
||||
colors = defaultColors.map { it.blend(primary, colorBlendFraction) },
|
||||
emitter = Emitter(duration = 2, TimeUnit.SECONDS).perSecond(100),
|
||||
position = Position.Relative(1.0, 1.0)
|
||||
)
|
||||
)
|
||||
|
||||
fun Int.blend(
|
||||
color: Int,
|
||||
@FloatRange(from = 0.0, to = 1.0) fraction: Float = 0.5f,
|
||||
): Int = ColorUtils.blendARGB(this, color, fraction)
|
||||
|
|
@ -3,17 +3,18 @@ package cn.super12138.todo.ui.navigation
|
|||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import cn.super12138.todo.ui.pages.main.MainPage
|
||||
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
||||
|
||||
@Composable
|
||||
fun ToDoNavigation(
|
||||
navController: NavHostController = rememberNavController(),
|
||||
startDestination: String = ToDoScreen.Main.name,
|
||||
viewModel: MainViewModel,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
NavHost(
|
||||
|
|
@ -23,7 +24,7 @@ fun ToDoNavigation(
|
|||
) {
|
||||
composable(ToDoScreen.Main.name) {
|
||||
MainPage(
|
||||
viewModel = viewModel(),
|
||||
viewModel = viewModel,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
|||
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
|
||||
// TODO: 寻找更好的适配方式
|
||||
val configuration = LocalConfiguration.current
|
||||
val orientation = when (configuration.orientation) {
|
||||
|
|
@ -134,6 +133,7 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
|||
id = id
|
||||
)
|
||||
)
|
||||
viewModel.playConfetti()
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
|
|
@ -170,6 +170,7 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
|||
id = id
|
||||
)
|
||||
)
|
||||
viewModel.playConfetti()
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package cn.super12138.todo.ui.viewmodels
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import cn.super12138.todo.logic.Repository
|
||||
|
|
@ -9,6 +10,7 @@ import kotlinx.coroutines.launch
|
|||
|
||||
class MainViewModel : ViewModel() {
|
||||
val toDos: Flow<List<TodoEntity>> = Repository.getAllTodos()
|
||||
val showConfetti = mutableStateOf(false)
|
||||
|
||||
fun addTodo(toDo: TodoEntity) {
|
||||
viewModelScope.launch {
|
||||
|
|
@ -21,4 +23,8 @@ class MainViewModel : ViewModel() {
|
|||
Repository.updateTodo(toDo)
|
||||
}
|
||||
}
|
||||
|
||||
fun playConfetti() {
|
||||
showConfetti.value = true
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue