1. ListAdapter

ListAdapter를 왜 쓰는가?

  • RecyclerView를 쓰면 업데이트 할 때마다 데이터를 지우고 추가하고... 하는 코드 쓰기가 너무 귀찮고, 데이터가 엄청 많으면 성능에 문제가 생겨 과부하가 올 수 있다.
  • 그래서 그런 업데이트를 자동으로 해주는 친구가 바로 이 친구이다.
  • 아직은 많이 익숙하지 않다.. ㅠㅠ

예시 코드

package com.example.appuiclone

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.appuiclone.databinding.RecyclerviewBinding

// 여기서 리스트의 값들을 비교하는 것이다. 리스트가 업데이트 되었을 때 변경된 부분만 캐치해 낼 수 있다고 한다.
// 전체 리스트와 string 값의 변화를 비교한다.
// 근데 이 친구는 데이터가 하나 뿐이라서 비교하는 걸 두 개 만들 이유가 없지만... ㅋㅋㅋ
class PapagoAdapter (private val onClick: (Item) -> Unit) :
    ListAdapter<Item, PapagoAdapter.ViewHolder>(object: DiffUtil.ItemCallBack<Item>() {
        override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
            return oldItem.string == newItem.string
        }
    
        override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
            return oldItem == newItem
        }
        
    }) {
    
    // 여기는 맨날 똑같다.
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding =
            RecyclerviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }
    
    // ViewHolder를 생성할 때 Item 객체를 position 위치에 맞는 걸로 가져온다.
    // bind로 값을 전달한다.
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = getItem(position)
        holder.bind(item, onClick)
    }

    inner class ViewHolder(private val binding: RecyclerviewBinding) :
        RecyclerView.ViewHolder(binding.root) {

		// 아이템 객체인데 거기에 대한 text 설정 및 클릭 리스너 설정. TransportResultAActivity로 가서 onClick  람다함수를 실행한다.
        fun bind(item: Item, onClick: (Item) -> Unit) {
            binding.tvTransportResultText.text = item.string
            itemView.setOnClickListener {
                onClick(item)
            }
        }
    }
}

2. ViewPager + TabLayout

// MainActivity.kt
package com.example.layouttraining

import ImagePagerAdapter
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.example.layouttraining.databinding.ActivityMainBinding
import com.google.android.material.tabs.TabLayoutMediator

class MainActivity : AppCompatActivity() {
    private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

// TabLayout에 들어갈 텍스트들
    private val tabTitles = listOf(R.string.tab1, R.string.tab2, R.string.tab3)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(binding.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
        }

// ViewPagerAdapter랑 연결한 후
// TabLayout 만든 것이랑 그 ViewPager를 연결하면 된다.
// tab.text는 Tab Layout에 들어갈 텍스트들을 차례로 넣어주는 것이다.
        with(binding) {
            viewPager.adapter = ViewPagerAdapter(this@MainActivity)
            TabLayoutMediator(tabLayout, viewPager) { tab, position ->
                tab.text = getString(tabTitles[position])
            }.attach()
        }
    }
}

ViewPagerAdapter
package com.example.layouttraining

import androidx.appcompat.app.AppCompatActivity
import androidx.viewpager2.adapter.FragmentStateAdapter

// 총 들어갈 TabeLayout 텍스트 개수는 3개
// 각각의 TabLayout 숫자에 따라 보여줄 Fragment를 나누어서 관리
class ViewPagerAdapter(activity: AppCompatActivity) : FragmentStateAdapter(activity) {
    override fun getItemCount() = 3
    override fun createFragment(position: Int) = when (position) {
        0 -> HomeFragment()
        1 -> SearchFragment()
        2 -> SettingFragment()
        else -> throw IllegalStateException("Invalid position: $position")
    }
}

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">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"/>

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="#FFEB3B"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tab_layout"
        app:layout_constraintVertical_bias="0.0" />

</androidx.constraintlayout.widget.ConstraintLayout>

// TabLayout과 ViewPager를 여기서 정의 해준다. 그리고  Fragment를 각각 만들어서 ViewPager 안에 넣어주는 것이다.

3. BottomNavigationView

주의!

// 맨 처음 Activity에서
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
    setContentView(binding.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
    }
    /*
    보면 여러 가지 설정이 있는데 setContentView 밑의 코드는 지워야 한다.
    안 그러면 NavigationView 밑에 여백이 뜬다.. ㅠㅠ
    이거 가지고 한참 헤매다 지우고 나서 실행하니 정상적으로 되었다.. ㅠㅠ */
package com.example.layouttraining

import android.os.Bundle
import android.text.TextUtils.replace
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import com.example.layouttraining.databinding.ActivitySecondBinding

class SecondActivity : AppCompatActivity() {
    private lateinit var binding: ActivitySecondBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        binding = ActivitySecondBinding.inflate(layoutInflater)
        setContentView(binding.root)


		// bottomNavigation의 아이템을 다른 xml로 정의하고
        // 아이템의 아이디에 따라 각각 다른 Fragment를 띄우는 것이다.
        binding.bottomNavigation.setOnItemSelectedListener { item ->
            when (item.itemId) {
                R.id.test1 -> {
                    HomeFragment().loadFragment()
                    true
                }

                R.id.test2 -> {
                    SearchFragment().loadFragment()
                    true
                }

                R.id.test3 -> {
                    SettingFragment().loadFragment()
                    true
                }

                else -> false
            }
        }
    }

    private fun Fragment.loadFragment() {
        supportFragmentManager.commit {
            replace(R.id.fragment_container, this@loadFragment)
            setReorderingAllowed(true)
            addToBackStack(null)
        }
    }

}



// 여기서 각각의 bottomNavigation의 아이템들을 정의하면 된다.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@+id/test1"
        android:title="test1" />
    <item android:id="@+id/test2"
        android:title="test2"/>
    <item android:id="@+id/test3"
        android:title="test3"/>


</menu>
profile
김성진의 개발 관련 내용 정리 블로그

0개의 댓글