이전 글 ViewPager2 를 이용한 자동 스크롤에 이어서, 자동 스크롤 시 애니메이션을 적용해 보았습니다.
ViewPager2 를 사용해서 뷰 스크롤 시, 뷰 이동이 너무 딱딱 끊기는 느낌이 있어서 부드러운 스크롤을 적용하기 위한 방법입니다.
그리고 ViewPager2 의 PageTransformer 를 사용한 애니메이션 맞춤설정을 사용해 보았습니다.
SliderMainActivity 의 일부만 변경하였습니다.
나머지는 이전 글과 동일한 소스입니다!
https://eunoia3jy.tistory.com/182
🚨 코틀린 확장함수를 사용한 부드러운 스크롤 적용 (ValueAnimator)
1. SliderMainActivity.kt
viewpager2.setCurrentItem(item: Int, smoothScroll: Boolean) 의 파라미터 중
smoothScroll 여부 값이 있으나 해당 값을 true 로 설정해도 그닥 부드럽지 않습니다 ㅠ_ㅠ
그래서 setCurrentItemWithDuration 이라는 코틀린 확장함수를 만들어서 사용하였습니다.
ViewPager2 는 상속이 안되기 때문에 클래스로 만들지 않고 코틀린 확장함수를 사용합니다.
ValueAnimator 객체 중 interpolator 객체는 뷰를 이동하는 애니메이션 속도의 효과를 표현할 수 있습니다.
animator.interpolator = LinearInterpolator() // 일정한 속도로 이동
animator.interpolator = AccelerateDecelerateInterpolator() // 점차 빠른 속도로 이동
animator.interpolator = DecelerateInterpolator() // 점차 느린 속도로 이동
Runnable 내에 viewpager2.setCurrentItem() 대신 viewpager2.setCurrentItemWithDuration() 을 사용해줍니다.
package com.eun.myappkotlin02.function
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.animation.ValueAnimator.AnimatorUpdateListener
import android.os.Bundle
import android.os.Handler
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.viewpager2.widget.ViewPager2
import com.eun.myappkotlin02.R
import com.eun.myappkotlin02.databinding.ActivitySliderMainBinding
class SliderMainActivity : AppCompatActivity() {
lateinit var binding: ActivitySliderMainBinding
private lateinit var mSliderPagerAdapter: SliderPagerAdapter
private lateinit var mSliderRecyclerAdapter: SliderRecyclerAdapter
private var sliderList: MutableList<SliderEntity> = mutableListOf()
/* viewPagerHorizontal 에 사용돠는 handler, runnable */
private val hHandler = Handler()
private val hRunnable =
Runnable {
// binding.viewPagerHorizontal.currentItem = binding.viewPagerHorizontal.currentItem + 1
var position = binding.viewPagerHorizontal.currentItem + 1
binding.viewPagerHorizontal.setCurrentWithDuration(position++, 1000)
}
/* viewPagerVertical 에 사용돠는 handler, runnable */
private val vHandler = Handler()
private val vRunnable =
Runnable {
// binding.viewPagerVertical.currentItem = binding.viewPagerVertical.currentItem + 1
var position = binding.viewPagerVertical.currentItem + 1
binding.viewPagerVertical.setCurrentWithDuration(position++, 2000)
}
.....
/**
* ViewPager2.setCurrentWithDuration()
* viewPager2 의 부드러운 스크롤 구현
* @param viewPager2
* @param item
* @param duration
*/
private fun ViewPager2.setCurrentWithDuration(item: Int, duration: Long) {
var pxTopDrag : Int = 0
if(orientation == ViewPager2.ORIENTATION_HORIZONTAL) { // 가로 방향인 경우
pxTopDrag = width * (item - currentItem)
} else { // 세로 방향인 경우
pxTopDrag = height * (item - currentItem)
}
val animator = ValueAnimator.ofInt(0, pxTopDrag)
animator.addUpdateListener(object : AnimatorUpdateListener {
var previousValue = 0
override fun onAnimationUpdate(valueAnimator: ValueAnimator) {
val currentValue = valueAnimator.animatedValue as Int
val currentPxToDrag = (currentValue - previousValue).toFloat()
fakeDragBy(-currentPxToDrag)
previousValue = currentValue
}
})
animator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {
super.onAnimationStart(animation)
beginFakeDrag()
}
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
endFakeDrag()
}
override fun onAnimationCancel(animation: Animator) {
super.onAnimationCancel(animation)
}
override fun onAnimationRepeat(animation: Animator) {
super.onAnimationRepeat(animation)
}
})
animator.interpolator = AccelerateDecelerateInterpolator()
animator.duration = duration
animator.start()
}
}
결과 화면
처음보단 조금 꿀렁꿀렁 느릿느릿 뷰가 이동하게 됩니다!
🚨 PageTransformer 를 사용한 애니메이션 맞춤설정
https://developer.android.com/training/animation/screen-slide-2?hl=ko
1. SliderMainActivity.kt
구글 공식 문서에 나와있는 페이지 축소 변환기 ZoomOutPageTransformer 클래스와
심도 페이지 변환기 DepthPageTransformer 클래스를 작성하여 setPageTransformer() 를 사용해 적용하였습니다.
// 페이지 축소 변환기 ZoomOutPageTransformer 클래스의 애니메이션 적용
binding.viewPagerHorizontal.setPageTransformer(ZoomOutPageTransformer())
// 심도 페이지 변환기 DepthPageTransformer 클래스의 애니메이션 적용
binding.viewPagerVertical.setPageTransformer(DepthPageTransformer())
(위에 구현한 부드러운 스크롤도 같이 적용하였습니다.)
package com.eun.myappkotlin02.function
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.animation.ValueAnimator.AnimatorUpdateListener
import android.os.Bundle
import android.os.Handler
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.viewpager2.widget.ViewPager2
import com.eun.myappkotlin02.R
import com.eun.myappkotlin02.databinding.ActivitySliderMainBinding
class SliderMainActivity : AppCompatActivity() {
lateinit var binding: ActivitySliderMainBinding
private lateinit var mSliderPagerAdapter: SliderPagerAdapter
private lateinit var mSliderRecyclerAdapter: SliderRecyclerAdapter
private var sliderList: MutableList<SliderEntity> = mutableListOf()
/* viewPagerHorizontal 에 사용돠는 handler, runnable */
private val hHandler = Handler()
private val hRunnable =
Runnable {
// binding.viewPagerHorizontal.currentItem = binding.viewPagerHorizontal.currentItem + 1
var position = binding.viewPagerHorizontal.currentItem + 1
binding.viewPagerHorizontal.setCurrentWithDuration(position++, 1000)
}
/* viewPagerVertical 에 사용돠는 handler, runnable */
private val vHandler = Handler()
private val vRunnable =
Runnable {
// binding.viewPagerVertical.currentItem = binding.viewPagerVertical.currentItem + 1
var position = binding.viewPagerVertical.currentItem + 1
binding.viewPagerVertical.setCurrentWithDuration(position++, 2000)
}
.....
/**
* initViewForHorizontal()
* RecyclerView.Adapter 를 사용한 ViewPager2 구현
*/
private fun initViewForHorizontal() {
if(sliderList.size > 0) {
binding.viewPagerHorizontal.isVisible = true
binding.tvEmptyHorizontal.isGone = true
mSliderRecyclerAdapter = SliderRecyclerAdapter()
binding.viewPagerHorizontal.adapter = mSliderRecyclerAdapter
binding.viewPagerHorizontal.orientation = ViewPager2.ORIENTATION_HORIZONTAL
mSliderRecyclerAdapter.setSliderList(this, binding.viewPagerHorizontal, sliderList)
binding.viewPagerHorizontal.setPageTransformer(ZoomOutPageTransformer())
binding.viewPagerHorizontal.registerOnPageChangeCallback(object :
ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
hHandler.removeCallbacks(hRunnable)
hHandler.postDelayed(hRunnable, 2000) // Slide duration 2 seconds
}
})
} else { // sliderList 가 없는 경우 viewPager2 hidden 처리
binding.viewPagerHorizontal.isGone = true
binding.tvEmptyHorizontal.isVisible = true
}
}
/**
* initViewForVertical()
* FragmentStateAdapter 를 사용한 ViewPager2 구현
*/
private fun initViewForVertical() {
if(sliderList.size > 0) {
binding.viewPagerVertical.isVisible = true
binding.tvEmptyVertical.isGone = true
mSliderPagerAdapter = SliderPagerAdapter(this)
/*for(i in 0 until sliderList.size) {
mSliderPagerAdapter.addFragment(SliderPagerFragment().newInstance1(i, sliderList))
}*/
binding.viewPagerVertical.adapter = mSliderPagerAdapter
binding.viewPagerVertical.orientation = ViewPager2.ORIENTATION_VERTICAL
mSliderPagerAdapter.setSliderList(sliderList)
binding.viewPagerVertical.setPageTransformer(DepthPageTransformer())
binding.viewPagerVertical.registerOnPageChangeCallback(object :
ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
vHandler.removeCallbacks(vRunnable)
vHandler.postDelayed(vRunnable, 2000) // Slide duration 2 seconds
}
})
} else { // sliderList 가 없는 경우 viewPager2 hidden 처리
binding.viewPagerVertical.isGone = true
binding.tvEmptyVertical.isVisible = true
}
}
.....
}
2. ZoomOutPageTransformer.kt
페이지 축소 변환기 ZoomOutPageTransformer 클래스 입니다.
package com.eun.myappkotlin02.function
import android.view.View
import androidx.viewpager2.widget.ViewPager2
class ZoomOutPageTransformer : ViewPager2.PageTransformer {
companion object {
const val TAG = "ZoomOutPageTransformer"
private const val MIN_SCALE = 0.85f
private const val MIN_ALPHA = 0.5f
}
override fun transformPage(page: View, position: Float) {
page.apply {
val pageWidth = width
val pageHeight = height
when {
position < -1 -> { // [-Infinity,-1)
// This page is way off-screen to the left.
alpha = 0f
}
position <= 1 -> { // [-1,1]
// Modify the default slide transition to shrink the page as well
val scaleFactor = Math.max(Companion.MIN_SCALE, 1 - Math.abs(position))
val vertMargin = pageHeight * (1 - scaleFactor) / 2
val horzMargin = pageWidth * (1 - scaleFactor) / 2
translationX = if (position < 0) {
horzMargin - vertMargin / 2
} else {
horzMargin + vertMargin / 2
}
// Scale the page down (between MIN_SCALE and 1)
scaleX = scaleFactor
scaleY = scaleFactor
// Fade the page relative to its size.
alpha = (Companion.MIN_ALPHA +
(((scaleFactor - Companion.MIN_SCALE) / (1 - Companion.MIN_SCALE)) * (1 - Companion.MIN_ALPHA)))
}
else -> { // (1,+Infinity]
// This page is way off-screen to the right.
alpha = 0f
}
}
}
}
}
3. DepthPageTransformer.kt
심도 페이지 변환기 DepthPageTransformer 클래스 입니다.
package com.eun.myappkotlin02.function
import android.view.View
import androidx.viewpager2.widget.ViewPager2
class DepthPageTransformer : ViewPager2.PageTransformer {
companion object {
const val TAG = "DepthPageTransformer"
private const val MIN_SCALE = 0.75f
}
override fun transformPage(page: View, position: Float) {
page.apply {
val pageWidth = width
when {
position < -1 -> { // [-Infinity,-1)
// This page is way off-screen to the left.
alpha = 0f
}
position <= 0 -> { // [-1,0]
// Use the default slide transition when moving to the left page
alpha = 1f
translationX = 0f
translationZ = 0f
scaleX = 1f
scaleY = 1f
}
position <= 1 -> { // (0,1]
// Fade the page out.
alpha = 1 - position
// Counteract the default slide transition
translationX = pageWidth * -position
// Move it behind the left page
translationZ = -1f
// Scale the page down (between MIN_SCALE and 1)
val scaleFactor = (MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)))
scaleX = scaleFactor
scaleY = scaleFactor
}
else -> { // (1,+Infinity]
// This page is way off-screen to the right.
alpha = 0f
}
}
}
}
}
결과 화면
뷰 이동 시 애니메이션 효과가 나타나는 것을 확인할 수 있습니다.
감사합니다 ^__^
'📱 안드로이드 Android ~ Kotlin' 카테고리의 다른 글
[안드로이드/Android] Jetpack Compose (0) | 2023.06.19 |
---|---|
[Android/Kotlin] Google Maps API 사용해서 지도 표시하기 (2) | 2023.01.10 |
[Android/Kotlin] ViewPager2 를 이용한 무한 스크롤(Infinite Scroll)/자동 스크롤(Auto Scroll) (0) | 2022.10.31 |
[Android/kotlin] 구글 Firebase Remote Config 사용하기 (0) | 2022.09.07 |
[Android/kotlin] 구글 Firebase In-App Messaging 사용하기 (0) | 2022.07.28 |