-
Notifications
You must be signed in to change notification settings - Fork 33
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
base: anyooin
Are you sure you want to change the base?
Changes from all commits
b4164c6
912bb20
903ff8b
0f3a757
9e9ca6b
90f69ff
c8a8b08
f04c523
69a6369
6b06a1a
4cb27ac
4a86524
13345f0
79caaae
865dec9
9bfc397
d38a856
7eb8f84
897b630
2bc1b2f
3059465
4cb1b67
d730094
8670bd7
730acd0
4720922
93a171f
9b660be
b6d4a12
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,39 @@ | ||
# android-contacts | ||
|
||
## 1단계 : 연락처 추가 | ||
- 연락처를 추가한다. | ||
- 이름과 전화번호는 필수 값이다. | ||
- 입력하지 않고 저장버튼 누르면 토스트 메시지 -> 포커스도 해당 부분으로 이동 | ||
- 전화번호 입력은 숫자만 가능하다. | ||
- 더보기를 눌러 입력 폼을 확장할 수 있다. | ||
- 생일, 성별, 메모 입력 폼이 등장한다. | ||
- 성별을 둘 중 하나를 선택할 수 있다. | ||
- 저장 버튼을 누르면 '저장이 완료 되었습니다' 라는 토스트 메시지를 보여준다. | ||
- 취소 버튼을 누르면 '취소 되었습니다' 라는 토스트 메시지를 보여준다. | ||
|
||
### 구현할 기능 | ||
- 이름을 입력하지 않고 저장버튼 누르면 '이름은 필수값입니다' 토스트 메시지, 포커스도 이름으로 | ||
- 전화번호를 입력하지 않고 저장버튼 누르면 '전화번호는 필수값입니다' 토스트 메시지, 포커스도 전화번호로 | ||
- 저장 버튼을 누르면 '저장이 완료 되었습니다' 토스트 메시지 | ||
- 취소 버튼을 누르면 '취소 되었습니다' 토스트 메시지 | ||
- 더보기를 눌러 입력 폼을 확장 | ||
- 생일 선택 후 텍스트 반영 | ||
|
||
## 2단계 : 연락처 목록 | ||
- 연락처 등록 화면을 구현한다. | ||
- 연락처를 저장하면 목록에 추가된다. | ||
- 저장된 연락처가 많을 경우 목록은 스크롤 되어야 한다. | ||
- 추가된 연락처를 선택하여 상세 화면을 볼 수 있다. | ||
- 연락처 작성 중 뒤로가기 버튼을 누르면 확인 팝업이 나타난다. | ||
- 추가된 연락처는 앱을 다시 실행하면 유지되지 않는다. | ||
|
||
### 구현할 기능 | ||
- 메인화면에서 +버튼을 누르면 연락처 등록 화면으로 이동 | ||
- 작성한 정보를 메인화면으로 넘겨주기 | ||
- 연락처 저장 버튼을 누르면 메인화면으로 이동 | ||
- 작성한 연락처를 메인 목록에 추가 | ||
- 연락처 작성 중인 상태인지 체크(작성 내용이 하나라도 있으면 작성 중인 상태) | ||
- 작성 중인 상태라면 뒤로가기 버튼 누르면 팝업 | ||
- 팝업에서 나가기 누르면 연락처 목록 화면으로 | ||
- 목록에 연락처가 하나라도 있으면 메인화면의 텍스트 없애기 | ||
- 추가된 연락처 목록을 선택하면 상세화면으로 이동 |
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") | ||
} | ||
} |
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>() | ||
|
||
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( | ||
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. 별도의 파일로 빼주는게 좋다고 생각합니다. |
||
val name: String, | ||
val phone: String, | ||
val mail: String, | ||
val birthday: String, | ||
val gender: String, | ||
val memo: String | ||
Comment on lines
+115
to
+119
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. 여기에 있는 값이 nullable이 되어야할 필드들은 없을까요? |
||
) |
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()) { | ||
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. 이 리스너 안쪽 로직은 별도의 메소드를 빼는것도 좋아보이네요. |
||
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
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. 이렇게 Main으로 이동하는게 아닌 |
||
} | ||
} | ||
AlertDialog.Builder(this).run { | ||
setMessage("작성 중인 내용이 있습니다. 정말 나가시겠습니까?") | ||
setNegativeButton("나가기", dialogListener) | ||
setPositiveButton("작성하기", dialogListener) | ||
show() | ||
} | ||
return true; | ||
} | ||
} | ||
return super.onKeyDown(keyCode, event) | ||
} | ||
} | ||
|
||
fun checkWriting( | ||
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. private으로 바깥에서 접근이 안되어도 괜찮은 메소드 아닐까요? |
||
name: EditText, phone: EditText, mail: EditText, birthday: TextView, | ||
gender: RadioGroup, memo: EditText | ||
Comment on lines
+140
to
+141
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. 각 뷰를 넘기기보다는, 각 뷰의 데이터만을 넘겨도 될거같네요. |
||
): 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 | ||
} |
This file was deleted.
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> |
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> |
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.
class의 바깥에 선언한 이유가 있나요?
이 변수는 Activity 안쪽으로 들어오는게 맞는거같습니다.
이렇게 구성했을때 contactList가 디바이스의 메모리에 언제 생성되고 언제 사라지게되는지 한번 알아보면 좋겠습니다.