ListView
ListView가 해야하는 일은 매우 많다. 그래서 ListView의 부담을 줄일 수 있도록 기능을 분산해야한다.
기능 분산을 위해 데이터 관리를 위임받는 것이 Adapter
<?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"
tools:context=".MainActivity">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Listview각각에 대한 레이아웃을 생성해준다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:id="@+id/linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="미션임파서블"
android:layout_marginBottom="10dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="4.4" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
tools:listitem="@layout/activity_item를 사용하면 화면을 바로 listView에 반영해서 확인할 수 있다.
변화하는 것이 이름과 별점인데 그것들을 하나의 데이터 클래스에 묶어서 만든다.
package com.example.listview
data class Movie(val title:String, val movie_rating: Double)
BaseAdapter: BaseAdapter를 상속받는다.
private val ~ : 아까만든 데이터타입에 맞춰 동일한 형태의 arrayList를 받아준다.
클릭 후
모두 받아온다.
11번째 줄:Context는 getSystemService 시스템에있는 api를 호출해서 어플리케이션에서 쓸 수 있는 쉽게 말해서 앱과 os의 중재자 역할
getView를 하기 위해 inflater필요
12번째 줄: 뷰바인딩을 사용하기 때문에 해당하는 xml의 바인딩 클래스 생성되어 있고 그걸 가져온다.
14번째 줄: 어레이리스트 크기 반환
16번째 줄:
18번째 줄:
20번째 줄:
override fun getView(position: Int, p1: View?, p2: ViewGroup?): View {
binding = ActivityItemBinding.inflate(inflater,p2, false)
binding.title.text = movieArrayList[position].title
binding.rating.text = "${movieArrayList[position].movie_rating}"
return binding.root
}
화면 크기만큼 레이아웃이 들어갈 수 있는 공간을 채울 때 최초로 실행되고 그다음에는 스크롤이 되어서 아이템이 보일때마다 호출되는 함수
리스트뷰의 아이템에 들어갈 title과 rating을 채워준다.
adapter에 movieList를 넘겨주고 listView에 adapter를 넣어주면 완성
RecyclerView
dependencies {
implementation 'com.android.support:recyclerview-v7:26.1.0'
...
}
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="미션임파서블"
android:layout_marginBottom="10dp" />
<TextView
android:id="@+id/rating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="4.4" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
부모 레이아웃의 높이를 match_parent로 하면 한페이지에 하나의 요소가 뜨는 불상사가 일어난다 그러니 높이는 wrap_content를 해야한다
<?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"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_data"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
class CustomAdapter(val movieList: ArrayList<Movie>): RecyclerView.Adapter {
}
나머지도 완성해준다
class CustomAdapter(val movieList: ArrayList<Movie>): RecyclerView.Adapter<CustomAdapter.CustomViewHolder>() {
}
CustomViewHolder라는 클래스를 만든다
에러가 계속 나올텐데 해결 가능하다.
implement Members를 누르고 다 받아오면 된다.
인데 저거를 눌러도 안받아오진데 다른 방법으로 진행하니 저게 제대로 작동한다. 대체 뭐지? 일단 그건 차차 알아보고 제대로 되는 방법을 보자
class DataRVAdapter {
}
// 1. RecyclerView.Adapter 상속
class DataRVAdapter(): RecyclerView.Adapter<> {
}
// 2. DataViewHolder라는 클래스를 생성
class DataRVAdapter(): RecyclerView.Adapter<RVAdapter.DataViewHolder>() {
}
// 3. DataViewHolder중첩 클래스 작성
class RVAdapter(private val movieList: ArrayList<Movie>): RecyclerView.Adapter<RVAdapter.DataViewHolder>() {
class DataViewHolder(private val viewBinding: ListItemBinding): RecyclerView.ViewHolder(viewBinding.root) {
fun bind(data:Movie) {
}
}
...
}
수업에서는 inner class로 되어있었지만 내부클래스 말고 중첩클래스를 사용해야하는 이유를 보고 3번을 수정했다.
(그리고 유튜브를 보면 3번을 하기 전에 implement Members가 되던데 나는 저걸 안적어주면 안되더라 무슨 이유인지는 찾아봐야겠다.)
create: ViewHolder 만들어질 때 실행할 동작
bindviewholder: ViewHolder가 실제로 데이터를 표시해야 할 때 호출되는 함수
getitemcount: 표현할 item 개수
리스트뷰의 요소 레이아웃을 제어하는 내용들(예: TextView의 text, setOnClickListener등)
fun bind(data:Movie) {
viewBinding.title.text = data.title
viewBinding.rating.text = "${data.rating}"
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RVAdapter.DataViewHolder {
val viewBinding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return RVAdapter.DataViewHolder(viewBinding)
}
어뎁트에서는 layoutInflater를 바로 가져올 수 없어서 부모의 context(activty에서 담고있는 모든 정보)정보를 이용해서 만들고 부모 정보도 넘겨주고, 부모에 바로 붙일거냐도 물어봄
그리고 여기서 부모는 연결할 activity 지금 상황의 parent는 MainActivity다.
return을 해줘서 커스텀 뷰홀더에 viewBinding을 담고 실행이 된다.
//1. movieList를 인자로 선언한다: 부모(메인액티비티)에서 변화하면 바로 수정이 되기때문)
class RVAdapter(private val movieList: ArrayList<Movie>) {
}
2. movieList의 크기를 반환
override fun getItemCount(): Int = movieList.size
override fun onBindViewHolder(holder: RVAdapter.DataViewHolder, position: Int) {
holder.bind(movieList[position])
}
package com.example.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.myapplication.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var viewBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
val movieList: ArrayList<Movie> = arrayListOf()
val RVAdapter = RVAdapter(movieList)
RVAdapter.notifyDataSetChanged()
viewBinding.rvData.adapter = RVAdapter
viewBinding.rvData.layoutManager = LinearLayoutManager(this)
Handler(mainLooper).postDelayed({
movieList.apply {
add(Movie("아이언맨1", 4.5))
add(Movie("상견니", 4.5))
add(Movie("토르 라그나로크", 4.4))
add(Movie("윤희에게", 4.2))
add(Movie("아이언맨1", 4.5))
add(Movie("상견니", 4.5))
add(Movie("토르 라그나로크", 4.4))
add(Movie("윤희에게", 4.2))
add(Movie("아이언맨1", 4.5))
add(Movie("상견니", 4.5))
add(Movie("토르 라그나로크", 4.4))
add(Movie("윤희에게", 4.2))
}
RVAdapter.notifyDataSetChanged()
}, 1000)
}
}