회원가입 단계 간소화

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>
<activity android:name=".main.MainActivity" /> <activity android:name=".main.MainActivity" />
<activity android:name=".user.login.LoginActivity" /> <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=".settings.terms.TermsActivity" />
<activity android:name=".user.find_password.FindPasswordActivity" /> <activity android:name=".user.find_password.FindPasswordActivity" />
<activity android:name=".mypage.can.status.CanStatusActivity" /> <activity android:name=".mypage.can.status.CanStatusActivity" />

View File

@ -5,8 +5,10 @@ import com.google.gson.annotations.SerializedName
enum class Gender { enum class Gender {
@SerializedName("MALE") @SerializedName("MALE")
MALE, MALE,
@SerializedName("FEMALE") @SerializedName("FEMALE")
FEMALE, FEMALE,
@SerializedName("NONE") @SerializedName("NONE")
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.common.ApiResponse
import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser 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.MarketingInfoUpdateRequest
import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest
import kr.co.vividnext.sodalive.mypage.MyPageResponse 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.find_password.ForgotPasswordRequest
import kr.co.vividnext.sodalive.user.login.LoginRequest import kr.co.vividnext.sodalive.user.login.LoginRequest
import kr.co.vividnext.sodalive.user.login.LoginResponse import kr.co.vividnext.sodalive.user.login.LoginResponse
import kr.co.vividnext.sodalive.user.signup.SignUpRequest
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header import retrofit2.http.Header
@ -34,11 +33,9 @@ interface UserApi {
@POST("/member/login") @POST("/member/login")
fun login(@Body request: LoginRequest): Single<ApiResponse<LoginResponse>> fun login(@Body request: LoginRequest): Single<ApiResponse<LoginResponse>>
@POST("/member/signup") @POST("/member/signup/v2")
@Multipart
fun signUp( fun signUp(
@Part profileImage: MultipartBody.Part?, @Body request: SignUpRequest
@Part("request") request: RequestBody
): Single<ApiResponse<LoginResponse>> ): Single<ApiResponse<LoginResponse>>
@POST("/member/forgot-password") @POST("/member/forgot-password")
@ -142,12 +139,6 @@ interface UserApi {
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<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") @PUT("/member/marketing-info/update")
fun updateMarketingInfo( fun updateMarketingInfo(
@Body request: MarketingInfoUpdateRequest, @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.settings.signout.SignOutRequest
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
import kr.co.vividnext.sodalive.user.login.LoginRequest import kr.co.vividnext.sodalive.user.login.LoginRequest
import kr.co.vividnext.sodalive.user.signup.SignUpRequest
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody
class UserRepository(private val userApi: UserApi) { class UserRepository(private val userApi: UserApi) {
fun login(request: LoginRequest) = userApi.login(request) fun login(request: LoginRequest) = userApi.login(request)
fun signUp(profileImage: MultipartBody.Part?, request: RequestBody) = userApi.signUp( fun signUp(request: SignUpRequest) = userApi.signUp(request)
profileImage,
request
)
fun findPassword(request: ForgotPasswordRequest) = userApi.findPassword(request = request) fun findPassword(request: ForgotPasswordRequest) = userApi.findPassword(request = request)

View File

@ -1,99 +1,50 @@
package kr.co.vividnext.sodalive.user.signup package kr.co.vividnext.sodalive.user.signup
import android.app.Service
import android.content.Intent import android.content.Intent
import android.os.Bundle 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 android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.annotation.OptIn
import androidx.activity.result.contract.ActivityResultContracts import androidx.media3.common.util.UnstableApi
import coil.load
import coil.transform.RoundedCornersTransformation
import com.github.dhaval2404.imagepicker.ImagePicker
import com.jakewharton.rxbinding4.widget.textChanges import com.jakewharton.rxbinding4.widget.textChanges
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog 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.databinding.ActivitySignupBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.main.MainActivity import kr.co.vividnext.sodalive.main.MainActivity
import kr.co.vividnext.sodalive.settings.terms.TermsActivity import kr.co.vividnext.sodalive.settings.terms.TermsActivity
import kr.co.vividnext.sodalive.user.Gender
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@OptIn(UnstableApi::class)
class SignUpActivity : BaseActivity<ActivitySignupBinding>(ActivitySignupBinding::inflate) { class SignUpActivity : BaseActivity<ActivitySignupBinding>(ActivitySignupBinding::inflate) {
private val viewModel: SignUpViewModel by inject() private val viewModel: SignUpViewModel by inject()
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
onClickBackButton()
}
}
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
private lateinit var imm: InputMethodManager
private val imageResult = private val handler = Handler(Looper.getMainLooper())
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()
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
imm = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
viewModel.getRealPathFromURI = {
RealPathUtil.getRealPath(applicationContext, it)
}
bindData() bindData()
} }
override fun setupView() { override fun setupView() {
binding.toolbar.tvBack.text = "회원가입" binding.toolbar.tvBack.text = "회원가입"
binding.toolbar.tvBack.setOnClickListener { onClickBackButton() } binding.toolbar.tvBack.setOnClickListener { finish() }
loadingDialog = LoadingDialog(this, layoutInflater) loadingDialog = LoadingDialog(this, layoutInflater)
binding.ivPhotoPicker.setOnClickListener { binding.etEmail.post {
ImagePicker.with(this) binding.etEmail.requestFocus()
.crop() imm.showSoftInput(binding.etEmail, InputMethodManager.SHOW_IMPLICIT)
.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.tvTermsOfService.setOnClickListener { binding.tvTermsOfService.setOnClickListener {
@ -108,15 +59,14 @@ class SignUpActivity : BaseActivity<ActivitySignupBinding>(ActivitySignupBinding
startActivity(intent) startActivity(intent)
} }
binding.ivTermsOfService.setOnClickListener { binding.rlTermsOfService.setOnClickListener { viewModel.onClickCheckboxTermsOfService() }
viewModel.onClickCheckboxTermsOfService() binding.ivTermsOfService.setOnClickListener { viewModel.onClickCheckboxTermsOfService() }
}
binding.ivPrivacyPolicy.setOnClickListener { binding.rlPrivacyPolicy.setOnClickListener { viewModel.onClickCheckboxPrivacyPolicy() }
viewModel.onClickCheckboxPrivacyPolicy() binding.ivPrivacyPolicy.setOnClickListener { viewModel.onClickCheckboxPrivacyPolicy() }
}
binding.tvSignUp.setOnClickListener { binding.tvSignUp.setOnClickListener {
hideKeyboard()
viewModel.signUp { viewModel.signUp {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
finishAffinity() 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) { viewModel.isAgreeTermsOfServiceLiveData.observe(this) {
binding.ivTermsOfService.isSelected = it binding.ivTermsOfService.isSelected = it
} }
@ -180,40 +112,19 @@ class SignUpActivity : BaseActivity<ActivitySignupBinding>(ActivitySignupBinding
binding.ivPrivacyPolicy.isSelected = it 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) { viewModel.signUpErrorLiveData.observe(this) {
Toast.makeText(applicationContext, it.message, Toast.LENGTH_LONG).show() Toast.makeText(applicationContext, it.message, Toast.LENGTH_LONG).show()
when (it.errorProperty) { when (it.errorProperty) {
"email" -> { "email" -> {
viewModel.setStep(step = SignUpViewModel.EmailSignUpStep.STEP_1)
binding.etEmail.error = it.message binding.etEmail.error = it.message
binding.etEmail.requestFocus() binding.etEmail.requestFocus()
} }
"password" -> { "password" -> {
viewModel.setStep(step = SignUpViewModel.EmailSignUpStep.STEP_1)
binding.etPassword.error = it.message binding.etPassword.error = it.message
binding.etPassword.requestFocus() 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) { viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } 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() { private fun hideKeyboard() {
if (viewModel.stepLiveData.value!! == SignUpViewModel.EmailSignUpStep.STEP_2) { handler.postDelayed({
viewModel.setStep(SignUpViewModel.EmailSignUpStep.STEP_1) imm.hideSoftInputFromWindow(
} else { window.decorView.applicationWindowToken,
finish() InputMethodManager.HIDE_NOT_ALWAYS
} )
}, 100)
} }
} }

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black" android:background="@color/black"
@ -24,170 +23,82 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/ll_step_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp" android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp" android:layout_marginTop="20dp"
android:background="@drawable/bg_round_corner_6_7_222222" android:hint="이메일"
android:orientation="vertical" app:boxBackgroundColor="@color/color_cc333333"
android:paddingHorizontal="13.3dp" app:boxBackgroundMode="filled"
android:paddingVertical="20dp"> 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: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:id="@+id/et_email"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/edittext_underline"
android:fontFamily="@font/gmarket_sans_medium" android:fontFamily="@font/gmarket_sans_medium"
android:hint="이메일 주소를 입력하세요"
android:importantForAutofill="no" android:importantForAutofill="no"
android:inputType="textWebEmailAddress" android:inputType="textEmailAddress"
android:paddingHorizontal="6.7dp" android:paddingVertical="16dp"
android:paddingTop="12dp"
android:paddingBottom="8dp"
android:textColor="@color/color_eeeeee" android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777" android:textSize="15sp" />
android:textCursorDrawable="@drawable/edit_text_cursor" </com.google.android.material.textfield.TextInputLayout>
android:textSize="13.3sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor" />
</LinearLayout> <com.google.android.material.textfield.TextInputLayout
<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_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp" android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp" android:layout_marginTop="16dp"
android:background="@drawable/bg_round_corner_6_7_222222" android:hint="비밀번호"
android:orientation="vertical" app:boxBackgroundColor="@color/color_cc333333"
android:padding="13.3dp"> 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">
<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>
<RelativeLayout <RelativeLayout
android:id="@+id/rl_terms_of_service"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="50dp"> android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="20dp">
<TextView <ImageView
android:id="@+id/iv_terms_of_service"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginStart="6.7dp" android:contentDescription="@null"
android:fontFamily="@font/gmarket_sans_bold" android:padding="10dp"
android:text="약관 동의" android:src="@drawable/ic_select" />
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 <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginStart="6.7dp"> android:layout_marginStart="10dp"
android:layout_toEndOf="@+id/iv_terms_of_service">
<TextView <TextView
android:id="@+id/tv_terms_of_service" android:id="@+id/tv_terms_of_service"
@ -204,38 +115,34 @@
android:layout_marginStart="6.7dp" android:layout_marginStart="6.7dp"
android:fontFamily="@font/gmarket_sans_medium" android:fontFamily="@font/gmarket_sans_medium"
android:text="(필수)" android:text="(필수)"
android:textColor="@color/color_9970ff" android:textColor="@color/color_3bb9f1"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </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>
<RelativeLayout <RelativeLayout
android:id="@+id/rl_privacy_policy"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="50dp"> 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 <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginStart="6.7dp"> android:layout_marginStart="10dp"
android:layout_toEndOf="@+id/iv_privacy_policy">
<TextView <TextView
android:id="@+id/tv_privacy_policy" android:id="@+id/tv_privacy_policy"
@ -252,188 +159,17 @@
android:layout_marginStart="6.7dp" android:layout_marginStart="6.7dp"
android:fontFamily="@font/gmarket_sans_medium" android:fontFamily="@font/gmarket_sans_medium"
android:text="(필수)" android:text="(필수)"
android:textColor="@color/color_9970ff" android:textColor="@color/color_3bb9f1"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </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> </RelativeLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_step_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<RelativeLayout
android:layout_width="96.7dp"
android:layout_height="116.8dp"
android:layout_marginTop="13.3dp">
<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" />
<ImageView
android:id="@+id/iv_photo_picker"
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>
<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:paddingHorizontal="13.3dp"
android:paddingTop="20dp"
android:paddingBottom="0dp">
<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="닉네임 (최대 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"
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>
</LinearLayout>
</LinearLayout>
<FrameLayout
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 <TextView
android:id="@+id/tv_sign_up" android:id="@+id/tv_sign_up"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="26.7dp"
android:background="@drawable/bg_round_corner_10_3bb9f1" android:background="@drawable/bg_round_corner_10_3bb9f1"
android:fontFamily="@font/gmarket_sans_bold" android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center" android:gravity="center"
@ -441,8 +177,6 @@
android:text="회원가입" android:text="회원가입"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="18.3sp" /> android:textSize="18.3sp" />
</FrameLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>