이곳저곳 관심이 많아요
11. RecyclerView : 리사이클러뷰 전체정리, ViewModel 적용 본문
- RecyclerView A to Z : ViewModel 적용 X
- 파일 구성
- MainActivity.kt
- activity_main.xml
- RecyclerView 디자인을 위한 item.xml파일
- RecyclerView Adapter.kt : init 단계에서 data class에 데이터 add하기, MyViewHolder class 구현하기
- RecyclerView data를 위한 data class.kt
- activity_main.xml 파일에 리사이클러뷰 등록
- MainActivity.kt 파일에서 xml에 등록된 리사이클러뷰를 아이디로 불러온 후, 레이아웃 매니저 설정
var recyclerView:RecyclerView = findViewById(R.id.recyclerView) recyclerView.layoutManager = LinearLayoutManager(this)
- item.xml파일에서 리사이클러뷰 디자인하기
- RecyclerView Adapter.kt 파일에 MyViewHolder class 생성(class MainActivity 안에), RecyclerView.ViewHolder(view) 로부터 상속받음, 클래스 안에 item.xml파일에서 만든 객체들 불러오기
- Adapter 클래스를 RecyclerView.Adapter<adapter이름.myviewholder>() 로부터 상속받음, 3개의 추상메소드 구현
class MovieAdapter: RecyclerView.Adapter<MovieAdapter.MyViewHolder>() { //데이터 클래스 타입으로 리스트 생성하기 var movieList = ArrayList<MovieItem>() //리스트에 데이터 넣어주기 init { movieList.add(MovieItem("The wizard of Oz(1939)", "9.0", "DRAMA", "1930", R.drawable.movie1)) movieList.add(MovieItem("Citizen Kane(1941)", "9.0", "DRAMA", "1930", R.drawable.movie2)) movieList.add(MovieItem("All about Eve(1950)", "9.0", "DRAMA", "1930", R.drawable.movie3)) movieList.add(MovieItem("The third man(1949)", "9.0", "DRAMA", "1930", R.drawable.movie4)) movieList.add(MovieItem("A hard day's night(1964)", "9.0", "DRAMA", "1930", R.drawable.movie5)) movieList.add(MovieItem("Modern times(1936)", "9.0", "DRAMA", "1930", R.drawable.movie6)) movieList.add(MovieItem("Metropolis(1927)", "9.0", "DRAMA", "1930", R.drawable.movie7)) movieList.add(MovieItem("Metropolis(1927)", "9.0", "DRAMA", "1930", R.drawable.movie7)) movieList.add(MovieItem("Metropolis(1927)", "9.0", "DRAMA", "1930", R.drawable.movie7)) } //RecyclerView.Adapter 의 3개의 추상 메소드 구현 //ViewHolder 객체 생성, item 출력을 위한 레이아웃 파일 inflate(팽창==화면에뿌리기)시킴. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val v = LayoutInflater.from(parent.context).inflate(R.layout.movie_item, parent, false) return MyViewHolder(v) } //리스트에 속한 item 을 하나씩 가져와 출력 => 리스트에 속한 값을 해당하는 뷰 객체에 넣어줌 override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val items = movieList[position] with(holder){ titleTextView.text = items.title ratingTextView.text = items.rating genreTextView.text = items.genre releaseYearTextView.text = items.year postImageView.setImageResource(items.resId) } } //아이템 리스트의 사이즈를 반환 override fun getItemCount(): Int { return movieList.size } //리사이클러뷰 아이템 레이아웃에 있는 객체들 불러오기 class MyViewHolder(view: View): RecyclerView.ViewHolder(view){ var postImageView: ImageView = view.findViewById(R.id.image) var titleTextView: TextView = view.findViewById(R.id.title) var ratingTextView: TextView = view.findViewById(R.id.rating) var genreTextView: TextView = view.findViewById(R.id.genre) var releaseYearTextView: TextView = view.findViewById(R.id.releaseYear) } }
- MainActivity.kt 에서 recyclerView.adapter에 방금 만든 아답터 클래스 등록
package com.example.myapplication import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //리사이클러뷰 객체 볼러오고 레이아웃 매니저를 리니어 레이아웃매니저로 설정 var recyclerView:RecyclerView = findViewById(R.id.recyclerView) recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.adapter = MovieAdapter() } }
- 파일 구성
ViewModel 적용 O
- ViewModel class 생성하고 ViewModel() 로 부터 상속받기
class MovieViewModel: ViewModel() { }
- ViewModel class에 리스트 생성하고 데이터 넣어줌
package com.example.myapplication import androidx.lifecycle.ViewModel class MovieViewModel: ViewModel() { val movieList = listOf( MovieItem("The wizard of Oz(1939)", "9.0", "DRAMA", "1930", R.drawable.movie1), MovieItem("Citizen Kane(1941)", "9.0", "DRAMA", "1930", R.drawable.movie2), MovieItem("All about Eve(1950)", "9.0", "DRAMA", "1930", R.drawable.movie3), MovieItem("The third man(1949)", "9.0", "DRAMA", "1930", R.drawable.movie4), MovieItem("A hard day's night(1964)", "9.0", "DRAMA", "1930", R.drawable.movie5), MovieItem("Modern times(1936)", "9.0", "DRAMA", "1930", R.drawable.movie6), MovieItem("Metropolis(1927)", "9.0", "DRAMA", "1930", R.drawable.movie7), MovieItem("Metropolis(1927)", "9.0", "DRAMA", "1930", R.drawable.movie7), MovieItem("Metropolis(1927)", "9.0", "DRAMA", "1930", R.drawable.movie7), ) }
- MainActivity.kt에서 ViewModel을 불러온다
package com.example.myapplication import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { private val movieViewModel: MovieViewModel by lazy{ ViewModelProvider(this).get(MovieViewModel::class.java) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //리사이클러뷰 객체 볼러오고 레이아웃 매니저를 리니어 레이아웃매니저로 설정 var recyclerView:RecyclerView = findViewById(R.id.recyclerView) recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.adapter = MovieAdapter(movieViewModel.movieList) } }
- Adapter
package com.example.myapplication import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView class MovieAdapter(private val list:List<MovieItem>): RecyclerView.Adapter<MovieAdapter.MyViewHolder>() { //RecyclerView.Adapter 의 3개의 추상 메소드 구현 //ViewHolder 객체 생성, item 출력을 위한 레이아웃 파일 inflate(팽창==화면에뿌리기)시킴. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val v = LayoutInflater.from(parent.context).inflate(R.layout.movie_item, parent, false) return MyViewHolder(v) } //리스트에 속한 item 을 하나씩 가져와 출력 => 리스트에 속한 값을 해당하는 뷰 객체에 넣어줌 override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val items = list[position] holder.apply{ titleTextView.text = items.title ratingTextView.text = items.rating genreTextView.text = items.genre releaseYearTextView.text = items.year postImageView.setImageResource(items.resId) } } //아이템 리스트의 사이즈를 반환 override fun getItemCount(): Int { return list.size } //리사이클러뷰 아이템 레이아웃에 있는 객체들 불러오기 class MyViewHolder(view: View): RecyclerView.ViewHolder(view){ var postImageView: ImageView = view.findViewById(R.id.image) var titleTextView: TextView = view.findViewById(R.id.title) var ratingTextView: TextView = view.findViewById(R.id.rating) var genreTextView: TextView = view.findViewById(R.id.genre) var releaseYearTextView: TextView = view.findViewById(R.id.releaseYear) } }
**메인에서 ViewModel을 불러오는 것이 아니라 Adapter 에서 모두 처리하는 방법은 시도해봤으나 찾지 못함.
- ViewModel class 생성하고 ViewModel() 로 부터 상속받기
Fragment, ViewModle 적용- MovieFragment
기본 프래그먼트의 onCreateView 메소드의 리턴 값이 rootView 값이다. 따라서 프래그먼트 안에서 레이아웃 객체를 참조하기 위해서는 이 리턴값을 루트뷰 변수로 선언한 후 참조해야한다. 그리고 레이아웃 매니저 안에도 this가 아닌 context를 넣어주어야 한다.
package com.example.myapplication import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView class MovieFragment : Fragment() { private val movieViewModel: MovieViewModel by lazy{ ViewModelProvider(this).get(MovieViewModel::class.java) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { //리사이클러뷰 객체 볼러오고 레이아웃 매니저를 리니어 레이아웃매니저로 설정 val rootView = inflater.inflate(R.layout.fragment_movie, container, false) //루트뷰 선언 꼭! var recyclerView: RecyclerView = rootView.findViewById(R.id.recyclerView) recyclerView.layoutManager = LinearLayoutManager(context) //this 대신 context recyclerView.adapter = MovieAdapter(movieViewModel.movieList) return rootView } }
- MainActicity
package com.example.myapplication import android.os.Bundle import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //동적 교체할때는 필요한 코드지만 그냥 하나만 띄워줄땐 이거 없어도 실행은 됨. 왜냐면 xml 파일에서 넣어줬기때문 val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) if(currentFragment == null){ //현재 프래그먼트가 비어 있을 때 supportFragmentManager //프래그먼트를 호출하는 매니저 .beginTransaction() //일련의 과정을 실행하라 .add(R.id.fragment_container,MovieFragment()) //프래그먼트 들어갈곳 == 컨테이너에 생성한 객체 ==MovieFragment 넣어줘라 .commit() //마무리? } } }
- MovieFragment
- 바인딩 방식 개선
- MovieAdapter.kt
package com.example.myapplication import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView class MovieAdapter(private val list:List<MovieItem>): RecyclerView.Adapter<MovieAdapter.MyViewHolder>() { //RecyclerView.Adapter 의 3개의 추상 메소드 구현 //ViewHolder 객체 생성, item 출력을 위한 레이아웃 파일 inflate(팽창==화면에뿌리기)시킴. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val v = LayoutInflater.from(parent.context).inflate(R.layout.movie_item, parent, false) return MyViewHolder(v) } //리스트에 속한 item 을 하나씩 가져와 출력 => 리스트에 속한 값을 해당하는 뷰 객체에 넣어줌 override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val items:MovieItem = list[position] holder.bind(items) } //아이템 리스트의 사이즈를 반환 override fun getItemCount(): Int { return list.size } //리사이클러뷰 아이템 레이아웃에 있는 객체들 불러오기 class MyViewHolder(view: View): RecyclerView.ViewHolder(view){ private lateinit var movie: MovieItem var postImageView: ImageView = view.findViewById(R.id.image) var titleTextView: TextView = view.findViewById(R.id.title) var ratingTextView: TextView = view.findViewById(R.id.rating) var genreTextView: TextView = view.findViewById(R.id.genre) var releaseYearTextView: TextView = view.findViewById(R.id.releaseYear) fun bind(movie: MovieItem){ this.movie = movie titleTextView.text = this.movie.title ratingTextView.text = this.movie.rating genreTextView.text = this.movie.genre releaseYearTextView.text = this.movie.year postImageView.setImageResource(this.movie.resId) } } }
- MovieAdapter.kt
- 버튼클릭 이벤트적용
- MovieAdapter.kt => MyViewModel 달라진 점 보기
package com.example.myapplication import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import android.widget.Toast import androidx.recyclerview.widget.RecyclerView class MovieAdapter(private val list:List<MovieItem>): RecyclerView.Adapter<MovieAdapter.MyViewHolder>() { //RecyclerView.Adapter 의 3개의 추상 메소드 구현 //ViewHolder 객체 생성, item 출력을 위한 레이아웃 파일 inflate(팽창==화면에뿌리기)시킴. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val v = LayoutInflater.from(parent.context).inflate(R.layout.movie_item, parent, false) return MyViewHolder(v) } //리스트에 속한 item 을 하나씩 가져와 출력 => 리스트에 속한 값을 해당하는 뷰 객체에 넣어줌 override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val items:MovieItem = list[position] holder.bind(items) } //아이템 리스트의 사이즈를 반환 override fun getItemCount(): Int { return list.size } //리사이클러뷰 아이템 레이아웃에 있는 객체들 불러오기 //버튼클릭리스너 상속받기 inner class MyViewHolder(view: View): RecyclerView.ViewHolder(view),View.OnClickListener{ private lateinit var movie: MovieItem var postImageView: ImageView = view.findViewById(R.id.image) var titleTextView: TextView = view.findViewById(R.id.title) var ratingTextView: TextView = view.findViewById(R.id.rating) var genreTextView: TextView = view.findViewById(R.id.genre) var releaseYearTextView: TextView = view.findViewById(R.id.releaseYear) init { //초기화 단계에서 버튼클릭리스너 달아주기 postImageView.setOnClickListener(this) } fun bind(movie: MovieItem){ this.movie = movie titleTextView.text = this.movie.title ratingTextView.text = this.movie.rating genreTextView.text = this.movie.genre releaseYearTextView.text = this.movie.year postImageView.setImageResource(this.movie.resId) } override fun onClick(v: View?) { //v가 View? 타입인데 토스트의 context 는 null 타입 허용 안하기 때문에 !!붙이기 Toast.makeText(v!!.context,movie.title, Toast.LENGTH_SHORT).show() } } }
- MovieAdapter.kt => MyViewModel 달라진 점 보기
'Programming > Android' 카테고리의 다른 글
11. Fragment (0) | 2021.12.07 |
---|---|
10. Intent : MainActivity <-> SubActivity 최종 (0) | 2021.12.05 |
10. Intent : Implicit intent (0) | 2021.11.05 |
10. Intent : MainActivity -> SubActivity 값 전달 (0) | 2021.11.05 |
Comments