<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
tools:context=".MainActivity">
...
<FrameLayout
android:id="@+id/frameLayout"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/topbar" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="69dp"
app:itemBackground="@android:color/white"
app:itemIconTint="#1C1C1C"
app:itemTextColor="#1C1C1C"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/menu_navigation"
tools:layout_editor_absoluteX="-16dp">
</com.google.android.material.bottomnavigation.BottomNavigationView>
</androidx.constraintlayout.widget.ConstraintLayout>
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val homeFragment = HomeFragment()
private val nearFragment = NearFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setFragment(homeFragment)
binding.bottomNavigationView.setOnItemSelectedListener {
when(it.itemId) {
R.id.navMenu_home -> {
setFragment(homeFragment)
}
R.id.navMenu_location -> {
setFragment(nearFragment)
}
}
true
}
...
}
private fun setFragment(frag: Fragment) {
supportFragmentManager.commit {
replace(R.id.frameLayout, frag)
setReorderingAllowed(true)
addToBackStack("")
}
}
}
activity_main.xml의bottomNavigationView을 연결하고, 거기에 setOnItemSelectedListener 를 걸어서 it(=MenuItem).itemID를 참조하는 when문으로 bottomNavigation을 제어한다.setFragment로 밑에 따로 빼서 재사용이 용이하게 한다.class HomeFragment : Fragment() {
private val binding by lazy { FragmentHomeBinding.inflate(layoutInflater) }
private lateinit var adapter: ProductAdapter
private lateinit var dataList: MutableList<ProductItem>
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
dataList = mutableListOf<ProductItem>()
dataList.add(ProductItem(R.drawable.product, "170,000원", "12월 1일", "애플워치 팝니다.", "군자동","상품설명입니다.-a24rkf!2"))
dataList.add(ProductItem(R.drawable.product2, "90,000원", "12월 3일", "중고카페의자.", "군자동","상품설명입니다.-38ghakj@"))
dataList.add(ProductItem(R.drawable.product3, "5,000원", "12월 7일", "귀여운 쿼카 파우치!", "군자동","상품설명입니다.-kajnkg9321dfga"))
dataList.add(ProductItem(R.drawable.product, "300,000원", "12월 11일", "애플워치 팝니다.", "군자동","상품설명입니다.-128dkgj1y"))
dataList.add(ProductItem(R.drawable.product2, "80,000원", "12월 12일", "중고카페의자.", "군자동","상품설명입니다.-gi4280th1j"))
dataList.add(ProductItem(R.drawable.product3, "3,000원", "12월 16일", "귀여운 쿼카 파우치!", "군자동","상품설명입니다.-agj8r12#2"))
dataList.add(ProductItem(R.drawable.product, "200,000원", "12월 20일", "애플워치 팝니다.", "군자동","상품설명입니다.-39gjsdkj2"))
dataList.add(ProductItem(R.drawable.product2, "50,000원", "12월 22일", "중고카페의자.", "군자동","상품설명입니다.-02409tf13jdfagj"))
dataList.add(ProductItem(R.drawable.product3, "5,000원", "12월 31일", "귀여운 쿼카 파우치!", "군자동","상품설명입니다.-39sdjgakl"))
adapter = ProductAdapter(dataList)
binding.productRecycle.adapter = adapter
binding.productRecycle.layoutManager = LinearLayoutManager(context)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.btnFloating.setOnClickListener{
val intent = Intent(context, SelectImageActivity::class.java)
startActivity(intent)
}
adapter.productClick = object : ProductAdapter.ProductClick {
override fun onClick(view: View, position: Int) {
val intent = Intent(context,MainDetailActivity::class.java)
intent.putExtra("productData",dataList[position])
startActivity(intent)
}
}
}
}
onCreateView 에서는 Fragment가 처음 생성될 때 Fragment에 필요한 View들을 생성하여 반환하므로, 리사이클러뷰가 반환 될 수 있게 dataList와 adapter, layoutManager를 연결하여 리사이클러 뷰를 연결해준다.
여기서, 프래그먼트는 액티비티와 달리 this로 컨텍스트를 참조하면 클릭 리스너의 컨텍스트를 가리키기 때문에 참조가 되지 않는다. context를 사용하여 현재 프래그먼트의 객체를 가지고 오도록 해줘야 한다.
플로팅 버튼이 홈프래그먼트에서만 보이길래, fragment_home.xml로 플로팅버튼을 옮겨주고, HomeFragment.kt에서 바인딩을 받아 onViewCreated에서 setOnClickListener으로 클릭 이벤트를 발생시킨다. SelectImageActivity는 액티비티이므로, intent를 사용하여 연결되도록 intent를 사용했다.
onViewCreated에서는 Fragment의 View가 완전히 생성된 후 호출되기 떄문에 UI요소에 대한 초기화나 이벤트 처리를 수행하기 좋다.
클래스변수로 private lateinit var dataList: MutableList<ProductItem>를 선언해줬기 때문에, onCreateView에서 초기화된 datalist를 onViewCreated에서도 사용할 수 있다. 이를 바탕으로 @Parcelize를 사용하는 ProductItem 데이터클래스에서 역직렬화를 통해 putExtra로 MainDetailActivity에 데이터를 전송한다.
datalist와 adapter가 onViewCreated에서 작성되어도 문제가 없다는 사실을 알았다. 헐..ㅎㅎ 다음엔 깔끔하게 onViewCreated에서 해결해봐야겠다.. ...
package com.example.clone_recyclerfragment
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.clone_recyclerfragment.databinding.ActivityMainDetailBinding
class MainDetailActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainDetailBinding
private val productData: ProductItem? by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra("productData", ProductItem::class.java)
} else {
intent.getParcelableExtra<ProductItem>("productData")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
productData?.let { binding.ivDetailProduct.setImageResource(it.product) }
binding.tvDetailTitle.text = productData?.title
binding.tvDetailLoca.text = productData?.loca
binding.tvDetailDescription.text = productData?.description
binding.tvDetailPrice.text = productData?.price
binding.ivBack.setOnClickListener{
finish()
}
}
}
getParcelableExtra로 HomeFragment에서 보낸 intent.putExtra("productData",dataList[position])를 같은 키 값으로 받아서 productData 변수에 저장한다. 티라미수버전을 기준으로 적용되는 코드가 다르기 때문에 if문을 통해 분리해줬다.onCreate에서 binding할 각 값에 productData에서 해당하는 데이터를 연결해준다.추가 과제를 할 때, 상세페이지는 상단바 및 하단바 없이 탭으로 이동하는 영역이 아니기 때문에 그냥 액티비티를 사용하기로 했다. 이때 리사이클러뷰가 프래그먼트 안에 있기 때문에, 프래그먼트에서 액티비티로 데이터를 이동하는 영역이라고 생각했는데, 그냥 개인과제 때 처럼 리사이클러뷰에서 intent를 사용하여 데이터를 전달하는 것 처럼 전달 할 수 있었다. 그러니까 그냥 리사이클러뷰에서 액티비티로 값을 전달하는 거였던 거다. 해봤던거라 어렵지 않게 연결할 수 있었다.
사실 NearFragment도 있긴 한데, 파프리카 마켓 팀 과제 때 만들어 놓은 것을 재탕 했기 때문에 그 안에서 사용하던 탭바의 처리를 어떻게 할 것인지 아직 정하지 못했다. 그 부분도 Fragment영역으로 바꿔서 레이아웃을 재활용하려고 했는데, 생각해보니까 리사이클러뷰의 layout_item.xml 처럼 고정된 레이아웃이라서 그냥 데이터만 전달하게 끔 바꿔야 하는 것 같아서.. 근데 또 리사이클러뷰는 아니고 고정된 부분이니까.. 데이터를 어떻게 연결해야 할지.. 그 와중에 전체 탭은 또 레이아웃이 달라서 여기만 따로 프래그먼트로 빼야하는 것일지.. 아직 손대기에 정리가 덜 되었다. 사실.. 그냥.. 이대로...... 냅두고 싶은 마음도 있다. 어쨌든 과제에서 요구하는 부분은 충족 되어 있으니까.. .... 그래서 이 부분은 정말로.. 시간이 남으면 해보기로 한다.
이번 수준별 과제 관리에 대해서, 저번에 RecyclerView과제 때 합쳐 놓은 깃허브 레포지토리를.. 다시.. 분리 했다. 진행 중인 프로젝트에 대해서는 수정 및 변경 사항이 있을 때 하위 폴더만 따로 무언가를 하기에 너무 불편하고.. 다음 심화 과제 때에도 빌드업 형태의 과제를 주신다고 하셨어서 그냥 아예 분리를 해버렸다. 이렇게 하기 까지 또 깃배쉬 터미널을 얼마나 들락날락거렸는지.. 애초에 합치지를 말았어야했는데.. 너무 성급했다. 이번에는 RecyclerView브런치와 Fragment브런치로 관리하고, 심화 때에 또 폴더를 만들어서 새로 업로드 해야겠다. 그리고 맨 나중에 수업을 수료하고나서 깃허브를 정리할 때나 합쳐야겠다.
월요일에는 새로 팀과제에 들어가니까 주말 중으로 수준별 과제 제출을 하는 것이 목표이다! 다 만들었으니까 깃허브에 올려서 정리만 하면 될 것 같다!