build.gradle
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-parcelize")
}
- @Parcelize 기능을 사용하기 위해 플러그인에 id를 넣어준다.
buildFeatures {
viewBinding = true
}
- 이번 프로젝트부터 viewBinding을 사용하기 위해 넣어주었다.
Post.kt
@Parcelize
data class Post() : Parcelable
- Serializable과 다르게 리플렉션을 사용하지 않고,필요한 부분만 직렬화 비직렬화 할 수 있어 효율성과 가독성을 높일 수 있다.
Adapter.kt
with(holder) {
imageView.setImageResource(item.itemImage)
titleText.text = item.itemTitle
address.text = item.itemAddress
price.text = "${format.format(money)}원"
comment.text = item.itemcomment.toString()
heart.text = item.itemheartCount.toString()
itemView.setOnClickListener {
if (itemClick != null) {
itemClick!!.onClick(it, position)
}
}
}
- View에 출력되는 데이터들을 Post데이터들과 연결시켜 View생성한다.
- scope function의 with를 사용해 holder를 묶어 가독성을 높였다.
init {
imageView.clipToOutline = true
with(binding) {
root.setOnClickListener {
val position = adapterPosition
if (position != RecyclerView.NO_POSITION) {
itemClick?.onClick(it, position)
}
}
root.setOnLongClickListener {
val position = adapterPosition
if (position != RecyclerView.NO_POSITION) {
longitemClick?.onLongClick(it, position)
true
} else false
}
}
}
- View를 클릭하거나 길게 클릭하게되면 View와 Position이 넘어간다.
MainActivity
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
- 뷰 바인딩을 사용하면 직접 id를 적고 타입을 정하고 이런 작업을 하지 않아도 된다.
- 자동으로 클래스 파일을 생성해주기 때문이다.
val adapter = Adapter(productList)
binding.recyclerview.adapter = adapter
binding.recyclerview.layoutManager = LinearLayoutManager(this)
- 생성한 어댑터를 초기화 해주고 recyclerview를 adapter와 연결해준다.
- layoutManager의 LinearLayoutManager()를 이용해 수직의 스크롤 리스트를 만든다.
val decoration = DividerItemDecoration(this, VERTICAL)
binding.recyclerview.addItemDecoration(decoration)
- 위의 코드를 사용해 recyclerview의 View 사이사이에 회색줄을 넣어준다.
adapter.itemClick = object : Adapter.ItemClick {
override fun onClick(view: View, position: Int) {
val clickProduct = productList[position]
val intent = Intent(this@MainActivity,DetailActivity::class.java)
intent.putExtra("Item", clickProduct)
intent.putExtra("likePosition", position)
activityResultLauncher.launch(intent)
}
}
- item을 클릭하게되면 View안에 있는 데이터와 position 값을 DetailActivity로 넘겨준다.
adapter.longitemClick = object : Adapter.LongItemClick {
@SuppressLint("NotifyDataSetChanged")
override fun onLongClick(view: View, position: Int) {
val deleteItem = productList[position]
AlertDialog.Builder(this@MainActivity).apply {
setIcon(R.drawable.chat)
setTitle("삭제")
setMessage("정말로 삭제하시겠습니까?")
setPositiveButton("확인") { _, _ ->
productList.remove(deleteItem)
adapter.notifyDataSetChanged()
}
setNegativeButton("취소") { dialog, _ ->
dialog.dismiss()
}
show()
}
}
}
- item을 길게 클릭하게되면 Adapter의 root.setOnLongClickListener {} 가 실행된다.
- 다이얼로그가 뜨면서 '확인'을 누르게되면 productList.remove(deleteItem)로 item이 삭제된다.
- adapter.notifyDataSetChanged()로 업데이트를 하게되면서 삭제된 데이터를 빼고 출력하게된다.
val adList = resources.getStringArray(R.array.location)
val adAdapter =
ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, adList)
binding.spinner.adapter = adAdapter
- 어댑터를 만들어 리스트와 스피너를 연결해 스피너를 클릭하면 연결된 리스트들이 출력된다.
var isTop = true
binding.recyclerview.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (!binding.recyclerview.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE) {
binding.fabTop.visibility = View.INVISIBLE
binding.fabTop.startAnimation(AlphaAnimation(1f, 0f).also {
it.duration = 1000
})
} else if (isTop) {
binding.fabTop.visibility = View.VISIBLE
binding.fabTop.startAnimation(AlphaAnimation(0f, 1f).also {
it.duration = 1000
})
isTop = false
}
}
})
binding.fabTop.setOnClickListener {
binding.recyclerview.smoothScrollToPosition(0)
}
- if문을 통해 스크롤이 최상단에 있거나 움직이 않았을 때는 플로팅 버튼이 보이지 않게 했다.
- 만일 아니라면 플로팅 버튼이 보이게되고 AlphaAnimation()을 이용해 서서히 보이는 애니메이션을 활용했다.
activityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
val likePosition = it.data?.getIntExtra("likePosition", 0) as Int
val isLiked = it.data?.getBooleanExtra("isLiked", false) as Boolean
if (isLiked) {
productList[likePosition].isLiked = true
productList[likePosition].itemheartCount++
} else {
if (productList[likePosition].isLiked) {
productList[likePosition].isLiked = false
productList[likePosition].itemheartCount--
}
}
adapter.notifyItemChanged(likePosition)
}
}
- DetailActivity에서 전달된 likePosition과 isLiked를 이용하여 true라면 좋아요 카운트를 1증가시킨다.
- notifyItemChanged()는 recyclerview를 업데이트 하는 역할을 한다.
DetailActivity
val money = getItem.itemPrice
val format = DecimalFormat("#,###")
- 데이터에서 itemPrice를 받아 DecimalFormat("#,###")를 이용해 1000단위마다 콤마(,)를 찍는다.
private fun goToMain() {
val likePosition = intent.getIntExtra("likePosition", 0)
val intent = Intent(this, MainActivity::class.java).apply {
putExtra("likePosition", likePosition)
putExtra("isLiked", isLiked)
}
setResult(RESULT_OK, intent)
if (!isFinishing) finish()
}
- MainActivity에 대한 intent를 생성해 putExtra로 데이터를 담고 setResult에 reaultCode와 intent를 인수로 준다.
- finish()로 SomeActivity를 종료하면 MainActivity로 돌아가서 resultLauncher함수에 작성한 내용들이 실행된다.
Product
object Product {}
- 오브젝트를 생성해 따로 데이터 저장공간을 만들어뒀다.
@MainActivity
val productList = Product.productData()
val adapter = Adapter(productList)
- MainActivity의 위 코드로 오브젝트의 데이터들을 불러오고 어댑터에 연결해준다.