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

Open
wants to merge 21 commits into
base: fivejinw
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e15f6d0
docs : Update README.md
fiveJinw Jun 24, 2024
83f72d5
feat : Add string input feature
fiveJinw Jun 25, 2024
38f158a
feat : Add feature that display Toast Message when cancel button is c…
fiveJinw Jun 25, 2024
bbd28cf
feat : Add feature that display toast message when save clicked
fiveJinw Jun 25, 2024
4cb00ca
feat : Add feature that display toast message if name, number field i…
fiveJinw Jun 25, 2024
6cb51ac
feat : Add feature that display more input field when button is clicked
fiveJinw Jun 26, 2024
72060a8
refactor : refactoring about feature that display more info field
fiveJinw Jun 26, 2024
b3bf8d9
feat : Add feature that input birthday field
fiveJinw Jun 26, 2024
1e6b013
feat : Add feature that select gender field
fiveJinw Jun 26, 2024
35ac9da
refactor : refector initializing variable code and listener code
fiveJinw Jun 26, 2024
847e50d
docs : Update README.md
fiveJinw Jun 28, 2024
ff5a8b4
style : Add xml file that list page and detail page
fiveJinw Jun 28, 2024
a33025b
feat : Add feature that save contact
fiveJinw Jun 28, 2024
a082d60
feat : Add feature that move to adding contact Activity when plus is …
fiveJinw Jun 28, 2024
8ded020
feat : Add feature that display contact list
fiveJinw Jun 28, 2024
8d5fd23
feat : Add feature that move to detail page
fiveJinw Jun 28, 2024
d3218c0
feat : Add feature that display only completed field at detail page
fiveJinw Jun 28, 2024
7bc6c79
feat : Add feature that display cancel dialog when back pressed
fiveJinw Jun 28, 2024
a22f786
feat : Add feature that display dialog when cancel button is clicked
fiveJinw Jun 28, 2024
9860402
refactor : refactor MainActivity, ListActivity, DetailActivity code
fiveJinw Jun 28, 2024
9f22f83
docs : Update README.md
fiveJinw 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
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,33 @@
# android-contacts
# android-contacts - 간단한 연락처 구현(1주차)
## 구현할 기능 목록

**지정된 형식의 데이터를 입력하는 기능**
- [x] 문자열을 입력할 수 있는 기능
- [x] 숫자만 입력할 수 있는 기능
- [x] 날짜 선택 기능
- [x] 성별 선택 기능

**더보기 기능**
- [x] 더보기를 클릭하면 숨겨져 있던 입력 폼을 드러낼 수 있는 기능

**저장 기능**
- [x] 저장 버튼을 클릭하면 토스트 메세지를 출력
- [x] 이름, 전화번호의 칸이 채워져 있지 않으면 저장이 되지 않으면 그에 맞는 토스트 메세지를 출력

**취소 기능**
- [x] 취소 버튼을 클릭하면 토스트 메세지를 출력

## step2 구현할 기능 목록

**연락처 추가 기능**
- [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
8 changes: 7 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@
android:supportsRtl="true"
android:theme="@style/Theme.Contacts"
tools:targetApi="31">
<activity
android:name=".DetailActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="false" />
<activity
android:name=".ListActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -23,4 +29,4 @@
</activity>
</application>

</manifest>
</manifest>
96 changes: 96 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,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
Comment on lines +12 to +23
Copy link

Choose a reason for hiding this comment

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

ViewBinding 을 통해서 findViewById 를 사용하지 않고 처리해볼 수 있어요 :)


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"))
Comment on lines +51 to +56
Copy link

Choose a reason for hiding this comment

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

intent 에서 공통으로 사용하는 key 들은 따로 관리하는 편이 좋습니다. 수동으로 복붙을 진행하다보면, 키 복사를 제대로 하지 못하는 등의 휴먼에러도 발생할 수 있으니까요.

Copy link

Choose a reason for hiding this comment

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

companion object 를 사용하여 const 로 관리해주시면 좋을 것 같네요 :)

class DetailActivity: AppCompatActivity() {
    ...
    companion object {
        const val KEY_NAME = "name"
    }
}

}

fun setVisibleCompletedFields() {
if(ExistMail()){
mail.visibility = View.VISIBLE
mailInputField.visibility = View.VISIBLE
Comment on lines +61 to +62
Copy link

Choose a reason for hiding this comment

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

View.isVisible 을 통해 visibility 컨트롤이 더 쉽습니다.

Suggested change
mail.visibility = View.VISIBLE
mailInputField.visibility = View.VISIBLE
mail.isVisible = true
mailInputField.isVisible = true

Copy link

Choose a reason for hiding this comment

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

또한 isVisible 을 활용한다면 아래처럼 if 분기를 태우지 않더라도 처리할 수 있습니다.

val isExistMailField = existMail()
mail.isVisible = isExistMailField
mailInputField =  = isExistMailField

}
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() != ""
Copy link

Choose a reason for hiding this comment

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

String 의 isEmpty 함수를 활용한다면 조금 더 명확하게 코드를 표현할 수 있을 것 같아요 :)

}

fun ExistBirthday(): Boolean {
return birthdayInputField.text.toString() != ""
}

fun ExistGender(): Boolean {
return genderInputField.text.toString() != ""
}

fun ExistMemo(): Boolean {
return memoInputField.text.toString() != ""
}
Comment on lines +81 to +95
Copy link

Choose a reason for hiding this comment

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

함수명은 kotlin naming convention 에 따라 소문자로 시작하는 편이 좋습니다.

}
136 changes: 136 additions & 0 deletions app/src/main/java/campus/tech/kakao/contacts/ListActivity.kt
Original file line number Diff line number Diff line change
@@ -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<Intent>
lateinit var descriptionText: TextView
lateinit var plusButton: TextView
lateinit var recyclerView: RecyclerView
lateinit var contactList: MutableList<Contact>
lateinit var adapter: RecyclerViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list)

initVar()
initListener()

}

fun initVar(){
contactList = mutableListOf<Contact>()
adapter = RecyclerViewAdapter(contactList, LayoutInflater.from(this), this)
Copy link

Choose a reason for hiding this comment

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

adapter 를 초기화하는 것 또한 recycler view 를 초기화하는 것과 같은 맥락이므로, initRecyclerView 로 이동하는 것은 어떨까요?

plusButton = findViewById<TextView>(R.id.plus_item_button)
descriptionText = findViewById<TextView>(R.id.description_text)
initRecyclerView()
initResultLauncher()
}

fun initRecyclerView() {
recyclerView = findViewById<RecyclerView>(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<Contact>,
var inflater: LayoutInflater,
var context: Context
Copy link

Choose a reason for hiding this comment

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

context 를 변수로 넘기는 것은 금기시해야할 것 중 하나입니다. inflater 또한 파라미터로 받을 필요는 없을 것 같구요.
아이템을 클릭했을 때 이동해야한다면, 아이템이 클릭되었음을 알려주는 listener 를 정의하고 그것을 파라미터로 받는 것이 좋습니다.

interface OnContactClickListener {
    fun onContactClicked(position: Int)
}

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

inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
Copy link

Choose a reason for hiding this comment

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

inner class 로 구현하지 않았으면 해요. 필요하다면 userTitle 과 userName 을 설정할 수 있는 함수를 만드는 편이 좋을 것 같습니다.

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
) {}
Loading