diff --git a/app/src/main/java/com/gyf/csams/ui/Base.kt b/app/src/main/java/com/gyf/csams/ui/Base.kt index b0f1121..61fc81f 100644 --- a/app/src/main/java/com/gyf/csams/ui/Base.kt +++ b/app/src/main/java/com/gyf/csams/ui/Base.kt @@ -1,22 +1,29 @@ package com.gyf.csams.ui import androidx.annotation.DrawableRes +import androidx.compose.animation.Crossfade import androidx.compose.animation.animateColor import androidx.compose.animation.core.* -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.State import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import androidx.navigation.compose.navigate import androidx.navigation.compose.rememberNavController import com.gyf.csams.R +import com.gyf.csams.ui.model.CarouselViewModel +import com.gyf.csams.ui.model.MarqueeViewModel import com.gyf.csams.ui.theme.CSAMSTheme /** @@ -57,6 +64,14 @@ enum class MainMenu(@DrawableRes val selectedIcon:Int, Center(R.drawable.ic_account_fill,R.drawable.ic_account) } +/** + * 底部菜单按钮 + * + * @param _menu + * @param menu + * @param modifier + * @param onClick + */ @Composable fun MenuIconButton(_menu: MainMenu,menu: MainMenu,modifier: Modifier,onClick: () -> Unit){ Row(modifier = modifier @@ -70,8 +85,15 @@ fun MenuIconButton(_menu: MainMenu,menu: MainMenu,modifier: Modifier,onClick: () } } +/** + * 底部菜单 + * + * @param menu + * @param nav + * @param modifier + */ @Composable -fun MyBottomAppBar(menu:MainMenu,nav: NavController,modifier: Modifier=Modifier){ +fun MainBottomAppBar(menu:MainMenu, nav: NavController, modifier: Modifier=Modifier){ BottomAppBar(backgroundColor = Color.White,modifier=modifier) { //图标宽度平等分 @@ -89,6 +111,102 @@ fun MyBottomAppBar(menu:MainMenu,nav: NavController,modifier: Modifier=Modifier) } +/** + * 跑马灯 + * + * @param model + * @param content + */ +@Composable +fun Marquee(model: MarqueeViewModel = viewModel(), content: @Composable BoxScope.(model: MarqueeViewModel, + value: State +) -> Unit) { + + val delayMillis=2000 + Column { + BoxWithConstraints { + val transition = rememberInfiniteTransition() + val maxWidth = maxWidth + 30.dp + val offset = transition.animateFloat( + initialValue = 0F, + targetValue = maxWidth.value, + animationSpec = infiniteRepeatable( + animation = tween(durationMillis = 3000, delayMillis = delayMillis), + repeatMode = RepeatMode.Restart + ) + ) + Box(modifier = Modifier.fillMaxWidth()) { + content(model = model, value = offset) + } + + if (offset.value.toInt() == maxWidth.value.toInt()) { + model.addAsync(delayMillis = delayMillis) + } + } + } +} + + +@Composable +fun MarqueeText(model: MarqueeViewModel = viewModel(), offset: State) { + val poetryIndex: Int by model.marqueeIndex.observeAsState(0) + +// Text(text = "$poetryIndex") +// Text(text = "offset.value=${offset.value}") + Text( + text = model.marqueeTexts[poetryIndex % model.marqueeTexts.size], + modifier = Modifier.offset(x = offset.value.dp), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + +} + +/** + * 图片轮播 + * + */ +@Composable +fun Carousel( + model: CarouselViewModel = viewModel(), + durationMillis: Int = 2000, + content: @Composable (id: Int) -> Unit +) { + val index: Int by model.index.observeAsState(0) + Crossfade(targetState = index, animationSpec = tween(durationMillis = durationMillis)) { + content(id = model.imageList[it]) + } +} + +@Preview +@Composable +fun CarouselPreview() { + val model = CarouselViewModel() +// ClubActivitiesImage(model = model) + + + Carousel(model = model) { + Card( + modifier = Modifier + .height(300.dp) + .fillMaxWidth() +// .rotate(180F) + ) { + Image( + painter = painterResource(id = R.drawable.hot_activity_background), + contentDescription = null + ) + + Image( + painter = painterResource(id = it), + contentDescription = null + ) + } + } +} + + + @Preview @Composable fun AnimationTextPreview(){ @@ -101,7 +219,7 @@ fun MyBottomAppBarPreview(){ val nav= rememberNavController() CSAMSTheme { Surface(color = MaterialTheme.colors.background) { - MyBottomAppBar(menu = MainMenu.Main, nav = nav) + MainBottomAppBar(menu = MainMenu.Main, nav = nav) } } } \ No newline at end of file diff --git a/app/src/main/java/com/gyf/csams/ui/MainActivity.kt b/app/src/main/java/com/gyf/csams/ui/MainActivity.kt index 74f749f..41b9d56 100644 --- a/app/src/main/java/com/gyf/csams/ui/MainActivity.kt +++ b/app/src/main/java/com/gyf/csams/ui/MainActivity.kt @@ -3,30 +3,26 @@ package com.gyf.csams.ui import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.compose.animation.core.* import androidx.compose.foundation.Image import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.runtime.Composable -import androidx.compose.runtime.State -import androidx.compose.runtime.getValue -import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import com.gyf.csams.R -import com.gyf.csams.ui.model.MainViewModel +import com.gyf.csams.ui.model.CarouselViewModel +import com.gyf.csams.ui.model.MarqueeViewModel import com.gyf.csams.ui.theme.CSAMSTheme @@ -63,7 +59,11 @@ fun Body() { ClubActivitiesTitle() Spacer(modifier = Modifier.height(10.dp)) Column(Modifier.rotate(180F)) { - MyBottomAppBar(MainMenu.Main, navController, Modifier.rotate(180F)) + MainBottomAppBar( + MainMenu.Main, + navController, + Modifier.rotate(180F) + ) ClubActivitiesImage() } } @@ -72,7 +72,7 @@ fun Body() { composable(MainMenu.List.name) { Box(modifier = Modifier.fillMaxSize()) { Column(Modifier.rotate(180F)) { - MyBottomAppBar(MainMenu.List, navController, Modifier.rotate(180F)) + MainBottomAppBar(MainMenu.List, navController, Modifier.rotate(180F)) Row( modifier = Modifier .fillMaxSize() @@ -88,7 +88,7 @@ fun Body() { composable(MainMenu.Center.name) { Box(modifier = Modifier.fillMaxSize()) { Column(Modifier.rotate(180F)) { - MyBottomAppBar(MainMenu.Center, navController, Modifier.rotate(180F)) + MainBottomAppBar(MainMenu.Center, navController, Modifier.rotate(180F)) Row( modifier = Modifier .fillMaxSize() @@ -152,7 +152,7 @@ fun MyBorder(content: @Composable BoxScope.() -> Unit) { * */ @Composable -fun MessageBoard(model: MainViewModel= viewModel()) { +fun MessageBoard(model: MarqueeViewModel = viewModel()) { MyBorder { Row( verticalAlignment = Alignment.CenterVertically @@ -169,9 +169,11 @@ fun MessageBoard(model: MainViewModel= viewModel()) { modifier = Modifier .fillMaxWidth() ) { - Marquee(model = model) { - model, value -> TestA(model = model, - offset = value) + Marquee(model = model) { model, value -> + MarqueeText( + model = model, + offset = value + ) } } @@ -208,52 +210,55 @@ fun ClubActivitiesTitle() { * */ @Composable -fun ClubActivitiesImage() { - Column(modifier = Modifier.fillMaxSize()) { - Card( - modifier = Modifier - .weight(0.4F) - .fillMaxWidth() - ) { - Image( - painter = painterResource(id = R.drawable.hot_activity_desc_background), - contentDescription = null - ) - Box( +fun ClubActivitiesImage(model: CarouselViewModel = viewModel()) { + Carousel(model = model) { + Column(modifier = Modifier.fillMaxSize()) { + Card( modifier = Modifier - .padding(horizontal = 85.dp, vertical = 30.dp) - .rotate(180F) + .weight(0.4F) + .fillMaxWidth() ) { - Text( - text = "文字对任何界面都属于核心内容,而利用 Jetpack Compose 可以更轻松地显示或写入文字。Compose 可以充分利用其构建块的组合,这意味着您无需覆盖各种属性和方法,也无需扩展大型类,即可拥有特定的可组合项设计以及按您期望的方式运行的逻辑。" - .repeat(10), overflow = TextOverflow.Ellipsis + Image( + painter = painterResource(id = R.drawable.hot_activity_desc_background), + contentDescription = null ) - } + Box( + modifier = Modifier + .padding(horizontal = 85.dp, vertical = 30.dp) + .rotate(180F) + ) { + Text( + text = "文字对任何界面都属于核心内容,而利用 Jetpack Compose 可以更轻松地显示或写入文字。Compose 可以充分利用其构建块的组合,这意味着您无需覆盖各种属性和方法,也无需扩展大型类,即可拥有特定的可组合项设计以及按您期望的方式运行的逻辑。" + .repeat(10), overflow = TextOverflow.Ellipsis + ) + } - } - Card( - modifier = Modifier - .weight(0.6F) - .fillMaxWidth() - ) { - Image( - painter = painterResource(id = R.drawable.hot_activity_background), - contentDescription = null - ) - Image( - painter = painterResource(id = R.drawable.ic_launcher_foreground), - contentDescription = null - ) + } + Card( + modifier = Modifier + .weight(0.6F) + .fillMaxWidth() + .rotate(180F) + ) { + Image( + painter = painterResource(id = R.drawable.hot_activity_background), + contentDescription = null + ) + Image( + painter = painterResource(id = it), + contentDescription = null + ) + } } } } -@Preview(showBackground = true) +//@Preview(showBackground = true) @Composable fun DefaultPreview() { - val model = MainViewModel() + val model = MarqueeViewModel() CSAMSTheme { Box(modifier = Modifier.fillMaxSize()) { Column { @@ -268,62 +273,3 @@ fun DefaultPreview() { } - - -@Composable -fun TestA(model: MainViewModel= viewModel(),offset:State){ - val poetryIndex: Int by model.poetryIndex.observeAsState(0) - -// Text(text = "$poetryIndex") -// Text(text = "offset.value=${offset.value}") - Text( - text = model.poetry[poetryIndex % model.poetry.size], - modifier = Modifier.offset(x = offset.value.dp), - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - -} - -/** - * 轮播 - * - */ -@Composable -fun Carousel(){ - -} - -/** - * 跑马灯 - * - * @param model - * @param content - */ -@Composable -fun Marquee(model: MainViewModel= viewModel(),content: @Composable BoxScope.(model:MainViewModel, - value:State) -> Unit) { - - val delayMillis=2000 - Column { - BoxWithConstraints { - val transition = rememberInfiniteTransition() - val maxWidth = maxWidth + 30.dp - val offset = transition.animateFloat( - initialValue = 0F, - targetValue = maxWidth.value, - animationSpec = infiniteRepeatable( - animation = tween(durationMillis = 3000,delayMillis = delayMillis), - repeatMode = RepeatMode.Restart - ) - ) - Box(modifier = Modifier.fillMaxWidth()) { - content(model=model,value=offset) - } - - if (offset.value.toInt() == maxWidth.value.toInt()) { - model.addAsync(delayMillis = delayMillis) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/gyf/csams/ui/model/MainViewModel.kt b/app/src/main/java/com/gyf/csams/ui/model/MainViewModel.kt deleted file mode 100644 index 6f7cf2a..0000000 --- a/app/src/main/java/com/gyf/csams/ui/model/MainViewModel.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.gyf.csams.ui.model - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch - - -class MainViewModel:ViewModel() { - val poetry= listOf("床前明月光","疑是地上霜","举头望明月","低头思故乡") - - private val _poetryIndex=MutableLiveData(0) - var poetryIndex:LiveData = _poetryIndex - var job:Job? = null - - - fun addAsync(delayMillis:Int){ - if(job == null || job?.isCompleted==true) { - job = viewModelScope.launch { - _poetryIndex.postValue( - if (_poetryIndex.value == poetry.size-1) 0 else _poetryIndex.value?.plus( - 1 - ) - ) - delay(timeMillis = delayMillis.toLong()) - } - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/gyf/csams/ui/model/MarqueeViewModel.kt b/app/src/main/java/com/gyf/csams/ui/model/MarqueeViewModel.kt new file mode 100644 index 0000000..edea942 --- /dev/null +++ b/app/src/main/java/com/gyf/csams/ui/model/MarqueeViewModel.kt @@ -0,0 +1,72 @@ +package com.gyf.csams.ui.model + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.gyf.csams.R +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +/** + * 跑马灯 + * + */ +class MarqueeViewModel:ViewModel() { + + val marqueeTexts= listOf("床前明月光","疑是地上霜","举头望明月","低头思故乡") + + private val _marqueeIndex=MutableLiveData(0) + var marqueeIndex:LiveData = _marqueeIndex + var marqueeJob:Job? = null + + + + fun addAsync(delayMillis:Int){ + if(marqueeJob == null || marqueeJob?.isCompleted==true) { + marqueeJob = viewModelScope.launch { + _marqueeIndex.postValue( + if (_marqueeIndex.value == marqueeTexts.size-1) 0 else _marqueeIndex.value?.plus( + 1 + ) + ) + delay(timeMillis = delayMillis.toLong()) + } + } + } +} + +class CarouselViewModel:ViewModel(){ + val imageList= listOf(R.drawable.ic_launcher_foreground,R.drawable.ic_account_fill,R.drawable.ic_all_fill,R.drawable.ic_home_fill) + + private val _index=MutableLiveData(0) + + val index:LiveData = _index + + var job:Job? = null + + init { + start() + } + + fun start(){ + job = viewModelScope.launch { + do{ + _index.postValue(if (_index.value==imageList.size-1) 0 else _index.value?.plus(1)) + println("值更新为 :$_index") + delay(5000) + }while (job?.isActive==true) + } + } + + fun stop(){ + println("停止更新") + job?.cancel() + } + +} + +class MainViewModel:ViewModel(){ + +} \ No newline at end of file