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

Open
wants to merge 29 commits into
base: anyooin
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b4164c6
docs : update readme
anyooin Jun 25, 2024
912bb20
design : ui setting
anyooin Jun 25, 2024
903ff8b
feat : toast & focus when empty name
anyooin Jun 25, 2024
0f3a757
feat : toast & focus when empty phone
anyooin Jun 25, 2024
9e9ca6b
fix : focus phone when empty phone
anyooin Jun 25, 2024
90f69ff
feat : toast when saveBtn click
anyooin Jun 25, 2024
c8a8b08
fix : adjust UI when keyboard mode
anyooin Jun 25, 2024
f04c523
feat : toast when cancelBtn click
anyooin Jun 25, 2024
69a6369
docs : update readme
anyooin Jun 25, 2024
6b06a1a
feat : moreBtn, scrollView
anyooin Jun 25, 2024
4cb27ac
docs : update readme
anyooin Jun 25, 2024
4a86524
feat : select birthday with datepicker
anyooin Jun 25, 2024
13345f0
design : change icons
anyooin Jun 26, 2024
79caaae
design : contactmain UI
anyooin Jun 27, 2024
865dec9
docs : update readme
anyooin Jun 27, 2024
9bfc397
feat : floatingbutton click event to writingactivity
anyooin Jun 27, 2024
d38a856
docs : update readme
anyooin Jun 27, 2024
7eb8f84
feat : data transfer from writing to main
anyooin Jun 27, 2024
897b630
feat : make contact list
anyooin Jun 27, 2024
2bc1b2f
design : add cardview corner radius, shadow
anyooin Jun 27, 2024
3059465
docs : update readme
anyooin Jun 27, 2024
4cb1b67
feat : check writing state
anyooin Jun 27, 2024
d730094
feat : back button popup
anyooin Jun 27, 2024
8670bd7
docs : update readme
anyooin Jun 27, 2024
730acd0
feat : press popup's exit button
anyooin Jun 27, 2024
4720922
feat : invisible ContactMainActivity text when contact exist
anyooin Jun 27, 2024
93a171f
feat : contactDetailActivity
anyooin Jun 28, 2024
9b660be
refactor : code refactoring
anyooin Jun 28, 2024
b6d4a12
docs : update readme
anyooin 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
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,39 @@
# android-contacts

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

### 구현할 기능
- 이름을 입력하지 않고 저장버튼 누르면 '이름은 필수값입니다' 토스트 메시지, 포커스도 이름으로
- 전화번호를 입력하지 않고 저장버튼 누르면 '전화번호는 필수값입니다' 토스트 메시지, 포커스도 전화번호로
- 저장 버튼을 누르면 '저장이 완료 되었습니다' 토스트 메시지
- 취소 버튼을 누르면 '취소 되었습니다' 토스트 메시지
- 더보기를 눌러 입력 폼을 확장
- 생일 선택 후 텍스트 반영

## 2단계 : 연락처 목록
- 연락처 등록 화면을 구현한다.
- 연락처를 저장하면 목록에 추가된다.
- 저장된 연락처가 많을 경우 목록은 스크롤 되어야 한다.
- 추가된 연락처를 선택하여 상세 화면을 볼 수 있다.
- 연락처 작성 중 뒤로가기 버튼을 누르면 확인 팝업이 나타난다.
- 추가된 연락처는 앱을 다시 실행하면 유지되지 않는다.

### 구현할 기능
- 메인화면에서 +버튼을 누르면 연락처 등록 화면으로 이동
- 작성한 정보를 메인화면으로 넘겨주기
- 연락처 저장 버튼을 누르면 메인화면으로 이동
- 작성한 연락처를 메인 목록에 추가
- 연락처 작성 중인 상태인지 체크(작성 내용이 하나라도 있으면 작성 중인 상태)
- 작성 중인 상태라면 뒤로가기 버튼 누르면 팝업
- 팝업에서 나가기 누르면 연락처 목록 화면으로
- 목록에 연락처가 하나라도 있으면 메인화면의 텍스트 없애기
- 추가된 연락처 목록을 선택하면 상세화면으로 이동
11 changes: 9 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@
android:theme="@style/Theme.Contacts"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".ContactDetailActivity"
android:exported="false" />
<activity
android:name=".ContactWritingActivity"
android:exported="false"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".ContactMainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -23,4 +30,4 @@
</activity>
</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package campus.tech.kakao.contacts

import android.os.Bundle
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat

class ContactDetailActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_contact_detail)

val nameTextView = findViewById<TextView>(R.id.name)
val phoneTextView = findViewById<TextView>(R.id.phone)
val mailTextView = findViewById<TextView>(R.id.mail)
val birthdayTextView = findViewById<TextView>(R.id.birthday)
val genderTextView = findViewById<TextView>(R.id.gender)
val memoTextView = findViewById<TextView>(R.id.memo)

nameTextView.text = intent.getStringExtra("name")
phoneTextView.text = intent.getStringExtra("phone")
mailTextView.text = intent.getStringExtra("mail")
birthdayTextView.text = intent.getStringExtra("birthday")
genderTextView.text = intent.getStringExtra("gender")
memoTextView.text = intent.getStringExtra("memo")
}
}
120 changes: 120 additions & 0 deletions app/src/main/java/campus/tech/kakao/contacts/ContactMainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package campus.tech.kakao.contacts

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.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton

val contactList = mutableListOf<Contact>()
Copy link

Choose a reason for hiding this comment

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

class의 바깥에 선언한 이유가 있나요?
이 변수는 Activity 안쪽으로 들어오는게 맞는거같습니다.

이렇게 구성했을때 contactList가 디바이스의 메모리에 언제 생성되고 언제 사라지게되는지 한번 알아보면 좋겠습니다.


class ContactMainActivity : AppCompatActivity() {

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

val text = findViewById<TextView>(R.id.text)
val floatingBtn = findViewById<FloatingActionButton>(R.id.floatingBtn)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
val name = intent.getStringExtra("name")
val phone = intent.getStringExtra("phone")
val mail = intent.getStringExtra("mail") ?: ""
val birthday = intent.getStringExtra("birthday") ?: ""
val gender = intent.getStringExtra("gender") ?: ""
val memo = intent.getStringExtra("memo") ?: ""

if (name != null && phone != null) {
contactList.add(Contact(name, phone, mail, birthday, gender, memo))
}

if (contactList.size > 0) {
text.visibility = View.INVISIBLE
} else {
text.visibility = View.VISIBLE
}

floatingBtn.setOnClickListener {
val intent = Intent(this, ContactWritingActivity::class.java)
//intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
startActivity(intent)
}

val adapter = RecyclerViewAdapter(contactList, LayoutInflater.from(this))

recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
adapter.setListener(object : RecyclerViewAdapter.ItemClickListener {
override fun onClick(v: View, position: Int) {
val intent = Intent(this@ContactMainActivity, ContactDetailActivity::class.java)
intent.putExtra("name", contactList.get(position).name)
intent.putExtra("phone", contactList.get(position).phone)
intent.putExtra("mail", contactList.get(position).mail)
intent.putExtra("birthday", contactList.get(position).birthday)
intent.putExtra("gender", contactList.get(position).gender)
intent.putExtra("memo", contactList.get(position).memo)
startActivity(intent)
}
})
}
}

class RecyclerViewAdapter(
val contactList: MutableList<Contact>, val layoutInflater: LayoutInflater
) : RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() {
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val name: TextView
val profile: TextView

init {
name = itemView.findViewById<TextView>(R.id.name)
profile = itemView.findViewById<TextView>(R.id.profile)
}
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = layoutInflater.inflate(R.layout.item_contact, parent, false)
return ViewHolder(view)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.name.text = contactList.get(position).name
holder.profile.text = contactList.get(position).name.substring(0, 1)

holder.itemView.setOnClickListener {
recyclerListener.onClick(it, position)
}
}

interface ItemClickListener {
fun onClick(v: View, position: Int)
}

fun setListener(itemClickListener: ItemClickListener) {
this.recyclerListener = itemClickListener
}

lateinit var recyclerListener: ItemClickListener

override fun getItemCount(): Int {
return contactList.size
}
}

class Contact(
Copy link

Choose a reason for hiding this comment

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

별도의 파일로 빼주는게 좋다고 생각합니다.
model이라는 패키지(폴더)를 파고 거기에 넣는게 어떨까요?

val name: String,
val phone: String,
val mail: String,
val birthday: String,
val gender: String,
val memo: String
Comment on lines +115 to +119
Copy link

Choose a reason for hiding this comment

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

여기에 있는 값이 nullable이 되어야할 필드들은 없을까요?

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

import android.app.AlertDialog
import android.app.DatePickerDialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.RadioGroup
import android.widget.TextView
import android.widget.Toast
import android.window.OnBackInvokedDispatcher
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.LinearLayoutCompat
import java.util.Calendar


class ContactWritingActivity : AppCompatActivity() {

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

val name = findViewById<EditText>(R.id.name)
val phone = findViewById<EditText>(R.id.phone)
val mail = findViewById<EditText>(R.id.mail)
val birthday = findViewById<TextView>(R.id.birthday)
val gender = findViewById<RadioGroup>(R.id.gender)
val memo = findViewById<EditText>(R.id.memo)
val moreBtn = findViewById<LinearLayoutCompat>(R.id.moreBtn)
val moreInfo = findViewById<LinearLayoutCompat>(R.id.moreInfo)
val save = findViewById<TextView>(R.id.save)
val cancel = findViewById<TextView>(R.id.cancel)

val inputMethodManager =
getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager

moreInfo.visibility = View.INVISIBLE

save.setOnClickListener {
if (name.text.isEmpty()) {
Copy link

Choose a reason for hiding this comment

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

이 리스너 안쪽 로직은 별도의 메소드를 빼는것도 좋아보이네요.

val toast = Toast.makeText(this, "이름은 필수값입니다", Toast.LENGTH_SHORT)
toast.show()
name.requestFocus()
inputMethodManager.showSoftInput(name, InputMethodManager.SHOW_IMPLICIT)
} else if (phone.text.isEmpty()) {
val toast = Toast.makeText(this, "전화번호는 필수값입니다", Toast.LENGTH_SHORT)
toast.show()
phone.requestFocus()
inputMethodManager.showSoftInput(phone, InputMethodManager.SHOW_IMPLICIT)
} else {
val toast = Toast.makeText(this, "저장이 완료 되었습니다", Toast.LENGTH_SHORT)
toast.show()

val intent = Intent(this, ContactMainActivity::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.putExtra("name", name.text.toString())
intent.putExtra("phone", phone.text.toString())
intent.putExtra("mail", mail.text.toString())
intent.putExtra("birthday", birthday.text)

if (gender.checkedRadioButtonId == R.id.radioBtn_man) {
intent.putExtra("gender", "남성")
} else if (gender.checkedRadioButtonId == R.id.radioBtn_woman) {
intent.putExtra("gender", "여성")
} else {
intent.putExtra("gender", "")
}

intent.putExtra("memo", memo.text.toString())
startActivity(intent)
}
}

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

moreBtn.setOnClickListener {
moreInfo.visibility = View.VISIBLE
moreBtn.visibility = View.INVISIBLE
}

birthday.setOnClickListener {
val calendar = Calendar.getInstance() //캘린더뷰 만들기
val dateSetListener =
DatePickerDialog.OnDateSetListener { view, year, month, dayOfMonth ->
birthday.text = "" + year + "." + (month + 1) + "." + dayOfMonth
}
DatePickerDialog(
this, dateSetListener,
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
).show()
}

}

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_BACK) {
val name = findViewById<EditText>(R.id.name)
val phone = findViewById<EditText>(R.id.phone)
val mail = findViewById<EditText>(R.id.mail)
val birthday = findViewById<TextView>(R.id.birthday)
val gender = findViewById<RadioGroup>(R.id.gender)
val memo = findViewById<EditText>(R.id.memo)

if (checkWriting(name, phone, mail, birthday, gender, memo)) {
val dialogListener = DialogInterface.OnClickListener { dialog, which ->
if (which == DialogInterface.BUTTON_POSITIVE) {
Log.d("uin", "작성하기 버튼")
} else if (which == DialogInterface.BUTTON_NEGATIVE) {
Log.d("uin", "나가기 버튼")
val intent = Intent(this, ContactMainActivity::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
startActivity(intent)
Comment on lines +121 to +123
Copy link

Choose a reason for hiding this comment

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

이렇게 Main으로 이동하는게 아닌 finish()를 써서 이 페이지 스택을 빼면 됩니다.
그러면 다시 아래의 스택 아이템인 Main으로 내려갈거에요.
이게 질문주신 내용의 솔루션입니다.

}
}
AlertDialog.Builder(this).run {
setMessage("작성 중인 내용이 있습니다. 정말 나가시겠습니까?")
setNegativeButton("나가기", dialogListener)
setPositiveButton("작성하기", dialogListener)
show()
}
return true;
}
}
return super.onKeyDown(keyCode, event)
}
}

fun checkWriting(
Copy link

Choose a reason for hiding this comment

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

private으로 바깥에서 접근이 안되어도 괜찮은 메소드 아닐까요?

name: EditText, phone: EditText, mail: EditText, birthday: TextView,
gender: RadioGroup, memo: EditText
Comment on lines +140 to +141
Copy link

Choose a reason for hiding this comment

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

각 뷰를 넘기기보다는, 각 뷰의 데이터만을 넘겨도 될거같네요.

): Boolean {
if (name.text.isNotEmpty() || phone.text.isNotEmpty() || mail.text.isNotEmpty()
|| birthday.text.isNotEmpty() || memo.text.isNotEmpty()
) {

return true
}
if (gender.checkedRadioButtonId != -1) {
return true
}
return false
}
11 changes: 0 additions & 11 deletions app/src/main/java/campus/tech/kakao/contacts/MainActivity.kt

This file was deleted.

5 changes: 5 additions & 0 deletions app/src/main/res/drawable/baseline_add_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">

<path android:fillColor="@android:color/white" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>

</vector>
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/baseline_keyboard_arrow_down_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">

<path android:fillColor="@android:color/white" android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>

</vector>
Loading