-
Notifications
You must be signed in to change notification settings - Fork 32
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
base: njiyeon
Are you sure you want to change the base?
Changes from all commits
21ade3c
0aa47e2
b87288b
e6b88aa
78dbecf
d9d3623
5d441af
8973b27
8c267d8
fefcefd
23a096a
4a25521
583fefc
fdb2300
6eafcfe
1c1db33
fdd1e37
6008464
7c0bf01
1bd2bd6
6fe1b91
70c6b7e
5a2b86e
92ac65d
2a9ce96
1fe6f03
dd4fe58
7480020
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,26 @@ | ||
# android-contacts | ||
|
||
|
||
## 연락처 만들기 | ||
|
||
### 기능 요구 사항 | ||
간단한 연락처를 구현한다. | ||
|
||
[1단계] | ||
- 연락처를 추가한다. | ||
- 이름과 전화번호는 필수 값이다. | ||
- 입력하지 않으면 토스트 메시지를 보여준다. | ||
- 전화번호 입력은 숫자만 가능하다. | ||
- 더보기를 눌러 입력 폼을 확장할 수 있다. | ||
- 생일, 성별, 메모 입력 폼이 등장한다. | ||
- 성별을 둘 중 하나를 선택할 수 있다. | ||
- 저장 버튼을 누르면 '저장이 완료 되었습니다' 라는 토스트 메시지를 보여준다. | ||
- 취소 버튼을 누르면 '취소 되었습니다' 라는 토스트 메시지를 보여준다. | ||
|
||
[2단계] | ||
- 연락처 등록 화면을 구현한다. | ||
- 연락처를 저장하면 목록에 추가된다. | ||
- 저장된 연락처가 많을 경우 목록은 스크롤 되어야 한다. | ||
- 추가된 연락처를 선택하여 상세 화면을 볼 수 있다. | ||
- 연락처 작성 중 뒤로가기 버튼을 누르면 확인 팝업이 나타난다. | ||
- 추가된 연락처는 앱을 다시 실행하면 유지되지 않는다. |
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() | ||
} | ||
} |
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 = "이름" | ||
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 | ||
} | ||
} | ||
} |
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CardView를 직접 inflate 해서 LinearLayout에 추가하셨네요. 매번 View를 직접 생성해서 넣어주면 어떤 이슈가 있을까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 매번 view를 생성해서 넣으면 앱의 메모리 사용량의 증가를 야기하는 문제점이 있음을 확인하였습니다. 또한 코드 자체가 복잡하고 유지보수가 어렵다는 문제가 생긴다는 것을 알 수 있었습니다. 이러한 문제점은 RecyclerView를 이용해서 해결할 수 있다는 것을 확인하였습니다. 다음 과제에서는 RecyclerView를 활용하도록 하겠습니다 ! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋습니다! |
||
} | ||
} | ||
} | ||
|
||
// '+' 버튼 클릭 이벤트 처리 | ||
fab.setOnClickListener { | ||
val intent = Intent(this, SecondActivity::class.java) | ||
addContactLauncher.launch(intent) | ||
} | ||
} | ||
} |
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> |
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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
디자인 관련해서 마땅히 있어야 하는 값이라면 xml에 세팅해주세요! 프로그래밍을 통해 값을 동적으로 바꾸는것보다 xml에 static하게 구성하는게 성능 상 이점이 있습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 !!