회원가입 단계 간소화

This commit is contained in:
klaus 2025-03-21 04:40:39 +09:00
parent 9331ba1276
commit dfe3b291a1
9 changed files with 178 additions and 642 deletions

View File

@ -98,7 +98,9 @@
</activity>
<activity android:name=".main.MainActivity" />
<activity android:name=".user.login.LoginActivity" />
<activity android:name=".user.signup.SignUpActivity" />
<activity
android:name=".user.signup.SignUpActivity"
android:windowSoftInputMode="stateVisible" />
<activity android:name=".settings.terms.TermsActivity" />
<activity android:name=".user.find_password.FindPasswordActivity" />
<activity android:name=".mypage.can.status.CanStatusActivity" />

View File

@ -5,8 +5,10 @@ import com.google.gson.annotations.SerializedName
enum class Gender {
@SerializedName("MALE")
MALE,
@SerializedName("FEMALE")
FEMALE,
@SerializedName("NONE")
NONE
}

View File

@ -4,7 +4,6 @@ import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
import kr.co.vividnext.sodalive.main.GaidUpdateRequest
import kr.co.vividnext.sodalive.main.MarketingInfoUpdateRequest
import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest
import kr.co.vividnext.sodalive.mypage.MyPageResponse
@ -18,8 +17,8 @@ import kr.co.vividnext.sodalive.settings.signout.SignOutRequest
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
import kr.co.vividnext.sodalive.user.login.LoginRequest
import kr.co.vividnext.sodalive.user.login.LoginResponse
import kr.co.vividnext.sodalive.user.signup.SignUpRequest
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
@ -34,11 +33,9 @@ interface UserApi {
@POST("/member/login")
fun login(@Body request: LoginRequest): Single<ApiResponse<LoginResponse>>
@POST("/member/signup")
@Multipart
@POST("/member/signup/v2")
fun signUp(
@Part profileImage: MultipartBody.Part?,
@Part("request") request: RequestBody
@Body request: SignUpRequest
): Single<ApiResponse<LoginResponse>>
@POST("/member/forgot-password")
@ -142,12 +139,6 @@ interface UserApi {
@Header("Authorization") authHeader: String
): Single<ApiResponse<String>>
@PUT("/member/adid/update")
fun updateGaid(
@Body request: GaidUpdateRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@PUT("/member/marketing-info/update")
fun updateMarketingInfo(
@Body request: MarketingInfoUpdateRequest,

View File

@ -13,16 +13,13 @@ import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingR
import kr.co.vividnext.sodalive.settings.signout.SignOutRequest
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
import kr.co.vividnext.sodalive.user.login.LoginRequest
import kr.co.vividnext.sodalive.user.signup.SignUpRequest
import okhttp3.MultipartBody
import okhttp3.RequestBody
class UserRepository(private val userApi: UserApi) {
fun login(request: LoginRequest) = userApi.login(request)
fun signUp(profileImage: MultipartBody.Part?, request: RequestBody) = userApi.signUp(
profileImage,
request
)
fun signUp(request: SignUpRequest) = userApi.signUp(request)
fun findPassword(request: ForgotPasswordRequest) = userApi.findPassword(request = request)

View File

@ -1,99 +1,50 @@
package kr.co.vividnext.sodalive.user.signup
import android.app.Service
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.os.Handler
import android.os.Looper
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts
import coil.load
import coil.transform.RoundedCornersTransformation
import com.github.dhaval2404.imagepicker.ImagePicker
import androidx.annotation.OptIn
import androidx.media3.common.util.UnstableApi
import com.jakewharton.rxbinding4.widget.textChanges
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.RealPathUtil
import kr.co.vividnext.sodalive.databinding.ActivitySignupBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.main.MainActivity
import kr.co.vividnext.sodalive.settings.terms.TermsActivity
import kr.co.vividnext.sodalive.user.Gender
import org.koin.android.ext.android.inject
@OptIn(UnstableApi::class)
class SignUpActivity : BaseActivity<ActivitySignupBinding>(ActivitySignupBinding::inflate) {
private val viewModel: SignUpViewModel by inject()
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
onClickBackButton()
}
}
private lateinit var loadingDialog: LoadingDialog
private lateinit var imm: InputMethodManager
private val imageResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val resultCode = result.resultCode
val data = result.data
if (resultCode == RESULT_OK) {
// Image Uri will not be null for RESULT_OK
val fileUri = data?.data!!
binding.ivProfile.background = null
binding.ivProfile.load(fileUri) {
crossfade(true)
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
}
viewModel.profileImageUri = fileUri
} else if (resultCode == ImagePicker.RESULT_ERROR) {
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
}
}
private val handler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
imm = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
super.onCreate(savedInstanceState)
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
viewModel.getRealPathFromURI = {
RealPathUtil.getRealPath(applicationContext, it)
}
bindData()
}
override fun setupView() {
binding.toolbar.tvBack.text = "회원가입"
binding.toolbar.tvBack.setOnClickListener { onClickBackButton() }
binding.toolbar.tvBack.setOnClickListener { finish() }
loadingDialog = LoadingDialog(this, layoutInflater)
binding.ivPhotoPicker.setOnClickListener {
ImagePicker.with(this)
.crop()
.galleryOnly()
.galleryMimeTypes( // Exclude gif images
mimeTypes = arrayOf(
"image/png",
"image/jpg",
"image/jpeg"
)
)
.createIntent { imageResult.launch(it) }
}
binding.tvMale.setOnClickListener {
viewModel.changeGender(Gender.MALE)
}
binding.tvFemale.setOnClickListener {
viewModel.changeGender(Gender.FEMALE)
}
binding.tvNone.setOnClickListener {
viewModel.changeGender(Gender.NONE)
binding.etEmail.post {
binding.etEmail.requestFocus()
imm.showSoftInput(binding.etEmail, InputMethodManager.SHOW_IMPLICIT)
}
binding.tvTermsOfService.setOnClickListener {
@ -108,15 +59,14 @@ class SignUpActivity : BaseActivity<ActivitySignupBinding>(ActivitySignupBinding
startActivity(intent)
}
binding.ivTermsOfService.setOnClickListener {
viewModel.onClickCheckboxTermsOfService()
}
binding.rlTermsOfService.setOnClickListener { viewModel.onClickCheckboxTermsOfService() }
binding.ivTermsOfService.setOnClickListener { viewModel.onClickCheckboxTermsOfService() }
binding.ivPrivacyPolicy.setOnClickListener {
viewModel.onClickCheckboxPrivacyPolicy()
}
binding.rlPrivacyPolicy.setOnClickListener { viewModel.onClickCheckboxPrivacyPolicy() }
binding.ivPrivacyPolicy.setOnClickListener { viewModel.onClickCheckboxPrivacyPolicy() }
binding.tvSignUp.setOnClickListener {
hideKeyboard()
viewModel.signUp {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
finishAffinity()
@ -154,24 +104,6 @@ class SignUpActivity : BaseActivity<ActivitySignupBinding>(ActivitySignupBinding
}
)
compositeDisposable.add(
binding.etPasswordRe.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
viewModel.passwordRe = it.toString()
}
)
compositeDisposable.add(
binding.etNickname.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
viewModel.nickname = it.toString()
}
)
viewModel.isAgreeTermsOfServiceLiveData.observe(this) {
binding.ivTermsOfService.isSelected = it
}
@ -180,40 +112,19 @@ class SignUpActivity : BaseActivity<ActivitySignupBinding>(ActivitySignupBinding
binding.ivPrivacyPolicy.isSelected = it
}
viewModel.genderLiveData.observe(this) {
binding.tvMale.isSelected = false
binding.tvFemale.isSelected = false
binding.tvNone.isSelected = false
when (it) {
Gender.MALE -> binding.tvMale.isSelected = true
Gender.FEMALE -> binding.tvFemale.isSelected = true
Gender.NONE -> binding.tvNone.isSelected = true
else -> {
}
}
}
viewModel.signUpErrorLiveData.observe(this) {
Toast.makeText(applicationContext, it.message, Toast.LENGTH_LONG).show()
when (it.errorProperty) {
"email" -> {
viewModel.setStep(step = SignUpViewModel.EmailSignUpStep.STEP_1)
binding.etEmail.error = it.message
binding.etEmail.requestFocus()
}
"password" -> {
viewModel.setStep(step = SignUpViewModel.EmailSignUpStep.STEP_1)
binding.etPassword.error = it.message
binding.etPassword.requestFocus()
}
"nickname" -> {
binding.etNickname.error = it.message
binding.etNickname.requestFocus()
}
}
}
@ -228,27 +139,14 @@ class SignUpActivity : BaseActivity<ActivitySignupBinding>(ActivitySignupBinding
viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
}
viewModel.stepLiveData.observe(this) {
if (it == SignUpViewModel.EmailSignUpStep.STEP_2) {
binding.toolbar.tvBack.text = "프로필 설정"
binding.tvSignUp.text = "회원가입"
binding.llStep1.visibility = View.GONE
binding.llStep2.visibility = View.VISIBLE
} else {
binding.toolbar.tvBack.text = "회원가입"
binding.tvSignUp.text = "다음"
binding.llStep1.visibility = View.VISIBLE
binding.llStep2.visibility = View.GONE
}
}
}
private fun onClickBackButton() {
if (viewModel.stepLiveData.value!! == SignUpViewModel.EmailSignUpStep.STEP_2) {
viewModel.setStep(SignUpViewModel.EmailSignUpStep.STEP_1)
} else {
finish()
}
private fun hideKeyboard() {
handler.postDelayed({
imm.hideSoftInputFromWindow(
window.decorView.applicationWindowToken,
InputMethodManager.HIDE_NOT_ALWAYS
)
}, 100)
}
}

View File

@ -2,14 +2,11 @@ package kr.co.vividnext.sodalive.user.signup
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.user.Gender
@Keep
data class SignUpRequest(
@SerializedName("email") val email: String,
@SerializedName("password") val password: String,
@SerializedName("nickname") val nickname: String,
@SerializedName("gender") val gender: Gender,
@SerializedName("marketingPid") val marketingPid: String,
@SerializedName("isAgreeTermsOfService") val isAgreeTermsOfService: Boolean,
@SerializedName("isAgreePrivacyPolicy") val isAgreePrivacyPolicy: Boolean,

View File

@ -1,42 +1,19 @@
package kr.co.vividnext.sodalive.user.signup
import android.net.Uri
import androidx.core.util.PatternsCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.tracking.FirebaseTracking
import kr.co.vividnext.sodalive.user.Gender
import kr.co.vividnext.sodalive.user.UserRepository
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
class SignUpViewModel(private val repository: UserRepository) : BaseViewModel() {
enum class EmailSignUpStep {
@SerializedName("STEP_1")
STEP_1,
@SerializedName("STEP_2")
STEP_2
}
var email = ""
var password = ""
var passwordRe = ""
var nickname = ""
var profileImageUri: Uri? = null
private val _genderLiveData = MutableLiveData(Gender.NONE)
val genderLiveData: LiveData<Gender>
get() = _genderLiveData
private val _isAgreeTermsOfServiceLiveData = MutableLiveData(false)
val isAgreeTermsOfServiceLiveData: LiveData<Boolean>
@ -58,55 +35,19 @@ class SignUpViewModel(private val repository: UserRepository) : BaseViewModel()
val isLoading: LiveData<Boolean>
get() = _isLoading
private val _stepLiveData = MutableLiveData(EmailSignUpStep.STEP_1)
val stepLiveData: LiveData<EmailSignUpStep>
get() = _stepLiveData
lateinit var getRealPathFromURI: (Uri) -> String?
fun setStep(step: EmailSignUpStep = EmailSignUpStep.STEP_2) {
_stepLiveData.postValue(step)
}
fun signUp(onSuccess: (String?) -> Unit) {
if (stepLiveData.value!! == EmailSignUpStep.STEP_1) {
if (validationStep1()) return
setStep()
return
}
if (validationStep2()) return
if (!validation()) return
val request = SignUpRequest(
email = email,
password = password,
nickname = nickname,
gender = _genderLiveData.value!!,
marketingPid = SharedPreferenceManager.marketingPid,
isAgreeTermsOfService = _isAgreeTermsOfServiceLiveData.value!!,
isAgreePrivacyPolicy = _isAgreePrivacyPolicyLiveData.value!!
)
val requestJson = Gson().toJson(request)
val profileImage = if (profileImageUri != null) {
val file = File(getRealPathFromURI(profileImageUri!!))
MultipartBody.Part.createFormData(
"profileImage",
file.name,
file.asRequestBody("image/*".toMediaType())
)
} else {
null
}
_isLoading.value = true
compositeDisposable.add(
repository.signUp(
profileImage,
requestJson.toRequestBody("text/plain".toMediaType())
)
repository.signUp(request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
@ -140,7 +81,7 @@ class SignUpViewModel(private val repository: UserRepository) : BaseViewModel()
)
}
private fun validationStep1(): Boolean {
private fun validation(): Boolean {
if (email.isBlank()) {
_signUpErrorLiveData.postValue(
SignUpError(
@ -149,7 +90,7 @@ class SignUpViewModel(private val repository: UserRepository) : BaseViewModel()
)
)
return true
return false
}
if (password.isBlank()) {
@ -160,18 +101,7 @@ class SignUpViewModel(private val repository: UserRepository) : BaseViewModel()
)
)
return true
}
if (password != passwordRe) {
_signUpErrorLiveData.postValue(
SignUpError(
"password",
"비밀번호가 일치하지 않습니다."
)
)
return true
return false
}
if (
@ -185,7 +115,7 @@ class SignUpViewModel(private val repository: UserRepository) : BaseViewModel()
)
)
return true
return false
}
if (!PatternsCompat.EMAIL_ADDRESS.matcher(email).matches()) {
@ -196,7 +126,7 @@ class SignUpViewModel(private val repository: UserRepository) : BaseViewModel()
)
)
return true
return false
}
if (
@ -211,24 +141,11 @@ class SignUpViewModel(private val repository: UserRepository) : BaseViewModel()
"영문, 숫자 포함 8자 이상의 비밀번호를 입력해 주세요."
)
)
return false
}
return false
}
private fun validationStep2(): Boolean {
if (nickname.isBlank() || nickname.length < 2) {
_signUpErrorLiveData.postValue(
SignUpError(
"nickname",
"닉네임은 2자 이상 입력해 주세요."
)
)
return true
}
return false
return true
}
fun onClickCheckboxTermsOfService() {
@ -238,8 +155,4 @@ class SignUpViewModel(private val repository: UserRepository) : BaseViewModel()
fun onClickCheckboxPrivacyPolicy() {
_isAgreePrivacyPolicyLiveData.postValue(!_isAgreePrivacyPolicyLiveData.value!!)
}
fun changeGender(gender: Gender) {
_genderLiveData.postValue(gender)
}
}

View File

@ -104,8 +104,9 @@
android:id="@+id/tv_forgot_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:layout_marginTop="30dp"
android:fontFamily="@font/gmarket_sans_medium"
android:paddingVertical="10dp"
android:text="비밀번호를 잊으셨나요?"
android:textColor="@color/color_bbbbbb"
android:textSize="13.3sp" />
@ -116,6 +117,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:fontFamily="@font/gmarket_sans_medium"
android:paddingVertical="10dp"
android:text="보이스온 회원이 아닌가요? 지금 가입하세요."
android:textColor="@color/color_bbbbbb"
android:textSize="13.3sp" />

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
@ -24,425 +23,160 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_step_1"
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="20dp"
android:hint="이메일"
app:boxBackgroundColor="@color/color_cc333333"
app:boxBackgroundMode="filled"
app:boxCornerRadiusBottomEnd="6.7dp"
app:boxCornerRadiusBottomStart="6.7dp"
app:boxCornerRadiusTopEnd="6.7dp"
app:boxCornerRadiusTopStart="6.7dp"
app:boxStrokeColor="@color/color_cc333333">
<LinearLayout
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:orientation="vertical"
android:paddingHorizontal="13.3dp"
android:paddingVertical="20dp">
android:fontFamily="@font/gmarket_sans_medium"
android:importantForAutofill="no"
android:inputType="textEmailAddress"
android:paddingVertical="16dp"
android:textColor="@color/color_eeeeee"
android:textSize="15sp" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="이메일"
android:textColor="@color/color_eeeeee"
android:textSize="12sp" />
<EditText
android:id="@+id/et_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/edittext_underline"
android:fontFamily="@font/gmarket_sans_medium"
android:hint="이메일 주소를 입력하세요"
android:importantForAutofill="no"
android:inputType="textWebEmailAddress"
android:paddingHorizontal="6.7dp"
android:paddingTop="12dp"
android:paddingBottom="8dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="26.7dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="비밀번호"
android:textColor="@color/color_eeeeee"
android:textSize="12sp" />
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/edittext_underline"
android:fontFamily="@font/gmarket_sans_medium"
android:hint="비밀번호 (영문, 숫자 포함 8~20자)"
android:importantForAutofill="no"
android:inputType="textWebPassword"
android:paddingHorizontal="6.7dp"
android:paddingTop="12dp"
android:paddingBottom="8dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="26.7dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="비밀번호 확인"
android:textColor="@color/color_eeeeee"
android:textSize="12sp" />
<EditText
android:id="@+id/et_password_re"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/edittext_underline"
android:fontFamily="@font/gmarket_sans_medium"
android:hint="비밀번호를 다시 입력해 주세요."
android:importantForAutofill="no"
android:inputType="textWebPassword"
android:paddingHorizontal="6.7dp"
android:paddingTop="12dp"
android:paddingBottom="8dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:orientation="vertical"
android:padding="13.3dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="6.7dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="약관 동의"
android:textColor="@color/color_eeeeee"
android:textSize="12sp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:background="@color/color_909090" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="6.7dp">
<TextView
android:id="@+id/tv_terms_of_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="이용약관"
android:textColor="@color/color_eeeeee"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="(필수)"
android:textColor="@color/color_9970ff"
android:textSize="12sp" />
</LinearLayout>
<ImageView
android:id="@+id/iv_terms_of_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:background="@color/color_909090" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="6.7dp">
<TextView
android:id="@+id/tv_privacy_policy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="개인정보수집 및 이용동의"
android:textColor="@color/color_eeeeee"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="(필수)"
android:textColor="@color/color_9970ff"
android:textSize="12sp" />
</LinearLayout>
<ImageView
android:id="@+id/iv_privacy_policy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:background="@color/color_909090" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_step_2"
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="16dp"
android:hint="비밀번호"
app:boxBackgroundColor="@color/color_cc333333"
app:boxBackgroundMode="filled"
app:boxCornerRadiusBottomEnd="6.7dp"
app:boxCornerRadiusBottomStart="6.7dp"
app:boxCornerRadiusTopEnd="6.7dp"
app:boxCornerRadiusTopStart="6.7dp"
app:boxStrokeColor="@color/color_cc333333"
app:endIconMode="password_toggle">
<RelativeLayout
android:layout_width="96.7dp"
android:layout_height="116.8dp"
android:layout_marginTop="13.3dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:imeOptions="actionDone"
android:inputType="textPassword"
android:paddingVertical="16dp"
android:textColor="@color/color_eeeeee"
android:textSize="15sp" />
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/iv_profile"
android:layout_width="80dp"
android:layout_height="116.8dp"
android:adjustViewBounds="true"
android:background="@color/color_3e3358"
android:contentDescription="@null"
android:src="@drawable/ic_logo" />
<RelativeLayout
android:id="@+id/rl_terms_of_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="20dp">
<ImageView
android:id="@+id/iv_photo_picker"
<ImageView
android:id="@+id/iv_terms_of_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:contentDescription="@null"
android:padding="10dp"
android:src="@drawable/ic_select" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="10dp"
android:layout_toEndOf="@+id/iv_terms_of_service">
<TextView
android:id="@+id/tv_terms_of_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:background="@drawable/bg_round_corner_33_3_3bb9f1"
android:contentDescription="@null"
android:padding="10dp"
android:src="@drawable/ic_camera" />
</RelativeLayout>
android:fontFamily="@font/gmarket_sans_medium"
android:text="이용약관"
android:textColor="@color/color_eeeeee"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="(필수)"
android:textColor="@color/color_3bb9f1"
android:textSize="12sp" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_privacy_policy"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="5dp">
<ImageView
android:id="@+id/iv_privacy_policy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:contentDescription="@null"
android:padding="10dp"
android:src="@drawable/ic_select" />
<LinearLayout
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:orientation="vertical"
android:paddingHorizontal="13.3dp"
android:paddingTop="20dp"
android:paddingBottom="0dp">
android:layout_centerVertical="true"
android:layout_marginStart="10dp"
android:layout_toEndOf="@+id/iv_privacy_policy">
<LinearLayout
android:layout_width="match_parent"
<TextView
android:id="@+id/tv_privacy_policy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
android:fontFamily="@font/gmarket_sans_medium"
android:text="개인정보수집 및 이용동의"
android:textColor="@color/color_eeeeee"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="닉네임 (최대 12자)"
android:textColor="@color/color_eeeeee"
android:textSize="12sp" />
<EditText
android:id="@+id/et_nickname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/edittext_underline"
android:fontFamily="@font/gmarket_sans_medium"
android:hint="닉네임"
android:importantForAutofill="no"
android:inputType="textWebEditText"
android:maxLength="12"
android:paddingHorizontal="6.7dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16.7dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="6.7dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="성별"
android:textColor="@color/color_eeeeee"
android:textSize="12sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="6.7dp"
android:paddingVertical="13.3dp">
<TextView
android:id="@+id/tv_female"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:button="@null"
android:drawablePadding="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="여자"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp"
app:drawableStartCompat="@drawable/ic_radio_button_select" />
<TextView
android:id="@+id/tv_male"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:button="@null"
android:drawablePadding="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="남자"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp"
app:drawableStartCompat="@drawable/ic_radio_button_select" />
<TextView
android:id="@+id/tv_none"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:button="@null"
android:drawablePadding="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="공개 안 함"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp"
app:drawableStartCompat="@drawable/ic_radio_button_select" />
</LinearLayout>
</LinearLayout>
android:layout_marginStart="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="(필수)"
android:textColor="@color/color_3bb9f1"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
<FrameLayout
<TextView
android:id="@+id/tv_sign_up"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="26.7dp"
android:background="@drawable/bg_round_corner_16_7_222222"
android:paddingHorizontal="13.3dp"
android:paddingVertical="13.7dp">
<TextView
android:id="@+id/tv_sign_up"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_10_3bb9f1"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:text="회원가입"
android:textColor="@color/white"
android:textSize="18.3sp" />
</FrameLayout>
android:background="@drawable/bg_round_corner_10_3bb9f1"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:text="회원가입"
android:textColor="@color/white"
android:textSize="18.3sp" />
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>