본문 바로가기
개발/안드로이드

Horizontal RecyclerView 자동으로 아이템 맞추기

by 창이2 2020. 7. 4.

안녕하세요!

어느덧 사회생활한지 6개월이 되었네요 :)

안드로이드 개발을 혼자하느라 항상 바쁘기때문에 시간이 참 빨리 간거같아요 

이번 포스팅에서는 Horizontal RecyclerView에서 가로로 스크롤링을 하면

아이템을 한 가운데에 맞춰놓게 하는 템플릿을 만들어 보겠습니다!

 

 

우선 이미지 적용을 위해 Glide 라이브러리를 사용하겠습니다.

  implementation "com.github.bumptech.glide:glide:4.11.0"

 

이 포스팅에서 쓰이는 클래스 파일은 총 3개 인데요. Item을 정의하는 data class,  recyclerview adapter, main activity

이렇게 3개 입니다. 

 

일단 RecyclerView 를 보여줄 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">


    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/title_text"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_weight="1"
        android:gravity="center"
        android:textColor="#000000"
        android:textSize="23sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/recyclerView"/>

    <TextView
        android:id="@+id/description_text"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_weight="1"
        android:gravity="center"
        android:textColor="#000000"
        android:textSize="18sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/title_text"/>
</androidx.constraintlayout.widget.ConstraintLayout>

다음은 데이터 클래스를 정의하는 부분입니다.

data class MainItemData(val title : String, val description :String, val profileImage:String)

 

이제 메인 액티비티입니다.

메인에서 중요한 부분은 바로 이부분인데요. RecyclerView를 스크롤링 할때 보여지는 아이템을 선택하는 알고리즘입니다.

1. 화면에 아이템이 보여질때 첫번째로 보이는 아이템과 첫번째로 완전히 보여지는 아이템의 위치값을 구합니다.

2. 첫번째로 완전히 보여지는 아이템이 존재하면 해당 아이템 포지션을(만약 완전히 보이지 않으면 -1을 리턴받습니다.) 존재하지 않는다면 첫번째로 보이는 아이템을 선택하도록 하였습니다. 

3. 만약 아이템에 마진값을 설정했다면  해당 마진값도 고려하여 스크롤을 합니다.  

 

binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener(){
    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)
        if (newState == RecyclerView.SCROLL_STATE_IDLE){
            val firstPos = (binding.recyclerView.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition()
            val secondPos = (binding.recyclerView.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
            val selectedPos = max(firstPos,secondPos)
            if(selectedPos!=-1){
                val viewItem = (binding.recyclerView.layoutManager as LinearLayoutManager).findViewByPosition(selectedPos)
                viewItem?.run{
                    val itemMargin = (binding.recyclerView.measuredWidth-viewItem.measuredWidth)/2
                    binding.recyclerView.smoothScrollBy( this.x.toInt()-itemMargin , 0)
                }

                binding.titleText.text = mainAdapter.dataList[selectedPos].title
                binding.descriptionText.text = String.format("%s ",mainAdapter.dataList[selectedPos].description)
            }
       }
    }
})

 

마지막으로 어댑터에서 중요한 부분을 알려드릴게요. 일단 이미지를 위해 Glide를 사용했습니다. 아래 코드는 아이템이 0번째 또는 마지막일때 마진값을 주는 부분입니다. 마진값을 주지 않으면 첫번째 아이템과 마지막 아이템이 보여질때 화면 가운데에 위치하지 않게 되어 설정을 해주었습니다.

아래 마진설정하는 부분은 전체화면 (너비값- 아이템의 너비값)/2 한 값인데 이는 왼쪽 또는 오른쪽으로만 마진값을 주기 위해서 입니다.

        fun bind(data: MainItemData) {

            Glide.with(mContext)
                .load(data.profileImage)
                .centerInside()
                .override(1000,1000)
                .into(binding.itemImage)
            if(adapterPosition==0 || adapterPosition == dataList.size-1){
                binding.itemLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)

                val displayMetrics = mContext.resources.displayMetrics
                val screenWidth = displayMetrics.widthPixels
                var mLayoutParam : RecyclerView.LayoutParams =  binding.itemLayout.layoutParams as RecyclerView.LayoutParams
                if(adapterPosition == 0)
                    mLayoutParam.leftMargin = (screenWidth - binding.itemLayout.measuredWidthAndState)/2
                else
                    mLayoutParam.rightMargin = (screenWidth - binding.itemLayout.measuredWidthAndState)/2
            }
        }

 

 

그리고 이렇게 만들어진 결과물은 다음과 같습니다.

 

 

 

해당 코드는 아래 깃허브의 RecyclerViewAutoFit 폴더에서 코드를 확인하실수 있습니다.

감사합니다 :)

 

github.com/ckdrb7017/BlogProject/tree/master/RecyclerViewAutoFit

 

ckdrb7017/BlogProject

개인 블로그 프로젝트를 올리는 곳입니다. Contribute to ckdrb7017/BlogProject development by creating an account on GitHub.

github.com

 

댓글