Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[1주차 과제] 안드로이드 UI 구현 기초 #2

Merged
merged 40 commits into from
Apr 10, 2023
Merged

[1주차 과제] 안드로이드 UI 구현 기초 #2

merged 40 commits into from
Apr 10, 2023

Conversation

leeeha
Copy link
Member

@leeeha leeeha commented Apr 6, 2023

  • [1주차 과제] 안드로이드 UI 구현 기초 #1
  • 요구사항에 따라 이슈에 기능 목록을 작성하고, 개발하면서 계속 업데이트 하는 식으로 구현했습니다!
  • 금잔디 조원분들 코드리뷰 마음껏 달아주세요! 여러분의 피드백을 통해 더 나은 코드로 개선하고 싶어요!

필수 과제

  • 로그인 화면
  • 회원가입 화면
  • 홈 화면 (유저 정보 표시)

심화 과제

도전 과제

실행 결과

  • 에뮬레이터로 녹화해서 반응 속도가 좀 느려요,,

leeeha added 27 commits April 5, 2023 00:53
@leeeha leeeha added the enhancement New feature or request label Apr 6, 2023
@leeeha leeeha requested review from b1urrrr and sxunea April 6, 2023 09:22
@leeeha leeeha requested review from a team and removed request for lsakee April 7, 2023 11:49
Copy link
Member

@b1urrrr b1urrrr left a comment

Choose a reason for hiding this comment

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

Issue에서 태스크를 정말 꼼꼼하게 나누셨네요!! 세밀한 To-do 작성을 기반으로 원자적 커밋을 할 수 있기 때문에 정말 좋은 습관인 것 같습니다 👏👏
code reformat 단축키(Window 기준 Ctrl + Alt + L)도 자주 눌러주시면 더 좋을 것 같아요!
코드 수정해주시고 reviewers에서 re-request 해주시면 달려올게요 💨

Comment on lines 25 to 69
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)

// SharedPreferences에 저장된 유저 정보가 있으면 메인 화면으로 진입
if (isLastUserLoggedIn()) {
navigateToMainScreen()
}

binding.root.setOnClickListener {
hideKeyboard()
}

// 회원가입 화면으로부터 유저 정보 가져오기
resultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK) {
Snackbar.make(binding.root, SIGN_UP_SUCCESS_MSG, Snackbar.LENGTH_SHORT).show()
registerUserInfo(result.data)

// 자동 로그인을 위해 SharedPreferences에 유저 정보 저장하기
saveUserInfoToPrefs()
}
}

binding.btnLogin.setOnClickListener {
if (userRegisteredStatus) {
if (checkInputValues()) {
Toast.makeText(this, LOGIN_SUCCESS_MSG, Toast.LENGTH_SHORT).show()
navigateToMainScreen()
} else {
Toast.makeText(this, LOGIN_FAIL_MSG, Toast.LENGTH_SHORT).show()
}
} else {
Toast.makeText(this, NOT_YET_REGISTERED_MSG, Toast.LENGTH_SHORT).show()
}
}

binding.btnSignUp.setOnClickListener {
val intent = Intent(this, SignUpActivity::class.java)
resultLauncher.launch(intent)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

onCreate() 함수에 로직이 몰려 있는 것 같습니다!
함수화를 통해 로직을 분리해주시면 가독성도 좋아지고 onCreate의 역할을 덜어줄 수 있어 좋을 것 같습니다 🙃

Comment on lines 55 to 61
Toast.makeText(this, LOGIN_SUCCESS_MSG, Toast.LENGTH_SHORT).show()
navigateToMainScreen()
} else {
Toast.makeText(this, LOGIN_FAIL_MSG, Toast.LENGTH_SHORT).show()
}
} else {
Toast.makeText(this, NOT_YET_REGISTERED_MSG, Toast.LENGTH_SHORT).show()
Copy link
Member

Choose a reason for hiding this comment

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

스낵바나 토스트를 발생시키는 함수는 프로젝트 전역에서 재사용될 수 있기 때문에 Activity나 Context, View의 확장함수를 사용하는 방법을 찾아보셔도 좋을 것 같습니다 :)
확장 함수 한번 만들어놓으면 코드량을 엄청 줄일 수 있기 때문에 굉장히 편리합니다!

Comment on lines 85 to 87
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
Intent(this, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(this)
}

스코프 함수 applyalso를 활용하여 코틀린스럽게 작성할 수도 있습니다 ㅎㅎ

Comment on lines 20 to 21
binding.tvName.text = "이름: $name"
binding.tvHobby.text = "특기: $hobby"
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
binding.tvName.text = "이름: $name"
binding.tvHobby.text = "특기: $hobby"
binding.tvName.append(name)
binding.tvHobby.append(hobby)

append() 함수를 활용하여 좀 더 간결하게 작성할 수도 있습니다!

Comment on lines 85 to 91
private fun getAllInputValues(): List<String> {
val id = binding.etId.text.toString()
val pw = binding.etPw.text.toString()
val name = binding.etName.text.toString()
val hobby = binding.etHobby.text.toString()
return listOf(id, pw, name, hobby)
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
private fun getAllInputValues(): List<String> {
val id = binding.etId.text.toString()
val pw = binding.etPw.text.toString()
val name = binding.etName.text.toString()
val hobby = binding.etHobby.text.toString()
return listOf(id, pw, name, hobby)
}
private fun getAllInputValues(): User {
val id = binding.etId.text.toString()
val pw = binding.etPw.text.toString()
val name = binding.etName.text.toString()
val hobby = binding.etHobby.text.toString()
return User(id, pw, name, hobby)
}

Data Class로 정의하신 User 클래스를 활용해보는 건 어떨까요?

Comment on lines +93 to +106
private fun checkLengthOfId(id: String): Boolean {
return id.length in ID_MIN_LEN..ID_MAX_LEN
}

private fun checkLengthOfPw(pw: String): Boolean {
return pw.length in PW_MIN_LEN..PW_MAX_LEN
}

companion object {
private const val ID_MIN_LEN = 6
private const val ID_MAX_LEN = 10
private const val PW_MIN_LEN = 8
private const val PW_MAX_LEN = 12
}
Copy link
Member

Choose a reason for hiding this comment

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

상수 관리 꼼꼼하게 잘 하셨네요!! 👍👍

Comment on lines 3 to 7
const val SIGN_UP_SUCCESS_MSG = "회원가입이 완료되었습니다."
const val LOGIN_SUCCESS_MSG = "로그인에 성공했습니다."
const val LOGIN_FAIL_MSG = "등록된 유저 정보가 없습니다."
const val NOT_YET_REGISTERED_MSG = "회원가입을 먼저 진행해주세요."
const val INVALID_INPUT_ERROR = "모든 항목에 유효한 값을 입력해주세요."
Copy link
Member

Choose a reason for hiding this comment

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

UI에 표시되는 메시지 문자열들은 strings.xml에서 정의하여 사용하는 것이 리소스 사용량을 줄이고 유지보수가 용이할 것 같습니다!
앱을 국제화하기 위해 다른 언어로 번역하여 지원하게 될 경우에도 편리할 것입니다 :)

android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Welcome to GO Android!"
Copy link
Member

Choose a reason for hiding this comment

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

귀찮긴 하지만 xml에서도 텍스트 추출로 보일러플레이트를 줄여봅시다!

android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:text="이름: "
Copy link
Member

Choose a reason for hiding this comment

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

위에서 설명드린 append 함수를 활용하게 된다면 android:text 속성을 활용하시면 될 것 같습니다!

Comment on lines 8 to +10
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="red">#F44336</color>
Copy link
Member

Choose a reason for hiding this comment

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

color까지 지정하시다니 👍👍
3주차 세미나 때 팟짱님이 알려주시겠지만 네이밍을 하실 때 Material Design을 참고하셔도 좋을 것 같아요 😉

override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
Copy link

Choose a reason for hiding this comment

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

addTextChangedListener를 활용해 길이제한조건을 사용자에게 명시해주는 점 좋았습니다 😘

return id.length in 6..10
}

private fun checkValidityOfPw(pw: String): Boolean {
private fun checkLengthOfPw(pw: String): Boolean {
Copy link

Choose a reason for hiding this comment

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

함수명이 항상 기능을 직관적으로 잘명시되어있는 것 같습니다 ! 참고해야겠어용

binding.btnLogin.setOnClickListener {
if (userRegisteredStatus) {
if (checkInputValues()) {
Toast.makeText(this, LOGIN_SUCCESS_MSG, Toast.LENGTH_SHORT).show()
Copy link

Choose a reason for hiding this comment

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

토스트 메시지 분리 해도 될거 같아요!

return id != null && pw != null && name != null && hobby != null
}

private fun hideKeyboard() {
Copy link

Choose a reason for hiding this comment

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

이기능도 util로 빼면 좋을거 같네요 !

Comment on lines +27 to +38
binding.etId.addTextChangedListener {
if (!checkLengthOfId(it.toString())) {
binding.tvIdLimitError.visibility = View.VISIBLE
} else {
binding.tvIdLimitError.visibility = View.INVISIBLE
}
}

binding.etPw.addTextChangedListener {
if (!checkLengthOfPw(it.toString())) {
binding.tvPwLimitError.visibility = View.VISIBLE
} else {
Copy link

Choose a reason for hiding this comment

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

에러체크까지 꼼꼼하시네요!

@leeeha leeeha requested a review from b1urrrr April 10, 2023 10:36
Copy link
Member

@b1urrrr b1urrrr left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@leeeha leeeha merged commit b4f7ece into develop Apr 10, 2023
@leeeha leeeha linked an issue Apr 10, 2023 that may be closed by this pull request
26 tasks
@leeeha leeeha added Essential 필수 과제 Advanced 심화 과제 Challenge 도전 과제 labels Apr 11, 2023
leeeha added a commit that referenced this pull request Apr 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Advanced 심화 과제 Challenge 도전 과제 Essential 필수 과제
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[1주차 과제] 안드로이드 UI 구현 기초
4 participants