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

Open
wants to merge 58 commits into
base: akuby21
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
76fda5f
docs: edit README.md
akuby21 Jun 24, 2024
a5acff4
design: add editText background drawable
akuby21 Jun 24, 2024
2e532c5
design: create main image drawable
akuby21 Jun 24, 2024
5df9b77
feat: create default editfield layout
akuby21 Jun 24, 2024
fb54b43
feat: set inputType phone
akuby21 Jun 25, 2024
dc69a07
refactor: remove unnecessary xmlns tag
akuby21 Jun 25, 2024
3d383aa
style: formatting xml
akuby21 Jun 25, 2024
40a4e1c
feat: showDetail on contact editText
akuby21 Jun 25, 2024
7b22e26
fix: fix editTextList element height
akuby21 Jun 25, 2024
b16ea1b
refactor: separte ui method
akuby21 Jun 26, 2024
df48123
feat: implement birthday input
akuby21 Jun 26, 2024
94afa3f
refactor: add ScrollView
akuby21 Jun 26, 2024
40f38f2
docs: edit README.md
akuby21 Jun 26, 2024
fd8b9bf
refactor: hide keyboard when unfocused
akuby21 Jun 26, 2024
439e144
feat: add RadioGroup, button
akuby21 Jun 26, 2024
1087ff7
feat: create Contact, Gender
akuby21 Jun 26, 2024
6785fe2
fix: disable hideSoftInputFromWindow
akuby21 Jun 26, 2024
2f5b9a4
refactor: edit view structure
akuby21 Jun 26, 2024
1d85198
fix: restore unintentional code remove
akuby21 Jun 26, 2024
6792384
docs: edit README
akuby21 Jun 26, 2024
bb9e6fc
design: add MaterialButton ripple effect
akuby21 Jun 26, 2024
7cc95e2
docs: edit README
akuby21 Jun 26, 2024
3940417
feat: add cancelContact
akuby21 Jun 26, 2024
df50f96
feat: add validation of contact
akuby21 Jun 26, 2024
457756d
feat: add setFocus()
akuby21 Jun 26, 2024
2ad4a08
refactor: init datePicker
akuby21 Jun 26, 2024
6b7e6f5
fix: init datePicker
akuby21 Jun 26, 2024
c291aa6
refactor: add styles.xml
akuby21 Jun 26, 2024
e70e2d2
design: adjust height
akuby21 Jun 26, 2024
a1d06c7
style: sort codes
akuby21 Jun 26, 2024
3c022b6
feat: implement backbutton event
akuby21 Jun 27, 2024
a2c2fb1
rename: rename MainActivity to ContactAddActivity
akuby21 Jun 27, 2024
232d5c9
docs: edit README
akuby21 Jun 27, 2024
db71408
feat: create contactRepository
akuby21 Jun 28, 2024
315b95d
rename: rename xml
akuby21 Jun 28, 2024
9b026d4
fix: edit Contact property type
akuby21 Jun 28, 2024
b2dd294
refactor: add getGender()
akuby21 Jun 28, 2024
34880d6
feat: implement submit Contact
akuby21 Jun 28, 2024
16cc8ec
rename: rename method
akuby21 Jun 28, 2024
47fa536
feat: implement ContactListActivity
akuby21 Jun 28, 2024
51f1b09
feat: get result from ContactAddActivity
akuby21 Jun 28, 2024
586e5dd
refactor: move setOnClickListener to ViewHolder
akuby21 Jun 28, 2024
0c84e9b
feat: implement ContactDetailActivity
akuby21 Jun 28, 2024
6b5dc38
fix: edit intent-filter
akuby21 Jun 28, 2024
90ac0cf
chore: make directory for each role
akuby21 Jun 28, 2024
e12d22a
design: create dimen for big text size
akuby21 Jun 28, 2024
2987faf
fix: fix if condition in checking editing
akuby21 Jun 28, 2024
dd54fc4
fix: typo
akuby21 Jun 28, 2024
9682493
refactor: assign korean on presenting contact detail
akuby21 Jun 28, 2024
a1c89eb
rename : apply PascalCase to ContactRepository
akuby21 Jun 28, 2024
e789e69
refactor: change type of bDay in Contact to Date
akuby21 Jul 1, 2024
b91ac9b
refactor: remove parm context from RecyclerAdapter
akuby21 Jul 1, 2024
f6fd76d
refactor: add Constant object for Const value
akuby21 Jul 1, 2024
5dab4c0
refactor: move View init to onCreate
akuby21 Jul 1, 2024
b643aff
refactor: set view variable to private
akuby21 Jul 1, 2024
c5b82d1
refactor: remove unnecessary method
akuby21 Jul 1, 2024
3964908
style: move onCreate() to top
akuby21 Jul 1, 2024
05b8afb
refactor: shorten view hierarchy
akuby21 Jul 1, 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
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,42 @@
# android-contacts
**카카오 테크 캠퍼스 STEP2 Android 1주차 1단계 미션 - 연락처 추가**
## 기능 요구사항
### 1단계 기능
- 연락처를 추가한다.
- 이름과 전화번호는 필수 값이다.
- 입력하지 않으면 토스트 메시지를 보여준다.
- 전화번호 입력은 숫자만 가능하다.
- 더보기를 눌러 입력 폼을 확장할 수 있다.
- 생일, 성별, 메모 입력 폼이 등장한다.
- 성별을 둘 중 하나를 선택할 수 있다.
- 저장 버튼을 누르면 '저장이 완료되었습니다' 라는 토스트 메시지를 보여준다.
- 취소 버튼을 누르면 '취소 되었습니다' 라는 토스트 메시지를 보여준다.
### 2단계 기능
- 연락처 등록 화면을 구현한다.
- 연락처를 저장하면 목록에 추가된다.
- 저장된 연락처가 많을 경우 목록은 스크롤 되어야 한다.
- 추가된 연락처를 선택하여 상세 화면을 볼 수 있다.
- 연락처 작성 중 뒤로가기 버튼을 누르면 확인 팝업이 나타난다.
- 추가된 연락처는 앱을 다시 실행하면 유지되지 않는다.

## 구현할 기능 목록
### 1단계 기능
- [x] 이름, 전화번호 입력 폼 구현
- [x] 전화번호 입력 폼 숫자 키보드로 전환
- [x] 더보기를 눌러 입력 폼 확장 기능 구현
- [x] 키보드로인한 화면 가릴시를 위한 스크롤 기능 구현
- [x] 생일, 성별, 메모 입력 폼 구현
- [x] 성별 입력 폼 남,여 둘 중 하나만 선택 가능하게 하기
- [x] 하단 취소, 저장버튼 ripple 효과 넣기
- [x] 저장 버튼을 누르면 토스트 출력
- [x] 필수 입력 값 미입력시 별도 메시지 출력
- [x] 미입력된 항목으로 Focus 이동
- [x] 취소 버튼을 누르면 토스트 메시지 출력
### 2단계 기능
- [x] 연락처 등록 화면 구현
- [x] 연락처 목록 화면 구현
- [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
11 changes: 9 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,21 @@
android:theme="@style/Theme.Contacts"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".View.ContactDetailActivity"
android:exported="false" />
<activity
android:name=".View.ContactListActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".View.ContactAddActivity"
android:exported="false"
android:windowSoftInputMode="adjustResize"/>
</application>

</manifest>
</manifest>
11 changes: 0 additions & 11 deletions app/src/main/java/campus/tech/kakao/contacts/MainActivity.kt

This file was deleted.

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

import java.util.Date

data class Contact(
var name: String,
var tel: String,
var mail: String? = null,
var bDay: Date? = null,
var gender: Gender? = null,
var memo: String? = null
)
5 changes: 5 additions & 0 deletions app/src/main/java/campus/tech/kakao/contacts/Model/Gender.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package campus.tech.kakao.contacts.Model

enum class Gender {
FEMALE,MALE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package campus.tech.kakao.contacts.Repository

import campus.tech.kakao.contacts.Model.Contact

object ContactRepository {
private var _contactList : MutableList<Contact> = mutableListOf<Contact>()
val contactList : List<Contact> = _contactList
akuby21 marked this conversation as resolved.
Show resolved Hide resolved

fun addContact(contact: Contact){
_contactList.add(contact)
}

fun removeContact(contact: Contact){
_contactList.remove(contact)
}

fun removeContact(position : Int){
_contactList.removeAt(position)
}

}
5 changes: 5 additions & 0 deletions app/src/main/java/campus/tech/kakao/contacts/Util/Constant.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package campus.tech.kakao.contacts.Util

object Constant {
const val NAV_KEY_POSITION = "position"
}
22 changes: 22 additions & 0 deletions app/src/main/java/campus/tech/kakao/contacts/Util/DateFormatter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package campus.tech.kakao.contacts.Util

import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

object DateFormatter {
private val dateFormatter = SimpleDateFormat("yyyy.MM.dd", Locale.KOREAN)

fun stringToDate(string: String) : Date?{
try{
return dateFormatter.parse(string)
} catch (e:Exception){
return null
}
}

fun dateToString(date : Date?) : String?{
if(date == null) return null
return dateFormatter.format(date)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package campus.tech.kakao.contacts.View.Adapter

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.recyclerview.widget.RecyclerView
import campus.tech.kakao.contacts.Model.Contact
import campus.tech.kakao.contacts.R
import campus.tech.kakao.contacts.Util.Constant
import campus.tech.kakao.contacts.View.ContactDetailActivity


class RecyclerAdapter(
private var contactList : List<Contact>,
private var inflater : LayoutInflater,

) : RecyclerView.Adapter<RecyclerAdapter.ViewHolder>(){

class ViewHolder(context: Context, itemView : View) : RecyclerView.ViewHolder(itemView){
val contactName : TextView
val contactImage : TextView
init{
contactImage = itemView.findViewById<TextView>(R.id.contactImage)
contactName = itemView.findViewById<TextView>(R.id.contactName)

itemView.setOnClickListener {
val intent = Intent(context, ContactDetailActivity::class.java)
intent.putExtra(Constant.NAV_KEY_POSITION,adapterPosition)
context.startActivity(intent)
}
}
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = inflater.inflate(R.layout.contact_element,parent,false)
akuby21 marked this conversation as resolved.
Show resolved Hide resolved

return ViewHolder(parent.context,view)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val contact = contactList[position]
holder.contactName.text = contact.name
holder.contactImage.text = contact.name[0].toString()
}

override fun getItemCount(): Int {
return contactList.size
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package campus.tech.kakao.contacts.View

import android.app.AlertDialog
import android.app.DatePickerDialog
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.RadioGroup
import android.widget.TextView
import android.widget.Toast
import androidx.activity.addCallback
import androidx.appcompat.app.AppCompatActivity
import campus.tech.kakao.contacts.Model.Contact
import campus.tech.kakao.contacts.Model.Gender
import campus.tech.kakao.contacts.R
import campus.tech.kakao.contacts.Repository.ContactRepository
import campus.tech.kakao.contacts.Util.DateFormatter
import com.google.android.material.button.MaterialButton

class ContactAddActivity : AppCompatActivity() {
private lateinit var name: EditText
private lateinit var tel: EditText
private lateinit var mail: EditText
private lateinit var bday: TextView
private lateinit var genderRadioGroup: RadioGroup
private lateinit var memo: EditText

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

name = findViewById<EditText>(R.id.contactName)
tel = findViewById<EditText>(R.id.contactTel)
mail = findViewById<EditText>(R.id.contactMail)
bday = findViewById<TextView>(R.id.contactBirthDay)
genderRadioGroup = findViewById<RadioGroup>(R.id.genderRadioGroup)
memo = findViewById<EditText>(R.id.contactMemo)
val editTextList = findViewById<LinearLayout>(R.id.editTextList)
val showDetail = findViewById<TextView>(R.id.more)
val cancelBtn = findViewById<MaterialButton>(R.id.cancelBtn)
val submitBtn = findViewById<MaterialButton>(R.id.submitBtn)

val essentialInputViews = listOf(name, tel)
val views = listOf(name, tel, mail, bday, genderRadioGroup, memo)

showDetail.setOnClickListener {
extendEditTextList(editTextList, R.dimen.contact_list_height_detail)
toggleViewVisibility(showDetail)
}

bday.setOnClickListener {
startCalenderDialog(it as TextView)
}

cancelBtn.setOnClickListener {
cancelAddContact()
}

submitBtn.setOnClickListener {
if (isValidContact(essentialInputViews)) {
submitContact()
val intent = Intent()
setResult(RESULT_OK, intent)
finish()
}
}

this.onBackPressedDispatcher.addCallback(this) {
if (isWriting(views)) {
showExitConfirmDialog()
} else {
finish()
}
}

}

fun showExitConfirmDialog() {
AlertDialog.Builder(this).setTitle("").setMessage(R.string.back_check_message)
.setPositiveButton("나가기") { _, _ ->
finish()
}.setNegativeButton("작성하기", null).create().show()
}

fun startCalenderDialog(textView: TextView) {
val datePickerDialog = DatePickerDialog(this)
datePickerDialog.updateDate(2000, 0, 1)

datePickerDialog.setOnDateSetListener { _, year, month, dayOfMonth ->
Log.d("testt", getString(R.string.birthday, year, month + 1, dayOfMonth))
textView.text = getString(R.string.birthday, year, month + 1, dayOfMonth)
}

datePickerDialog.show()
}

fun extendEditTextList(view: View, sizeId: Int) {
var layoutParams = view.layoutParams
layoutParams.height = resources.getDimensionPixelSize(sizeId)
view.layoutParams = layoutParams
}

fun toggleViewVisibility(view: View) {
view.visibility = when (view.visibility) {
GONE -> VISIBLE
else -> GONE
}
}

fun cancelAddContact() {
finish()
showToast("취소 되었습니다.")
}

fun showToast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

fun setFocus(view: View) {
view.requestFocus()
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(view, 0);
}

fun isValidContact(views: List<EditText>): Boolean {
views.forEach {
if (it.text.isEmpty()) {
showToast(resources.getString(R.string.essential_value, it.hint.toString()))
setFocus(it)
return false
}
}
return true
}

fun isWriting(views: List<View>): Boolean {
views.forEach {
when (it) {
is EditText -> if (it.text.isNotEmpty()) return true
is TextView -> if (it.text.isNotEmpty()) return true
is RadioGroup -> if (getGender() != null) return true
}
}
return false
}

fun getGender(): Gender? = when (genderRadioGroup.checkedRadioButtonId) {
R.id.male -> Gender.MALE
R.id.female -> Gender.FEMALE
else -> null
}


fun submitContact() {
val newContact = Contact(
name.text.toString(),
tel.text.toString(),
mail.text.toString(),
DateFormatter.stringToDate(bday.text.toString()),
getGender(),
memo.text.toString()
)
ContactRepository.addContact(newContact)
showToast("저장이 완료 되었습니다.")
}

}
Loading