为什么第一次单击图标按钮时索引不加一个
Why doesnt the index plus one on the first click of the Iconbutton
所以我做了一个日历。我现在正试图让两个箭头按钮跳过几个月。唯一的问题是,每次我单击图标按钮时,++ 第一次不执行任何操作,但第二次执行某些操作...为什么有人可以帮忙
package com.jens.svensson.jenson_calendar
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.*
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.navigation.compose.hiltViewModel
import com.jens.svensson.jenson_calendar.data.model.CalendarColors
import com.jens.svensson.jenson_calendar.ui.CalendarViewModel
import com.jens.svensson.jenson_calendar.ui.events.CalendarEvent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@Composable
fun Calendar(
calendarViewModel: CalendarViewModel = hiltViewModel(),
isRoundedCorners: Boolean = false,
startMonth: Int,
calendarColor: CalendarColors,
textStyle: TextStyle,
onDissmissDialog: () -> Unit,
size: Configuration
){
val calendarYears = calendarViewModel.calendarYears
val showMenuState = calendarViewModel.dropDownMenuState.value.showDropDown
var dropDownPickedState = calendarViewModel.dropDownMenuState.value.pickedItem
val sizeHeight = size.screenHeightDp
val sizeWidth = size.screenWidthDp
val width = sizeWidth * 0.95
val height = sizeHeight * 0.95
val coroutineScope = rememberCoroutineScope()
Dialog(onDismissRequest = onDissmissDialog, properties = DialogProperties(dismissOnClickOutside = true)) {
Column(modifier = Modifier
.size(width = width.dp, height = height.dp)
.padding(15.dp)
.background(
calendarColor.calendarColor,
shape = if (isRoundedCorners) RoundedCornerShape(10) else RectangleShape
)) {
Header(isRoundedCorners = isRoundedCorners, color = calendarColor.headerColor)
Row(
modifier = Modifier
.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
) {
Box(
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(start = 22.dp)
) {
Row(Modifier.clickable { calendarViewModel.onEvent(CalendarEvent.ShowDropDown(!showMenuState)) }) {
Text(
text = calendarViewModel.standardMonths[calendarViewModel.dropDownMenuState.value.pickedItem],
style = MaterialTheme.typography.h6,
color = calendarColor.mainCalendarTextColor
)
Icon(
imageVector = Icons.Default.ArrowDropDown,
contentDescription = "Month dropdown arrow",
tint = calendarColor.mainCalendarTextColor
)
}
DropdownMenu(
expanded = showMenuState,
onDismissRequest = { calendarViewModel.onEvent(CalendarEvent.ShowDropDown(!showMenuState)) }) {
calendarViewModel.standardMonths.forEachIndexed { index, month ->
DropdownMenuItem(onClick = { calendarViewModel.onEvent(CalendarEvent.ClickedMenuItem(index))
}) {
Row() {
Text(text = month, style = MaterialTheme.typography.h6)
}
}
}
}
}
IconButton(modifier = Modifier.align(Alignment.CenterVertically), onClick = {}) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "Go back one month arrow",
tint = calendarColor.mainCalendarTextColor
)
}
IconButton(modifier = Modifier.align(Alignment.CenterVertically), onClick = {
calendarViewModel.onEvent(CalendarEvent.ClickedMenuItem(dropDownPickedState++))
}) {
Icon(
imageVector = Icons.Default.ArrowForward,
contentDescription = "Go forward one month arrow",
tint = calendarColor.mainCalendarTextColor
)
}
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
calendarViewModel.datesList.forEach {
Text(text = it, color = calendarColor.mainCalendarTextColor)
}
}
LazyRow(state = calendarViewModel.listState, modifier = Modifier.fillMaxWidth()) {
calendarYears.forEach {
items(it.months.count()) { index ->
CalendarRowItem(
modifier = Modifier.fillParentMaxWidth(),
calendarSize = it.months[index].amountOfDays,
initWeekday = it.months[index].startDayOfMonth.ordinal,
textColor = MaterialTheme.colors.secondaryVariant,
clickedColor = MaterialTheme.colors.primary,
textStyle = MaterialTheme.typography.body1
)
}
}
}
DisposableEffect(Unit) {
coroutineScope.launch {
calendarViewModel.listState.scrollToItem(calendarViewModel.currentMonth)
}
onDispose { }
}
CalendarButtonSection()
}
}
}
日历视图模型
package com.jens.svensson.jenson_calendar.ui
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.jens.svensson.jenson_calendar.data.model.CalendarMonth
import com.jens.svensson.jenson_calendar.data.model.CalendarYear
import com.jens.svensson.jenson_calendar.data.model.YearMonths
import com.jens.svensson.jenson_calendar.domain.repository.CalendarInterface
import com.jens.svensson.jenson_calendar.ui.events.CalendarEvent
import com.jens.svensson.jenson_calendar.ui.state.DropDownMenuState
import dagger.hilt.android.lifecycle.HiltViewModel
import java.time.Month
import java.util.*
import javax.inject.Inject
@HiltViewModel
class CalendarViewModel @Inject constructor(private val repository: CalendarInterface): ViewModel() {
val datesList: List<String> = repository.getShortenedWeekDays()
val calendarYears: List<CalendarYear> = repository.createAndReturnYears()
val standardMonths: List<String> = repository.getStandardMonths()
val listState = LazyListState()
val currentMonth: Int = Calendar.getInstance().get(Calendar.MONTH)
private val _dropdownMenuState = mutableStateOf(DropDownMenuState())
val dropDownMenuState: State<DropDownMenuState> = _dropdownMenuState
init {
_dropdownMenuState.value = dropDownMenuState.value.copy(pickedItem = currentMonth)
}
fun onEvent(event: CalendarEvent){
when(event){
is CalendarEvent.ShowDropDown -> _dropdownMenuState.value = dropDownMenuState.value.copy(showDropDown = event.value)
is CalendarEvent.ClickedMenuItem -> {
_dropdownMenuState.value = _dropdownMenuState.value.copy(pickedItem = event.value)
}
}
}
fun getCalendarMonths(yearIndex: Int): List<CalendarMonth>{
return calendarYears[yearIndex].months
}
}
新视图模型
package com.jens.svensson.jenson_calendar.ui
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.jens.svensson.jenson_calendar.data.model.CalendarMonth
import com.jens.svensson.jenson_calendar.data.model.CalendarYear
import com.jens.svensson.jenson_calendar.data.model.YearMonths
import com.jens.svensson.jenson_calendar.domain.repository.CalendarInterface
import com.jens.svensson.jenson_calendar.ui.events.CalendarEvent
import com.jens.svensson.jenson_calendar.ui.state.DropDownMenuState
import dagger.hilt.android.lifecycle.HiltViewModel
import java.time.Month
import java.util.*
import javax.inject.Inject
@HiltViewModel
class CalendarViewModel @Inject constructor(private val repository: CalendarInterface): ViewModel() {
val datesList: List<String> = repository.getShortenedWeekDays()
val calendarYears: List<CalendarYear> = repository.createAndReturnYears()
val standardMonths: List<String> = repository.getStandardMonths()
val listState = LazyListState()
val currentMonth: Int = Calendar.getInstance().get(Calendar.MONTH)
private var datePickedState: Int = 0
private val _dropdownMenuState = mutableStateOf(DropDownMenuState())
val dropDownMenuState: State<DropDownMenuState> = _dropdownMenuState
init {
_dropdownMenuState.value = dropDownMenuState.value.copy(pickedItem = currentMonth)
}
fun onEvent(event: CalendarEvent){
when(event){
is CalendarEvent.ShowDropDown -> _dropdownMenuState.value = dropDownMenuState.value.copy(showDropDown = event.value)
is CalendarEvent.ClickedMenuItem -> {
_dropdownMenuState.value = _dropdownMenuState.value.copy(pickedItem = event.value)
}
is CalendarEvent.NextMonth -> nextMonth()
is CalendarEvent.PreviousMonth -> previousMonth()
}
}
fun nextMonth(){
datePickedState = _dropdownMenuState.value.pickedItem
if(datePickedState == 11){
datePickedState = 0
}else{
_dropdownMenuState.value = _dropdownMenuState.value.copy(pickedItem = ++datePickedState)
}
}
fun previousMonth(){
datePickedState = _dropdownMenuState.value.pickedItem
if(datePickedState == 0){
datePickedState == 11
}else{
_dropdownMenuState.value = dropDownMenuState.value.copy(pickedItem = --datePickedState)
}
}
fun getCalendarMonths(yearIndex: Int): List<CalendarMonth>{
return calendarYears[yearIndex].months
}
}
按钮
IconButton(modifier = Modifier.align(Alignment.CenterVertically), onClick = {calendarViewModel.onEvent(CalendarEvent.PreviousMonth)}) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "Go back one month arrow",
tint = calendarColor.mainCalendarTextColor
)
}
IconButton(modifier = Modifier.align(Alignment.CenterVertically), onClick = {
calendarViewModel.onEvent(CalendarEvent.NextMonth)
}) {
Icon(
imageVector = Icons.Default.ArrowForward,
contentDescription = "Go forward one month arrow",
tint = calendarColor.mainCalendarTextColor
)
}
一个问题是 i++
在增加之前将当前 i
值传递给计算,因此您将相同的旧值传递给 onEvent
。您可以在 this answer 中找到更多详细信息 - 它是关于 C 的,但是 inc/dec 运算符在它们存在的所有语言中都是一样的。您可以使用 ++i
,这会在计算中使用它之前增加该值。
但是第二个问题来了。这一行:
var dropDownPickedState = calendarViewModel.dropDownMenuState.value.pickedItem
创建一个局部变量,这些变量不会在重组之间保存。因此,您第一次单击时,会增加局部值,但会将旧值传递给 onEvent
,这不会导致重组。
您第二次单击 - 之前增加的值会传递到您的视图模型,这会触发重组并重置您的 dropDownPickedState。
为防止此类错误,请勿在 Compose 视图中使用 var
,除非您将其与状态委托一起使用,例如:
var item by viewModel.stateValue // is ok
var item = viewModel.stateValue.value // is not ok
所以我做了一个日历。我现在正试图让两个箭头按钮跳过几个月。唯一的问题是,每次我单击图标按钮时,++ 第一次不执行任何操作,但第二次执行某些操作...为什么有人可以帮忙
package com.jens.svensson.jenson_calendar
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.*
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.navigation.compose.hiltViewModel
import com.jens.svensson.jenson_calendar.data.model.CalendarColors
import com.jens.svensson.jenson_calendar.ui.CalendarViewModel
import com.jens.svensson.jenson_calendar.ui.events.CalendarEvent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@Composable
fun Calendar(
calendarViewModel: CalendarViewModel = hiltViewModel(),
isRoundedCorners: Boolean = false,
startMonth: Int,
calendarColor: CalendarColors,
textStyle: TextStyle,
onDissmissDialog: () -> Unit,
size: Configuration
){
val calendarYears = calendarViewModel.calendarYears
val showMenuState = calendarViewModel.dropDownMenuState.value.showDropDown
var dropDownPickedState = calendarViewModel.dropDownMenuState.value.pickedItem
val sizeHeight = size.screenHeightDp
val sizeWidth = size.screenWidthDp
val width = sizeWidth * 0.95
val height = sizeHeight * 0.95
val coroutineScope = rememberCoroutineScope()
Dialog(onDismissRequest = onDissmissDialog, properties = DialogProperties(dismissOnClickOutside = true)) {
Column(modifier = Modifier
.size(width = width.dp, height = height.dp)
.padding(15.dp)
.background(
calendarColor.calendarColor,
shape = if (isRoundedCorners) RoundedCornerShape(10) else RectangleShape
)) {
Header(isRoundedCorners = isRoundedCorners, color = calendarColor.headerColor)
Row(
modifier = Modifier
.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
) {
Box(
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(start = 22.dp)
) {
Row(Modifier.clickable { calendarViewModel.onEvent(CalendarEvent.ShowDropDown(!showMenuState)) }) {
Text(
text = calendarViewModel.standardMonths[calendarViewModel.dropDownMenuState.value.pickedItem],
style = MaterialTheme.typography.h6,
color = calendarColor.mainCalendarTextColor
)
Icon(
imageVector = Icons.Default.ArrowDropDown,
contentDescription = "Month dropdown arrow",
tint = calendarColor.mainCalendarTextColor
)
}
DropdownMenu(
expanded = showMenuState,
onDismissRequest = { calendarViewModel.onEvent(CalendarEvent.ShowDropDown(!showMenuState)) }) {
calendarViewModel.standardMonths.forEachIndexed { index, month ->
DropdownMenuItem(onClick = { calendarViewModel.onEvent(CalendarEvent.ClickedMenuItem(index))
}) {
Row() {
Text(text = month, style = MaterialTheme.typography.h6)
}
}
}
}
}
IconButton(modifier = Modifier.align(Alignment.CenterVertically), onClick = {}) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "Go back one month arrow",
tint = calendarColor.mainCalendarTextColor
)
}
IconButton(modifier = Modifier.align(Alignment.CenterVertically), onClick = {
calendarViewModel.onEvent(CalendarEvent.ClickedMenuItem(dropDownPickedState++))
}) {
Icon(
imageVector = Icons.Default.ArrowForward,
contentDescription = "Go forward one month arrow",
tint = calendarColor.mainCalendarTextColor
)
}
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
calendarViewModel.datesList.forEach {
Text(text = it, color = calendarColor.mainCalendarTextColor)
}
}
LazyRow(state = calendarViewModel.listState, modifier = Modifier.fillMaxWidth()) {
calendarYears.forEach {
items(it.months.count()) { index ->
CalendarRowItem(
modifier = Modifier.fillParentMaxWidth(),
calendarSize = it.months[index].amountOfDays,
initWeekday = it.months[index].startDayOfMonth.ordinal,
textColor = MaterialTheme.colors.secondaryVariant,
clickedColor = MaterialTheme.colors.primary,
textStyle = MaterialTheme.typography.body1
)
}
}
}
DisposableEffect(Unit) {
coroutineScope.launch {
calendarViewModel.listState.scrollToItem(calendarViewModel.currentMonth)
}
onDispose { }
}
CalendarButtonSection()
}
}
}
日历视图模型
package com.jens.svensson.jenson_calendar.ui
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.jens.svensson.jenson_calendar.data.model.CalendarMonth
import com.jens.svensson.jenson_calendar.data.model.CalendarYear
import com.jens.svensson.jenson_calendar.data.model.YearMonths
import com.jens.svensson.jenson_calendar.domain.repository.CalendarInterface
import com.jens.svensson.jenson_calendar.ui.events.CalendarEvent
import com.jens.svensson.jenson_calendar.ui.state.DropDownMenuState
import dagger.hilt.android.lifecycle.HiltViewModel
import java.time.Month
import java.util.*
import javax.inject.Inject
@HiltViewModel
class CalendarViewModel @Inject constructor(private val repository: CalendarInterface): ViewModel() {
val datesList: List<String> = repository.getShortenedWeekDays()
val calendarYears: List<CalendarYear> = repository.createAndReturnYears()
val standardMonths: List<String> = repository.getStandardMonths()
val listState = LazyListState()
val currentMonth: Int = Calendar.getInstance().get(Calendar.MONTH)
private val _dropdownMenuState = mutableStateOf(DropDownMenuState())
val dropDownMenuState: State<DropDownMenuState> = _dropdownMenuState
init {
_dropdownMenuState.value = dropDownMenuState.value.copy(pickedItem = currentMonth)
}
fun onEvent(event: CalendarEvent){
when(event){
is CalendarEvent.ShowDropDown -> _dropdownMenuState.value = dropDownMenuState.value.copy(showDropDown = event.value)
is CalendarEvent.ClickedMenuItem -> {
_dropdownMenuState.value = _dropdownMenuState.value.copy(pickedItem = event.value)
}
}
}
fun getCalendarMonths(yearIndex: Int): List<CalendarMonth>{
return calendarYears[yearIndex].months
}
}
新视图模型
package com.jens.svensson.jenson_calendar.ui
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.jens.svensson.jenson_calendar.data.model.CalendarMonth
import com.jens.svensson.jenson_calendar.data.model.CalendarYear
import com.jens.svensson.jenson_calendar.data.model.YearMonths
import com.jens.svensson.jenson_calendar.domain.repository.CalendarInterface
import com.jens.svensson.jenson_calendar.ui.events.CalendarEvent
import com.jens.svensson.jenson_calendar.ui.state.DropDownMenuState
import dagger.hilt.android.lifecycle.HiltViewModel
import java.time.Month
import java.util.*
import javax.inject.Inject
@HiltViewModel
class CalendarViewModel @Inject constructor(private val repository: CalendarInterface): ViewModel() {
val datesList: List<String> = repository.getShortenedWeekDays()
val calendarYears: List<CalendarYear> = repository.createAndReturnYears()
val standardMonths: List<String> = repository.getStandardMonths()
val listState = LazyListState()
val currentMonth: Int = Calendar.getInstance().get(Calendar.MONTH)
private var datePickedState: Int = 0
private val _dropdownMenuState = mutableStateOf(DropDownMenuState())
val dropDownMenuState: State<DropDownMenuState> = _dropdownMenuState
init {
_dropdownMenuState.value = dropDownMenuState.value.copy(pickedItem = currentMonth)
}
fun onEvent(event: CalendarEvent){
when(event){
is CalendarEvent.ShowDropDown -> _dropdownMenuState.value = dropDownMenuState.value.copy(showDropDown = event.value)
is CalendarEvent.ClickedMenuItem -> {
_dropdownMenuState.value = _dropdownMenuState.value.copy(pickedItem = event.value)
}
is CalendarEvent.NextMonth -> nextMonth()
is CalendarEvent.PreviousMonth -> previousMonth()
}
}
fun nextMonth(){
datePickedState = _dropdownMenuState.value.pickedItem
if(datePickedState == 11){
datePickedState = 0
}else{
_dropdownMenuState.value = _dropdownMenuState.value.copy(pickedItem = ++datePickedState)
}
}
fun previousMonth(){
datePickedState = _dropdownMenuState.value.pickedItem
if(datePickedState == 0){
datePickedState == 11
}else{
_dropdownMenuState.value = dropDownMenuState.value.copy(pickedItem = --datePickedState)
}
}
fun getCalendarMonths(yearIndex: Int): List<CalendarMonth>{
return calendarYears[yearIndex].months
}
}
按钮
IconButton(modifier = Modifier.align(Alignment.CenterVertically), onClick = {calendarViewModel.onEvent(CalendarEvent.PreviousMonth)}) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "Go back one month arrow",
tint = calendarColor.mainCalendarTextColor
)
}
IconButton(modifier = Modifier.align(Alignment.CenterVertically), onClick = {
calendarViewModel.onEvent(CalendarEvent.NextMonth)
}) {
Icon(
imageVector = Icons.Default.ArrowForward,
contentDescription = "Go forward one month arrow",
tint = calendarColor.mainCalendarTextColor
)
}
一个问题是 i++
在增加之前将当前 i
值传递给计算,因此您将相同的旧值传递给 onEvent
。您可以在 this answer 中找到更多详细信息 - 它是关于 C 的,但是 inc/dec 运算符在它们存在的所有语言中都是一样的。您可以使用 ++i
,这会在计算中使用它之前增加该值。
但是第二个问题来了。这一行:
var dropDownPickedState = calendarViewModel.dropDownMenuState.value.pickedItem
创建一个局部变量,这些变量不会在重组之间保存。因此,您第一次单击时,会增加局部值,但会将旧值传递给 onEvent
,这不会导致重组。
您第二次单击 - 之前增加的值会传递到您的视图模型,这会触发重组并重置您的 dropDownPickedState。
为防止此类错误,请勿在 Compose 视图中使用 var
,除非您将其与状态委托一起使用,例如:
var item by viewModel.stateValue // is ok
var item = viewModel.stateValue.value // is not ok