Kamis, 25 April 2024

waterBottleApp

Kali ini saya mencoba membuaat water bottle. Aplikasi ini menampilkan visualisasi mengisi botol air dengan animasi yang menunjukkan jumlah air yang telah di isikan. Dengan antarmuka yang sederhana dan menarik, pengguna dapat dengan mudah melihat seberapa banyak air yang sudah mereka isikan.
  •  Visualisasi Botol Air: 
 Menampilkan botol air dengan bentuk yang dapat diubah dan diwarnai sesuai keinginan. Warna botol default adalah kuning, namun dapat diubah sesuai preferensi pengguna. 
  • Animasi Pengisian Air: 
 Animasi yang menunjukkan persentase air yang telah diisi. 
  • Interaksi Pengguna: 
 Pengguna dapat menambahkan jumlah air dengan menekan tombol "Isi Air". Tampilan informasi mengenai jumlah air yang telah diisi. 
  • Desain yang Responsif: 
Antarmuka yang responsif dan mudah digunakan pada berbagai ukuran layar. 
  • Dependensi: 
Aplikasi ini menggunakan beberapa dependensi utama dari Jetpack Compose untuk membangun antarmuka pengguna yang modern dan responsif: Material3 untuk komponen UI yang mengikuti pedoman desain Material Design. Compose UI untuk komposisi dan tata letak. Compose Foundation untuk elemen dasar UI seperti Canvas. Compose Animation untuk animasi halus. 

Berikut adalah source codenya:

WaterBottle.kt
package com.example.waterbottleapp

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.animateIntAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.clipPath
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

@Composable
fun WaterBottle(
modifier: Modifier = Modifier,
totalWaterAmount: Int,
unit: String,
usedWaterAmount: Int,
waterWavesColor: Color = Color(0xFFF4CE14),
bottleColor: Color = Color.Gray,
capColor: Color = Color(0xFF379777)
) {
val waterPercentage = animateFloatAsState(
targetValue = (usedWaterAmount.toFloat() / totalWaterAmount.toFloat()),
label = "Water Waves animation",
animationSpec = tween(durationMillis = 1000)
).value

val usedWaterAmountAnimation = animateIntAsState(
targetValue = usedWaterAmount,
label = "Used water amount animation",
animationSpec = tween(durationMillis = 1000)
).value

Box(
modifier = modifier
.width(200.dp)
.height(600.dp)
) {
Canvas(modifier = Modifier.fillMaxSize()) {
val width = size.width
val height = size.height

val capWidth = size.width * 0.55f
val capHeight = size.height * 0.13f

// Draw the bottle body
val bodyPath = Path().apply {
// Start at the top-left corner of the bottle's body
moveTo(width * 0.25f, height * 0.05f)
// Top curve
cubicTo(width * 0.25f, height * 0.05f, width * 0.1f, height * 0.15f, width * 0.25f, height * 0.2f)
// Left side curve
cubicTo(width * 0.25f, height * 0.2f, width * 0.05f, height * 0.4f, width * 0.05f, height * 0.6f)
// Left bottom curve
cubicTo(width * 0.05f, height * 0.6f, width * 0.15f, height * 0.95f, width * 0.3f, height * 0.95f)
// Bottom straight line
lineTo(width * 0.7f, height * 0.95f)
// Right bottom curve
cubicTo(width * 0.85f, height * 0.95f, width * 0.95f, height * 0.6f, width * 0.95f, height * 0.6f)
// Right side curve
cubicTo(width * 0.95f, height * 0.4f, width * 0.75f, height * 0.2f, width * 0.75f, height * 0.2f)
// Top curve to close the path
cubicTo(width * 0.75f, height * 0.15f, width * 0.6f, height * 0.05f, width * 0.25f, height * 0.05f)

close()
}
clipPath(
path = bodyPath
) {
// Draw the color of the bottle
drawRect(
color = bottleColor,
// size = size,
// topLeft = Offset(0f, 0f)
size = Size(size.width * 0.55f, size.height * 0.1f),
topLeft = Offset(size.width / 2 - size.width * 0.55f / 2f, 0f),
// cornerRadius = CornerRadius(45f, 45f)
)

// Draw the water waves
val waterWavesYPosition = (1 - waterPercentage) * size.height

val wavesPath = Path().apply {
moveTo(
x = 0f,
y = waterWavesYPosition
)
lineTo(
x = size.width,
y = waterWavesYPosition
)
lineTo(
x = size.width,
y = size.height
)
lineTo(
x = 0f,
y = size.height
)
close()
}
drawPath(
path = wavesPath,
color = waterWavesColor,
)
}

// Draw the bottle cap
drawRoundRect(
color = capColor,
size = Size(capWidth, capHeight),
topLeft = Offset(size.width / 2 - capWidth / 2f, 0f),
cornerRadius = CornerRadius(45f, 45f)
)
}

val text = buildAnnotatedString {
withStyle(
style = SpanStyle(
color = if (waterPercentage > 0.5f) bottleColor else waterWavesColor,
fontSize = 44.sp
)
) {
append(usedWaterAmountAnimation.toString())
}
withStyle(
style = SpanStyle(
color = if (waterPercentage > 0.5f) bottleColor else waterWavesColor,
fontSize = 22.sp
)
) {
append(" ")
append(unit)
}
}

Box(
modifier = Modifier
.fillMaxSize()
.fillMaxHeight(),
contentAlignment = Alignment.Center
) {
Text(text = text)
}
}
}

@Preview
@Composable
fun WaterBottlePreview() {
WaterBottle(
totalWaterAmount = 2500,
unit = "ml",
usedWaterAmount = 120
)
}
MainActivity.kt
package com.example.waterbottleapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.example.waterbottleapp.ui.theme.WaterBottleAppTheme

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
WaterBottleAppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
var usedWaterAmount by remember {
mutableStateOf(100)
}
val totalWaterAmount = remember {
2500
}

Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
WaterBottle(
totalWaterAmount = totalWaterAmount,
unit = "ml",
usedWaterAmount = usedWaterAmount
)
Spacer(modifier = Modifier.height(24.dp))
Text(
text = "Total Amount: $totalWaterAmount ml",
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = { if (usedWaterAmount < totalWaterAmount) usedWaterAmount += 200 },
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF379777))
) {
Text(text = "Isi Air")
}
}
}
}
}
}
}

Tampilan:





Rabu, 24 April 2024

calculatorApp


Kalkulator Sederhana adalah aplikasi Android yang dirancang untuk melakukan operasi matematika dasar seperti penjumlahan, pengurangan, perkalian, dan pembagian. Aplikasi ini memanfaatkan Jetpack Compose untuk membuat antarmuka pengguna yang modern dan responsif. Dengan tampilan yang bersih dan intuitif, pengguna dapat dengan mudah memasukkan angka dan operator untuk menghitung hasil ekspresi matematika.

Fitur Utama:

  1. Input Ekspresi: Pengguna dapat memasukkan angka dan operator matematika secara langsung pada bidang teks yang disediakan.
  2. Tampilan Hasil: Hasil dari ekspresi yang dimasukkan akan ditampilkan secara real-time setelah pengguna menekan tombol "=".
  3. Operasi Matematika: Mendukung operasi penjumlahan (+), pengurangan (-), perkalian (*), dan pembagian (/).
  4. Tombol C: Tombol ini digunakan untuk menghapus semua input yang telah dimasukkan, memungkinkan pengguna untuk memulai perhitungan baru.
  5. Desain Responsif: Antarmuka pengguna yang dirancang menggunakan Jetpack Compose memastikan aplikasi tetap responsif dan mudah digunakan pada berbagai ukuran layar.

Teknologi yang Digunakan:

  • Jetpack Compose: Digunakan untuk membangun antarmuka pengguna modern dan deklaratif.
  • Kotlin: Bahasa pemrograman utama untuk pengembangan aplikasi Android.
  • Android Studio: Lingkungan pengembangan terintegrasi (IDE) yang digunakan untuk mengembangkan dan menguji aplikasi.

Cara Menggunakan:

  1. Memasukkan Angka dan Operator: Gunakan tombol angka dan operator untuk membentuk ekspresi matematika.
  2. Menghitung Hasil: Tekan tombol "=" untuk menghitung dan menampilkan hasil dari ekspresi yang dimasukkan.
  3. Menghapus Input: Tekan tombol "C" untuk menghapus semua input dan memulai perhitungan baru.

    Source Code sebagai berikut:
    package com.example.calculatorapp

    import android.os.Bundle
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.compose.foundation.background
    import androidx.compose.foundation.layout.*
    import androidx.compose.foundation.text.BasicTextField
    import androidx.compose.material3.*
    import androidx.compose.runtime.*
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.graphics.Color
    import androidx.compose.ui.text.TextStyle
    import androidx.compose.ui.unit.dp
    import androidx.compose.ui.unit.sp
    import androidx.compose.ui.tooling.preview.Preview
    import com.example.calculatorapp.ui.theme.CalculatorAppTheme

    class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
    CalculatorAppTheme {
    CalculatorScreen()
    }
    }
    }
    }

    @Composable
    fun CalculatorScreen() {
    var input by remember { mutableStateOf("") }
    var result by remember { mutableStateOf("") }

    Column(
    modifier = Modifier
    .fillMaxSize()
    .padding(16.dp),
    verticalArrangement = Arrangement.Center,
    horizontalAlignment = Alignment.CenterHorizontally
    ) {
    BasicTextField(
    value = input,
    onValueChange = { input = it },
    textStyle = TextStyle(fontSize = 32.sp, color = Color.Black),
    modifier = Modifier
    .fillMaxWidth()
    .background(Color.White)
    .padding(16.dp)
    )
    Spacer(modifier = Modifier.height(16.dp))
    BasicTextField(
    value = result,
    onValueChange = { },
    textStyle = TextStyle(fontSize = 32.sp, color = Color.Black),
    modifier = Modifier
    .fillMaxWidth()
    .background(Color.LightGray)
    .padding(16.dp)
    )
    Spacer(modifier = Modifier.height(16.dp))
    Row(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.SpaceEvenly
    ) {
    CalculatorButton(text = "1", onClick = { input += "1" })
    CalculatorButton(text = "2", onClick = { input += "2" })
    CalculatorButton(text = "3", onClick = { input += "3" })
    CalculatorButton(text = "+", onClick = { input += "+" })
    }
    Row(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.SpaceEvenly
    ) {
    CalculatorButton(text = "4", onClick = { input += "4" })
    CalculatorButton(text = "5", onClick = { input += "5" })
    CalculatorButton(text = "6", onClick = { input += "6" })
    CalculatorButton(text = "-", onClick = { input += "-" })
    }
    Row(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.SpaceEvenly
    ) {
    CalculatorButton(text = "7", onClick = { input += "7" })
    CalculatorButton(text = "8", onClick = { input += "8" })
    CalculatorButton(text = "9", onClick = { input += "9" })
    CalculatorButton(text = "*", onClick = { input += "*" })
    }
    Row(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.SpaceEvenly
    ) {
    CalculatorButton(text = "0", onClick = { input += "0" })
    CalculatorButton(text = "C", onClick = { input = ""; result = "" })
    CalculatorButton(text = "=", onClick = { result = evaluateExpression(input) })
    CalculatorButton(text = "/", onClick = { input += "/" })
    }
    }
    }

    @Composable
    fun CalculatorButton(text: String, onClick: () -> Unit) {
    Button(
    onClick = onClick,
    modifier = Modifier
    .size(64.dp)
    .padding(4.dp)
    ) {
    Text(text = text, style = TextStyle(fontSize = 24.sp))
    }
    }

    fun evaluateExpression(expression: String): String {
    return try {
    val result = expressionEvaluator(expression)
    result.toString()
    } catch (e: Exception) {
    "Error"
    }
    }

    fun expressionEvaluator(expression: String): Double {
    val tokens = expression.replace(" ", "").toList()
    val values = mutableListOf<Double>()
    val ops = mutableListOf<Char>()

    var i = 0
    while (i < tokens.size) {
    if (tokens[i].isDigit() || tokens[i] == '.') {
    val sbuf = StringBuffer()
    while (i < tokens.size && (tokens[i].isDigit() || tokens[i] == '.')) {
    sbuf.append(tokens[i++])
    }
    values.add(sbuf.toString().toDouble())
    i--
    } else if (tokens[i] == '(') {
    ops.add(tokens[i])
    } else if (tokens[i] == ')') {
    while (ops.isNotEmpty() && ops.last() != '(') {
    values.add(applyOp(ops.removeAt(ops.size - 1), values.removeAt(values.size - 1), values.removeAt(values.size - 1)))
    }
    ops.removeAt(ops.size - 1)
    } else if (tokens[i] in listOf('+', '-', '*', '/')) {
    while (ops.isNotEmpty() && hasPrecedence(tokens[i], ops.last())) {
    values.add(applyOp(ops.removeAt(ops.size - 1), values.removeAt(values.size - 1), values.removeAt(values.size - 1)))
    }
    ops.add(tokens[i])
    }
    i++
    }

    while (ops.isNotEmpty()) {
    values.add(applyOp(ops.removeAt(ops.size - 1), values.removeAt(values.size - 1), values.removeAt(values.size - 1)))
    }

    return values.last()
    }

    fun hasPrecedence(op1: Char, op2: Char): Boolean {
    if (op2 == '(' || op2 == ')') return false
    if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-')) return false
    return true
    }

    fun applyOp(op: Char, b: Double, a: Double): Double {
    return when (op) {
    '+' -> a + b
    '-' -> a - b
    '*' -> a * b
    '/' -> {
    if (b == 0.0) throw UnsupportedOperationException("Cannot divide by zero")
    a / b
    }
    else -> throw UnsupportedOperationException("Unknown operator: $op")
    }
    }

    @Preview(showBackground = true)
    @Composable
    fun CalculatorScreenPreview() {
    CalculatorAppTheme {
    CalculatorScreen()
    }
    }
  4. Tampilan seperti berikut:






Selasa, 09 April 2024

loginPage

LoginPage adalah aplikasi Android sederhana yang menampilkan halaman login yang dibangun menggunakan Jetpack Compose Material 3. Aplikasi ini bertujuan untuk memberikan contoh dasar tentang bagaimana membuat UI interaktif dan modern dengan Jetpack Compose.

Fitur Utama

  1. Form Login: Menampilkan dua input field untuk username dan password.
  2. Input Teks yang Aman: Password field menggunakan visual transformation untuk menyembunyikan teks password.
  3. Desain Responsif: Layout yang menyesuaikan diri dengan berbagai ukuran layar dan orientasi perangkat.
  4. Tombol Login: Tombol untuk memicu aksi login ketika ditekan.

    Berikut adalah source code dan tampilanya:

    package com.example.loginpage

    import android.os.Bundle
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.compose.foundation.layout.*
    import androidx.compose.foundation.text.KeyboardOptions
    import androidx.compose.material3.*
    import androidx.compose.runtime.*
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.text.input.ImeAction
    import androidx.compose.ui.text.input.KeyboardType
    import androidx.compose.ui.text.input.PasswordVisualTransformation
    import androidx.compose.ui.unit.dp
    import androidx.compose.ui.tooling.preview.Preview
    import com.example.loginpage.ui.theme.LoginPageTheme

    class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
    LoginPageTheme {
    LoginScreen()
    }
    }
    }
    }

    @Composable
    fun LoginScreen() {
    var username by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }

    Column(
    modifier = Modifier
    .fillMaxSize()
    .padding(16.dp),
    verticalArrangement = Arrangement.Center,
    horizontalAlignment = Alignment.CenterHorizontally
    ) {
    Text(text = "Login", style = MaterialTheme.typography.headlineMedium)
    Spacer(modifier = Modifier.height(16.dp))
    OutlinedTextField(
    value = username,
    onValueChange = { username = it },
    label = { Text("Username") },
    modifier = Modifier.fillMaxWidth()
    )
    Spacer(modifier = Modifier.height(8.dp))
    OutlinedTextField(
    value = password,
    onValueChange = { password = it },
    label = { Text("Password") },
    modifier = Modifier.fillMaxWidth(),
    visualTransformation = PasswordVisualTransformation(),
    keyboardOptions = KeyboardOptions.Default.copy(
    keyboardType = KeyboardType.Password,
    imeAction = ImeAction.Done
    )
    )
    Spacer(modifier = Modifier.height(16.dp))
    Button(
    onClick = { /* Handle login action */ },
    modifier = Modifier.fillMaxWidth()
    ) {
    Text(text = "Login")
    }
    }
    }

    @Preview(showBackground = true)
    @Composable
    fun LoginScreenPreview() {
    LoginPageTheme {
    LoginScreen()
    }
    }
    Tampilan Login:



ComposeApp

Pada kesempatan kali ini saya mebuat app dengan menerapkan Jetpack Compose. Aplikasi sederhana yang saya buat bernama ComposeApp. Aplikasi ini menampilkan teks dinamis yang memperbarui jumlah klik setiap kali tombol ditekan. Dengan antarmuka yang minimalis dan interaktif, ComposeApp menunjukkan bagaimana Jetpack Compose dapat digunakan untuk membuat aplikasi yang responsif dan mudah dipelihara.

package com.example.composeapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.tooling.preview.Preview
import com.example.composeapp.ui.theme.ComposeAppTheme

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeAppTheme {
MyApp()
}
}
}
}

@Composable
fun MyApp() {
var count by remember { mutableIntStateOf(0) }

Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "You clicked the button $count times")
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { count++ }) {
Text(text = "Click me")
}
}
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
ComposeAppTheme {
MyApp()
}
}
Tampilan App:






EAS-PPB I