From 725eaed4b6f9fb4a8d1085b56305d8c6374bf451 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Fri, 5 Apr 2024 21:58:31 +0900 Subject: [PATCH 01/17] reset --- app/src/main/java/com/sopt/now/compose/SignInActivity.kt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 app/src/main/java/com/sopt/now/compose/SignInActivity.kt diff --git a/app/src/main/java/com/sopt/now/compose/SignInActivity.kt b/app/src/main/java/com/sopt/now/compose/SignInActivity.kt new file mode 100644 index 0000000..b87b3a0 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/SignInActivity.kt @@ -0,0 +1,2 @@ +package com.sopt.now.compose + From 52e4846c09b04478183122ec790e1f609112e111 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Fri, 5 Apr 2024 23:06:55 +0900 Subject: [PATCH 02/17] =?UTF-8?q?[feat]=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85,=20=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20UI=20=EA=B8=B0=EC=B4=88=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 10 +- .../java/com/sopt/now/compose/MainActivity.kt | 120 +++++++++++++--- .../com/sopt/now/compose/SignInActivity.kt | 109 +++++++++++++++ .../com/sopt/now/compose/SignUpActivity.kt | 128 ++++++++++++++++++ 5 files changed, 350 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/com/sopt/now/compose/SignUpActivity.kt diff --git a/app/build.gradle b/app/build.gradle index f534bf6..3d66ac1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -50,7 +50,7 @@ dependencies { implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0' implementation 'androidx.activity:activity-compose:1.8.2' - implementation platform('androidx.compose:compose-bom:2024.03.00') + implementation platform('androidx.compose:compose-bom:2024.04.00') implementation 'androidx.compose.ui:ui' implementation 'androidx.compose.ui:ui-graphics' implementation 'androidx.compose.ui:ui-tooling-preview' @@ -58,7 +58,7 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - androidTestImplementation platform('androidx.compose:compose-bom:2024.03.00') + androidTestImplementation platform('androidx.compose:compose-bom:2024.04.00') androidTestImplementation 'androidx.compose.ui:ui-test-junit4' debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-test-manifest' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9d2cd40..c76606c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,7 +13,7 @@ android:theme="@style/Theme.NOWSOPTAndroid" tools:targetApi="31"> @@ -23,6 +23,14 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/MainActivity.kt b/app/src/main/java/com/sopt/now/compose/MainActivity.kt index 73fb148..1e9741a 100644 --- a/app/src/main/java/com/sopt/now/compose/MainActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/MainActivity.kt @@ -3,13 +3,35 @@ package com.sopt.now.compose import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Face +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.Person +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.sopt.now.compose.ui.theme.NOWSOPTAndroidTheme class MainActivity : ComponentActivity() { @@ -17,30 +39,92 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContent { NOWSOPTAndroidTheme { - // A surface container using the 'background' color from the theme - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - Greeting("Android") - } + MainScreen() } } } } - -@Composable -fun Greeting(name: String, modifier: Modifier = Modifier) { - Text( - text = "Hello $name!", - modifier = modifier - ) -} - @Preview(showBackground = true) @Composable -fun GreetingPreview() { +fun MainScreen() { NOWSOPTAndroidTheme { - Greeting("Android") + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 30.dp), + ) { + + // Title + Text( + text = "SOPT에 온걸 환영해!", + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // Name + Text( + text = "SYAAINN", + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // ID + Text( + text = "아이디", + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // ID Info + Text( + text = "alswo0831", + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // PW + Text( + text = "비밀번호", + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // PW Info + Text( + text = "88888888", + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // Place + Text( + text = "거주지", + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // Place Info + Text( + text = "Hongdae", + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/SignInActivity.kt b/app/src/main/java/com/sopt/now/compose/SignInActivity.kt index b87b3a0..1238fcd 100644 --- a/app/src/main/java/com/sopt/now/compose/SignInActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/SignInActivity.kt @@ -1,2 +1,111 @@ package com.sopt.now.compose +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.Person +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.sopt.now.compose.ui.theme.NOWSOPTAndroidTheme + +class SignInActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + NOWSOPTAndroidTheme { + SignInScreen() + } + } + } +} +@Preview(showBackground = true) +@Composable +fun SignInScreen() { + NOWSOPTAndroidTheme { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 30.dp),) { + // 상태 관리를 위한 state 변수 선언 + var ID by remember { mutableStateOf("") } + var PW by remember { mutableStateOf("") } + + // Title + Text( + text = "Welcome to SOPT!", + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // ID 입력 필드 + TextField( + value = ID, + onValueChange = { ID = it }, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text("아이디를 입력하세요") }, // 입력 전 보이는 텍스트 + leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, // 시작 부분 아이콘 + singleLine = true, // 텍스트 단일 줄 제한 + ) + + // PW 입력 필드 + TextField( + value = PW, + onValueChange = { PW = it }, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text("비밀번호를 입력하세요") }, // 입력 전 보이는 텍스트 + leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, // 시작 부분 아이콘 + singleLine = true, // 텍스트 단일 줄 제한 + ) + + // Sign In + Button( + onClick = { /* 클릭 시 수행될 동작 */ }, // 버튼 클릭 시 실행되는 콜백 함수 + modifier = Modifier.padding(10.dp), // 버튼의 크기, 위치, 패딩 등을 조정 + colors = ButtonDefaults.buttonColors(containerColor = Color.Green), + shape = RoundedCornerShape(8.dp) + ) { + Text("로그인 하기",color=Color.Black) + } + + // Sign Up + Button( + onClick = { /* 클릭 시 수행될 동작 */ }, // 버튼 클릭 시 실행되는 콜백 함수 + modifier = Modifier.padding(10.dp), // 버튼의 크기, 위치, 패딩 등을 조정 + colors = ButtonDefaults.buttonColors(containerColor = Color.Green), + shape = RoundedCornerShape(8.dp) + ) { + Text("회원가입 하기",color=Color.Black) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt b/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt new file mode 100644 index 0000000..6925103 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt @@ -0,0 +1,128 @@ +package com.sopt.now.compose + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Face +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.Person +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.sopt.now.compose.ui.theme.NOWSOPTAndroidTheme +import java.util.jar.Attributes.Name + +class SignUpActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + NOWSOPTAndroidTheme { + SignUpScreen() + } + } + } +} +@Preview(showBackground = true) +@Composable +fun SignUpScreen() { + NOWSOPTAndroidTheme { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 30.dp),) { + // 상태 관리를 위한 state 변수 선언 + var ID by remember { mutableStateOf("") } + var PW by remember { mutableStateOf("") } + var Name by remember { mutableStateOf("") } + var Place by remember { mutableStateOf("") } + + // Title + Text( + text = "SIGN UP", + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // ID 입력 필드 + TextField( + value = ID, + onValueChange = { ID = it }, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text("아이디를 입력하세요") }, // 입력 전 보이는 텍스트 + leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, // 시작 부분 아이콘 + singleLine = true, // 텍스트 단일 줄 제한 + ) + + // PW 입력 필드 + TextField( + value = PW, + onValueChange = { PW = it }, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text("비밀번호를 입력하세요") }, // 입력 전 보이는 텍스트 + leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, // 시작 부분 아이콘 + singleLine = true, // 텍스트 단일 줄 제한 + ) + + // Name(닉네임) 입력 필드 + TextField( + value = Name, + onValueChange = { Name = it }, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text("닉네임을 입력하세요") }, // 입력 전 보이는 텍스트 + leadingIcon = { Icon(Icons.Filled.Face, contentDescription = "Name Icon") }, // 시작 부분 아이콘 + singleLine = true, // 텍스트 단일 줄 제한 + ) + + // Place(거주지) 입력 필드 + TextField( + value = Place, + onValueChange = { Place = it }, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text("거주지를 입력하세요") }, // 입력 전 보이는 텍스트 + leadingIcon = { Icon(Icons.Filled.Home, contentDescription = "Place Icon") }, // 시작 부분 아이콘 + singleLine = true, // 텍스트 단일 줄 제한 + ) + + // Sign Up + Button( + onClick = { /* 클릭 시 수행될 동작 */ }, // 버튼 클릭 시 실행되는 콜백 함수 + modifier = Modifier.padding(10.dp), // 버튼의 크기, 위치, 패딩 등을 조정 + colors = ButtonDefaults.buttonColors(containerColor = Color.Green), + shape = RoundedCornerShape(8.dp) + ) { + Text("회원가입 하기",color= Color.Black) + } + } + } +} From 1244772df09f3d5b25a6b45404daf4cb6b87ec21 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Fri, 5 Apr 2024 23:10:36 +0900 Subject: [PATCH 03/17] =?UTF-8?q?[refactor]=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/sopt/now/compose/SignInActivity.kt | 18 ++++++------ .../com/sopt/now/compose/SignUpActivity.kt | 28 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/sopt/now/compose/SignInActivity.kt b/app/src/main/java/com/sopt/now/compose/SignInActivity.kt index 1238fcd..b69c62e 100644 --- a/app/src/main/java/com/sopt/now/compose/SignInActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/SignInActivity.kt @@ -70,9 +70,9 @@ fun SignInScreen() { modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text("아이디를 입력하세요") }, // 입력 전 보이는 텍스트 + label = { Text("아이디를 입력하세요") }, // 입력 전 보이는 텍스트 leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, // 시작 부분 아이콘 - singleLine = true, // 텍스트 단일 줄 제한 + singleLine = true, // 텍스트 단일 줄 제한 ) // PW 입력 필드 @@ -82,15 +82,15 @@ fun SignInScreen() { modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text("비밀번호를 입력하세요") }, // 입력 전 보이는 텍스트 - leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, // 시작 부분 아이콘 - singleLine = true, // 텍스트 단일 줄 제한 + label = { Text("비밀번호를 입력하세요") }, + leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, + singleLine = true, ) // Sign In Button( - onClick = { /* 클릭 시 수행될 동작 */ }, // 버튼 클릭 시 실행되는 콜백 함수 - modifier = Modifier.padding(10.dp), // 버튼의 크기, 위치, 패딩 등을 조정 + onClick = { /* 클릭 시 수행될 동작 */ }, // 버튼 클릭 시 실행되는 콜백 함수 + modifier = Modifier.padding(10.dp), // 버튼의 크기, 위치, 패딩 등을 조정 colors = ButtonDefaults.buttonColors(containerColor = Color.Green), shape = RoundedCornerShape(8.dp) ) { @@ -99,8 +99,8 @@ fun SignInScreen() { // Sign Up Button( - onClick = { /* 클릭 시 수행될 동작 */ }, // 버튼 클릭 시 실행되는 콜백 함수 - modifier = Modifier.padding(10.dp), // 버튼의 크기, 위치, 패딩 등을 조정 + onClick = { /* 클릭 시 수행될 동작 */ }, + modifier = Modifier.padding(10.dp), colors = ButtonDefaults.buttonColors(containerColor = Color.Green), shape = RoundedCornerShape(8.dp) ) { diff --git a/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt b/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt index 6925103..66c79bf 100644 --- a/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt @@ -73,9 +73,9 @@ fun SignUpScreen() { modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text("아이디를 입력하세요") }, // 입력 전 보이는 텍스트 - leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, // 시작 부분 아이콘 - singleLine = true, // 텍스트 단일 줄 제한 + label = { Text("아이디를 입력하세요") }, + leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, + singleLine = true, ) // PW 입력 필드 @@ -85,9 +85,9 @@ fun SignUpScreen() { modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text("비밀번호를 입력하세요") }, // 입력 전 보이는 텍스트 - leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, // 시작 부분 아이콘 - singleLine = true, // 텍스트 단일 줄 제한 + label = { Text("비밀번호를 입력하세요") }, + leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, + singleLine = true, ) // Name(닉네임) 입력 필드 @@ -97,9 +97,9 @@ fun SignUpScreen() { modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text("닉네임을 입력하세요") }, // 입력 전 보이는 텍스트 - leadingIcon = { Icon(Icons.Filled.Face, contentDescription = "Name Icon") }, // 시작 부분 아이콘 - singleLine = true, // 텍스트 단일 줄 제한 + label = { Text("닉네임을 입력하세요") }, + leadingIcon = { Icon(Icons.Filled.Face, contentDescription = "Name Icon") }, + singleLine = true, ) // Place(거주지) 입력 필드 @@ -109,15 +109,15 @@ fun SignUpScreen() { modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text("거주지를 입력하세요") }, // 입력 전 보이는 텍스트 - leadingIcon = { Icon(Icons.Filled.Home, contentDescription = "Place Icon") }, // 시작 부분 아이콘 - singleLine = true, // 텍스트 단일 줄 제한 + label = { Text("거주지를 입력하세요") }, + leadingIcon = { Icon(Icons.Filled.Home, contentDescription = "Place Icon") }, + singleLine = true, ) // Sign Up Button( - onClick = { /* 클릭 시 수행될 동작 */ }, // 버튼 클릭 시 실행되는 콜백 함수 - modifier = Modifier.padding(10.dp), // 버튼의 크기, 위치, 패딩 등을 조정 + onClick = { /* 클릭 시 수행될 동작 */ }, + modifier = Modifier.padding(10.dp), colors = ButtonDefaults.buttonColors(containerColor = Color.Green), shape = RoundedCornerShape(8.dp) ) { From 92314142db837f0e69bcc691ceb6bef307d61a77 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Fri, 5 Apr 2024 23:41:41 +0900 Subject: [PATCH 04/17] =?UTF-8?q?[feat]=20=ED=99=94=EB=A9=B4=20=EA=B0=84?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/sopt/now/compose/MainActivity.kt | 4 +++ .../com/sopt/now/compose/SignInActivity.kt | 19 +++++++++--- .../com/sopt/now/compose/SignUpActivity.kt | 31 +++++++++++++++---- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/sopt/now/compose/MainActivity.kt b/app/src/main/java/com/sopt/now/compose/MainActivity.kt index 1e9741a..67065dd 100644 --- a/app/src/main/java/com/sopt/now/compose/MainActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/MainActivity.kt @@ -3,6 +3,7 @@ package com.sopt.now.compose import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -25,6 +26,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight @@ -52,6 +54,8 @@ fun MainScreen() { modifier = Modifier .fillMaxSize() .padding(horizontal = 30.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Top ) { // Title diff --git a/app/src/main/java/com/sopt/now/compose/SignInActivity.kt b/app/src/main/java/com/sopt/now/compose/SignInActivity.kt index b69c62e..4e6a984 100644 --- a/app/src/main/java/com/sopt/now/compose/SignInActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/SignInActivity.kt @@ -1,5 +1,6 @@ package com.sopt.now.compose +import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -25,6 +26,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -49,10 +51,13 @@ fun SignInScreen() { Column( modifier = Modifier .fillMaxSize() - .padding(horizontal = 30.dp),) { + .padding(horizontal = 30.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Top) { // 상태 관리를 위한 state 변수 선언 var ID by remember { mutableStateOf("") } var PW by remember { mutableStateOf("") } + val context = LocalContext.current // Title Text( @@ -89,8 +94,11 @@ fun SignInScreen() { // Sign In Button( - onClick = { /* 클릭 시 수행될 동작 */ }, // 버튼 클릭 시 실행되는 콜백 함수 - modifier = Modifier.padding(10.dp), // 버튼의 크기, 위치, 패딩 등을 조정 + onClick = { + val intent = Intent(context, MainActivity::class.java) + context.startActivity(intent) + }, + modifier = Modifier.padding(10.dp), colors = ButtonDefaults.buttonColors(containerColor = Color.Green), shape = RoundedCornerShape(8.dp) ) { @@ -99,7 +107,10 @@ fun SignInScreen() { // Sign Up Button( - onClick = { /* 클릭 시 수행될 동작 */ }, + onClick = { + val intent = Intent(context, SignUpActivity::class.java) + context.startActivity(intent) + }, modifier = Modifier.padding(10.dp), colors = ButtonDefaults.buttonColors(containerColor = Color.Green), shape = RoundedCornerShape(8.dp) diff --git a/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt b/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt index 66c79bf..07c7f3b 100644 --- a/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt @@ -1,8 +1,11 @@ package com.sopt.now.compose +import android.content.Intent import android.os.Bundle +import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -23,13 +26,16 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.core.content.ContextCompat.startActivity import com.sopt.now.compose.ui.theme.NOWSOPTAndroidTheme import java.util.jar.Attributes.Name @@ -50,12 +56,19 @@ fun SignUpScreen() { Column( modifier = Modifier .fillMaxSize() - .padding(horizontal = 30.dp),) { + .padding(horizontal = 30.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Top){ + // 상태 관리를 위한 state 변수 선언 var ID by remember { mutableStateOf("") } var PW by remember { mutableStateOf("") } var Name by remember { mutableStateOf("") } var Place by remember { mutableStateOf("") } + var isIDValid by remember { mutableStateOf(false) } + var isPWValid by remember { mutableStateOf(false) } + var isNameValid by remember { mutableStateOf(false) } + val context = LocalContext.current // Title Text( @@ -69,7 +82,8 @@ fun SignUpScreen() { // ID 입력 필드 TextField( value = ID, - onValueChange = { ID = it }, + onValueChange = { newID -> ID = newID + isIDValid = ID.trim().length in 6..10}, modifier = Modifier .fillMaxWidth() .padding(10.dp), @@ -81,7 +95,8 @@ fun SignUpScreen() { // PW 입력 필드 TextField( value = PW, - onValueChange = { PW = it }, + onValueChange = { newPW -> PW = newPW + isPWValid = PW.trim().length in 8..12}, modifier = Modifier .fillMaxWidth() .padding(10.dp), @@ -93,7 +108,8 @@ fun SignUpScreen() { // Name(닉네임) 입력 필드 TextField( value = Name, - onValueChange = { Name = it }, + onValueChange = { newName -> Name = newName + isNameValid = Name.trim().isNotEmpty()}, modifier = Modifier .fillMaxWidth() .padding(10.dp), @@ -105,7 +121,7 @@ fun SignUpScreen() { // Place(거주지) 입력 필드 TextField( value = Place, - onValueChange = { Place = it }, + onValueChange = { newPlace -> Place = newPlace }, modifier = Modifier .fillMaxWidth() .padding(10.dp), @@ -116,7 +132,10 @@ fun SignUpScreen() { // Sign Up Button( - onClick = { /* 클릭 시 수행될 동작 */ }, + onClick = { + val intent = Intent(context, SignInActivity::class.java) + context.startActivity(intent) + }, modifier = Modifier.padding(10.dp), colors = ButtonDefaults.buttonColors(containerColor = Color.Green), shape = RoundedCornerShape(8.dp) From 33cd85b3628c7b29e2e4fd2a397734b8dde19ce6 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Sun, 7 Apr 2024 20:34:57 +0900 Subject: [PATCH 05/17] [refactor] extract strings --- app/src/main/AndroidManifest.xml | 1 - .../java/com/sopt/now/compose/MainActivity.kt | 17 +-- .../com/sopt/now/compose/SignInActivity.kt | 135 +++++++++--------- .../com/sopt/now/compose/SignUpActivity.kt | 13 +- app/src/main/res/values/strings.xml | 23 +++ 5 files changed, 106 insertions(+), 83 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c76606c..0e0a4f3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,7 +15,6 @@ diff --git a/app/src/main/java/com/sopt/now/compose/MainActivity.kt b/app/src/main/java/com/sopt/now/compose/MainActivity.kt index 67065dd..91dc77c 100644 --- a/app/src/main/java/com/sopt/now/compose/MainActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/MainActivity.kt @@ -29,6 +29,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -60,7 +61,7 @@ fun MainScreen() { // Title Text( - text = "SOPT에 온걸 환영해!", + text = stringResource(R.string.txt_Main_Title), modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, @@ -69,7 +70,7 @@ fun MainScreen() { // Name Text( - text = "SYAAINN", + text = stringResource(R.string.txt_Main_ShowName), modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, @@ -78,7 +79,7 @@ fun MainScreen() { // ID Text( - text = "아이디", + text = stringResource(R.string.txt_Main_Id), modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, @@ -87,7 +88,7 @@ fun MainScreen() { // ID Info Text( - text = "alswo0831", + text = stringResource(R.string.txt_Main_ShowId), modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, @@ -96,7 +97,7 @@ fun MainScreen() { // PW Text( - text = "비밀번호", + text = stringResource(R.string.txt_Main_Pw), modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, @@ -105,7 +106,7 @@ fun MainScreen() { // PW Info Text( - text = "88888888", + text = stringResource(R.string.txt_Main_ShowPw), modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, @@ -114,7 +115,7 @@ fun MainScreen() { // Place Text( - text = "거주지", + text = stringResource(R.string.txt_Main_Place), modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, @@ -123,7 +124,7 @@ fun MainScreen() { // Place Info Text( - text = "Hongdae", + text = stringResource(R.string.txt_Main_ShowPlace), modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, diff --git a/app/src/main/java/com/sopt/now/compose/SignInActivity.kt b/app/src/main/java/com/sopt/now/compose/SignInActivity.kt index 4e6a984..7aef838 100644 --- a/app/src/main/java/com/sopt/now/compose/SignInActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/SignInActivity.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color 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.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -38,85 +39,83 @@ class SignInActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - NOWSOPTAndroidTheme { - SignInScreen() - } + SignInScreen() } } } + @Preview(showBackground = true) @Composable fun SignInScreen() { - NOWSOPTAndroidTheme { - Column( - modifier = Modifier - .fillMaxSize() - .padding(horizontal = 30.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Top) { - // 상태 관리를 위한 state 변수 선언 - var ID by remember { mutableStateOf("") } - var PW by remember { mutableStateOf("") } - val context = LocalContext.current + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 30.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Top + ) { + var ID by remember { mutableStateOf("") } + var PW by remember { mutableStateOf("") } + val context = LocalContext.current - // Title - Text( - text = "Welcome to SOPT!", - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) + // Title + Text( + text = stringResource(R.string.txt_SignIn_Title), + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) - // ID 입력 필드 - TextField( - value = ID, - onValueChange = { ID = it }, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text("아이디를 입력하세요") }, // 입력 전 보이는 텍스트 - leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, // 시작 부분 아이콘 - singleLine = true, // 텍스트 단일 줄 제한 - ) + // ID 입력 필드 + TextField( + value = ID, + onValueChange = { ID = it }, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text(stringResource(R.string.tf_SignIn_Id_Hint)) }, + leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, + singleLine = true, + ) - // PW 입력 필드 - TextField( - value = PW, - onValueChange = { PW = it }, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text("비밀번호를 입력하세요") }, - leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, - singleLine = true, - ) + // PW 입력 필드 + TextField( + value = PW, + onValueChange = { PW = it }, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text(stringResource(R.string.tf_SignIn_Pw_Hint)) }, + leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, + singleLine = true, + ) - // Sign In - Button( - onClick = { - val intent = Intent(context, MainActivity::class.java) - context.startActivity(intent) - }, - modifier = Modifier.padding(10.dp), - colors = ButtonDefaults.buttonColors(containerColor = Color.Green), - shape = RoundedCornerShape(8.dp) - ) { - Text("로그인 하기",color=Color.Black) - } + // Sign In + Button( + onClick = { + val intent = Intent(context, MainActivity::class.java) + context.startActivity(intent) + }, + modifier = Modifier.padding(10.dp), + colors = ButtonDefaults.buttonColors(containerColor = Color.Green), + shape = RoundedCornerShape(8.dp) + ) { + Text(stringResource(R.string.btn_SignIn_SignIn), color = Color.Black) + } - // Sign Up - Button( - onClick = { - val intent = Intent(context, SignUpActivity::class.java) - context.startActivity(intent) - }, - modifier = Modifier.padding(10.dp), - colors = ButtonDefaults.buttonColors(containerColor = Color.Green), - shape = RoundedCornerShape(8.dp) - ) { - Text("회원가입 하기",color=Color.Black) - } + // Sign Up + Button( + onClick = { + val intent = Intent(context, SignUpActivity::class.java) + context.startActivity(intent) + }, + modifier = Modifier.padding(10.dp), + colors = ButtonDefaults.buttonColors(containerColor = Color.Green), + shape = RoundedCornerShape(8.dp) + ) { + Text(stringResource(R.string.btn_SignIn_SignUp), color = Color.Black) } } + } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt b/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt index 07c7f3b..f07483d 100644 --- a/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color 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.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -72,7 +73,7 @@ fun SignUpScreen() { // Title Text( - text = "SIGN UP", + text = stringResource(R.string.txt_SignUp_Title), modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, @@ -87,7 +88,7 @@ fun SignUpScreen() { modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text("아이디를 입력하세요") }, + label = { Text(stringResource(R.string.tf_SignUp_Id_Hint)) }, leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, singleLine = true, ) @@ -100,7 +101,7 @@ fun SignUpScreen() { modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text("비밀번호를 입력하세요") }, + label = { Text(stringResource(R.string.tf_SignUp_Pw_Hint)) }, leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, singleLine = true, ) @@ -113,7 +114,7 @@ fun SignUpScreen() { modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text("닉네임을 입력하세요") }, + label = { Text(stringResource(R.string.tf_SignUp_Name_Hint)) }, leadingIcon = { Icon(Icons.Filled.Face, contentDescription = "Name Icon") }, singleLine = true, ) @@ -125,7 +126,7 @@ fun SignUpScreen() { modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text("거주지를 입력하세요") }, + label = { Text(stringResource(R.string.tf_SignUp_Place_Hint)) }, leadingIcon = { Icon(Icons.Filled.Home, contentDescription = "Place Icon") }, singleLine = true, ) @@ -140,7 +141,7 @@ fun SignUpScreen() { colors = ButtonDefaults.buttonColors(containerColor = Color.Green), shape = RoundedCornerShape(8.dp) ) { - Text("회원가입 하기",color= Color.Black) + Text(stringResource(R.string.btn_SignUp_SignUp),color= Color.Black) } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index be0d381..482d09b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,26 @@ NOW SOPT Android + + + Welcome to SOPT! + 아이디를 입력하세요 + 비밀번호를 입력하세요 + 로그인 하기 + 회원가입 하기 + + + SIGN UP + 아이디를 입력하세요 + 비밀번호를 입력하세요 + 닉네임을 입력하세요 + 거주지를 입력하세요 + 회원가입 하기 + SOPT에 온걸 환영해! + SYAAINN + 아이디 + alswo0831 + 비밀번호 + 88888888 + 거주지 + Hongdae \ No newline at end of file From a59a0e40892bc798b3b6dced3ba93dcdaabb83b6 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Sun, 7 Apr 2024 20:46:18 +0900 Subject: [PATCH 06/17] [refactor] add string comment --- app/src/main/res/values/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 482d09b..717e5e7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -15,6 +15,8 @@ 닉네임을 입력하세요 거주지를 입력하세요 회원가입 하기 + + SOPT에 온걸 환영해! SYAAINN 아이디 From c535420d76f655c912c7c63df305bc93b05651b6 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Tue, 9 Apr 2024 10:17:47 +0900 Subject: [PATCH 07/17] =?UTF-8?q?feat=20:=20navigation=20implementation=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/app/build.gradle b/app/build.gradle index 3d66ac1..ed73adf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,7 @@ dependencies { implementation 'androidx.compose.ui:ui-graphics' implementation 'androidx.compose.ui:ui-tooling-preview' implementation 'androidx.compose.material3:material3' + implementation 'androidx.navigation:navigation-compose:2.7.7' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' From 70339ac272d0bc545e253d53f62f1af151fd07f2 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Wed, 10 Apr 2024 20:14:53 +0900 Subject: [PATCH 08/17] =?UTF-8?q?feat=20:=20navigation=EC=9D=84=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=9C=20=ED=99=94=EB=A9=B4=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=EA=B3=BC=20=EC=A0=95=EB=B3=B4=20=EC=A0=84=EB=8B=AC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 10 +- .../java/com/sopt/now/compose/MainActivity.kt | 149 ++++-------------- .../com/sopt/now/compose/SignUpActivity.kt | 148 ----------------- .../sopt/now/compose/screen/MyPageScreen.kt | 104 ++++++++++++ .../SignInScreen.kt} | 68 ++++---- .../sopt/now/compose/screen/SignUpScreen.kt | 136 ++++++++++++++++ 7 files changed, 301 insertions(+), 315 deletions(-) delete mode 100644 app/src/main/java/com/sopt/now/compose/SignUpActivity.kt create mode 100644 app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt rename app/src/main/java/com/sopt/now/compose/{SignInActivity.kt => screen/SignInScreen.kt} (61%) create mode 100644 app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt diff --git a/app/build.gradle b/app/build.gradle index ed73adf..35c39c6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,6 +55,7 @@ dependencies { implementation 'androidx.compose.ui:ui-graphics' implementation 'androidx.compose.ui:ui-tooling-preview' implementation 'androidx.compose.material3:material3' + // Navigation implementation 'androidx.navigation:navigation-compose:2.7.7' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0e0a4f3..a9fa545 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,7 +13,7 @@ android:theme="@style/Theme.NOWSOPTAndroid" tools:targetApi="31"> @@ -22,14 +22,6 @@ - - \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/MainActivity.kt b/app/src/main/java/com/sopt/now/compose/MainActivity.kt index 91dc77c..2bd6b48 100644 --- a/app/src/main/java/com/sopt/now/compose/MainActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/MainActivity.kt @@ -3,133 +3,40 @@ package com.sopt.now.compose import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Face -import androidx.compose.material.icons.filled.Home -import androidx.compose.material.icons.filled.Lock -import androidx.compose.material.icons.filled.Person -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.sopt.now.compose.ui.theme.NOWSOPTAndroidTheme +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import com.sopt.now.compose.screen.SignInScreen +import com.sopt.now.compose.screen.SignUpScreen +import com.sopt.now.compose.screen.MyPageScreen class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - NOWSOPTAndroidTheme { - MainScreen() + val navController = rememberNavController() + + NavHost(navController = navController, startDestination = "SignIn") { + composable("SignIn?ID={ID}&PW={PW}&Name={Name}&Place={Place}") { backStackEntry -> + SignInScreen( + navController = navController, + ID = backStackEntry.arguments?.getString("ID") ?: "", + PW = backStackEntry.arguments?.getString("PW") ?: "", + Name = backStackEntry.arguments?.getString("Name") ?: "", + Place = backStackEntry.arguments?.getString("Place") ?: "" + ) + } + composable("SignUp") { + SignUpScreen(navController = navController) + } + composable("MyPage?ID={InputID}&PW={InputPW}&Name={Name}&Place={Place}") {backStackEntry -> + MyPageScreen( + ID = backStackEntry.arguments?.getString("InputID") ?: "", + PW = backStackEntry.arguments?.getString("InputPW") ?: "", + Name = backStackEntry.arguments?.getString("Name") ?: "", + Place = backStackEntry.arguments?.getString("Place") ?: "") + } } } } -} -@Preview(showBackground = true) -@Composable -fun MainScreen() { - NOWSOPTAndroidTheme { - Column( - modifier = Modifier - .fillMaxSize() - .padding(horizontal = 30.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Top - ) { - - // Title - Text( - text = stringResource(R.string.txt_Main_Title), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // Name - Text( - text = stringResource(R.string.txt_Main_ShowName), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // ID - Text( - text = stringResource(R.string.txt_Main_Id), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // ID Info - Text( - text = stringResource(R.string.txt_Main_ShowId), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // PW - Text( - text = stringResource(R.string.txt_Main_Pw), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // PW Info - Text( - text = stringResource(R.string.txt_Main_ShowPw), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // Place - Text( - text = stringResource(R.string.txt_Main_Place), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // Place Info - Text( - text = stringResource(R.string.txt_Main_ShowPlace), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt b/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt deleted file mode 100644 index f07483d..0000000 --- a/app/src/main/java/com/sopt/now/compose/SignUpActivity.kt +++ /dev/null @@ -1,148 +0,0 @@ -package com.sopt.now.compose - -import android.content.Intent -import android.os.Bundle -import android.widget.Toast -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Face -import androidx.compose.material.icons.filled.Home -import androidx.compose.material.icons.filled.Lock -import androidx.compose.material.icons.filled.Person -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -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.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.core.content.ContextCompat.startActivity -import com.sopt.now.compose.ui.theme.NOWSOPTAndroidTheme -import java.util.jar.Attributes.Name - -class SignUpActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContent { - NOWSOPTAndroidTheme { - SignUpScreen() - } - } - } -} -@Preview(showBackground = true) -@Composable -fun SignUpScreen() { - NOWSOPTAndroidTheme { - Column( - modifier = Modifier - .fillMaxSize() - .padding(horizontal = 30.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Top){ - - // 상태 관리를 위한 state 변수 선언 - var ID by remember { mutableStateOf("") } - var PW by remember { mutableStateOf("") } - var Name by remember { mutableStateOf("") } - var Place by remember { mutableStateOf("") } - var isIDValid by remember { mutableStateOf(false) } - var isPWValid by remember { mutableStateOf(false) } - var isNameValid by remember { mutableStateOf(false) } - val context = LocalContext.current - - // Title - Text( - text = stringResource(R.string.txt_SignUp_Title), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // ID 입력 필드 - TextField( - value = ID, - onValueChange = { newID -> ID = newID - isIDValid = ID.trim().length in 6..10}, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text(stringResource(R.string.tf_SignUp_Id_Hint)) }, - leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, - singleLine = true, - ) - - // PW 입력 필드 - TextField( - value = PW, - onValueChange = { newPW -> PW = newPW - isPWValid = PW.trim().length in 8..12}, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text(stringResource(R.string.tf_SignUp_Pw_Hint)) }, - leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, - singleLine = true, - ) - - // Name(닉네임) 입력 필드 - TextField( - value = Name, - onValueChange = { newName -> Name = newName - isNameValid = Name.trim().isNotEmpty()}, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text(stringResource(R.string.tf_SignUp_Name_Hint)) }, - leadingIcon = { Icon(Icons.Filled.Face, contentDescription = "Name Icon") }, - singleLine = true, - ) - - // Place(거주지) 입력 필드 - TextField( - value = Place, - onValueChange = { newPlace -> Place = newPlace }, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text(stringResource(R.string.tf_SignUp_Place_Hint)) }, - leadingIcon = { Icon(Icons.Filled.Home, contentDescription = "Place Icon") }, - singleLine = true, - ) - - // Sign Up - Button( - onClick = { - val intent = Intent(context, SignInActivity::class.java) - context.startActivity(intent) - }, - modifier = Modifier.padding(10.dp), - colors = ButtonDefaults.buttonColors(containerColor = Color.Green), - shape = RoundedCornerShape(8.dp) - ) { - Text(stringResource(R.string.btn_SignUp_SignUp),color= Color.Black) - } - } - } -} diff --git a/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt new file mode 100644 index 0000000..97ab0e5 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt @@ -0,0 +1,104 @@ +package com.sopt.now.compose.screen + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.sopt.now.compose.R +import com.sopt.now.compose.ui.theme.NOWSOPTAndroidTheme + +@Composable +fun MyPageScreen(ID: String, PW: String, Name: String, Place: String) { + + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 30.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Top + ) { + + // Title + Text( + text = stringResource(R.string.txt_Main_Title), + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // Name + Text( + text = Name, + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // ID + Text( + text = stringResource(R.string.txt_Main_Id), + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // ID Info + Text( + text = ID, + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // PW + Text( + text = stringResource(R.string.txt_Main_Pw), + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // PW Info + Text( + text = PW, + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // Place + Text( + text = stringResource(R.string.txt_Main_Place), + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // Place Info + Text( + text = Place, + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/SignInActivity.kt b/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt similarity index 61% rename from app/src/main/java/com/sopt/now/compose/SignInActivity.kt rename to app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt index 7aef838..d1e2e26 100644 --- a/app/src/main/java/com/sopt/now/compose/SignInActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt @@ -1,9 +1,5 @@ -package com.sopt.now.compose +package com.sopt.now.compose.screen -import android.content.Intent -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -26,27 +22,20 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -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.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.sopt.now.compose.ui.theme.NOWSOPTAndroidTheme +import androidx.navigation.NavController -class SignInActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContent { - SignInScreen() - } - } -} - -@Preview(showBackground = true) @Composable -fun SignInScreen() { +fun SignInScreen( + navController: NavController, + ID: String, + PW: String, + Name: String, + Place: String +) { Column( modifier = Modifier .fillMaxSize() @@ -54,13 +43,13 @@ fun SignInScreen() { horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Top ) { - var ID by remember { mutableStateOf("") } - var PW by remember { mutableStateOf("") } - val context = LocalContext.current + // 상태 관리를 위한 state 변수 선언 + var InputID by remember { mutableStateOf("") } + var InputPW by remember { mutableStateOf("") } // Title Text( - text = stringResource(R.string.txt_SignIn_Title), + text = "Welcome to SOPT!", modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, @@ -69,24 +58,29 @@ fun SignInScreen() { // ID 입력 필드 TextField( - value = ID, - onValueChange = { ID = it }, + value = InputID, + onValueChange = { InputID = it }, modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text(stringResource(R.string.tf_SignIn_Id_Hint)) }, - leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, + label = { Text("아이디를 입력하세요") }, + leadingIcon = { + Icon( + Icons.Filled.Person, + contentDescription = "ID Icon" + ) + }, singleLine = true, ) // PW 입력 필드 TextField( - value = PW, - onValueChange = { PW = it }, + value = InputPW, + onValueChange = { InputPW = it }, modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text(stringResource(R.string.tf_SignIn_Pw_Hint)) }, + label = { Text("비밀번호를 입력하세요") }, leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, singleLine = true, ) @@ -94,27 +88,27 @@ fun SignInScreen() { // Sign In Button( onClick = { - val intent = Intent(context, MainActivity::class.java) - context.startActivity(intent) + if ((InputID == ID) && (InputPW == PW)) { + navController.navigate("MyPage?ID=$InputID&PW=$InputPW&Name=$Name&Place=$Place") + } }, modifier = Modifier.padding(10.dp), colors = ButtonDefaults.buttonColors(containerColor = Color.Green), shape = RoundedCornerShape(8.dp) ) { - Text(stringResource(R.string.btn_SignIn_SignIn), color = Color.Black) + Text("로그인 하기", color = Color.Black) } // Sign Up Button( onClick = { - val intent = Intent(context, SignUpActivity::class.java) - context.startActivity(intent) + navController.navigate("SignUp") }, modifier = Modifier.padding(10.dp), colors = ButtonDefaults.buttonColors(containerColor = Color.Green), shape = RoundedCornerShape(8.dp) ) { - Text(stringResource(R.string.btn_SignIn_SignUp), color = Color.Black) + Text("회원가입 하기", color = Color.Black) } } diff --git a/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt new file mode 100644 index 0000000..019a0a8 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt @@ -0,0 +1,136 @@ +package com.sopt.now.compose.screen + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Face +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.Person +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import com.sopt.now.compose.R +@Composable +fun SignUpScreen(navController: NavController) { + val (value, setValue) = remember { + mutableStateOf("") + } + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 30.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Top + ) { + + // 상태 관리를 위한 state 변수 선언 + var ID by remember { mutableStateOf("") } + var PW by remember { mutableStateOf("") } + var Name by remember { mutableStateOf("") } + var Place by remember { mutableStateOf("") } + var isIDValid by remember { mutableStateOf(false) } + var isPWValid by remember { mutableStateOf(false) } + var isNameValid by remember { mutableStateOf(false) } + + // Title + Text( + text = stringResource(R.string.txt_SignUp_Title), + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + + // ID 입력 필드 + TextField( + value = ID, + onValueChange = { newID -> + ID = newID + isIDValid = ID.trim().length in 6..10 + }, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text(stringResource(R.string.tf_SignUp_Id_Hint)) }, + leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, + singleLine = true, + ) + + // PW 입력 필드 + TextField( + value = PW, + onValueChange = { newPW -> + PW = newPW + isPWValid = PW.trim().length in 8..12 + }, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text(stringResource(R.string.tf_SignUp_Pw_Hint)) }, + leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, + singleLine = true, + ) + + // Name(닉네임) 입력 필드 + TextField( + value = Name, + onValueChange = { newName -> + Name = newName + isNameValid = Name.trim().isNotEmpty() + }, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text(stringResource(R.string.tf_SignUp_Name_Hint)) }, + leadingIcon = { Icon(Icons.Filled.Face, contentDescription = "Name Icon") }, + singleLine = true, + ) + + // Place(거주지) 입력 필드 + TextField( + value = Place, + onValueChange = { newPlace -> Place = newPlace }, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text(stringResource(R.string.tf_SignUp_Place_Hint)) }, + leadingIcon = { Icon(Icons.Filled.Home, contentDescription = "Place Icon") }, + singleLine = true, + ) + + // Sign Up + Button( + onClick = { + navController.navigate("SignIn?ID=$ID&PW=$PW&Name=$Name&Place=$Place") + }, + modifier = Modifier.padding(10.dp), + colors = ButtonDefaults.buttonColors(containerColor = Color.Green), + shape = RoundedCornerShape(8.dp) + ) { + Text(stringResource(R.string.btn_SignUp_SignUp), color = Color.Black) + } + } + +} From 90758387daa33ee7f60fdc7fdc31444710c37ec4 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Thu, 11 Apr 2024 01:03:21 +0900 Subject: [PATCH 09/17] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EA=B2=80=EC=82=AC=20=ED=95=A8=EC=88=98=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EB=B0=8F=20=ED=95=A8=EC=88=98=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/now/compose/screen/SignInScreen.kt | 150 ++++++++++-------- .../sopt/now/compose/screen/SignUpScreen.kt | 4 +- app/src/main/res/values/strings.xml | 15 +- 3 files changed, 97 insertions(+), 72 deletions(-) diff --git a/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt index d1e2e26..2c8df0d 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt @@ -1,17 +1,16 @@ package com.sopt.now.compose.screen +import android.content.Context +import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Person -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable @@ -21,20 +20,19 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +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.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController +import com.sopt.now.compose.R @Composable fun SignInScreen( - navController: NavController, - ID: String, - PW: String, - Name: String, - Place: String + navController: NavController, ID: String, PW: String, Name: String, Place: String ) { Column( modifier = Modifier @@ -43,73 +41,101 @@ fun SignInScreen( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Top ) { - // 상태 관리를 위한 state 변수 선언 - var InputID by remember { mutableStateOf("") } - var InputPW by remember { mutableStateOf("") } + var inputID by remember { mutableStateOf("") } + var inputPW by remember { mutableStateOf("") } + val context = LocalContext.current - // Title Text( - text = "Welcome to SOPT!", + text = stringResource(id = R.string.txt_SignIn_Title ), modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center ) - // ID 입력 필드 - TextField( - value = InputID, - onValueChange = { InputID = it }, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text("아이디를 입력하세요") }, - leadingIcon = { - Icon( - Icons.Filled.Person, - contentDescription = "ID Icon" - ) - }, - singleLine = true, + SOPTTextField( + inputID, + { inputID = it }, + stringResource(id = R.string.tf_SignUp_ID_Hint), + Icons.Filled.Person ) - // PW 입력 필드 - TextField( - value = InputPW, - onValueChange = { InputPW = it }, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text("비밀번호를 입력하세요") }, - leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, - singleLine = true, + SOPTTextField( + inputPW, + { inputPW = it }, + stringResource(id = R.string.tf_SignUp_PW_Hint ), + Icons.Filled.Person ) - // Sign In - Button( - onClick = { - if ((InputID == ID) && (InputPW == PW)) { - navController.navigate("MyPage?ID=$InputID&PW=$InputPW&Name=$Name&Place=$Place") - } - }, - modifier = Modifier.padding(10.dp), - colors = ButtonDefaults.buttonColors(containerColor = Color.Green), - shape = RoundedCornerShape(8.dp) - ) { - Text("로그인 하기", color = Color.Black) + SOPTOutlinedButton( + R.string.btn_SignIn_SignIn, + { isSignInValid(navController, context, inputID, inputPW, ID, PW, Name, Place) }, + true + ) + + SOPTOutlinedButton( + R.string.btn_SignIn_SignUp, + { navController.navigate("SignUp") }, + true + ) + } +} + +@Composable +fun SOPTTextField( + value: String, + onValueChange: (String) -> Unit, + label: String, + leadingIcon: ImageVector, + modifier: Modifier = Modifier, +) { + TextField( + value = value, + onValueChange = onValueChange, + modifier = modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text(label) }, + leadingIcon = { Icon(leadingIcon, contentDescription = null) }, + singleLine = true, + ) +} + +@Composable +fun SOPTOutlinedButton(text: Int, onClick: () -> Unit, enabled: Boolean) { + OutlinedButton( + onClick = onClick, modifier = Modifier.fillMaxWidth(), enabled = enabled + ) { + Text(text = stringResource(id = text)) + } +} + +fun isSignInValid( + navController: NavController, + context: Context, + inputID: String, + inputPW: String, + ID: String, + PW: String, + Name: String, + Place: String +) { + when { + (inputID == ID && inputPW == PW) -> { + showToast(context, R.string.toast_SignIn_ValidSignIn) + navController.navigate("MyPage?ID=$inputID&PW=$inputPW&Name=$Name&Place=$Place") } - // Sign Up - Button( - onClick = { - navController.navigate("SignUp") - }, - modifier = Modifier.padding(10.dp), - colors = ButtonDefaults.buttonColors(containerColor = Color.Green), - shape = RoundedCornerShape(8.dp) - ) { - Text("회원가입 하기", color = Color.Black) + (inputID != ID) -> { + showToast(context, R.string.toast_SignIn_InvalidID) + } + + (inputPW != PW) -> { + showToast(context, R.string.toast_SignIn_InvalidPW) } } +} +fun showToast(context: Context, message: Int) { + Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt index 019a0a8..6038d2b 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt @@ -73,7 +73,7 @@ fun SignUpScreen(navController: NavController) { modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text(stringResource(R.string.tf_SignUp_Id_Hint)) }, + label = { Text(stringResource(R.string.tf_SignUp_ID_Hint)) }, leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, singleLine = true, ) @@ -88,7 +88,7 @@ fun SignUpScreen(navController: NavController) { modifier = Modifier .fillMaxWidth() .padding(10.dp), - label = { Text(stringResource(R.string.tf_SignUp_Pw_Hint)) }, + label = { Text(stringResource(R.string.tf_SignUp_PW_Hint)) }, leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, singleLine = true, ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 717e5e7..4f0b48e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,26 +3,25 @@ Welcome to SOPT! - 아이디를 입력하세요 - 비밀번호를 입력하세요 + 아이디를 입력하세요 + 비밀번호를 입력하세요 로그인 하기 회원가입 하기 + 로그인 성공 + 아이디가 잘못되었습니다. + 비밀번호가 잘못되었습니다. SIGN UP - 아이디를 입력하세요 - 비밀번호를 입력하세요 + 아이디를 입력하세요 + 비밀번호를 입력하세요 닉네임을 입력하세요 거주지를 입력하세요 회원가입 하기 SOPT에 온걸 환영해! - SYAAINN 아이디 - alswo0831 비밀번호 - 88888888 거주지 - Hongdae \ No newline at end of file From 1bd49b146dc47b575d6fee5d801dbc1291d3af3e Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Thu, 11 Apr 2024 01:41:33 +0900 Subject: [PATCH 10/17] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EA=B2=80=EC=82=AC=20=ED=95=A8=EC=88=98=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=B0=8F=20=ED=95=A8=EC=88=98=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/sopt/now/compose/Sources.kt | 120 ++++++++++++++++++ .../sopt/now/compose/screen/MyPageScreen.kt | 2 - .../sopt/now/compose/screen/SignInScreen.kt | 73 +---------- .../sopt/now/compose/screen/SignUpScreen.kt | 88 +++++-------- app/src/main/res/values/strings.xml | 5 +- 5 files changed, 161 insertions(+), 127 deletions(-) create mode 100644 app/src/main/java/com/sopt/now/compose/Sources.kt diff --git a/app/src/main/java/com/sopt/now/compose/Sources.kt b/app/src/main/java/com/sopt/now/compose/Sources.kt new file mode 100644 index 0000000..5783025 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/Sources.kt @@ -0,0 +1,120 @@ +package com.sopt.now.compose + +import android.content.Context +import android.widget.Toast +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController + +@Composable +fun SignInTextField( + value: String, + onValueChange: (String) -> Unit, + label: String, + leadingIcon: ImageVector, + modifier: Modifier = Modifier, +) { + TextField( + value = value, + onValueChange = onValueChange, + modifier = modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text(label) }, + leadingIcon = { Icon(leadingIcon, contentDescription = null) }, + singleLine = true, + ) +} + +@Composable +fun SignUpTextField( + value: String, + onValueChange: (String) -> Unit, + label: String, + leadingIcon: ImageVector?, + modifier: Modifier = Modifier, + isPassword: Boolean = false +) { + TextField( + value = value, + onValueChange = onValueChange, + modifier = modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text(label) }, + leadingIcon = if (leadingIcon != null) { + { Icon(leadingIcon, contentDescription = null) } + } else { + null + }, + singleLine = true, + visualTransformation = if (isPassword) PasswordVisualTransformation() else VisualTransformation.None + ) +} + +@Composable +fun SOPTOutlinedButton(text: Int, onClick: () -> Unit, enabled: Boolean) { + OutlinedButton( + onClick = onClick, modifier = Modifier.fillMaxWidth(), enabled = enabled + ) { + Text(text = stringResource(id = text)) + } +} + +fun isSignInValid( + navController: NavController, + context: Context, + inputID: String, + inputPW: String, + ID: String, + PW: String, + Name: String, + Place: String +) { + when { + (inputID == ID && inputPW == PW) -> { + showToast(context, R.string.toast_SignIn_ValidSignIn) + navController.navigate("MyPage?ID=$inputID&PW=$inputPW&Name=$Name&Place=$Place") + } + + (inputID != ID) -> { + showToast(context, R.string.toast_SignIn_InvalidID) + } + + (inputPW != PW) -> { + showToast(context, R.string.toast_SignIn_InvalidPW) + } + } +} + +fun isSignUpValid( + navController: NavController, + context: Context, + ID: String, + IDValid: Boolean, + PW: String, + PWValid: Boolean, + Name: String, + NameValid: Boolean, + Place: String +) { + if (IDValid && PWValid && NameValid) { + showToast(context, R.string.toast_SignUp_ValidSignUp) + navController.navigate("SignIn?ID=$ID&PW=$PW&Name=$Name&Place=$Place") + } +} + +fun showToast(context: Context, message: Int) { + Toast.makeText(context, message, Toast.LENGTH_SHORT).show() +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt index 97ab0e5..07ecc63 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt @@ -11,11 +11,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.sopt.now.compose.R -import com.sopt.now.compose.ui.theme.NOWSOPTAndroidTheme @Composable fun MyPageScreen(ID: String, PW: String, Name: String, Place: String) { diff --git a/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt index 2c8df0d..57fa4a8 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt @@ -1,18 +1,12 @@ package com.sopt.now.compose.screen -import android.content.Context -import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person -import androidx.compose.material3.Icon -import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text -import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -20,7 +14,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -29,6 +22,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import com.sopt.now.compose.R +import com.sopt.now.compose.SOPTOutlinedButton +import com.sopt.now.compose.SignInTextField +import com.sopt.now.compose.isSignInValid @Composable fun SignInScreen( @@ -53,14 +49,14 @@ fun SignInScreen( textAlign = TextAlign.Center ) - SOPTTextField( + SignInTextField( inputID, { inputID = it }, stringResource(id = R.string.tf_SignUp_ID_Hint), Icons.Filled.Person ) - SOPTTextField( + SignInTextField( inputPW, { inputPW = it }, stringResource(id = R.string.tf_SignUp_PW_Hint ), @@ -79,63 +75,4 @@ fun SignInScreen( true ) } -} - -@Composable -fun SOPTTextField( - value: String, - onValueChange: (String) -> Unit, - label: String, - leadingIcon: ImageVector, - modifier: Modifier = Modifier, -) { - TextField( - value = value, - onValueChange = onValueChange, - modifier = modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text(label) }, - leadingIcon = { Icon(leadingIcon, contentDescription = null) }, - singleLine = true, - ) -} - -@Composable -fun SOPTOutlinedButton(text: Int, onClick: () -> Unit, enabled: Boolean) { - OutlinedButton( - onClick = onClick, modifier = Modifier.fillMaxWidth(), enabled = enabled - ) { - Text(text = stringResource(id = text)) - } -} - -fun isSignInValid( - navController: NavController, - context: Context, - inputID: String, - inputPW: String, - ID: String, - PW: String, - Name: String, - Place: String -) { - when { - (inputID == ID && inputPW == PW) -> { - showToast(context, R.string.toast_SignIn_ValidSignIn) - navController.navigate("MyPage?ID=$inputID&PW=$inputPW&Name=$Name&Place=$Place") - } - - (inputID != ID) -> { - showToast(context, R.string.toast_SignIn_InvalidID) - } - - (inputPW != PW) -> { - showToast(context, R.string.toast_SignIn_InvalidPW) - } - } -} - -fun showToast(context: Context, message: Int) { - Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt index 6038d2b..8d64b1e 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt @@ -3,19 +3,13 @@ package com.sopt.now.compose.screen import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Face import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Person -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon import androidx.compose.material3.Text -import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -23,15 +17,18 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color +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.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import com.sopt.now.compose.R +import com.sopt.now.compose.SOPTOutlinedButton +import com.sopt.now.compose.SignUpTextField +import com.sopt.now.compose.isSignUpValid + @Composable fun SignUpScreen(navController: NavController) { val (value, setValue) = remember { @@ -45,7 +42,6 @@ fun SignUpScreen(navController: NavController) { verticalArrangement = Arrangement.Top ) { - // 상태 관리를 위한 state 변수 선언 var ID by remember { mutableStateOf("") } var PW by remember { mutableStateOf("") } var Name by remember { mutableStateOf("") } @@ -53,6 +49,7 @@ fun SignUpScreen(navController: NavController) { var isIDValid by remember { mutableStateOf(false) } var isPWValid by remember { mutableStateOf(false) } var isNameValid by remember { mutableStateOf(false) } + val context = LocalContext.current // Title Text( @@ -63,74 +60,55 @@ fun SignUpScreen(navController: NavController) { textAlign = TextAlign.Center ) - // ID 입력 필드 - TextField( + SignUpTextField( value = ID, onValueChange = { newID -> ID = newID - isIDValid = ID.trim().length in 6..10 + isIDValid = newID.trim().length in 6..10 }, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text(stringResource(R.string.tf_SignUp_ID_Hint)) }, - leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "ID Icon") }, - singleLine = true, + label = stringResource(R.string.tf_SignUp_ID_Hint), + leadingIcon = Icons.Filled.Person ) - // PW 입력 필드 - TextField( + SignUpTextField( value = PW, onValueChange = { newPW -> PW = newPW - isPWValid = PW.trim().length in 8..12 + isPWValid = newPW.trim().length in 8..12 }, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text(stringResource(R.string.tf_SignUp_PW_Hint)) }, - leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "PW Icon") }, - singleLine = true, + label = stringResource(R.string.tf_SignUp_PW_Hint), + leadingIcon = Icons.Filled.Lock, + isPassword = true ) - // Name(닉네임) 입력 필드 - TextField( + SignUpTextField( value = Name, onValueChange = { newName -> Name = newName - isNameValid = Name.trim().isNotEmpty() + isNameValid = newName.trim().isNotEmpty() }, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text(stringResource(R.string.tf_SignUp_Name_Hint)) }, - leadingIcon = { Icon(Icons.Filled.Face, contentDescription = "Name Icon") }, - singleLine = true, + label = stringResource(R.string.tf_SignUp_Name_Hint), + leadingIcon = Icons.Filled.Face ) - // Place(거주지) 입력 필드 - TextField( + SignUpTextField( value = Place, onValueChange = { newPlace -> Place = newPlace }, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text(stringResource(R.string.tf_SignUp_Place_Hint)) }, - leadingIcon = { Icon(Icons.Filled.Home, contentDescription = "Place Icon") }, - singleLine = true, + label = stringResource(R.string.tf_SignUp_Place_Hint), + leadingIcon = Icons.Filled.Home ) - // Sign Up - Button( - onClick = { - navController.navigate("SignIn?ID=$ID&PW=$PW&Name=$Name&Place=$Place") + SOPTOutlinedButton( + R.string.btn_SignUp_SignUp, + { + isSignUpValid( + navController, context, + ID, isIDValid, + PW, isPWValid, + Name, isNameValid, Place + ) }, - modifier = Modifier.padding(10.dp), - colors = ButtonDefaults.buttonColors(containerColor = Color.Green), - shape = RoundedCornerShape(8.dp) - ) { - Text(stringResource(R.string.btn_SignUp_SignUp), color = Color.Black) - } + true + ) } - } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4f0b48e..dd41ad3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,8 +8,8 @@ 로그인 하기 회원가입 하기 로그인 성공 - 아이디가 잘못되었습니다. - 비밀번호가 잘못되었습니다. + 아이디가 잘못되었습니다 + 비밀번호가 잘못되었습니다 SIGN UP @@ -18,6 +18,7 @@ 닉네임을 입력하세요 거주지를 입력하세요 회원가입 하기 + 회원가입 성공 SOPT에 온걸 환영해! From a358c978160388afecae04131260a10d3752b0e6 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Sun, 14 Apr 2024 20:39:02 +0900 Subject: [PATCH 11/17] =?UTF-8?q?feat=20:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/sopt/now/compose/Sources.kt | 17 ++++ .../sopt/now/compose/screen/MyPageScreen.kt | 80 +++---------------- .../sopt/now/compose/screen/SignInScreen.kt | 8 +- .../sopt/now/compose/screen/SignUpScreen.kt | 33 ++++---- app/src/main/res/values/strings.xml | 8 +- 5 files changed, 52 insertions(+), 94 deletions(-) diff --git a/app/src/main/java/com/sopt/now/compose/Sources.kt b/app/src/main/java/com/sopt/now/compose/Sources.kt index 5783025..facd777 100644 --- a/app/src/main/java/com/sopt/now/compose/Sources.kt +++ b/app/src/main/java/com/sopt/now/compose/Sources.kt @@ -12,9 +12,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.navigation.NavController @Composable @@ -24,6 +27,7 @@ fun SignInTextField( label: String, leadingIcon: ImageVector, modifier: Modifier = Modifier, + isPassword: Boolean = false, ) { TextField( value = value, @@ -34,6 +38,7 @@ fun SignInTextField( label = { Text(label) }, leadingIcon = { Icon(leadingIcon, contentDescription = null) }, singleLine = true, + visualTransformation = if (isPassword) PasswordVisualTransformation() else VisualTransformation.None ) } @@ -115,6 +120,18 @@ fun isSignUpValid( } } +@Composable +fun MyPageText( + text: String +) { + Text( + text = text, + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center) +} + fun showToast(context: Context, message: Int) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt index 07ecc63..741fe8a 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.sopt.now.compose.MyPageText import com.sopt.now.compose.R @Composable @@ -26,77 +27,14 @@ fun MyPageScreen(ID: String, PW: String, Name: String, Place: String) { verticalArrangement = Arrangement.Top ) { - // Title - Text( - text = stringResource(R.string.txt_Main_Title), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - // Name - Text( - text = Name, - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // ID - Text( - text = stringResource(R.string.txt_Main_Id), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // ID Info - Text( - text = ID, - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // PW - Text( - text = stringResource(R.string.txt_Main_Pw), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // PW Info - Text( - text = PW, - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // Place - Text( - text = stringResource(R.string.txt_Main_Place), - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - - // Place Info - Text( - text = Place, - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) + MyPageText(text = stringResource(R.string.txt_MyPage_Title)) + MyPageText(text = Name) + MyPageText(text = stringResource(R.string.txt_MyPage_Id)) + MyPageText(text = ID) + MyPageText(text = stringResource(R.string.txt_MyPage_Pw)) + MyPageText(text = PW) + MyPageText(text = stringResource(R.string.txt_MyPage_Place)) + MyPageText(text = Place) } - } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt index 57fa4a8..7334e21 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Person import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -42,7 +43,7 @@ fun SignInScreen( val context = LocalContext.current Text( - text = stringResource(id = R.string.txt_SignIn_Title ), + text = stringResource(id = R.string.txt_SignIn_Title), modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, @@ -59,8 +60,9 @@ fun SignInScreen( SignInTextField( inputPW, { inputPW = it }, - stringResource(id = R.string.tf_SignUp_PW_Hint ), - Icons.Filled.Person + stringResource(id = R.string.tf_SignUp_PW_Hint), + Icons.Filled.Lock, + isPassword = true ) SOPTOutlinedButton( diff --git a/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt index 8d64b1e..8b23db9 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt @@ -51,6 +51,7 @@ fun SignUpScreen(navController: NavController) { var isNameValid by remember { mutableStateOf(false) } val context = LocalContext.current + // Title Text( text = stringResource(R.string.txt_SignUp_Title), @@ -61,41 +62,41 @@ fun SignUpScreen(navController: NavController) { ) SignUpTextField( - value = ID, - onValueChange = { newID -> + ID, + { newID -> ID = newID isIDValid = newID.trim().length in 6..10 }, - label = stringResource(R.string.tf_SignUp_ID_Hint), - leadingIcon = Icons.Filled.Person + stringResource(R.string.tf_SignUp_ID_Hint), + Icons.Filled.Person ) SignUpTextField( - value = PW, - onValueChange = { newPW -> + PW, + { newPW -> PW = newPW isPWValid = newPW.trim().length in 8..12 }, - label = stringResource(R.string.tf_SignUp_PW_Hint), - leadingIcon = Icons.Filled.Lock, + stringResource(R.string.tf_SignUp_PW_Hint), + Icons.Filled.Lock, isPassword = true ) SignUpTextField( - value = Name, - onValueChange = { newName -> + Name, + { newName -> Name = newName isNameValid = newName.trim().isNotEmpty() }, - label = stringResource(R.string.tf_SignUp_Name_Hint), - leadingIcon = Icons.Filled.Face + stringResource(R.string.tf_SignUp_Name_Hint), + Icons.Filled.Face ) SignUpTextField( - value = Place, - onValueChange = { newPlace -> Place = newPlace }, - label = stringResource(R.string.tf_SignUp_Place_Hint), - leadingIcon = Icons.Filled.Home + Place, + { newPlace -> Place = newPlace }, + stringResource(R.string.tf_SignUp_Place_Hint), + Icons.Filled.Home ) SOPTOutlinedButton( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dd41ad3..793384e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -21,8 +21,8 @@ 회원가입 성공 - SOPT에 온걸 환영해! - 아이디 - 비밀번호 - 거주지 + SOPT에 온걸 환영해! + 아이디 + 비밀번호 + 거주지 \ No newline at end of file From 9a3a8fd7064598d9b5fc56cb799bde595992ebe9 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Sun, 14 Apr 2024 20:46:22 +0900 Subject: [PATCH 12/17] =?UTF-8?q?=C3=A3refactor=20:=20import=EB=AC=B8=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/sopt/now/compose/MainActivity.kt | 7 ++++--- app/src/main/java/com/sopt/now/compose/Sources.kt | 3 ++- .../main/java/com/sopt/now/compose/screen/MyPageScreen.kt | 4 ---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/sopt/now/compose/MainActivity.kt b/app/src/main/java/com/sopt/now/compose/MainActivity.kt index 2bd6b48..a2577b2 100644 --- a/app/src/main/java/com/sopt/now/compose/MainActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/MainActivity.kt @@ -6,9 +6,9 @@ import androidx.activity.compose.setContent import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import com.sopt.now.compose.screen.MyPageScreen import com.sopt.now.compose.screen.SignInScreen import com.sopt.now.compose.screen.SignUpScreen -import com.sopt.now.compose.screen.MyPageScreen class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -29,12 +29,13 @@ class MainActivity : ComponentActivity() { composable("SignUp") { SignUpScreen(navController = navController) } - composable("MyPage?ID={InputID}&PW={InputPW}&Name={Name}&Place={Place}") {backStackEntry -> + composable("MyPage?ID={InputID}&PW={InputPW}&Name={Name}&Place={Place}") { backStackEntry -> MyPageScreen( ID = backStackEntry.arguments?.getString("InputID") ?: "", PW = backStackEntry.arguments?.getString("InputPW") ?: "", Name = backStackEntry.arguments?.getString("Name") ?: "", - Place = backStackEntry.arguments?.getString("Place") ?: "") + Place = backStackEntry.arguments?.getString("Place") ?: "" + ) } } } diff --git a/app/src/main/java/com/sopt/now/compose/Sources.kt b/app/src/main/java/com/sopt/now/compose/Sources.kt index facd777..b531643 100644 --- a/app/src/main/java/com/sopt/now/compose/Sources.kt +++ b/app/src/main/java/com/sopt/now/compose/Sources.kt @@ -129,7 +129,8 @@ fun MyPageText( modifier = Modifier.padding(10.dp), fontSize = 20.sp, fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center) + textAlign = TextAlign.Center + ) } fun showToast(context: Context, message: Int) { diff --git a/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt index 741fe8a..2a7143a 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt @@ -4,15 +4,11 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import com.sopt.now.compose.MyPageText import com.sopt.now.compose.R From 2594baec6bba759b2fe0c225780614651052be1b Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Sun, 14 Apr 2024 21:17:23 +0900 Subject: [PATCH 13/17] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=A1=B0=EA=B1=B4=20=EC=83=81=EC=88=98=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/sopt/now/compose/Sources.kt | 3 +++ .../main/java/com/sopt/now/compose/screen/SignUpScreen.kt | 8 ++++++-- app/src/main/res/values/strings.xml | 5 +++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/sopt/now/compose/Sources.kt b/app/src/main/java/com/sopt/now/compose/Sources.kt index b531643..7a0e041 100644 --- a/app/src/main/java/com/sopt/now/compose/Sources.kt +++ b/app/src/main/java/com/sopt/now/compose/Sources.kt @@ -118,6 +118,9 @@ fun isSignUpValid( showToast(context, R.string.toast_SignUp_ValidSignUp) navController.navigate("SignIn?ID=$ID&PW=$PW&Name=$Name&Place=$Place") } + + else + showToast(context, R.string.toast_SignUp_InvalidSignUp) } @Composable diff --git a/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt index 8b23db9..a7d2cef 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt @@ -51,6 +51,10 @@ fun SignUpScreen(navController: NavController) { var isNameValid by remember { mutableStateOf(false) } val context = LocalContext.current + val MIN_ID_LENGTH = 6 + val MAX_ID_LENGTH = 10 + val MIN_PW_LENGTH = 8 + val MAX_PW_LENGTH = 12 // Title Text( @@ -65,7 +69,7 @@ fun SignUpScreen(navController: NavController) { ID, { newID -> ID = newID - isIDValid = newID.trim().length in 6..10 + isIDValid = newID.trim().length in MIN_ID_LENGTH..MAX_ID_LENGTH }, stringResource(R.string.tf_SignUp_ID_Hint), Icons.Filled.Person @@ -75,7 +79,7 @@ fun SignUpScreen(navController: NavController) { PW, { newPW -> PW = newPW - isPWValid = newPW.trim().length in 8..12 + isPWValid = newPW.trim().length in MIN_PW_LENGTH..MAX_PW_LENGTH }, stringResource(R.string.tf_SignUp_PW_Hint), Icons.Filled.Lock, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 793384e..902a537 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,12 +13,13 @@ SIGN UP - 아이디를 입력하세요 - 비밀번호를 입력하세요 + 아이디를 입력하세요 (6~10자) + 비밀번호를 입력하세요(8~12자) 닉네임을 입력하세요 거주지를 입력하세요 회원가입 하기 회원가입 성공 + 회원가입 실패 SOPT에 온걸 환영해! From fc1e6dba3f7ab8cdd0a657324b07f37947ae6a02 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Sun, 14 Apr 2024 21:31:46 +0900 Subject: [PATCH 14/17] =?UTF-8?q?feat=20:=20MyPage=EC=97=90=20image=20?= =?UTF-8?q?=EC=82=BD=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/sopt/now/compose/screen/MyPageScreen.kt | 8 ++++++++ .../com/sopt/now/compose/screen/SignInScreen.kt | 4 ++-- .../res/drawable-nodpi/img_mypage_profile.jpg | Bin 0 -> 63260 bytes 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 app/src/main/res/drawable-nodpi/img_mypage_profile.jpg diff --git a/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt index 2a7143a..730573f 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt @@ -1,12 +1,15 @@ package com.sopt.now.compose.screen +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.sopt.now.compose.MyPageText @@ -24,6 +27,11 @@ fun MyPageScreen(ID: String, PW: String, Name: String, Place: String) { ) { + Image(painter = painterResource(id = R.drawable.img_mypage_profile), + contentDescription = "Profile", + modifier = Modifier.size(300.dp) + ) + MyPageText(text = stringResource(R.string.txt_MyPage_Title)) MyPageText(text = Name) MyPageText(text = stringResource(R.string.txt_MyPage_Id)) diff --git a/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt index 7334e21..4dff7f6 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt @@ -53,14 +53,14 @@ fun SignInScreen( SignInTextField( inputID, { inputID = it }, - stringResource(id = R.string.tf_SignUp_ID_Hint), + stringResource(id = R.string.tf_SignIn_ID_Hint), Icons.Filled.Person ) SignInTextField( inputPW, { inputPW = it }, - stringResource(id = R.string.tf_SignUp_PW_Hint), + stringResource(id = R.string.tf_SignIn_PW_Hint), Icons.Filled.Lock, isPassword = true ) diff --git a/app/src/main/res/drawable-nodpi/img_mypage_profile.jpg b/app/src/main/res/drawable-nodpi/img_mypage_profile.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2f55c3c18d789d0a8ae169360a725a9f5524d123 GIT binary patch literal 63260 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 literal 0 HcmV?d00001 From c891ca1985d19f4c953473ab9743f28202a87630 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Sun, 14 Apr 2024 21:51:05 +0900 Subject: [PATCH 15/17] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/sopt/now/compose/Sources.kt | 14 ++++++++++++-- app/src/main/res/values/strings.xml | 3 +++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/sopt/now/compose/Sources.kt b/app/src/main/java/com/sopt/now/compose/Sources.kt index 7a0e041..c62b199 100644 --- a/app/src/main/java/com/sopt/now/compose/Sources.kt +++ b/app/src/main/java/com/sopt/now/compose/Sources.kt @@ -88,6 +88,14 @@ fun isSignInValid( Place: String ) { when { + (inputID.isEmpty()) -> { + showToast(context, R.string.toast_SignIn_InvalidSignIn_IDBlank) + } + + (inputPW.isEmpty()) -> { + showToast(context, R.string.toast_SignIn_InvalidSignIn_PWBlank) + } + (inputID == ID && inputPW == PW) -> { showToast(context, R.string.toast_SignIn_ValidSignIn) navController.navigate("MyPage?ID=$inputID&PW=$inputPW&Name=$Name&Place=$Place") @@ -118,9 +126,11 @@ fun isSignUpValid( showToast(context, R.string.toast_SignUp_ValidSignUp) navController.navigate("SignIn?ID=$ID&PW=$PW&Name=$Name&Place=$Place") } - + else if (ID.trim().isEmpty()||PW.trim().isEmpty()|| + Name.trim().isEmpty()||Place.trim().isEmpty()) + showToast(context, R.string.toast_SignUp_InvalidSignUp_Blank) else - showToast(context, R.string.toast_SignUp_InvalidSignUp) + showToast(context,R.string.toast_SignUp_InvalidSignUp) } @Composable diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 902a537..d86d5cf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -10,6 +10,8 @@ 로그인 성공 아이디가 잘못되었습니다 비밀번호가 잘못되었습니다 + 아이디를 입력해주세요 + 비밀번호를 입력해주세요 SIGN UP @@ -20,6 +22,7 @@ 회원가입 하기 회원가입 성공 회원가입 실패 + 입력하지 않은 정보가 있습니다 SOPT에 온걸 환영해! From 6068ebeac1c5bd972ed0abb3762e6f9290d78236 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Sun, 14 Apr 2024 21:53:33 +0900 Subject: [PATCH 16/17] =?UTF-8?q?feat=20:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/sopt/now/compose/Sources.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/sopt/now/compose/Sources.kt b/app/src/main/java/com/sopt/now/compose/Sources.kt index c62b199..a511607 100644 --- a/app/src/main/java/com/sopt/now/compose/Sources.kt +++ b/app/src/main/java/com/sopt/now/compose/Sources.kt @@ -122,13 +122,15 @@ fun isSignUpValid( NameValid: Boolean, Place: String ) { - if (IDValid && PWValid && NameValid) { + if (ID.isEmpty()||PW.isEmpty()|| + Name.isEmpty()||Place.isEmpty()) + showToast(context, R.string.toast_SignUp_InvalidSignUp_Blank) + + else if (IDValid && PWValid && NameValid) { showToast(context, R.string.toast_SignUp_ValidSignUp) navController.navigate("SignIn?ID=$ID&PW=$PW&Name=$Name&Place=$Place") } - else if (ID.trim().isEmpty()||PW.trim().isEmpty()|| - Name.trim().isEmpty()||Place.trim().isEmpty()) - showToast(context, R.string.toast_SignUp_InvalidSignUp_Blank) + else showToast(context,R.string.toast_SignUp_InvalidSignUp) } From d7f5efa4a797f5aaada5a4f13ab8f6768503bc39 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Sun, 14 Apr 2024 22:14:40 +0900 Subject: [PATCH 17/17] =?UTF-8?q?refactor:=20=EA=B0=81=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=B0=EC=97=90=20=EB=A7=9E=EB=8A=94=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=ED=8C=8C=EC=9D=BC=20=EC=9E=AC=EB=B0=B0=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/sopt/now/compose/Sources.kt | 118 ------------------ .../sopt/now/compose/screen/MyPageScreen.kt | 18 ++- .../sopt/now/compose/screen/SignInScreen.kt | 66 +++++++++- .../sopt/now/compose/screen/SignUpScreen.kt | 60 ++++++++- 4 files changed, 139 insertions(+), 123 deletions(-) diff --git a/app/src/main/java/com/sopt/now/compose/Sources.kt b/app/src/main/java/com/sopt/now/compose/Sources.kt index a511607..f0a3471 100644 --- a/app/src/main/java/com/sopt/now/compose/Sources.kt +++ b/app/src/main/java/com/sopt/now/compose/Sources.kt @@ -20,53 +20,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController -@Composable -fun SignInTextField( - value: String, - onValueChange: (String) -> Unit, - label: String, - leadingIcon: ImageVector, - modifier: Modifier = Modifier, - isPassword: Boolean = false, -) { - TextField( - value = value, - onValueChange = onValueChange, - modifier = modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text(label) }, - leadingIcon = { Icon(leadingIcon, contentDescription = null) }, - singleLine = true, - visualTransformation = if (isPassword) PasswordVisualTransformation() else VisualTransformation.None - ) -} - -@Composable -fun SignUpTextField( - value: String, - onValueChange: (String) -> Unit, - label: String, - leadingIcon: ImageVector?, - modifier: Modifier = Modifier, - isPassword: Boolean = false -) { - TextField( - value = value, - onValueChange = onValueChange, - modifier = modifier - .fillMaxWidth() - .padding(10.dp), - label = { Text(label) }, - leadingIcon = if (leadingIcon != null) { - { Icon(leadingIcon, contentDescription = null) } - } else { - null - }, - singleLine = true, - visualTransformation = if (isPassword) PasswordVisualTransformation() else VisualTransformation.None - ) -} @Composable fun SOPTOutlinedButton(text: Int, onClick: () -> Unit, enabled: Boolean) { @@ -77,77 +30,6 @@ fun SOPTOutlinedButton(text: Int, onClick: () -> Unit, enabled: Boolean) { } } -fun isSignInValid( - navController: NavController, - context: Context, - inputID: String, - inputPW: String, - ID: String, - PW: String, - Name: String, - Place: String -) { - when { - (inputID.isEmpty()) -> { - showToast(context, R.string.toast_SignIn_InvalidSignIn_IDBlank) - } - - (inputPW.isEmpty()) -> { - showToast(context, R.string.toast_SignIn_InvalidSignIn_PWBlank) - } - - (inputID == ID && inputPW == PW) -> { - showToast(context, R.string.toast_SignIn_ValidSignIn) - navController.navigate("MyPage?ID=$inputID&PW=$inputPW&Name=$Name&Place=$Place") - } - - (inputID != ID) -> { - showToast(context, R.string.toast_SignIn_InvalidID) - } - - (inputPW != PW) -> { - showToast(context, R.string.toast_SignIn_InvalidPW) - } - } -} - -fun isSignUpValid( - navController: NavController, - context: Context, - ID: String, - IDValid: Boolean, - PW: String, - PWValid: Boolean, - Name: String, - NameValid: Boolean, - Place: String -) { - if (ID.isEmpty()||PW.isEmpty()|| - Name.isEmpty()||Place.isEmpty()) - showToast(context, R.string.toast_SignUp_InvalidSignUp_Blank) - - else if (IDValid && PWValid && NameValid) { - showToast(context, R.string.toast_SignUp_ValidSignUp) - navController.navigate("SignIn?ID=$ID&PW=$PW&Name=$Name&Place=$Place") - } - - else - showToast(context,R.string.toast_SignUp_InvalidSignUp) -} - -@Composable -fun MyPageText( - text: String -) { - Text( - text = text, - modifier = Modifier.padding(10.dp), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) -} - fun showToast(context: Context, message: Int) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt index 730573f..5b4027e 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/MyPageScreen.kt @@ -6,13 +6,16 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import com.sopt.now.compose.MyPageText +import androidx.compose.ui.unit.sp import com.sopt.now.compose.R @Composable @@ -41,4 +44,17 @@ fun MyPageScreen(ID: String, PW: String, Name: String, Place: String) { MyPageText(text = stringResource(R.string.txt_MyPage_Place)) MyPageText(text = Place) } +} + +@Composable +fun MyPageText( + text: String +) { + Text( + text = text, + modifier = Modifier.padding(10.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt index 4dff7f6..7eb6c27 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/SignInScreen.kt @@ -1,13 +1,17 @@ package com.sopt.now.compose.screen +import android.content.Context import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Person +import androidx.compose.material3.Icon import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -15,17 +19,19 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import com.sopt.now.compose.R import com.sopt.now.compose.SOPTOutlinedButton -import com.sopt.now.compose.SignInTextField -import com.sopt.now.compose.isSignInValid +import com.sopt.now.compose.showToast @Composable fun SignInScreen( @@ -77,4 +83,60 @@ fun SignInScreen( true ) } +} + +@Composable +fun SignInTextField( + value: String, + onValueChange: (String) -> Unit, + label: String, + leadingIcon: ImageVector, + modifier: Modifier = Modifier, + isPassword: Boolean = false, +) { + TextField( + value = value, + onValueChange = onValueChange, + modifier = modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text(label) }, + leadingIcon = { Icon(leadingIcon, contentDescription = null) }, + singleLine = true, + visualTransformation = if (isPassword) PasswordVisualTransformation() else VisualTransformation.None + ) +} + +fun isSignInValid( + navController: NavController, + context: Context, + inputID: String, + inputPW: String, + ID: String, + PW: String, + Name: String, + Place: String +) { + when { + (inputID.isEmpty()) -> { + showToast(context, R.string.toast_SignIn_InvalidSignIn_IDBlank) + } + + (inputPW.isEmpty()) -> { + showToast(context, R.string.toast_SignIn_InvalidSignIn_PWBlank) + } + + (inputID == ID && inputPW == PW) -> { + showToast(context, R.string.toast_SignIn_ValidSignIn) + navController.navigate("MyPage?ID=$inputID&PW=$inputPW&Name=$Name&Place=$Place") + } + + (inputID != ID) -> { + showToast(context, R.string.toast_SignIn_InvalidID) + } + + (inputPW != PW) -> { + showToast(context, R.string.toast_SignIn_InvalidPW) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt b/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt index a7d2cef..b22b7ba 100644 --- a/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/screen/SignUpScreen.kt @@ -1,15 +1,19 @@ package com.sopt.now.compose.screen +import android.content.Context import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Face import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Person +import androidx.compose.material3.Icon import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -17,17 +21,19 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import com.sopt.now.compose.R import com.sopt.now.compose.SOPTOutlinedButton -import com.sopt.now.compose.SignUpTextField -import com.sopt.now.compose.isSignUpValid +import com.sopt.now.compose.showToast @Composable fun SignUpScreen(navController: NavController) { @@ -117,3 +123,53 @@ fun SignUpScreen(navController: NavController) { ) } } + +@Composable +fun SignUpTextField( + value: String, + onValueChange: (String) -> Unit, + label: String, + leadingIcon: ImageVector?, + modifier: Modifier = Modifier, + isPassword: Boolean = false +) { + TextField( + value = value, + onValueChange = onValueChange, + modifier = modifier + .fillMaxWidth() + .padding(10.dp), + label = { Text(label) }, + leadingIcon = if (leadingIcon != null) { + { Icon(leadingIcon, contentDescription = null) } + } else { + null + }, + singleLine = true, + visualTransformation = if (isPassword) PasswordVisualTransformation() else VisualTransformation.None + ) +} + +fun isSignUpValid( + navController: NavController, + context: Context, + ID: String, + IDValid: Boolean, + PW: String, + PWValid: Boolean, + Name: String, + NameValid: Boolean, + Place: String +) { + if (ID.isEmpty()||PW.isEmpty()|| + Name.isEmpty()||Place.isEmpty()) + showToast(context, R.string.toast_SignUp_InvalidSignUp_Blank) + + else if (IDValid && PWValid && NameValid) { + showToast(context, R.string.toast_SignUp_ValidSignUp) + navController.navigate("SignIn?ID=$ID&PW=$PW&Name=$Name&Place=$Place") + } + + else + showToast(context,R.string.toast_SignUp_InvalidSignUp) +}