일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 아비투스
- 브런치작가되기
- 자취필수템
- 커스텀린트
- 프래그먼트
- 한단어의힘
- 한달독서
- 소프시스
- 슬기로운 온라인 게임
- 지지않는다는말
- 어떻게 나답게 살 것인가
- 면접
- 목적 중심 리더십
- 리얼하다
- 테트리스
- 베드트레이
- 목적중심리더십
- T자형인재
- 북한살둘레길
- 한달어스
- 끝말잇기
- 소프시스 밤부 좌식 엑슬 테이블
- 캐치마인드
- 함수형 프로그래밍
- 안드로이드
- 1일1커밋
- 재택근무
- 한달브런치북만들기
- 좌식테이블
- 베드테이블
- Today
- Total
정상에서 IT를 외치다
[Android, StickyHeader] StickyHeader in RecyclerView 본문
안녕하세요. 블랙진입니다.
이번에는 RecyclerView 에서 StickyHeader 를 만드는 법에 대해 포스팅 하겠습니다.
StickyHeader?
리스트 중 하나의 아이템을 상단에 고정 시키는 것을 의미합니다.
아래의 화면과 같이 리스트를 스크롤 할 때 중간에 있던 아이템이 상단에 고정되는 것을 확인하실 수 있습니다.
저의 Github 예제 중 MyMaterialDesign 에 적용해 보았는데요.
제가 사용한 StickyHeader 라이브러리는 아래 링크입니다.
How to Use StickyHeader in RecyclerView
라이브러리 사용법은 위 링크를 통해 보시면 잘 나와있습니다.
아래 게시글은 MyMaterialDesign 을 수정하여 아래 gif 와 같은 효과를 나타내는 방법으로 제 git hub 예제 파일을 다운받아 따라하셔야 합니다.
1. app 단계의 build.gradle dependencies 에 아래 코드를 추가해 줍니다.
아래 라이브러리에 대한 최신 버전은 How to Use StickyHeader in RecyclerView 에서 확인 하실 수 있습니다.
//sticky
implementation 'com.brandongogetap:stickyheaders:0.5.0'
2. StickyLayoutManager 를 상속 하는 MyStickyLayoutManager 를 만들어 줍니다.
class MyStickyLayoutManager constructor(context: Context, headerHandler: StickyHeaderHandler)
: StickyLayoutManager(context, headerHandler) {
override fun scrollToPosition(position: Int) {
super.scrollToPositionWithOffset(position, 0)
}
}
3. MaterialActivity 에서 RecyclerView 의 layoutManager 을 StickyLayoutManager 로 변경해 줍니다.
val stickyLayoutManager = StickyLayoutManager(this@MaterialActivity, adapter)
stickyLayoutManager.elevateHeaders(true) // Default elevation of 5dp
// init recycler view
with(rvActivityMaterial) {
//layoutManager = LinearLayoutManager(this@MaterialActivity)
layoutManager = stickyLayoutManager
adapter = this@MaterialActivity.adapter
}
adapter.setItems(getMovieItems())
stickyLayoutManager 의 elevateHeaders 값을 설정하여 Header 의 그림자 효과를 컨트롤 할 수 있습니다.
4. activity_material.xml 의 recycler view 를 FrameLayout 으로 감싸줍니다.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/rvActivityMaterial"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"/>
</FrameLayout>
</android.support.constraint.ConstraintLayout>
5. MaterialAdapter 에 StickyHeaderHandler 을 상속하면 getAdapterData() 를 오버라이딩 해야 합니다.
class MaterialAdapter: RecyclerView.Adapter<CommonViewHolder>(), StickyHeaderHandler {
...
override fun getAdapterData(): MutableList<*> {
return items
}}
6. 기존에는 MovieHolder 를 하나만 사용했지만 StickyHeader 로 사용할 Holder 를 6-1 ~ 6-3 과정을 따라 추가해줍니다.
6-1 CommonViewHolder 를 만들어 공통으로 사용할 함수와 상속 변수를 설정해 줍니다.
abstract class CommonViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun onBindView(item: Movie)
}
6-2 기존에 사용 했던 MovieHolder 를 Adapter 내부가 아닌 밖에 새로운 Class 로 만들어 줍니다.
class MovieHolder(private val movieView: View)
: CommonViewHolder(movieView) {
private val placeholder = ColorDrawable(Color.GRAY)
override fun onBindView(item: Movie) {
with(movieView) {
Glide.with(context)
.load(item.url)
.placeholder(placeholder)
.into(ivItemMovie)
tvItemMovieGenre.text = item.genre
tvItemMovieTitle.text = item.title
tvItemMovieContent.text = item.content
}
}
companion object {
fun newInstance(parent: ViewGroup): MovieHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_movie, parent, false)
return MovieHolder(itemView)
}
}
}
6-3 StickyHeader 로 사용할 StickyHolder 또한 새로운 Class 로 생성합니다.
class StickyHolder(private val movieView: View)
: CommonViewHolder(movieView) {
override fun onBindView(item: Movie) {
with(movieView) {
}
}
companion object {
fun newInstance(parent: ViewGroup): StickyHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_sticky_header, parent, false)
return StickyHolder(itemView)
}
}
}
item_sticky_header
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="48dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#000000">
<TextView
android:text="StickyHeader"
android:textSize="20dp"
android:textColor="#ffffff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
7. Movie 에 MovieHolder 와 StickyHolder 를 구분한 listType 변수를 추가해 줘야 합니다.
listType 이 1 일때는 MovieHolder 를 2일때는 StickyHolder 를 생성해 줄 겁니다.
open class Movie(val listType: Int,
val url: String?,
val genre: String?,
val title: String?,
val content: String?): Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString())
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(listType)
parcel.writeString(url)
parcel.writeString(genre)
parcel.writeString(title)
parcel.writeString(content)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Movie> {
override fun createFromParcel(parcel: Parcel): Movie {
return Movie(parcel)
}
override fun newArray(size: Int): Array<Movie?> {
return arrayOfNulls(size)
}
}
}
8. MaterialAdapter 의 getItemViewType, onCreateVIewHolder, onBindViewHolder 를 수정하면 MaterialAdapter 의 전체 모습은 아래와 같습니다.
class MaterialAdapter: RecyclerView.Adapter<CommonViewHolder>(), StickyHeaderHandler {
private var items = mutableListOf<Movie>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommonViewHolder {
when(viewType) {
1 -> return MovieHolder.newInstance(parent)
2 -> return StickyHolder.newInstance(parent)
}
return MovieHolder.newInstance(parent)
}
override fun onBindViewHolder(holder: CommonViewHolder, position: Int) {
holder.onBindView(items[position])
}
override fun getItemCount() = items.size
/**
* 1 -> MovieHolder
* 2 -> StickyHolder
*/
override fun getItemViewType(position: Int): Int = items[position].listType
/**
* StickyHeaderHandler
*/
override fun getAdapterData(): MutableList<*> {
return items
}
// 아이템 설정
fun setItems(items: MutableList<Movie>) {
this.items = items
}
}
9. 헤더로 사용할 아이템을 만들어 StickyHeader 를 상속해 줍니다.
위 예제에서는 MovieHeader class 를 아래와 같이 만듭니다.
class MovieHeader(headerListType: Int, headerUrl: String?, headerGenre: String?, headerTitle: String?, headerContent: String?)
: Movie(headerListType, headerUrl, headerGenre, headerTitle, headerContent), StickyHeader
10. MovieHeader 를 SampleData 에 추가해 줍니다.
val items = mutableListOf<Movie>()
val movieHeader = MovieHeader(2, null, null, null, null)
val movie1 = Movie(1, "http://static.hubzum.zumst.com/hubzum/2018/02/06/09/962ec338ca3b4153b037168ec92756ac.jpg","action", "Black Panther", "this movie open in 2018.01")
val movie2 = Movie(1, "https://t1.daumcdn.net/cfile/tistory/0138F14A517F77713A","action", "Iron Man 3", "this movie open in 2013.04")
val movie3 = Movie(1, "https://i.ytimg.com/vi/5-mWvUR7_P0/maxresdefault.jpg","action", "Ant Man", "this movie open in 2015.06")
items.add(movieHeader)
items.add(movie1)
items.add(movieHeader)
items.add(movie2)
items.add(movieHeader)
items.add(movie3)
'안드로이드' 카테고리의 다른 글
[Android] 안드로이드 용어 (0) | 2018.06.15 |
---|---|
[Android Architecture Component] Room 라이브러리 사용하기 (2) | 2018.06.12 |
[Android, jetpack] Android Jetpack (0) | 2018.06.08 |
[Android, TabLayout] 탭 레이아웃의 모든것! (2) | 2018.06.04 |
[Android, setUserVisibleHint] 뷰페이저에서 보인 화면만 데이터 받아오기 (0) | 2018.06.04 |