Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Step1: 장바구니 목록 구성 #48

Open
wants to merge 7 commits into
base: ironelder
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ dependencies {
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")

//coil
implementation("io.coil-kt:coil:2.7.0")
implementation("io.coil-kt:coil-compose:2.7.0")

//glide
implementation("com.github.bumptech.glide:compose:1.0.0-beta01")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

glide compose는 아직 beta이군요, 평소에도 glide compose를 활용하시는 편이신가요?

그러나, 이번 과제에서는 glide를 사용하지 않는거 같아요 :)
지워주셔도 좋을거같네요!


testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand All @@ -12,6 +14,7 @@
android:supportsRtl="true"
android:theme="@style/Theme.ShoppingCart"
tools:targetApi="31">

<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
67 changes: 64 additions & 3 deletions app/src/main/java/nextstep/shoppingcart/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,87 @@ package nextstep.shoppingcart
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ShoppingCart
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import nextstep.shoppingcart.data.model.Product
import nextstep.shoppingcart.ui.component.ShoppingItemView
import nextstep.shoppingcart.ui.theme.ShoppingCartTheme

class MainActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onCreate가 너무 비대해졌네요!
컴포즈 관련된 코드는 분리해보면 어떨까요?

super.onCreate(savedInstanceState)
val listOfItems = listOf(
Product(name = "name", price = 10000, description = "description"),
Product(name = "name2", price = 15000, description = "description"),
Product(name = "name3", price = 12000, description = "description"),
Product(name = "name4", price = 9000, description = "description"),
Product(name = "name5", price = 5500, description = "description"),
Product(name = "name6", price = 125000, description = "description"),
Product(name = "name7", price = 9900, description = "description"),
Product(name = "name8", price = 52000, description = "description")
)
setContent {
ShoppingCartTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
modifier = Modifier
.fillMaxSize()
.background(Color.White),
color = MaterialTheme.colorScheme.background,
) {
Greeting("Android")
Scaffold(
topBar = {
TopAppBar(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

컴포넌트를 나누는 본인만의 규칙을 고민해보는건 어떨까요?

title = {
Text(
text = stringResource(id = R.string.product_list),
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
},
actions = {
Icon(Icons.Filled.ShoppingCart, contentDescription = stringResource(id = R.string.app_name))
}
)
},
content = { padding ->
LazyVerticalGrid(
modifier = Modifier
.fillMaxSize()
.padding(padding),
columns = GridCells.Fixed(2),
verticalArrangement = Arrangement.spacedBy(10.dp),
horizontalArrangement = Arrangement.Center
) {
items(listOfItems) { item ->
ShoppingItemView(item)
}
}
}
)
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/java/nextstep/shoppingcart/data/model/Product.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package nextstep.shoppingcart.data.model

data class Product(
val imageUrl: String = "https://picsum.photos/500",
val name: String,
val price: Int,
val description: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package nextstep.shoppingcart.ui.component

import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil.ImageLoader
import coil.compose.AsyncImage
import coil.compose.AsyncImagePainter
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
import nextstep.shoppingcart.R
import nextstep.shoppingcart.data.model.Product
import nextstep.shoppingcart.ui.theme.getColorScheme
import okhttp3.OkHttpClient
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager

@OptIn(ExperimentalGlideComposeApi::class)
@Composable
fun ShoppingItemView(product: Product) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존 XML 기반에 View 와는 구분하기위해, 컴포즈에선 보통 ~View라고 네이밍 짓지 않고 있어요
View라는 네이밍 보다는 ShoppingItem만 사용하셔도 좋을거같아요!

val colorScheme = getColorScheme()

val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
})

val sslContext = SSLContext.getInstance("SSL").apply {
init(null, trustAllCerts, SecureRandom())
}

val okHttpClient = OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier { _, _ -> true }
.build()
Comment on lines +43 to +56

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ssl 관련 설정을 하신 이유가 있나요?
불필요해보이는거 같아요!


val imageLoader = ImageLoader.Builder(LocalContext.current)
.okHttpClient { okHttpClient }
.build()

Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(5.dp)
.background(colorScheme.background),
Comment on lines +63 to +67

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ShoppingItemView에 Modifier를 파라미터로 넘겨주는것과
내부에 선언한것은 어떤 차이가 있을까요?
재활용이나 프리뷰 측면에서도 고민해봐도 좋을거같네요!

horizontalAlignment = Alignment.CenterHorizontally
) {
AsyncImage(
modifier = Modifier
.fillMaxWidth()
.height(158.dp),
contentScale = ContentScale.Crop,
model = product.imageUrl,
contentDescription = null,
imageLoader = imageLoader,
onState = { state ->
when (state) {
Comment on lines +78 to +79

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onState를 사용하신 이유가 있을까요?
사용하지 않는 코드 같아요!

is AsyncImagePainter.State.Loading -> {

}

is AsyncImagePainter.State.Success -> {

}

is AsyncImagePainter.State.Error -> {
Log.d("irondler", "Error = ${state.result.throwable.message}")
}

else -> {

}
}
}
)
Text(
text = product.name,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 2.dp),
maxLines = 1,
softWrap = true,
fontWeight = FontWeight.Bold,
overflow = TextOverflow.Ellipsis,
color = if(isSystemInDarkTheme()) Color.White else Color.Black
)
Text(
text = stringResource(id = R.string.product_price, product.price),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 2.dp),
maxLines = 1,
softWrap = true,
overflow = TextOverflow.Ellipsis,
color = if(isSystemInDarkTheme()) Color.White else Color.Black
)
}
}

@Preview
@Composable
fun ShoppingItemViewPreview() {
Surface {
ShoppingItemView(Product(name = "name", price = 9999, description = "description"))
}
}
27 changes: 17 additions & 10 deletions app/src/main/java/nextstep/shoppingcart/ui/theme/Theme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package nextstep.shoppingcart.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
Expand Down Expand Up @@ -44,20 +45,12 @@ fun ShoppingCartTheme(
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}

darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val colorScheme = getColorScheme(darkTheme, dynamicColor)
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
window.statusBarColor = colorScheme.background.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
Expand All @@ -68,3 +61,17 @@ fun ShoppingCartTheme(
content = content
)
}

@Composable
fun getColorScheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
): ColorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}

darkTheme -> DarkColorScheme
else -> LightColorScheme
}
4 changes: 3 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<resources>
<string name="app_name">Shopping Cart</string>
</resources>
<string name="product_list">상품 목록</string>
<string name="product_price">%1d원</string>
</resources>