У меня есть приложение, которое позволяет пользователю добавлять/удалять/редактировать доходы и расходы. Главный экран выглядит ниже. Игнорируйте £ 0s, они делают приращение и уменьшение, когда добавляются доходы и расходы. Когда я нажимаю на эти кнопки, пользователь должен быть доставлен в неэкранированное или расходы, которое будет иметь общее количество в центре (получите: £ X за доход и оплату: £ за расходы), окруженная анимированной циркулярной дугой с разделами, равными сумме доходов /расходов, которые вы видите на домашнем экране.
e.g. Для IncomeScreen я должен увидеть что -то вроде этого:
То же самое изображение для Expensescreen, но с схемой разных цветов. Телефон. Я не получаю изображение непосредственно выше. Я просто получаю «Получить: 0 фунтов стерлингов» и пустой беспрозрачный (без окрашенной дуги, а не ряды доходов ниже, на которые я могу провести, ничего), но пока в моих предварительных композиционных композиционных процессах он показывает правильное рендеринг в предварительном просмотре анимации. Мне нужна помощь в том, почему я не получаю доход и расходы на расходы, как они должны. У меня есть другие компоненты, такие как NavigationHostController, ViewModels, база данных комнаты (в памяти), MainActivity, Drawable Icons и т. Д. < /p>
Я получаю это как вывод в logcat: < /p>
//LOGCAT
2025-08-14 11:23:29.250 2322-23637 DynamicCodeLogger system_server E Could not infer CE/DE storage for path /data/data/com.example.income_and_expenses_application/code_cache/startup_agents/1553d31e-agent.so
---------------------------- PROCESS STARTED (25853) for package com.example.income_and_expenses_application ----------------------------
2025-08-14 11:26:48.762 25853-25853 ses_applicatio com...come_and_expenses_application W ClassLoaderContext classpath size mismatch. expected=0, found=1 (PCL[] | PCL[/data/data/com.example.income_and_expenses_application/code_cache/.overlay/base.apk/classes9.dex*1474684005])
2025-08-14 11:26:48.789 25853-25853 ses_applicatio com...come_and_expenses_application W Found duplicate classes, falling back to extracting from APK : /data/app/~~CzssoFyGlwBy4ekxx0FiCQ==/com.example.income_and_expenses_application-OinhwHZ_SUohN_Gjb02REQ==/base.apk
2025-08-14 11:26:48.789 25853-25853 ses_applicatio com...come_and_expenses_application W NOTE: This wastes RAM and hurts startup performance.
2025-08-14 11:26:48.789 25853-25853 ses_applicatio com...come_and_expenses_application W Found duplicated class when checking oat files: 'Lcom/example/income_and_expenses_application/data/local/models/Expense;' in /data/app/~~CzssoFyGlwBy4ekxx0FiCQ==/com.example.income_and_expenses_application-OinhwHZ_SUohN_Gjb02REQ==/base.apk!classes9.dex and /data/data/com.example.income_and_expenses_application/code_cache/.overlay/base.apk/classes9.dex
2025-08-14 11:26:50.261 25853-25880 System com...come_and_expenses_application W A resource failed to call close.
2025-08-14 11:26:50.262 25853-25880 System com...come_and_expenses_application W A resource failed to call close.
2025-08-14 11:26:51.991 25853-25853 NetworkSecurityConfig com...come_and_expenses_application D No Network Security Config specified, using platform default
2025-08-14 11:26:51.992 25853-25853 NetworkSecurityConfig com...come_and_expenses_application D No Network Security Config specified, using platform default
2025-08-14 11:26:53.581 25853-25853 Choreographer com...come_and_expenses_application I Skipped 59 frames! The application may be doing too much work on its main thread.
2025-08-14 11:26:58.178 25853-25950 ProfileInstaller com...come_and_expenses_application D Installing profile for com.example.income_and_expenses_application
2025-08-14 11:27:08.578 25853-25909 AdrenoGLES-0 com...come_and_expenses_application I QUALCOMM build : 191610ae03, Ic907de5ed0
< /code>
//HOMESCREEN
@Composable
fun HomeScreen(
state: HomeUiState,
modifier: Modifier,
onIncomeClick: (Int) -> Unit,
onExpenseClick: (Int) -> Unit,
onClickSeeAllIncome: () -> Unit,
onClickSeeAllExpense: () -> Unit,
onInsertIncome: () -> Unit,
onInsertExpense: () -> Unit
) {
LazyColumn(
modifier = modifier
) {
item{
Column {
Row {
val balance = state.totalIncome - state.totalExpense
Text("Your total balance:")
Spacer(modifier = Modifier.size(4.dp))
Text(
text = "£" +
formatAmount(balance),
fontWeight = FontWeight.Bold
)
}
Row(
modifier = Modifier.fillMaxWidth()
){
AccountCard(
cardTitle = "TOTAL INCOME",
amount = "+£" + formatAmount(state.totalIncome),
cardIcon = Icons.Default.ArrowDownward,
modifier = Modifier
.weight(1f)
.padding(
end =4.dp,
top = 8.dp,
bottom = 8.dp
)
)
AccountCard(
cardTitle = "TOTAL EXPENSE",
amount = "-£" + formatAmount(state.totalExpense),
cardIcon = Icons.Default.ArrowUpward,
modifier = Modifier
.weight(1f)
.padding(
end =4.dp,
top = 8.dp,
bottom = 8.dp
)
)
}
}
Spacer(modifier = Modifier.size(12.dp))
}
item{
IncomeCard(
account = state,
onIncomeClick = onIncomeClick,
onClickSeeAll = onClickSeeAllIncome
)
Spacer(modifier = Modifier.size(12.dp))
}
item{
ExpenseCard(
account = state,
onExpenseClick = onExpenseClick,
onClickSeeAll = onClickSeeAllExpense
)
Spacer(modifier = Modifier.size(12.dp))
}
item{
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
){
ElevatedButton(
onClick = onInsertIncome
) {
Text(text = "Insert Income")
}
ElevatedButton(
onClick = onInsertExpense
) {
Text(text = "Insert Expense")
}
}
}
}
}
< /code>
//INCOMESCREEN
@Composable
fun IncomeScreen(
modifier: Modifier = Modifier,
incomes: List,
onIncomeItemClick: (id:Int) -> Unit,
onIncomeItemDelete: (Int) -> Unit
) {
TransactionStatement( // generates the animated arc and total income
modifier = modifier,
items = incomes,
colours = { getColour(it.incomeAmount.toFloat(),
Util.incomeColour
) },
amounts = {it.incomeAmount.toFloat()},
amountsTotal = incomes.sumOf{ it.incomeAmount }.toFloat(),
circleLabel = "Receive",
onItemSwiped = {
onIncomeItemDelete.invoke(it.id)
},
key = {it.id} //will increase performance of the composable
){
//row composable scope
IncomeRow(
name = it.title,
description = "Receive ${formatDetailDate(it.date)}",
amount= it.incomeAmount.toFloat(),
colour = getColour(it.incomeAmount.toFloat(),
Util.incomeColour
),
modifier = Modifier.clickable {
onIncomeItemClick.invoke(it.id)
}
)
}
}
< /code>
@Composable
//TRANSACTIONSTATEMENT CALLED WITHIN THE INCOMESCREEN ABOVE
fun TransactionStatement(
modifier: Modifier = Modifier,
items: List,
colours: (T) -> Color,
amounts: (T) -> Float,
amountsTotal: Float,
circleLabel: String,
onItemSwiped: (T) -> Unit,
key: (T) -> Int,
rows:@Composable (T) -> Unit
) {
LazyColumn(
modifier = modifier
) {
item{
Box(modifier = Modifier.padding(16.dp)){
//extracting items
val transProportions = items.extractProportions{amounts(it)}
val circularRingColour = items.map{ colours(it)}
AnimateCircle( //need extension function for proportions above
proportions = transProportions,
colours = circularRingColour,
modifier = Modifier
.height(300.dp)
.align(Alignment.Center)
.fillMaxWidth()
)
//Text with the amount will be on top of the circular ring in a Box layout
Column(
modifier = Modifier
.align(Alignment.Center)
){
Text(
text = circleLabel,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Text(
text = formatAmount(amountsTotal),
style = MaterialTheme.typography.headlineLarge,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
}
}
Spacer(modifier = Modifier
.height(10.dp)
)
}
//Now I need to render this list of Income/Expense items
items(items,key = key){ item ->
//create a dismiss state as I want to delete item on swipe
var isDismissed by remember{ mutableStateOf(false)}
var showDismissedBackground by remember { mutableStateOf(false) }
//We have a Box
val dismissState = rememberSwipeToDismissBoxState(
confirmValueChange = { state ->
when(state){
SwipeToDismissBoxValue.StartToEnd -> {
isDismissed = true
}
else ->{
isDismissed = false
return@rememberSwipeToDismissBoxState false
}
}
return@rememberSwipeToDismissBoxState true
}
)
LaunchedEffect(isDismissed) {
if(isDismissed){
onItemSwiped.invoke(item)
}
} //1st LaunchedEffect
LaunchedEffect(dismissState.progress) {
showDismissedBackground = when{
dismissState.progress {
true
}
else ->{
false
}
}
} //2nd LaunchedEffect
SwipeToDismissBox(
state = dismissState,
backgroundContent = {
AnimatedVisibility(showDismissedBackground) {
Surface(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.CenterVertically)
.height(68.dp),
color = MaterialTheme.colorScheme.error
){
Box(
contentAlignment = Alignment.Center
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = null
)
} //Box
}//Surface
}//AnimatedVisibility
}, //backgroundContent
content = {
rows(item)
},
enableDismissFromStartToEnd = true,
enableDismissFromEndToStart = false
)//SwipeToDismissBox
}
}
}
@Composable
fun AnimateCircle(
proportions: List,
colours: List,
modifier: Modifier = Modifier
) {
val currentState = remember{
MutableTransitionState(AnimatedCircleProgress.START)
.apply{ targetState = AnimatedCircleProgress.END}
} //transition state defined from START to END
//gives the current density (stroke converted from dp to pixels)
val stroke = with(LocalDensity.current){
Stroke(5.dp.toPx())
}
val transition = rememberTransition(transitionState = currentState, label = "Circle/Ring")
//angle offset for offsetting the cycle
val angleOffset by transition.animateFloat(
transitionSpec ={ tween(
durationMillis = 900,
delayMillis = 500,
easing = LinearOutSlowInEasing
)},
label = ""
){progress -> //targetValueByState
if(progress == AnimatedCircleProgress.START){
0f
}else{
360f
}
}
val shift by transition.animateFloat(
transitionSpec ={ tween(
durationMillis = 900,
delayMillis = 500,
//custom easing (animation) curve
easing = CubicBezierEasing(0f,0.75f,0.35f,0.85f)
)},
label = ""
){progress -> //targetValueByState
if(progress == AnimatedCircleProgress.START){
0f
}else{
360f
}
}
//Now creating a Canvas (with a DrawScope) to draw the arcs
Canvas(modifier = modifier) { //size is part of the DrawScope
val innerRadius = (size.minDimension - stroke.width)/2 //stay within bounding box
val halfSize = size/2.0f
val topLeft = Offset(
x = halfSize.width - innerRadius,
y = halfSize.height - innerRadius
)
//Get the full size
val size = Size(innerRadius*2,innerRadius*2)
//start drawing from (0,innerRadius*2)
var startAngle = shift - 90f //shift initially is 0f
//Use the proportions to draw the arcs
proportions.forEachIndexed{ index,proportion ->
val sweep = proportion * angleOffset
drawArc(
color = colours[index],
startAngle = startAngle + DividerLengthInDegrees / 2,
//we want to change the angle Offset based upon the sweep
//arc accommodated with divider
sweepAngle = sweep - DividerLengthInDegrees,
topLeft = topLeft,
size = size,
//I want to draw an arc not a full circle
useCenter = false,
style = stroke
)
//as different arcs will be drawn
startAngle += sweep
}
}
}
private enum class AnimatedCircleProgress{
START,
END
}
//divider length for the different incomes in the circle/arc in degrees
private const val DividerLengthInDegrees = 1.8f
//create extension function to extract the proportions
fun List.extractProportions(selector: (E) -> Float): List{
/*So if we have 10 Income items we need to extract them;
next sum the incomes and calculate the proportion of each Income on the total
*/
//this refers to the List
val total = this.sumOf{selector(it).toDouble()}
//Now we want to return a list with these particular proportions
return this.map{(selector(it)/total).toFloat()}
}
Подробнее здесь: https://stackoverflow.com/questions/797 ... -compose-a
IncomeScreen и ExpenseScreen (композиции), не отображаясь в JetPack Compose и Drawarc, не анимирован ⇐ Android
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение
-
-
Android Jetpack Compose кисть drawArc с настраиваемыми радиусами концов
Anonymous » » в форуме Android - 0 Ответы
- 21 Просмотры
-
Последнее сообщение Anonymous
-