목차
1. build.kts (Module: app)
1.1 build.kts (Module: app)
2. xml
2.1 activity_main.xml
2.2 activity_show_data.xml
2.3 activity_input_data.xml
2.4 row.xml
3. Activity
3.1 MainActivity
3.2 ShowDataActivity
3.3 InputDataActivity
4. 그외
4.1 Values
4.2 DataModel
4.3 AnimalList
5. 동작영상
1. build.kts (Module: app)
1.1 build.kts (Module: app)
android {
...
//viewBinding 설정
viewBinding {
enable = true
}
}
2. xml
2.1 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewMain"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floatingActionButtonMain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp"
android:clickable="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@android:drawable/ic_menu_edit" />
</androidx.constraintlayout.widget.ConstraintLayout>
2.2 activity_show_data.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ShowDataActivity">
<TextView
android:id="@+id/textViewShowDataType"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="종류"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<Space
android:layout_width="match_parent"
android:layout_height="20dp" />
<TextView
android:id="@+id/textViewShowDataName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="이름"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<Space
android:layout_width="match_parent"
android:layout_height="20dp" />
<TextView
android:id="@+id/textViewShowDataAge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="나이"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<Space
android:layout_width="match_parent"
android:layout_height="20dp" />
<TextView
android:id="@+id/textViewShowDataGender"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="성별"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<Space
android:layout_width="match_parent"
android:layout_height="20dp" />
<TextView
android:id="@+id/textViewShowDataSnack"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="간식"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<Space
android:layout_width="match_parent"
android:layout_height="20dp" />
<Button
android:id="@+id/buttonShowDataFinish"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="메인 화면" />
<Button
android:id="@+id/buttonDataDeleteFinish"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="삭제" />
</LinearLayout>
2.3 activity_input_data.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".InputDataActivity">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="종류"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<RadioGroup
android:id="@+id/radioGroupType"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/radioButtonDog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="강아지" />
<RadioButton
android:id="@+id/radioButtonCat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="고양이" />
<RadioButton
android:id="@+id/radioButtonParrot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="앵무새" />
</RadioGroup>
<Space
android:layout_width="match_parent"
android:layout_height="20dp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textFieldName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="이름"
app:endIconMode="clear_text"
app:startIconDrawable="@android:drawable/ic_menu_add">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<Space
android:layout_width="match_parent"
android:layout_height="20dp" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="나이"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<com.google.android.material.slider.Slider
android:id="@+id/sliderAge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stepSize="1"
android:valueFrom="0.0"
android:valueTo="50.0"
app:labelBehavior="visible"
app:tickVisible="true" />
<Space
android:layout_width="match_parent"
android:layout_height="20dp" />
<TextView
android:id="@+id/Snack"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:gravity="center"
android:text="좋아하는 간식(다중선택)"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.chip.ChipGroup
android:id="@+id/checkBoxGroup2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal">
<com.google.android.material.chip.Chip
android:id="@+id/chipCheckApple"
style="@style/Widget.Material3.Chip.Filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="사과" />
<com.google.android.material.chip.Chip
android:id="@+id/chipCheckBanana"
style="@style/Widget.Material3.Chip.Filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="바나나" />
<com.google.android.material.chip.Chip
android:id="@+id/chipCheckJujube"
style="@style/Widget.Material3.Chip.Filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="대추" />
</com.google.android.material.chip.ChipGroup>
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="20dp" />
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="성별"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<RadioGroup
android:id="@+id/radioGroupGender"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/radioButtonMale"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="수컷" />
<RadioButton
android:id="@+id/radioButtonFemale"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="암컷" />
</RadioGroup>
<Button
android:id="@+id/buttonSave"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="저장하기" />
</LinearLayout>
2.4 row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:orientation="vertical">
<TextView
android:id="@+id/textViewRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="TextView"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</LinearLayout>
3. Activity
3.1 MainActivity
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import android.view.View.OnClickListener
import android.view.ViewGroup
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
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.lion.a033ex_animalmanager.databinding.ActivityMainBinding
import com.lion.a033ex_animalmanager.databinding.RowBinding
class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding: ActivityMainBinding
private lateinit var showDataActivityLauncher: ActivityResultLauncher<Intent>
private lateinit var inputDataActivityLauncher: ActivityResultLauncher<Intent>
//val dataList = mutableListOf<DataModel>()
//val animalList = AnimalList
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
// InputDataActivity 런처 구성(중요함 안하면 앱깨짐)
settingInputDataActivityLauncher()
// ShowDataActivity 런처 구성(중요함 안하면 앱깨짐)
settingShowDataActivityLauncher()
//FloatingActionButton 설정
settingFloatingActionButtonMain()
//리싸이클러뷰 설정
settingRecyclerViewMain()
}
//리싸이클러뷰 설정
fun settingRecyclerViewMain() {
activityMainBinding.apply {
recyclerViewMain.adapter = RecyclerViewAdapter()
recyclerViewMain.layoutManager = LinearLayoutManager(this@MainActivity)
val deco = MaterialDividerItemDecoration(
this@MainActivity,
MaterialDividerItemDecoration.VERTICAL
)
recyclerViewMain.addItemDecoration(deco)
// 리사이클러뷰가 스크롤 상태가 변경되면 동작하는 리스너
recyclerViewMain.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
if (oldScrollY == 0) {
floatingActionButtonMain.show()
} else {
// canScrollVertically : 위나 아래로 스크롤이 가능한지를 확인하는 메서드
// 만약 -1 을 넣어줬는데 false가 반환되면 : 제일 위에 있는 상태
// 만약 1을 넣어줬는데 false가 반환되면 : 제일 마지막에 있는 상태
// 만약 제일 아래에 있는 상태라면..
if (recyclerViewMain.canScrollVertically(1) == false) {
// FloatingActionButton을 사라지게 된다.
floatingActionButtonMain.hide()
} else {
// 만약 제일 아래에 있는 상태가 아니고 FloatingActionButton이 보이지 않는 상태라면
if (floatingActionButtonMain.isShown == false) {
// FloatingActionButton을 나타나게 한다.
floatingActionButtonMain.show()
}
}
}
}
}
}
//FloatingActionButton 설정
private fun settingFloatingActionButtonMain() {
activityMainBinding.apply {
//Click Listener
floatingActionButtonMain.setOnClickListener {
val inputDataIntent = Intent(this@MainActivity, InputDataActivity::class.java)
inputDataActivityLauncher.launch(inputDataIntent)
}
}
}
// ShowDataActivity 런처 구성
fun settingShowDataActivityLauncher() {
val contract = ActivityResultContracts.StartActivityForResult()
showDataActivityLauncher = registerForActivityResult(contract) {
//showDataActivity에서 삭제하고 돌아옴
if (it.resultCode == RESULT_FIRST_USER){
//리사이클러를 갱신한다
activityMainBinding.recyclerViewMain.adapter?.notifyDataSetChanged()
}
}
}
// InputDataActivity 런처 구성
fun settingInputDataActivityLauncher() {
val contract = ActivityResultContracts.StartActivityForResult()
inputDataActivityLauncher = registerForActivityResult(contract) {
if (it.resultCode == RESULT_OK) {
if (it.data != null) {
val type = it.data?.getStringExtra("type")
val name = it.data?.getStringExtra("name")
val age = it.data?.getStringExtra("age")
val snackList = it.data?.getStringArrayListExtra("snackList")
val gender = it.data?.getStringExtra("gender")
//객체에 담는다
var dataModel = DataModel(type, name, age, snackList, gender)
//리스트에 추가한다
AnimalList.dataList.add(dataModel)
//dataList.add(dataModel)
if (snackList != null) {
// 배열의 내용을 ", "로 구분하여 하나의 문자열로 결합
val snackText = snackList.joinToString(", ")
}
//리사이클러를 갱신한다
activityMainBinding.recyclerViewMain.adapter?.notifyDataSetChanged()
}
}
}
}
//밑으로 리싸이클러 관련코드
inner class ViewHolder(val rowBinding: RowBinding) : RecyclerView.ViewHolder(rowBinding.root),
OnClickListener {
override fun onClick(v: View?) {
//todo 리스트에서 고른 아이템정보를 ShowDataActivity로 보내줌
if (AnimalList.dataList.size == 0){
}else{
Handler(Looper.getMainLooper()).postDelayed({
val showDataIntent = Intent(this@MainActivity, ShowDataActivity::class.java)
//사용자가 누른 항목번째 객체에서 데이터를 추출해 Intent에 담아준다
showDataIntent.putExtra("name",AnimalList.dataList[adapterPosition].name)
showDataIntent.putExtra("type",AnimalList.dataList[adapterPosition].type)
showDataIntent.putExtra("age",AnimalList.dataList[adapterPosition].age)
showDataIntent.putStringArrayListExtra("snackList",AnimalList.dataList[adapterPosition].snackList)
showDataIntent.putExtra("gender",AnimalList.dataList[adapterPosition].gender)
showDataIntent.putExtra("index",adapterPosition)
showDataActivityLauncher.launch(showDataIntent)
},200)
}
}
}
inner class RecyclerViewAdapter : RecyclerView.Adapter<ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val rowBinding = RowBinding.inflate(layoutInflater)
val viewHolder = ViewHolder(rowBinding)
rowBinding.apply {
root.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
root.setOnClickListener(viewHolder)
}
return viewHolder
}
override fun getItemCount(): Int {
if (AnimalList.dataList.size == 0) {
return 1
}
return AnimalList.dataList.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.rowBinding.apply {
if (AnimalList.dataList.size == 0) {
textViewRow.text = "등록된 동물이 없습니다"
} else {
textViewRow.text = AnimalList.dataList[position].name
}
}
}
}
}
3.2 ShowDataActivity
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.lion.a033ex_animalmanager.databinding.ActivityShowDataBinding
class ShowDataActivity : AppCompatActivity() {
lateinit var activityShowDataBinding: ActivityShowDataBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
activityShowDataBinding = ActivityShowDataBinding.inflate(layoutInflater)
setContentView(activityShowDataBinding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
// 나가기 버튼 메서드
settingButtonShowDataFinish()
//데이터를 띄워주는 메서드
showData()
// 삭제하는 버튼 메서드
settingButtonDeleteFinish()
}
// 나가기 버튼 메서드
fun settingButtonShowDataFinish(){
activityShowDataBinding.apply {
buttonShowDataFinish.setOnClickListener {
// 현재 Activity를 종료한다.
finish()
}
}
}
// 삭제하는 버튼 메서드
fun settingButtonDeleteFinish(){
activityShowDataBinding.apply {
buttonDataDeleteFinish.setOnClickListener {
//해당 동물정보의 인덱스를 받아온다
val deleteIndex = intent.getIntExtra("index", -1)
//인덱스로 삭제한다
AnimalList.dataList.removeAt(deleteIndex)
//삭제하고 메인으로 돌아갔다는것을 알린다
setResult(RESULT_FIRST_USER)
finish()
}
}
}
//데이터를 띄워주는 메서드
fun showData(){
activityShowDataBinding.apply {
textViewShowDataType.text = intent.getStringExtra("type")
textViewShowDataName.text = intent.getStringExtra("name")
val age = intent.getStringExtra("age")?.toFloatOrNull()?.toInt() ?: 0
textViewShowDataAge.text = age.toString()
textViewShowDataGender.text = intent.getStringExtra("gender")
//snackList는 getStringArrayExtra로 받아왔기때문에 처리가필요함
val snackList = intent.getStringArrayListExtra("snackList")
if (snackList != null) {
// 배열의 내용을 ", "로 구분하여 하나의 문자열로 결합
val snackText = snackList.joinToString(", ")
// TextView에 표시
activityShowDataBinding.textViewShowDataSnack.text = snackText
}
}
}
}
3.3 InputDataActivity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.MotionEvent
import android.view.inputmethod.InputMethodManager
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.lion.a033ex_animalmanager.databinding.ActivityInputDataBinding
class InputDataActivity : AppCompatActivity() {
lateinit var activityInputDataBinding: ActivityInputDataBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
activityInputDataBinding = ActivityInputDataBinding.inflate(layoutInflater)
setContentView(activityInputDataBinding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
//저장버튼을 누르면 입력한 동물정보를 저장한다
settingAnimalInfo()
}
//저장버튼을 누르면 입력한 동물정보를 저장한다
fun settingAnimalInfo() {
activityInputDataBinding.apply {
buttonSave.setOnClickListener {
//동물타입을 받는다
var type:String? = null
if (radioButtonDog.isChecked) {
type = Values.TypeValues.TYPE_DOG.str
}
else if (radioButtonDog.isChecked) {
type = Values.TypeValues.TYPE_CAT.str
}
else if (radioButtonDog.isChecked) {
type = Values.TypeValues.TYPE_PARROT.str
}
//동물이름을 받는다
var name:String? =null
name = textFieldName.editText?.text.toString()
//동물나이를 받는다
var age:String? =null
age = sliderAge.value.toString()
//동물이 좋아하는간식을 받는다
var snackList = ArrayList<String>()
if (chipCheckApple.isChecked){
snackList.add(Values.SnackValues.SNACK_APPLE.str)
}
if (chipCheckBanana.isChecked){
snackList.add(Values.SnackValues.SNACK_BANANA.str)
}
if (chipCheckJujube.isChecked){
snackList.add(Values.SnackValues.SNACK_JUJUBE.str)
}
//동물 성별을 받는다
var gender:String? = null
if (radioButtonMale.isChecked){
gender = Values.GenderValues.GENDER_MALE.str
}
if (radioButtonFemale.isChecked){
gender = Values.GenderValues.GENDER_FEMALE.str
}
// 데이터를 담을 인텐트를 생성한다.
val dataIntent = Intent()
dataIntent.putExtra("type", type)
dataIntent.putExtra("name",name)
dataIntent.putExtra("age",age)
dataIntent.putStringArrayListExtra("snackList",snackList)
dataIntent.putExtra("gender",gender)
setResult(RESULT_OK, dataIntent)
finish()
false
}
}
}
//입력하다가 editText외부를 터치하면 키보드가 내려감
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
if (currentFocus != null) {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(currentFocus!!.windowToken, 0)
currentFocus!!.clearFocus()
}
return super.dispatchTouchEvent(ev)
}
}
4. 그외
4.1 Values
class Values {
enum class GenderValues(var num:Int, var str:String){
GENDER_MALE(1,"수컷"),
GENDER_FEMALE(2,"암컷"),
}
enum class TypeValues(var num:Int, var str:String){
TYPE_DOG(1,"강아지"),
TYPE_CAT(2,"고양이"),
TYPE_PARROT(3,"앵무새")
}
enum class SnackValues(var num:Int, var str:String){
SNACK_APPLE(1,"사과"),
SNACK_BANANA(2,"바나나"),
SNACK_JUJUBE(3,"대추")
}
}
4.2 DataModel
data class DataModel(var type:String?,var name:String?, var age:String?, var snackList:ArrayList<String>?, var gender:String?) {
}
4.3 AnimalList
object AnimalList {
var dataList = mutableListOf<DataModel>()
}
5. 동작영상