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주차_과제 #28

Open
wants to merge 28 commits into
base: njiyeon
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
21ade3c
Update README.md
nJiyeon Jun 25, 2024
0aa47e2
Update README.md
nJiyeon Jun 26, 2024
b87288b
Update MainActivity.kt
nJiyeon Jun 28, 2024
e6b88aa
Create SecondActivity
nJiyeon Jun 28, 2024
78dbecf
Rename SecondActivity to SecondActivity.kt
nJiyeon Jun 28, 2024
d9d3623
Create DetailActivity
nJiyeon Jun 28, 2024
5d441af
Update AndroidManifest.xml
nJiyeon Jun 28, 2024
8973b27
Update activity_main.xml
nJiyeon Jun 28, 2024
8c267d8
Create activity_detail.xml
nJiyeon Jun 28, 2024
fefcefd
Create activity_second.xml
nJiyeon Jun 28, 2024
23a096a
Create contact_item.xml
nJiyeon Jun 28, 2024
4a25521
Create circle_background.xml
nJiyeon Jun 28, 2024
583fefc
Create rounded_orange_button.xml
nJiyeon Jun 28, 2024
fdb2300
Create ic_main.xml
nJiyeon Jun 28, 2024
6eafcfe
Rename DetailActivity to DetailActivity.kt
nJiyeon Jun 29, 2024
1c1db33
Update AndroidManifest.xml
nJiyeon Jul 4, 2024
fdd1e37
Update AndroidManifest.xml
nJiyeon Jul 4, 2024
6008464
Rename activity_second.xml to activity_app_contact.xml
nJiyeon Jul 4, 2024
7c0bf01
Rename activity_app_contact.xml to activity_add_contact.xml
nJiyeon Jul 4, 2024
1bd2bd6
Rename activity_add_contact.xml to activity_app_contact.xml
nJiyeon Jul 4, 2024
6fe1b91
Update and rename SecondActivity.kt to AppContactActivity.kt
nJiyeon Jul 4, 2024
70c6b7e
Update activity_main.xml
nJiyeon Jul 4, 2024
5a2b86e
Update activity_main.xml
nJiyeon Jul 4, 2024
92ac65d
Update AppContactActivity.kt
nJiyeon Jul 4, 2024
2a9ce96
Update AppContactActivity.kt
nJiyeon Jul 4, 2024
1fe6f03
Update AppContactActivity.kt
nJiyeon Jul 4, 2024
dd4fe58
Update AppContactActivity.kt
nJiyeon Jul 4, 2024
7480020
Update AppContactActivity.kt
nJiyeon Jul 4, 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
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,26 @@
# android-contacts


## 연락처 만들기

### 기능 요구 사항
간단한 연락처를 구현한다.

[1단계]
- 연락처를 추가한다.
- 이름과 전화번호는 필수 값이다.
- 입력하지 않으면 토스트 메시지를 보여준다.
- 전화번호 입력은 숫자만 가능하다.
- 더보기를 눌러 입력 폼을 확장할 수 있다.
- 생일, 성별, 메모 입력 폼이 등장한다.
- 성별을 둘 중 하나를 선택할 수 있다.
- 저장 버튼을 누르면 '저장이 완료 되었습니다' 라는 토스트 메시지를 보여준다.
- 취소 버튼을 누르면 '취소 되었습니다' 라는 토스트 메시지를 보여준다.

[2단계]
- 연락처 등록 화면을 구현한다.
- 연락처를 저장하면 목록에 추가된다.
- 저장된 연락처가 많을 경우 목록은 스크롤 되어야 한다.
- 추가된 연락처를 선택하여 상세 화면을 볼 수 있다.
- 연락처 작성 중 뒤로가기 버튼을 누르면 확인 팝업이 나타난다.
- 추가된 연락처는 앱을 다시 실행하면 유지되지 않는다.
11 changes: 10 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,24 @@
android:supportsRtl="true"
android:theme="@style/Theme.Contacts"
tools:targetApi="31">

<activity
android:name=".AddContactActivity"
android:exported="false" />

<activity
android:name=".DetailActivity"
android:exported="false" />

<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>

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

import android.app.AlertDialog
import android.app.DatePickerDialog
import android.content.Intent
import android.os.Bundle
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.RadioButton
import android.widget.TextView
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import java.text.SimpleDateFormat
import java.util.*

class AppContactActivity : AppCompatActivity() {

private lateinit var etName: EditText
private lateinit var etPhone: EditText
private lateinit var etEmail: EditText
private lateinit var etBirthdate: EditText
private lateinit var rbFemale: RadioButton
private lateinit var rbMale: RadioButton
private lateinit var etNotes: EditText
private lateinit var btnMore: TextView
private lateinit var moreForm: LinearLayout
private lateinit var btnCancel: TextView
private lateinit var btnSave: TextView

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_app_contact)

etName = findViewById(R.id.et_name)
etPhone = findViewById(R.id.et_phone)
etEmail = findViewById(R.id.et_email)
etBirthdate = findViewById(R.id.et_birthdate)
rbFemale = findViewById(R.id.rb_female)
rbMale = findViewById(R.id.rb_male)
etNotes = findViewById(R.id.et_notes)
btnMore = findViewById(R.id.btn_more)
moreForm = findViewById(R.id.more_form)
btnCancel = findViewById(R.id.btn_cancel)
btnSave = findViewById(R.id.btn_save)

etBirthdate.setOnClickListener {
showDatePickerDialog(etBirthdate)
}

btnMore.setOnClickListener {
moreForm.isVisible = true
btnMore.isVisible = false
}

btnCancel.setOnClickListener {
Toast.makeText(this, "취소 되었습니다", Toast.LENGTH_SHORT).show()
}

btnSave.setOnClickListener {
val name = etName.text.toString()
val phone = etPhone.text.toString()

if (name.isEmpty()) {
Toast.makeText(this, "이름은 필수 값입니다.", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (phone.isEmpty()) {
Toast.makeText(this, "전화 번호는 필수 값입니다.", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (!phone.matches(Regex("\\d+"))) {
Toast.makeText(this, "전화번호는 숫자만 입력 가능합니다", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}

val email = etEmail.text.toString()
val birthdate = etBirthdate.text.toString()
val gender = if (rbFemale.isChecked) "여성" else if (rbMale.isChecked) "남성" else ""
val notes = etNotes.text.toString()

val intent = Intent().apply {
putExtra("name", name)
putExtra("phone", phone)
putExtra("email", email)
putExtra("birthdate", birthdate)
putExtra("gender", gender)
putExtra("notes", notes)
}
setResult(RESULT_OK, intent)
Toast.makeText(this, "저장이 완료 되었습니다", Toast.LENGTH_SHORT).show()
finish()
}

onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (isFormFilled()) {
showExitConfirmationDialog()
} else {
finish()
}
}
})
}

private fun isFormFilled(): Boolean {
return etName.text.isNotEmpty() ||
etPhone.text.isNotEmpty() ||
etEmail.text.isNotEmpty() ||
etBirthdate.text.isNotEmpty() ||
rbFemale.isChecked ||
rbMale.isChecked ||
etNotes.text.isNotEmpty()
}

private fun showExitConfirmationDialog() {
val builder = AlertDialog.Builder(this)
builder.setMessage("작성중인 내용이 있습니다. 정말 나가시겠습니까?")
.setCancelable(false)
.setPositiveButton("네") { dialog, id ->
finish()
}
.setNegativeButton("아니요") { dialog, id ->
dialog.dismiss()
}
val alert = builder.create()
alert.show()
}

private fun showDatePickerDialog(editText: EditText) {
val calendar = Calendar.getInstance()
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH)
val day = calendar.get(Calendar.DAY_OF_MONTH)

val datePickerDialog = DatePickerDialog(
this,
{ _, selectedYear, selectedMonth, selectedDay ->
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val selectedDate = Calendar.getInstance()
selectedDate.set(selectedYear, selectedMonth, selectedDay)
editText.setText(dateFormat.format(selectedDate.time))
},
year,
month,
day
)
datePickerDialog.show()
}
}
45 changes: 45 additions & 0 deletions app/src/main/java/campus/tech/kakao/contacts/DetailActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package campus.tech.kakao.contacts

import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class DetailActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)

val name = intent.getStringExtra("name") ?: ""
val phone = intent.getStringExtra("phone") ?: ""
val email = intent.getStringExtra("email") ?: ""
val birthdate = intent.getStringExtra("birthdate") ?: ""
val gender = intent.getStringExtra("gender") ?: ""
val notes = intent.getStringExtra("notes") ?: ""

if (name.isNotEmpty()) {
findViewById<TextView>(R.id.name_label).text = "이름"
Copy link

Choose a reason for hiding this comment

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

디자인 관련해서 마땅히 있어야 하는 값이라면 xml에 세팅해주세요! 프로그래밍을 통해 값을 동적으로 바꾸는것보다 xml에 static하게 구성하는게 성능 상 이점이 있습니다.

Copy link
Author

Choose a reason for hiding this comment

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

넵 !!

findViewById<TextView>(R.id.name_value).text = name
}
if (phone.isNotEmpty()) {
findViewById<TextView>(R.id.phone_label).text = "전화번호"
findViewById<TextView>(R.id.phone_value).text = phone
}
if (email.isNotEmpty()) {
findViewById<TextView>(R.id.email_label).text = "메일"
findViewById<TextView>(R.id.email_value).text = email
}
if (birthdate.isNotEmpty()) {
findViewById<TextView>(R.id.birthdate_label).text = "생일"
findViewById<TextView>(R.id.birthdate_value).text = birthdate
}
if (gender.isNotEmpty()) {
findViewById<TextView>(R.id.gender_label).text = "성별"
findViewById<TextView>(R.id.gender_value).text = gender
}
if (notes.isNotEmpty()) {
findViewById<TextView>(R.id.notes_label).text = "메모"
findViewById<TextView>(R.id.notes_value).text = notes
}
}
}
60 changes: 60 additions & 0 deletions app/src/main/java/campus/tech/kakao/contacts/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,71 @@
package campus.tech.kakao.contacts

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView

class MainActivity : AppCompatActivity() {

private lateinit var contactList: LinearLayout
private lateinit var emptyText: TextView
private lateinit var addContactLauncher: ActivityResultLauncher<Intent>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

contactList = findViewById(R.id.contact_list)
emptyText = findViewById(R.id.empty_text)
val fab = findViewById<TextView>(R.id.fab)

// ActivityResultLauncher 초기화
addContactLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
val data = result.data
if (data != null) {
val name = data.getStringExtra("name") ?: ""
val phone = data.getStringExtra("phone") ?: ""
val email = data.getStringExtra("email") ?: ""
val birthdate = data.getStringExtra("birthdate") ?: ""
val gender = data.getStringExtra("gender") ?: ""
val notes = data.getStringExtra("notes") ?: ""

// 연락처가 추가되면 가운데 텍스트를 숨김
emptyText.visibility = View.GONE

val contactView = LayoutInflater.from(this).inflate(R.layout.contact_item, contactList, false) as CardView

contactView.findViewById<TextView>(R.id.contact_name).text = name
contactView.findViewById<TextView>(R.id.contact_icon).text = name.first().toString()

contactView.setOnClickListener {
val detailIntent = Intent(this@MainActivity, DetailActivity::class.java).apply {
putExtra("name", name)
putExtra("phone", phone)
putExtra("email", email)
putExtra("birthdate", birthdate)
putExtra("gender", gender)
putExtra("notes", notes)
}
startActivity(detailIntent)
}

contactList.addView(contactView)
Copy link

Choose a reason for hiding this comment

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

CardView를 직접 inflate 해서 LinearLayout에 추가하셨네요. 매번 View를 직접 생성해서 넣어주면 어떤 이슈가 있을까요?
그리고 이를 위한 해결법은 어떤게 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

매번 view를 생성해서 넣으면 앱의 메모리 사용량의 증가를 야기하는 문제점이 있음을 확인하였습니다. 또한 코드 자체가 복잡하고 유지보수가 어렵다는 문제가 생긴다는 것을 알 수 있었습니다. 이러한 문제점은 RecyclerView를 이용해서 해결할 수 있다는 것을 확인하였습니다. 다음 과제에서는 RecyclerView를 활용하도록 하겠습니다 !

Copy link

Choose a reason for hiding this comment

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

좋습니다!

}
}
}

// '+' 버튼 클릭 이벤트 처리
fab.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
addContactLauncher.launch(intent)
}
}
}
4 changes: 4 additions & 0 deletions app/src/main/res/drawable/circle_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/holo_orange_light"/>
<corners android:radius="20dp"/>
</shape>
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/rounded_orange_button.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/holo_orange_light"/>
<corners android:radius="28dp"/>
</shape>
Loading