본문 바로가기
SW Programming/Android

[안드로이드] 카드뷰, 무한로딩, View pager transformation

by Crystal.k 2022. 8. 18.

[원하는 것]

카드가 세로로 넘어가는 스타일의 뷰.

다음 카드가 하단에 살짝 보인다.

슬라이드 하게되면 이전 카드 위로 다음 카드가 덮히고 이전 카드는 카드 사이즈가 작아지면서 흐리게 처리된다.

 

 

<?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">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="카드들이 넘어가는 스타일의 ViewPager"
        android:textColor="@color/black"
        android:textSize="20sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/card_viewpager"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginHorizontal="30dp"
        android:layout_marginTop="20dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_title" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

binding.cardViewpager.apply {
            offscreenPageLimit = 1
            adapter = CardAdapter(
                mutableListOf(Store("이거 먹어볼래요?", "위치 정보", "맛잇는 음식점"),
                    Store("아주 아주 인기 많은 핫플레이스1", "위치 위치", "이름모를 음식점"),
                    Store("아주 아주 인기 많은 핫플레이스2", "위치 위치", "이름모를 음식점"),
                    Store("아주 아주 인기 많은 핫플레이스3", "위치 위치", "이름모를 음식점"),
                    Store("아주 아주 인기 많은 핫플레이스4", "위치 위치", "이름모를 음식점")
                    )
                )
            setPageTransformer(SliderTransformer())
            registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
                var prePosition = -1
                override fun onPageScrolled(
                    position: Int,
                    positionOffset: Float,
                    positionOffsetPixels: Int
                ) {
                    super.onPageScrolled(position, positionOffset, positionOffsetPixels)
                    Log.d("MainActivity", "scroll : position ${position}, offset : ${positionOffset}")
                }

                override fun onPageScrollStateChanged(state: Int) {
                    super.onPageScrollStateChanged(state)
                    when (state){
                        ViewPager2.SCROLL_STATE_IDLE -> {
                            if(prePosition < currentItem){
                                Log.d("MainActivity", "scroll :up")
                            }else if(prePosition > currentItem){
                                Log.d("MainActivity", "scroll : down")
                            }
                            prePosition = currentItem
                        }
                    }
                }
            })
        }
class SliderTransformer : ViewPager2.PageTransformer {

    companion object {

        private const val SCALE_MAX = .85f
        private const val SCALE_DEFAULT = 1f
        private const val SCALE_FACTOR = .9f

        private const val ALPHA_FACTOR = .8f
        private const val ALPHA_DEFAULT = 1.0f

        private const val TOP_Y = .0f

        private const val CARD_GAP_DP = 20
    }

    override fun transformPage(page: View, position: Float) {
        val cardView = page.findViewById<CardView>(R.id.card_view)
        val cardViewHeight = cardView.height

        page.apply {

            when {
                position < 0.0f -> {
                    val scale = SCALE_DEFAULT + SCALE_FACTOR * position
                    if (scale < SCALE_MAX) {
                        scaleX = SCALE_MAX
                        scaleY = SCALE_MAX
                    } else {
                        scaleX = scale
                        scaleY = scale
                    }
                    alpha = ALPHA_DEFAULT + position * ALPHA_FACTOR
                    y = TOP_Y
                }
                position < 1.0f && position >= 0.0f -> {
                    scaleX = SCALE_DEFAULT
                    scaleY = SCALE_DEFAULT
                    alpha = ALPHA_DEFAULT
                    y = TOP_Y
                    translationY = position
                }
                else -> {
                    scaleX = SCALE_DEFAULT
                    scaleY = SCALE_DEFAULT
                    alpha = ALPHA_DEFAULT
                    val bottomGap = height - cardViewHeight
                    val cardGap = CARD_GAP_DP.dpToPx(context)

                    translationY = if (bottomGap < cardGap) {
                        position
                    } else {
                        position - bottomGap + cardGap
                    }
                }
            }
            Log.d(
                "transformPage",
                "transform \nposition : ${position},\n translationY : ${translationY}, \n scaleX : ${scaleX}, \n" +
                        " scaleY: ${scaleY}, \n alpha: ${alpha}"
            )
        }
    }
}

참고했던 viewpagertransformation github 코드는 원하는 동작과 상이해서 다시 작성하였다.

ViewPager2.PageTransformer의 transformpage에서 설정해주는 포지션에 대한 스케일, 좌표값을 원하는 대로 설정하는 것이 관건이였다.

 

[전체 코드]

https://github.com/kwonSoojeong/SwipeCardListView

 

 

 

 

 

[참고한 사이트]

반응형

댓글