ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Fragment와 WebData 활용하기
    Android_Kotlin/Android_공부 2023. 8. 1. 00:55
    728x90

    안드로이드에서 Fragment와 WebData파싱을 이용해서 현재 해외축구의 득점 순위를 보여주는 어플을 구현해보겠습니다.

    소스코드


    본격적인 코드 작성 전 해당 코드에서는 Glide 라이브러리를 사용하기 때문에 프로젝트의 Gradle 파일을 수정해야 합니다.

    dependencies 블록에 아래의 코드를 추가합니다.

    dependencies {
        // 기존 dependencies 내용
    
        implementation 'com.github.bumptech.glide:glide:4.12.0'
        kapt 'com.github.bumptech.glide:compiler:4.12.0'
    }
    

    이제 Glide를 사용하여 이미지를 로드하고 표시할 수 있습니다.

    먼저 프래그먼트 구현을위해 메인 레이아웃을 작성해줍니다.

     

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <FrameLayout
            android:id="@+id/main_frm"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            app:layout_constraintTop_toTopOf="parent" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <Button
                android:id="@+id/fragment1Button"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="EPL"
                android:background="@color/space_gray"
                android:onClick="loadFragment1"
                android:textColor="#FFFFFF"
                android:textStyle="bold" />
    
            <Button
                android:id="@+id/fragment2Button"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Laliga"
                android:background="@color/space_gray"
                android:onClick="loadFragment2"
                android:textColor="#FFFFFF"
                android:textStyle="bold" />
    
        </LinearLayout>
    
    </LinearLayout>

    프래그먼트를 전환할수있는 버튼과 메인 프레임레이아웃으로 구성되어있습니다.

    <FrameLayout
            android:id="@+id/main_frm"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            app:layout_constraintTop_toTopOf="parent" />

    main_frm에 동적으로 프래그먼트가 추가되고, 사용자가 EPL 버튼을 클릭하면 loadFragment1 메서드가 호출되어 첫 번째 프래그먼트를 로드하고, Laliga 버튼을 클릭하면 loadFragment2 메서드가 호출되어 두 번째 프래그먼트를 로드할 수 있습니다.

     

    이제 전환시에 보여줄 프래그먼트 레이아웃을 작성합니다.

    fragment_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        tools:context=".MainFragment">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:padding="8dp">
    
            <EditText
                android:id="@+id/searchEditText"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:hint="검색어를 입력하세요"
                tools:ignore="DuplicateIds" />
    
            <Button
                android:id="@+id/searchButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="검색"
                android:backgroundTint="@color/space_gray"/>
    
        </LinearLayout>
    
        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:id="@+id/swipe"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">
    
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginStart="8dp"
                android:layout_marginTop="8dp"
                android:layout_marginEnd="8dp"
                android:layout_marginBottom="8dp" />
    
        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    
    </LinearLayout>

    이렇게 정의된 XML 코드를 사용하여 앱의 레이아웃을 구성하면, 사용자는 검색어를 입력하고 검색 버튼을 클릭하여 원하는 동작을 수행할 수 있습니다. RecyclerView는 데이터를 표시하고, SwipeRefreshLayout을 통해 사용자는 스크롤을 내릴 때 새로고침할 수 있습니다.

     

    같은 방식으로 두번째 레이아웃도 구성해 줍니다.

    fragment_sub.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        tools:context=".MainFragment">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:padding="8dp">
    
            <EditText
                android:id="@+id/searchEditText_2"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:hint="검색어를 입력하세요!"
                tools:ignore="DuplicateIds" />
    
            <Button
                android:id="@+id/searchButton_2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="검색"
                android:backgroundTint="@color/space_gray"/>
    
        </LinearLayout>
    
        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:id="@+id/swipe_2"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">
    
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginStart="8dp"
                android:layout_marginTop="8dp"
                android:layout_marginEnd="8dp"
                android:layout_marginBottom="8dp" />
    
        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    
    </LinearLayout>

     

    다음으로 파싱된 정보를 담을 row.xml을 작성합니다.

    row.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:gravity="start">
    
        <ImageView
            android:id="@+id/playerImage"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_gravity="start"
            android:adjustViewBounds="true"
            android:src="@drawable/placeholder" />
    
        <TextView
            android:id="@+id/teamName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:padding="5dp"
            android:textSize="12sp" />
    
        <TextView
            android:id="@+id/playerName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:padding="5dp"
            android:textSize="15sp"
            android:textStyle="bold" />
    
    </LinearLayout>

    이렇게 정의된 XML 코드는 파싱된 정보를 표시하기 위한 레이아웃으로 사용될 수 있습니다. 선수 이미지, 팀 이름, 선수 이름을 표시하기 위한 뷰들이 세로로 나열되어 있으며, 각 뷰의 속성을 조정하여 원하는 디자인을 구성할 수 있습니다.

     

    이제 코틀린 코드를 작성해 기능을 구현하겠습니다.

     

    먼저 메인 액티비티를 작성해줍니다.

    MainActivity.kt

    
    import android.content.Intent
    import android.net.Uri
    import com.example.myapplication0508.databinding.ActivityMainBinding
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.webkit.WebViewClient
    import androidx.recyclerview.widget.LinearLayoutManager
    import kotlinx.coroutines.CoroutineScope
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.launch
    import org.jsoup.Jsoup
    import android.util.Log
    import android.view.View
    import android.widget.Toast
    import androidx.recyclerview.widget.DividerItemDecoration
    import kotlinx.coroutines.withContext
    import org.jsoup.parser.Parser
    
    import java.sql.Connection
    
    
    class MainActivity : AppCompatActivity() {
        lateinit var binding: ActivityMainBinding
        
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(binding.root)
            initLayout()
        }
    
    
    
        private fun initLayout(){
    
        }
    
        fun loadFragment1(view: View) {
            val fragment = MainFragment()
            supportFragmentManager.beginTransaction()
                .replace(R.id.main_frm, fragment)
                .commit()
        }
    
        fun loadFragment2(view: View) {
            val fragment = SubFragment()
            supportFragmentManager.beginTransaction()
                .replace(R.id.main_frm, fragment)
                .commit()
        }
    
    }
    • items: MyData 객체의 리스트인 items는 어댑터가 표시할 데이터를 저장합니다.
    • itemClickListener: OnItemClickListener 인터페이스를 구현한 리스너 객체를 저장하는 변수입니다. 이 리스너는 아이템 클릭 이벤트를 처리하기 위해 사용됩니다.
    • MyViewHolder: RecyclerView.ViewHolder를 상속한 내부 클래스로, 각 아이템의 뷰를 보유하고 클릭 이벤트를 처리합니다.
    • onCreateViewHolder(): 아이템 뷰를 위한 ViewHolder 객체를 생성하는 역할을 합니다. RowBinding을 사용하여 뷰를 생성하고, 해당 뷰를 이용해 MyViewHolder 객체를 생성하여 반환합니다.
    • onBindViewHolder(): ViewHolder와 데이터를 바인딩하는 역할을 합니다. 데이터를 가져와 해당 아이템 뷰에 표시하고, 이미지를 로드하여 이미지뷰에 표시합니다.
    • getItemCount(): 전체 아이템 개수를 반환합니다.
    • setOnItemClickListener(): 외부에서 아이템 클릭 리스너를 설정하기 위한 메서드입니다.

     

    이제 각 프래그먼트파일웹파싱과 검색 기능 등을 구현해 보겠습니다.

    MainFragment.kt

    import android.os.Bundle
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.Toast
    import androidx.fragment.app.Fragment
    import androidx.recyclerview.widget.DividerItemDecoration
    import androidx.recyclerview.widget.LinearLayoutManager
    import com.example.myapplication0508.databinding.FragmentMainBinding
    import kotlinx.coroutines.CoroutineScope
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.launch
    import kotlinx.coroutines.withContext
    import org.jsoup.Jsoup
    
    class MainFragment : Fragment(), MyAdapter.OnItemClickListener {
        private val scope = CoroutineScope(Dispatchers.IO)
        private lateinit var adapter: MyAdapter
        private lateinit var binding: FragmentMainBinding
        private val htmlUrl = "<https://sports.news.naver.com/wfootball/record/index?category=epl&league=100&tab=player>"
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            binding = FragmentMainBinding.inflate(inflater, container, false)
            return binding.root
        }
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            initLayout()
        }
    
        private fun initLayout() {
            binding.swipe.setOnRefreshListener {
                binding.swipe.isRefreshing = true
                getPlayers()
            }
            binding.recyclerView.layoutManager =
                LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
            binding.recyclerView.addItemDecoration(
                DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
            )
            adapter = MyAdapter(ArrayList())
            adapter.setOnItemClickListener(this)
    
            binding.searchButton.setOnClickListener {
                val keyword = binding.searchEditText.text.toString()
                searchPlayers(keyword)
            }
    
            binding.recyclerView.adapter = adapter
            getPlayers()
        }
    
        override fun onItemClick(data: MyData) {
            Toast.makeText(requireContext(), data.playerName, Toast.LENGTH_SHORT).show()
            binding.searchEditText.setText(data.playerName)
        }
    
        private fun getPlayers() {
            scope.launch {
                adapter.items.clear()
                val doc = Jsoup.connect(htmlUrl).get()
                val playerRows = doc.select("tbody tr")
    
                for (row in playerRows) {
                    val nameElement = row.selectFirst("td.align_l > div > span.name")
                    val teamElement = row.selectFirst("td.align_l > div > span.team")
                    val imageElement = row.selectFirst("td.align_l > div > span.emblem img")
    
                    val name = nameElement?.text()
                    val team = teamElement?.text()
                    val imageUrl = imageElement?.attr("src")
    
                    if (!name.isNullOrBlank() && !team.isNullOrBlank() && !imageUrl.isNullOrBlank()) {
                        adapter.items.add(MyData(name, team, imageUrl))
                    }
                }
    
                withContext(Dispatchers.Main) {
                    adapter.notifyDataSetChanged()
                    binding.swipe.isRefreshing = false
                }
            }
        }
    
        private fun searchPlayers(keyword: String) {
            scope.launch {
                adapter.items.clear()
                val doc = Jsoup.connect(htmlUrl).get()
                val playerRows = doc.select("tbody tr")
    
                for (row in playerRows) {
                    val name = row.select("td.align_l > div > span.name").text()
                    val team = row.select("td.align_l > div > span.team").text()
                    if (name.contains(keyword, ignoreCase = true) || team.contains(keyword, ignoreCase = true)) {
                        adapter.items.add(MyData(name, team, "")) // Pass an empty string for imageUrl
                    }
                }
    
                withContext(Dispatchers.Main) {
                    adapter.notifyDataSetChanged()
                    binding.swipe.isRefreshing = false
                }
            }
        }
    
    }
    
    • MainFragment 클래스는 Fragment를 상속하며, MyAdapter.OnItemClickListener 인터페이스를 구현합니다.
    • onCreateView 함수에서 프래그먼트의 UI를 초기화하고 FragmentMainBinding을 사용하여 레이아웃을 바인딩합니다.
    • onViewCreated 함수에서 UI 초기화를 호출합니다.
    • initLayout 함수에서 UI 컴포넌트의 초기화와 이벤트 리스너 설정을 수행합니다.
      • swipe를 사용한 리프레시 이벤트 리스너 설정
      • recyclerView를 위한 LinearLayoutManager 및 구분선 설정
      • 어댑터 생성 및 클릭 리스너 설정
      • 검색 버튼 클릭 시 searchPlayers 함수 호출
      • getPlayers 함수를 호출하여 선수 목록 가져오기
    • onItemClick 함수에서 어댑터 아이템 클릭 시 동작을 정의하며, 토스트 메시지를 표시하고 검색창에 해당 플레이어 이름을 설정합니다.
    • getPlayers 함수에서는 백그라운드에서 네이버 스포츠 페이지로부터 선수 목록을 가져옵니다. Jsoup 라이브러리를 사용하여 HTML 페이지 파싱을 수행하고, 필요한 데이터를 추출하여 어댑터 아이템으로 추가합니다. 마지막으로 메인 스레드에서 어댑터 갱신과 리프레시 완료를 처리합니다.
    • searchPlayers 함수에서는 입력된 키워드와 일치하는 선수를 검색하여 어댑터 아이템으로 추가합니다. 이 역시 백그라운드에서 처리되며, 메인 스레드에서 어댑터 갱신과 리프레시 완료를 처리합니다.
    • 웹 파싱 상세설명
      private fun getPlayers() {
              scope.launch {
                  adapter.items.clear()
                  val doc = Jsoup.connect(htmlUrl).get()
                  val playerRows = doc.select("tbody tr")
      
                  for (row in playerRows) {
                      val nameElement = row.selectFirst("td.align_l > div > span.name")
                      val teamElement = row.selectFirst("td.align_l > div > span.team")
                      val imageElement = row.selectFirst("td.align_l > div > span.emblem img")
      
                      val name = nameElement?.text()
                      val team = teamElement?.text()
                      val imageUrl = imageElement?.attr("src")
      
                      if (!name.isNullOrBlank() && !team.isNullOrBlank() && !imageUrl.isNullOrBlank()) {
                          adapter.items.add(MyData(name, team, imageUrl))
                      }
                  }
      
                  withContext(Dispatchers.Main) {
                      adapter.notifyDataSetChanged()
                      binding.swipe.isRefreshing = false
                  }
              }
          }
      
      코드에서의 getPlayers 함수는 다음과 같은 HTML 구조에서 파싱을 수행합니다.주어진 HTML 예시에서 태그 클래스에 접근하는 방법은 다음과 같습니다.
      1. nameElement 접근: nameElement은 <span class="name">...</span> 요소를 선택합니다. .selectFirst("td.align_l > div > span.name")을 사용하여 td 태그의 align_l 클래스를 가진 자식 요소들 중에서 div 태그를 선택하고, 그 하위에 있는 span 태그 중에서 name 클래스를 가진 요소를 선택합니다.
      2. teamElement 접근: teamElement은 <span class="team">...</span> 요소를 선택합니다. .selectFirst("td.align_l > div > span.team")을 사용하여 td 태그의 align_l 클래스를 가진 자식 요소들 중에서 div 태그를 선택하고, 그 하위에 있는 span 태그 중에서 team 클래스를 가진 요소를 선택합니다.
      3. imageElement 접근: imageElement은 <img> 요소를 선택합니다. .selectFirst("td.align_l > div > span.emblem img")을 사용하여 td 태그의 align_l 클래스를 가진 자식 요소들 중에서 div 태그를 선택하고, 그 하위에 있는 span 태그 중에서 emblem 클래스를 가진 요소를 선택하고, 그 하위에 있는 img 태그를 선택합니다. 그 후 **attr("src")**를 호출하여 src 속성의 값을 가져옵니다.
      4. 예시에서는 "https://dthumb-phinf.pstatic.net/?src=https://sports-phinf.pstatic.net/team/wfootball/default/11.png&type=f25_25&refresh=1"이 됩니다.
    • 1
    • 맨시티 엘링 홀란 맨시티
    • getPlayers() 함수는 Jsoup 라이브러리를 사용하여 HTML 문서를 파싱합니다. 함수 내에서 HTML URL에 접속하고, **Jsoup.connect(htmlUrl).get()**을 통해 HTML 문서를 가져옵니다. 그 후, 파싱된 문서에서 필요한 데이터를 선택하여 선수 정보를 추출하고, 추출된 데이터를 adapter.items에 추가합니다. 마지막으로 UI를 업데이트하여 화면에 변경된 데이터가 표시되도록 합니다.

    SubFragment.kt

    import android.os.Bundle
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.Toast
    import androidx.fragment.app.Fragment
    import androidx.recyclerview.widget.DividerItemDecoration
    import androidx.recyclerview.widget.LinearLayoutManager
    import com.example.myapplication0508.databinding.FragmentMainBinding
    import com.example.myapplication0508.databinding.FragmentSubBinding
    import kotlinx.coroutines.CoroutineScope
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.launch
    import kotlinx.coroutines.withContext
    import org.jsoup.Jsoup
    
    class SubFragment : Fragment(), MyAdapter.OnItemClickListener {
        private val scope = CoroutineScope(Dispatchers.IO)
        private lateinit var adapter: MyAdapter
        private lateinit var binding: FragmentSubBinding
        private val htmlUrl = "<https://sports.news.naver.com/wfootball/record/index?category=primera&tab=player>"
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            binding = FragmentSubBinding.inflate(inflater, container, false)
            return binding.root
        }
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            initLayout()
        }
    
        private fun initLayout() {
            binding.swipe2.setOnRefreshListener {
                binding.swipe2.isRefreshing = true
                getPlayers()
            }
            binding.recyclerView.layoutManager =
                LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
            binding.recyclerView.addItemDecoration(
                DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
            )
            adapter = MyAdapter(ArrayList())
            adapter.setOnItemClickListener(this)
    
            binding.searchButton2.setOnClickListener {
                val keyword = binding.searchEditText2.text.toString()
                searchPlayers(keyword)
            }
    
            binding.recyclerView.adapter = adapter
            getPlayers()
        }
    
        override fun onItemClick(data: MyData) {
            Toast.makeText(requireContext(), data.playerName, Toast.LENGTH_SHORT).show()
            binding.searchEditText2.setText(data.playerName)
        }
    
        private fun getPlayers() {
            scope.launch {
                adapter.items.clear()
                val doc = Jsoup.connect(htmlUrl).get()
                val playerRows = doc.select("tbody tr")
    
                for (row in playerRows) {
                    val nameElement = row.selectFirst("td.align_l > div > span.name")
                    val teamElement = row.selectFirst("td.align_l > div > span.team")
                    val imageElement = row.selectFirst("td.align_l > div > span.emblem img")
    
                    val name = nameElement?.text()
                    val team = teamElement?.text()
                    val imageUrl = imageElement?.attr("src")
    
                    if (!name.isNullOrBlank() && !team.isNullOrBlank() && !imageUrl.isNullOrBlank()) {
                        adapter.items.add(MyData(name, team, imageUrl))
                    }
                }
    
                withContext(Dispatchers.Main) {
                    adapter.notifyDataSetChanged()
                    binding.swipe2.isRefreshing = false
                }
            }
        }
    
        private fun searchPlayers(keyword: String) {
            scope.launch {
                adapter.items.clear()
                val doc = Jsoup.connect(htmlUrl).get()
                val playerRows = doc.select("tbody tr")
    
                for (row in playerRows) {
                    val name = row.select("td.name > div > strong").text()
                    val team = row.select("td.align_l > div > span").text()
                    if (name.contains(keyword, ignoreCase = true) || team.contains(keyword, ignoreCase = true)) {
                        adapter.items.add(MyData(name, team, "")) // Pass an empty string for imageUrl
                    }
                }
    
                withContext(Dispatchers.Main) {
                    adapter.notifyDataSetChanged()
                    binding.swipe2.isRefreshing = false
                }
            }
        }
    
    }
    

    실행화면

    선수의 득점순위가 보여집니다.
    선수이름 클릭하면 검색창에 이름이 자동으로 추가됩니다.
    스와이프시 새로고침이 됩니다.
    하단에 LALIGA 버튼 클릭시 화면전환이 이루어집니다.

    이 어플리케이션은 웹 파싱과 프래그먼트를 활용하여 축구 선수 정보를 제공하는 간단한 어플리케이션입니다. 사용자는 어플리케이션을 통해 선수 목록을 검색하고, 선택한 선수의 상세 정보를 확인할 수 있습니다.

    웹 파싱을 통해 실시간으로 업데이트되는 선수 정보를 가져오며, Jsoup 라이브러리를 사용하여 HTML 문서를 파싱합니다. 검색 기능을 통해 사용자는 선수의 이름이나 팀 정보로 선수 목록을 필터링할 수 있습니다.

    프래그먼트를 사용하여 화면 전환을 구현하였습니다. 선수 목록 프래그먼트에서는 RecyclerView와 어댑터를 사용하여 선수 목록을 표시하고, 사용자의 검색어에 따라 동적으로 필터링된 목록을 제공합니다.

    'Android_Kotlin > Android_공부' 카테고리의 다른 글

    fetchListData() 일반적인 디자인 패턴  (0) 2023.08.08
    TextInputLayout 사용하기  (0) 2023.08.01
    SQLite 실습1-2(추가 : 예외처리)  (0) 2023.08.01
    SQLite 실습1-2  (0) 2023.07.31
    SQLite 실습1-1  (0) 2023.07.31
Designed by Tistory.