diff --git a/README.md b/README.md index cc0acc5a..f61ce0a0 100644 --- a/README.md +++ b/README.md @@ -1 +1,33 @@ -# android-contacts +# android-contacts - 간단한 연락처 구현(1주차) +## 구현할 기능 목록 + +**지정된 형식의 데이터를 입력하는 기능** +- [x] 문자열을 입력할 수 있는 기능 +- [x] 숫자만 입력할 수 있는 기능 +- [x] 날짜 선택 기능 +- [x] 성별 선택 기능 + +**더보기 기능** +- [x] 더보기를 클릭하면 숨겨져 있던 입력 폼을 드러낼 수 있는 기능 + +**저장 기능** +- [x] 저장 버튼을 클릭하면 토스트 메세지를 출력 +- [x] 이름, 전화번호의 칸이 채워져 있지 않으면 저장이 되지 않으면 그에 맞는 토스트 메세지를 출력 + +**취소 기능** +- [x] 취소 버튼을 클릭하면 토스트 메세지를 출력 + +## step2 구현할 기능 목록 + +**연락처 추가 기능** +- [x] + 버튼을 통해 연락처를 추가하는 창을 띄우는 기능 + +**연락처 출력 기능** +- [x] 저장된 연락처들을 간단하게 이름만 띄워주는 기능 +- [x] 각 연락처를 클릭하면 상세 화면을 띄워주는 기능 +- [x] 상세화면에서 적힌 내용만 출력하는 기능 + +**연락처 저장 기능** +- [x] 저장 버튼을 누르면 연락처를 저장할 수 있는 기능 +- [x] 뒤로가기 버튼을 누를 때 만약 작성된 내용이 있다면 확인 팝업이 나타나는 기능 +- [x] 취소 버튼을 누를 때 만약 작성된 내용이 있다면 확인 팝업이 나타나는 기능 \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c5add08f..12f60dcb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 89dc9d8b..1793994b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,8 +12,14 @@ android:supportsRtl="true" android:theme="@style/Theme.Contacts" tools:targetApi="31"> + + @@ -23,4 +29,4 @@ - + \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/contacts/DetailActivity.kt b/app/src/main/java/campus/tech/kakao/contacts/DetailActivity.kt new file mode 100644 index 00000000..8a7bdd91 --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/contacts/DetailActivity.kt @@ -0,0 +1,96 @@ +package campus.tech.kakao.contacts + +import android.os.Bundle +import android.view.View +import android.widget.TextView +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat + +class DetailActivity : AppCompatActivity() { + lateinit var name: TextView + lateinit var phoneNumber: TextView + lateinit var mail: TextView + lateinit var birthday: TextView + lateinit var gender: TextView + lateinit var memo: TextView + lateinit var nameInputField: TextView + lateinit var phoneNumberInputField: TextView + lateinit var mailInputField: TextView + lateinit var birthdayInputField: TextView + lateinit var genderInputField: TextView + lateinit var memoInputField: TextView + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_detail) + + initVar() + bindField() + setVisibleCompletedFields() + } + + fun initVar(){ + name = findViewById(R.id.name) + phoneNumber = findViewById(R.id.phone_number) + mail = findViewById(R.id.mail) + birthday = findViewById(R.id.birthday) + gender = findViewById(R.id.gender) + memo = findViewById(R.id.memo) + nameInputField = findViewById(R.id.input_name_field) + phoneNumberInputField = findViewById(R.id.input_phone_number_field) + mailInputField = findViewById(R.id.input_mail_field) + birthdayInputField = findViewById(R.id.input_birthday_field) + genderInputField = findViewById(R.id.input_gender_field) + memoInputField = findViewById(R.id.input_memo_field) + } + + fun bindField(){ + val intent = intent + nameInputField.setText(intent.getStringExtra("name")) + phoneNumberInputField.setText(intent.getStringExtra("phoneNumber")) + mailInputField.setText(intent.getStringExtra("mail")) + birthdayInputField.setText(intent.getStringExtra("birthday")) + genderInputField.setText(intent.getStringExtra("gender")) + memoInputField.setText(intent.getStringExtra("memo")) + } + + fun setVisibleCompletedFields() { + if(ExistMail()){ + mail.visibility = View.VISIBLE + mailInputField.visibility = View.VISIBLE + } + if(ExistBirthday()){ + birthday.visibility = View.VISIBLE + birthdayInputField.visibility = View.VISIBLE + } + if(ExistGender()){ + gender.visibility = View.VISIBLE + genderInputField.visibility = View.VISIBLE + } + if(ExistMemo()){ + memo.visibility = View.VISIBLE + memoInputField.visibility = View.VISIBLE + } + + } + + + + fun ExistMail(): Boolean { + return mailInputField.text.toString() != "" + } + + fun ExistBirthday(): Boolean { + return birthdayInputField.text.toString() != "" + } + + fun ExistGender(): Boolean { + return genderInputField.text.toString() != "" + } + + fun ExistMemo(): Boolean { + return memoInputField.text.toString() != "" + } +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/contacts/ListActivity.kt b/app/src/main/java/campus/tech/kakao/contacts/ListActivity.kt new file mode 100644 index 00000000..ac1a7ca4 --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/contacts/ListActivity.kt @@ -0,0 +1,136 @@ +package campus.tech.kakao.contacts + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.activity.enableEdgeToEdge +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView + +class ListActivity : AppCompatActivity() { + lateinit var resultLauncher: ActivityResultLauncher + lateinit var descriptionText: TextView + lateinit var plusButton: TextView + lateinit var recyclerView: RecyclerView + lateinit var contactList: MutableList + lateinit var adapter: RecyclerViewAdapter + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_list) + + initVar() + initListener() + + } + + fun initVar(){ + contactList = mutableListOf() + adapter = RecyclerViewAdapter(contactList, LayoutInflater.from(this), this) + plusButton = findViewById(R.id.plus_item_button) + descriptionText = findViewById(R.id.description_text) + initRecyclerView() + initResultLauncher() + } + + fun initRecyclerView() { + recyclerView = findViewById(R.id.recyclerView) + recyclerView.adapter = adapter + recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + } + + fun initResultLauncher() { + resultLauncher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + val contact = Contact( + result.data?.getStringExtra("name") ?: "", + result.data?.getStringExtra("phoneNumber") ?: "", + result.data?.getStringExtra("mail") ?: "", + result.data?.getStringExtra("birthday") ?: "", + result.data?.getStringExtra("gender") ?: "", + result.data?.getStringExtra("memo") ?: "", + ) + Log.d("contact2", contact.name) + contactList.add(contact) + setDescriptionTextVisibilityGone() + adapter.notifyItemInserted(contactList.size) + } + } + } + fun initListener(){ + plusButton.setOnClickListener { + moveToAddContact() + } + } + fun moveToAddContact() { + val intent = Intent(this@ListActivity, MainActivity::class.java) + resultLauncher.launch(intent) + } + + fun setDescriptionTextVisibilityGone() { + descriptionText.visibility = View.GONE + } + +} + +class RecyclerViewAdapter( + var contactList: MutableList, + var inflater: LayoutInflater, + var context: Context +) : RecyclerView.Adapter() { + + inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val userTitle: TextView + val userName: TextView + + init { + userTitle = itemView.findViewById(R.id.user_title) + userName = itemView.findViewById(R.id.user_name) + itemView.setOnClickListener { + val position: Int = adapterPosition + val intent = Intent(context, DetailActivity::class.java) + val sendContact = contactList.get(position) + intent.putExtra("name", sendContact.name) + intent.putExtra("phoneNumber", sendContact.phoneNumber) + intent.putExtra("mail", sendContact.mail) + intent.putExtra("birthday", sendContact.birthday) + intent.putExtra("gender", sendContact.gender) + intent.putExtra("memo", sendContact.memo) + context.startActivity(intent) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = inflater.inflate(R.layout.contact_item, parent, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.userTitle.text = contactList.get(position).name[0].toString() + holder.userName.text = contactList.get(position).name + } + + override fun getItemCount(): Int { + return contactList.size + } +} + +class Contact( + var name: String, + var phoneNumber: String, + var mail: String, + var birthday: String, + var gender: String, + var memo: String +) {} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/contacts/MainActivity.kt b/app/src/main/java/campus/tech/kakao/contacts/MainActivity.kt index 7aae79fe..03f9c44a 100644 --- a/app/src/main/java/campus/tech/kakao/contacts/MainActivity.kt +++ b/app/src/main/java/campus/tech/kakao/contacts/MainActivity.kt @@ -1,11 +1,201 @@ package campus.tech.kakao.contacts +import android.app.DatePickerDialog +import android.content.Context +import android.content.DialogInterface import android.os.Bundle +import android.view.View +import android.widget.EditText +import android.widget.RadioGroup +import android.widget.TextView +import android.widget.Toast +import androidx.activity.OnBackPressedCallback +import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +const val EXIST_NAME_AND_PHONE_NUMBER = 1 +const val NOT_EXIST_NAME = 2 +const val NOT_EXIST_PHONE_NUMBER = 3 + class MainActivity : AppCompatActivity() { + lateinit var name : EditText + lateinit var phoneNumber : EditText + lateinit var mail : EditText + lateinit var gender : EditText + lateinit var genderRadioGroup : RadioGroup + lateinit var memo : EditText + lateinit var birthday : EditText + lateinit var moreInfoButton : TextView + + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + initVar() + initListener() + onBackPressedDispatcher.addCallback(this, onBackPressedCallback) + } + + private val onBackPressedCallback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + finishContactAdding(this@MainActivity) + } + } + + fun initListener(){ + saveButtonSetOnClickListener(this) + cancelButtonSetOnClickListener(this) + moreButtonSetOnClickListener() + birthdayFieldSetOnclickListener() + genderFieldSetOnChangeListener() + } + + fun finishContactAdding(context: Context){ + if(isNowWritting()) + displayCancelDialog(context) + else + finish() + } + + + fun displayCancelDialog(context: Context){ + val alert = AlertDialog.Builder(this@MainActivity) + alert.setTitle("") + alert.setMessage("작성중인 내용이 있습니다. 정말 나가시겠습니까?") + alert.setPositiveButton("작성하기", DialogInterface.OnClickListener{ dialog, which -> + }) + alert.setNegativeButton("나가기", DialogInterface.OnClickListener { dialog, which -> + displayCancelToastMessage(context) + finish() + }) + alert.show() + } + + fun isNowWritting() : Boolean{ + return if(name.text.toString() != "" || phoneNumber.text.toString() != "" || mail.text.toString() != "" || birthday.text.toString() != "" || gender.text.toString() != "" || memo.text.toString() != "") + true + else false + } + + fun cancelButtonSetOnClickListener(context : Context){ + findViewById(R.id.button_cancel).setOnClickListener(){ + finishContactAdding(context) + } + } + + fun displayCancelToastMessage(context: Context){ + val cancelMessage = Toast.makeText(context, "취소 되었습니다", Toast.LENGTH_SHORT) + cancelMessage.show() + } + + fun saveButtonSetOnClickListener(context : Context){ + findViewById(R.id.button_save).setOnClickListener(){ + saveContact(context) + } + } + + fun existNameAndPhoneNum() : Boolean{ + val checkNum = checkNameAndPhoneNum() + return if(checkNum == NOT_EXIST_NAME || checkNum == NOT_EXIST_PHONE_NUMBER) false + else true + } + + fun saveContact(context:Context){ + if (existNameAndPhoneNum()){ + displaySaveMessage(context) + intent.putExtra("name", name.text.toString()) + intent.putExtra("phoneNumber", phoneNumber.text.toString()) + intent.putExtra("mail", mail.text.toString()) + intent.putExtra("birthday", birthday.text.toString()) + intent.putExtra("gender", gender.text.toString()) + intent.putExtra("memo", memo.text.toString()) + setResult(RESULT_OK, intent) + finish() + } + else { + displaySaveMessage(context) + } + + } + + fun displaySaveMessage(context: Context){ + val text = getToastMessageToNameAndPhoneNum() + val saveMessage = Toast.makeText(context, text, Toast.LENGTH_SHORT) + saveMessage.show() + } + + + fun checkNameAndPhoneNum() : Int{ + return if (name.text.toString() == ""){ + name.requestFocus() + NOT_EXIST_NAME + } else if(phoneNumber.text.toString() == ""){ + phoneNumber.requestFocus() + NOT_EXIST_PHONE_NUMBER + } else{ + EXIST_NAME_AND_PHONE_NUMBER + } + } + + fun getToastMessageToNameAndPhoneNum() : String{ + val checkNum = checkNameAndPhoneNum() + return when(checkNum){ + EXIST_NAME_AND_PHONE_NUMBER -> "저장이 완료되었습니다" + NOT_EXIST_NAME -> "이름을 입력해 주세요" + NOT_EXIST_PHONE_NUMBER -> "전화번호를 입력해 주세요" + else -> "오류" + } + } + + fun moreButtonSetOnClickListener(){ + moreInfoButton.setOnClickListener(){ + setInputFieldVisible() + } + } + + fun setInputFieldVisible(){ + birthday.visibility = View.VISIBLE + gender.visibility = View.VISIBLE + genderRadioGroup.visibility = View.VISIBLE + memo.visibility = View.VISIBLE + moreInfoButton.visibility = View.GONE + } + + fun birthdayFieldSetOnclickListener(){ + birthday.setOnClickListener(){ + setBirthday() + } + } + + fun setBirthday(){ + val datePicker = DatePickerDialog(this, DatePickerDialog.OnDateSetListener { + _, year, month, day -> + birthday.setText("${year}/${month}/${day}") + }, 2000, 1, 1).show() + } + + fun genderFieldSetOnChangeListener(){ + genderRadioGroup.setOnCheckedChangeListener { _, id -> + fillGenderField(id) + } + } + fun fillGenderField(id : Int){ + when(id){ + R.id.button_woman -> gender.setText("여성") + R.id.button_man -> gender.setText("남성") + else -> gender.setText("") + } + } + + fun initVar(){ + name = findViewById(R.id.input_name) + phoneNumber = findViewById(R.id.input_phone_number) + mail = findViewById(R.id.input_mail) + birthday = findViewById(R.id.input_birthday) + gender = findViewById(R.id.input_gender) + genderRadioGroup = findViewById(R.id.gender_radio_group) + memo = findViewById(R.id.input_memo) + moreInfoButton = findViewById(R.id.more_info_button) } } + diff --git a/app/src/main/res/drawable/input_form.xml b/app/src/main/res/drawable/input_form.xml new file mode 100644 index 00000000..2abf3180 --- /dev/null +++ b/app/src/main/res/drawable/input_form.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/item.xml b/app/src/main/res/drawable/item.xml new file mode 100644 index 00000000..766b434e --- /dev/null +++ b/app/src/main/res/drawable/item.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/yellow_box.xml b/app/src/main/res/drawable/yellow_box.xml new file mode 100644 index 00000000..2a670c65 --- /dev/null +++ b/app/src/main/res/drawable/yellow_box.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/yellow_circle.xml b/app/src/main/res/drawable/yellow_circle.xml new file mode 100644 index 00000000..29375f91 --- /dev/null +++ b/app/src/main/res/drawable/yellow_circle.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_detail.xml b/app/src/main/res/layout/activity_detail.xml new file mode 100644 index 00000000..ad84b28e --- /dev/null +++ b/app/src/main/res/layout/activity_detail.xml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_list.xml b/app/src/main/res/layout/activity_list.xml new file mode 100644 index 00000000..8892367e --- /dev/null +++ b/app/src/main/res/layout/activity_list.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 24d17df2..f73d1e58 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -7,13 +7,193 @@ android:layout_height="match_parent" tools:context=".MainActivity"> + + + + + + + + + + + + + + + + + + + + + + + + + + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintLeft_toRightOf="@+id/button_cancel" + android:text="저장" + android:gravity="center" + android:textSize="20sp"/> + diff --git a/app/src/main/res/layout/contact_item.xml b/app/src/main/res/layout/contact_item.xml new file mode 100644 index 00000000..98045290 --- /dev/null +++ b/app/src/main/res/layout/contact_item.xml @@ -0,0 +1,45 @@ + + + + + + + + \ No newline at end of file