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

경북대 Android_주수민 1주차 과제 제출합니다 #21

Open
wants to merge 24 commits into
base: cleonno3o
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
92b3b04
docs: Update README.md
cleonno3o Jun 25, 2024
325f3ee
feat: 이름, 전화번호, 메일 입력 화면 구현
cleonno3o Jun 25, 2024
bb13e2e
feat: 취소 버튼 클릭시 취소 메시지 표시 구현
cleonno3o Jun 25, 2024
1f6e84c
feat: 저장 버튼 클릭시 저장 메시지 표시 구현
cleonno3o Jun 25, 2024
bf9277b
docs: README.md 업데이트
cleonno3o Jun 25, 2024
74f056e
feat: 이름, 전화번호가 입력되지 않으면 저장하지 않는 기능 구현
cleonno3o Jun 25, 2024
587e425
feat: 전화번호가 숫자로만 구성되었때 저장하도록 하도록 구현
cleonno3o Jun 25, 2024
7c65060
feat: 더보기를 눌렀을 때 추가적인 정보입력칸이 보이는 기능 구현
cleonno3o Jun 25, 2024
0ecf494
style: EditText, ConstraintLayout, Button의 스타일 수정
cleonno3o Jun 25, 2024
7a19ce2
feat: 입력한 정보를 하나의 Contact 객체에 저장하는 기능 구현
cleonno3o Jun 26, 2024
165ae24
refactor: 성별 RadioGroup의 Listener 삭제
cleonno3o Jun 26, 2024
e7fbecc
style: ContactActivity의 ScrollView 수정
cleonno3o Jun 27, 2024
8bff3b7
docs: README.md 업데이트
cleonno3o Jun 27, 2024
45e01cf
feat: 연락처 추가버튼 구현
cleonno3o Jun 27, 2024
055c71b
feat: 저장된 연락처가 없을때 메시지 표시 기능 구현
cleonno3o Jun 27, 2024
d41638c
feat: 입력한 정보를 MainActivity로 전달하는 기능 구현
cleonno3o Jun 27, 2024
fa15edf
docs: 구현한 기능에 대해 README.md 업데이트
cleonno3o Jun 27, 2024
0569683
feat: 저장한 연락처를 MainActivity에서 RecylerView를 통해 보여주는 기능 구현
cleonno3o Jun 27, 2024
2f99bf1
style: RecyclerView에 표시되는 연락처 UI 업데이트
cleonno3o Jun 27, 2024
79fafd6
feat: 저장된 연락처를 선택하면 해당하는 정보를 표시하는 기능 구현
cleonno3o Jun 27, 2024
5fcea7d
feat: 입력한 정보가 있을 때 뒤로가기 버튼 클릭시 재확인 다이얼로그 표시 기능 구현
cleonno3o Jun 27, 2024
89ea4ba
refactor: 불필요한 코드제거 및 코드 정리
cleonno3o Jun 28, 2024
22fdb19
style: 연락처 등록화면에서 이름에 따라 프로필이 바뀌도록 수정
cleonno3o Jun 28, 2024
85b0c35
style: 메일이 길어지는 경우 잘리는 UI 수정
cleonno3o Jun 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,21 @@
# android-contacts

## Step1 기능목록
- [x] 추가 버튼을 누르면 정보를 입력할 화면 표시
- [x] 한 대상의 이름, 전화번호, 메일 등 여러가지 정보를 저장하는 객체에 정보를 저장
- [x] 이름, 전화번호를 입력하지 않으면 토스트 메시지를 띄우고 저장하지 않음
- [x] 전화번호 양식이 올바르지 않으면 토스트 메시지를 띄우고 저장하지 않음
- [x] 이름, 전화번호, 메일 이외의 정보 입력은 더보기를 눌렀을 때 보임
- [x] 성별의 경우 여성, 남성 중 1가지를 선택할 수 있도록 구현
- [x] 저장 버튼을 누르면 토스트 메시지를 띄우고 저장
- [x] 취소 버튼을 누르면 토스트 메시지를 띄우고 저장하지 않음

## Step2 기능목록
- [x] 연락처 등록화면 구현
- [x] 추가버튼 구현
- [x] 입력한 연락처를 MainActivity로 전달
- [x] 저장한 연락처가 없으면 메시지 표시
- [x] 저장한 연락처가 있으면 모두 표시
- [x] 많을 경우 스크롤 가능하게 구현
- [x] 저장된 연락처를 선택하면 해당하는 정보 표시
- [x] 정보를 저장하지 않으면 확인 창 표시
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ dependencies {
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.activity:activity:1.8.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
android:supportsRtl="true"
android:theme="@style/Theme.Contacts"
tools:targetApi="31">
<activity
android:name=".ContactInfoActivity"
android:exported="false" />
<activity
android:name=".ContactActivity"
android:exported="false"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".MainActivity"
android:exported="true">
Expand All @@ -23,4 +30,4 @@
</activity>
</application>

</manifest>
</manifest>
17 changes: 17 additions & 0 deletions app/src/main/java/campus/tech/kakao/contacts/Contact.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package campus.tech.kakao.contacts

import java.io.Serializable

data class Contact (
var name: String,
var phoneNumber: String,
var mail: String,
var birth: String,
var sex: Int,
var memo: String) : Serializable {
Copy link

Choose a reason for hiding this comment

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

여기 코드스타일이 조금 이상하네요
커밋하시기 전에 IDE의 reformat code 기능을 이용하면
코드스타일을 일관되게 유지할수 있어요!

val FEMALE: Int = R.id.female
val MALE: Int = R.id.male
companion object CONST {
const val KEY: String = "CONTACT"
}
}
196 changes: 196 additions & 0 deletions app/src/main/java/campus/tech/kakao/contacts/ContactActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package campus.tech.kakao.contacts

import android.app.DatePickerDialog
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.icu.util.Calendar
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.RadioGroup
import android.widget.TextView
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isEmpty
import androidx.core.widget.addTextChangedListener
import com.google.android.material.dialog.MaterialAlertDialogBuilder

class ContactActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_contact)

val cancelButton: Button = findViewById(R.id.cancel)
val saveButton: Button = findViewById(R.id.save)

val profile: TextView = findViewById(R.id.profile)
val name: EditText = findViewById(R.id.name)
val phoneNumber: EditText = findViewById(R.id.phone_number)
val mail: EditText = findViewById(R.id.mail)

val viewMore: ConstraintLayout = findViewById(R.id.view_more)
val moreInfos: ConstraintLayout = findViewById(R.id.more_infos)
val birth: TextView = findViewById(R.id.birthday)
val sex: RadioGroup = findViewById(R.id.sex_radio_group)
val memo: EditText = findViewById(R.id.memo)

registerBackPressedCallback(name, phoneNumber, mail, birth, sex, memo)

cancelButton.setOnClickListener {
cancelProcess()
}

saveButton.setOnClickListener {
saveProcess(name, phoneNumber, mail, birth, sex, memo)
}

name.addTextChangedListener(nameTextWatcher(profile))

viewMore.setOnClickListener {
extendInput(viewMore, moreInfos)
}

birth.setOnClickListener {
birthInput(birth)
}
}

fun registerBackPressedCallback(name: EditText, phoneNumber: EditText, mail: EditText,
Copy link

Choose a reason for hiding this comment

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

registerBackPressedCallback isInfoEmpty 함수들에서 editText 등을 매번 전부 받고있는데
이 뷰들을 전역변수로 관리하면 어떨까요? 매번 함수 파라미터로 넘기는건 불편할거 같아서요

birth: TextView, sex: RadioGroup, memo: EditText) {
val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (!isInfoEmpty(name, phoneNumber, mail, birth, sex, memo)) {
buildAlertDialog(this@ContactActivity)
} else finish()
}
}
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
}

private fun isInfoEmpty(name: EditText, phoneNumber: EditText, mail: EditText,
birth: TextView, sex: RadioGroup, memo: EditText): Boolean {
return name.text.isEmpty() &&
phoneNumber.text.isEmpty() &&
mail.text.isEmpty() &&
birth.text.isEmpty() &&
sex.checkedRadioButtonId == -1 &&
memo.text.isEmpty()
}

private fun buildAlertDialog(aContext: Context) {
MaterialAlertDialogBuilder(aContext)
.setMessage("작성 중인 내용이 있습니다. 정말 나가시겠습니까?")
.setNegativeButton("취소") { dialog, which ->
}
.setPositiveButton("확인") { dialog, which ->
finish()
}
.show()
}

fun cancelProcess() {
setResult(RESULT_CANCELED, Intent())
finish()
}

fun saveProcess(name: EditText, phoneNumber: EditText, mail: EditText,
birth: TextView, sex: RadioGroup, memo: EditText) {
if (isValidInfo(name, phoneNumber)) {
val contact: Contact = saveInfo(name, phoneNumber, mail, birth, sex, memo)
val returnIntent: Intent = Intent(this, MainActivity::class.java)
returnIntent.putExtra(Contact.KEY, contact)
setResult(RESULT_OK, returnIntent)
finish()
}
}

fun isValidInfo(name: EditText, phoneNumber: EditText): Boolean {
return if (!isValidName(name)) {
false
} else if (!isValidNumber(phoneNumber)) {
false
} else true
}

private fun isValidName(name: EditText): Boolean {
return if (name.text.toString() == "") {
Toast.makeText(this, "이름은 반드시 입력해야 합니다", Toast.LENGTH_SHORT).show()
false
} else true
}

private fun isValidNumber(phoneNumber: EditText): Boolean {
val v : Regex = Regex("^[0-9]+$")
return if (v.matches(phoneNumber.text.toString())) {
true
} else if (phoneNumber.text.toString() == "") {
Toast.makeText(this, "전화번호는 반드시 입력해야 합니다", Toast.LENGTH_SHORT).show()
false
} else {
Toast.makeText(this, "전화번호는 숫자로만 구성되어야 합니다", Toast.LENGTH_SHORT).show()
false
}
}

private fun saveInfo(name: EditText, phoneNumber: EditText, mail: EditText,
Copy link

Choose a reason for hiding this comment

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

여기 saveInfo 함수명과 실제 역할이 좀 다른거 같네요
함수명만 보면 저장을 해줄거 같은데, 실제론 객체 생성만 해주고 있어서요

birth: TextView, sex: RadioGroup, memo: EditText): Contact {
val nameInfo: String = name.text.toString()
val phoneNumberInfo: String = phoneNumber.text.toString()
val mailInfo: String = mail.text.toString()
val birthInfo: String = birth.text.toString()
val sexInfo: Int = sex.checkedRadioButtonId
val memoInfo: String = memo.text.toString()

val contact: Contact = Contact(nameInfo, phoneNumberInfo,mailInfo, birthInfo, sexInfo, memoInfo)
return contact
}

fun nameTextWatcher(profile: TextView): TextWatcher {
return object: TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}

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

override fun afterTextChanged(s: Editable?) {
s?.let {
updateProfile(profile, s.toString())
}
}
}
}

private fun updateProfile(profile: TextView, name: String) {
if (name.isEmpty())
profile.text = ""
else
profile.text = name[0].toString()
}

fun extendInput(viewMore: ConstraintLayout, moreInfos: ConstraintLayout) {
viewMore.visibility = ConstraintLayout.GONE
moreInfos.visibility = ConstraintLayout.VISIBLE
}

fun birthInput(birth: TextView) {
val calendar = Calendar.getInstance()
val cYear = calendar.get(Calendar.YEAR)
val cMonth = calendar.get(Calendar.MONTH)
val cDay = calendar.get(Calendar.DAY_OF_MONTH)

DatePickerDialog(this,
{ _, year, month, dayOfMonth -> birth.text = "$year-%02d-%02d".format(month + 1, dayOfMonth) },
cYear, cMonth, cDay).show()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package campus.tech.kakao.contacts

import android.os.Build
import android.os.Bundle
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat

class ContactInfoActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_contact_info)
val infoContact =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
intent.extras?.getSerializable(Contact.KEY,Contact::class.java)
else
intent.extras?.getSerializable(Contact.KEY) as Contact?

if (infoContact is Contact) {
val infoProfile: TextView = findViewById(R.id.info_profile)
val infoName: TextView = findViewById(R.id.info_name)
val infoPhoneNumber: TextView = findViewById(R.id.info_phone_number)
val infoMail: TextView = findViewById(R.id.info_mail)
val infoBirth: TextView = findViewById(R.id.info_birth)
val infoSex: TextView = findViewById(R.id.info_sex)
val infoMemo: TextView = findViewById(R.id.info_memo)

matchInfo(infoProfile, infoName, infoPhoneNumber, infoMail, infoBirth, infoSex, infoMemo, infoContact)
}
}

private fun matchInfo(infoProfile: TextView, infoName: TextView, infoPhoneNumber: TextView, infoMail: TextView,
infoBirth: TextView, infoSex: TextView, infoMemo: TextView, infoContact: Contact) {

infoProfile.text = infoContact.name[0].toString()
infoName.text = infoContact.name
infoPhoneNumber.text = infoContact.phoneNumber
infoMail.text = infoContact.mail
infoBirth.text = infoContact.birth
infoSex.text =
when (infoContact.sex) {
infoContact.FEMALE -> "여"
infoContact.MALE -> "남"
else -> ""
}
infoMemo.text = infoContact.memo
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package campus.tech.kakao.contacts

import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView

class ContactRecyclerAdapter(
var contactList: MutableList<Contact>,
val inflater: LayoutInflater,
val mContext: Context
) : RecyclerView.Adapter<ContactRecyclerAdapter.ContactViewHolder>() {
inner class ContactViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val itemLayout: ConstraintLayout
val profile: TextView
val name: TextView
init {
itemLayout = itemView.findViewById(R.id.item_contact)
itemLayout.setOnClickListener { showContactInfo(mContext, adapterPosition) }
profile = itemView.findViewById(R.id.contact_item_profile)
name = itemView.findViewById(R.id.contact_item_name)
}
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactViewHolder {
val view = inflater.inflate(R.layout.item_contact, parent, false)
return ContactViewHolder(view)
}

override fun onBindViewHolder(holder: ContactViewHolder, position: Int) {
holder.profile.text = contactList[position].name[0].toString()
holder.name.text = contactList[position].name
}

override fun getItemCount(): Int {
return contactList.size
}

private fun showContactInfo(mContext: Context, adapterPosition: Int) {
val contactIntent: Intent = Intent(mContext, ContactInfoActivity::class.java)
contactIntent.putExtra(Contact.KEY, contactList[adapterPosition])
mContext.startActivity(contactIntent)
}
}
Loading