2020年5月29日金曜日

Scroller を使う

Scroller というのは、スクロール時のアニメーションを実現するための x,y 位置を計算してくれるクラスです。

ScrollerOverScroller が用意されています。 OverScroller は行き過ぎて戻ってくるようなアニメーションができます。

Scroller にはアニメーションを開始するメソッドとして が用意されています。

使い方はこんな感じです。
  • 1. scroller.forceFinished() でアニメーションを止める
  • 2. scroller.fling() または scroller.startScroll() でアニメーションを開始する
  • 3. View.postInvalidateOnAnimation() を呼ぶ。これを呼ぶと View.computeScroll() が呼ばれる
  • 4. View.computeScroll() で scroller.computeScrollOffset() を呼ぶ。戻り値が true の場合アニメーションが終わっていないということ
  • 5. scroller.currX, scroller.currY を使って View の位置などを変える
setFriction() で摩擦を設定できます。デフォルトは ViewConfiguration.getScrollFriction() が設定されています。

  1. class ScrollerSampleView : FrameLayout {  
  2.   
  3.     constructor(context: Context) : super(context)  
  4.     constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)  
  5.     constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(  
  6.         context,  
  7.         attrs,  
  8.         defStyleAttr  
  9.     )  
  10.   
  11.     private val size = (100 * resources.displayMetrics.density).toInt()  
  12.   
  13.     private val targetView: View = View(context).apply {  
  14.         layoutParams = LayoutParams(size, size)  
  15.         setBackgroundColor(Color.RED)  
  16.     }  
  17.     private val textView: TextView = TextView(context).apply {  
  18.         layoutParams = LayoutParams(  
  19.             LayoutParams.WRAP_CONTENT,  
  20.             LayoutParams.WRAP_CONTENT  
  21.         )  
  22.     }  
  23.   
  24.     private val scroller = OverScroller(context)  
  25.   
  26.     init {  
  27.         addView(targetView)  
  28.         addView(textView)  
  29.     }  
  30.   
  31.     fun scroll(dx: Int, dy: Int, duration: Int, friction: Float) {  
  32.         scroller.setFriction(friction)  
  33.   
  34.         // scroll の前に今のアニメーションを止める  
  35.         scroller.forceFinished(true)  
  36.   
  37.         targetView.translationX = 0f  
  38.         targetView.translationY = 0f  
  39.   
  40.         val startX = 0  
  41.         val startY = 0  
  42.   
  43.         // アニメーションを開始  
  44.         scroller.startScroll(  
  45.             startX,   // scroll の開始位置 (X)  
  46.             startY,   // scroll の開始位置 (Y)  
  47.             dx,       // 移動する距離、正の値だとコンテンツが左にスクロールする (X)  
  48.             dy,       // 移動する距離、正の値だとコンテンツが左にスクロールする (Y)  
  49.             duration  // スクロールにかかる時間 [milliseconds]  
  50.         )  
  51.   
  52.         // これにより computeScroll() が呼ばれる  
  53.         postInvalidateOnAnimation()  
  54.     }  
  55.   
  56.     fun fling(velocityX: Int, velocityY: Int, overX: Int, overY: Int, friction: Float) {  
  57.         scroller.setFriction(friction)  
  58.   
  59.         // fling の前に今のアニメーションを止める  
  60.         scroller.forceFinished(true)  
  61.   
  62.         targetView.translationX = 0f  
  63.         targetView.translationY = 0f  
  64.   
  65.         val startX = 0  
  66.         val startY = 0  
  67.   
  68.         val minX = 0  
  69.         val maxX = 800  
  70.   
  71.         val minY = 0  
  72.         val maxY = 800  
  73.   
  74.         // アニメーションを開始  
  75.         scroller.fling(  
  76.             startX,      // scroll の開始位置 (X)  
  77.             startY,      // scroll の開始位置 (Y)  
  78.             velocityX,   // fling の初速 [px/sec] (X)  
  79.             velocityY,   // fling の初速 [px/sec] (Y)  
  80.             minX,        // X の最小値. minX - overX まで移動し、minX 未満のところは overfling 中になる  
  81.             maxX,        // X の最大値. maxX + overX まで移動し、maxX を超えたところは overfling 中になる  
  82.             minY,        // Y の最小値. minY - overY まで移動し、minY 未満のところは overfling 中になる  
  83.             maxY,        // Y の最大値. maxY + overY まで移動し、maxY を超えたところは overfling 中になる  
  84.             overX,       // overfling の範囲 (X). overfling の範囲は両端に適用される  
  85.             overY        // Overfling の範囲 (Y). overfling の範囲は両端に適用される  
  86.         )  
  87.   
  88.         // これにより computeScroll() が呼ばれる  
  89.         postInvalidateOnAnimation()  
  90.     }  
  91.   
  92.     override fun computeScroll() {  
  93.         super.computeScroll()  
  94.   
  95.         // computeScrollOffset() の戻り値が true == まだアニメーション中  
  96.         if (scroller.computeScrollOffset()) {  
  97.             textView.text = """  
  98.                 currVelocity: ${scroller.currVelocity}  
  99.                 currX: ${scroller.currX}  
  100.                 currY: ${scroller.currY}  
  101.                 startX: ${scroller.startX}  
  102.                 startY: ${scroller.startY}  
  103.                 finalX: ${scroller.finalX}  
  104.                 finalY: ${scroller.finalY}  
  105.                 isFinished: ${scroller.isFinished}  
  106.                 isOverScrolled: ${scroller.isOverScrolled}  
  107.             """.trimIndent()  
  108.   
  109.             targetView.translationX = scroller.currX.toFloat()  
  110.             targetView.translationY = scroller.currY.toFloat()  
  111.   
  112.             // アニメーション中なので再度呼ぶ  
  113.             postInvalidateOnAnimation()  
  114.         }  
  115.     }  
  116. }  
速度を 1000 [px/sec], 2000 [px/sec], 3000 [px/sec], 4000 [px/sec]、摩擦を ViewConfiguration.getScrollFriction(), ViewConfiguration.getScrollFriction() / 2、overfling 範囲を 0, 200 で上記の fling() を呼んだ結果が次の動画です。





摩擦を半分にすると同じ速度でも遠くまで移動し、overfling 範囲をつけると行き過ぎて戻ってくるようになります。



0 件のコメント:

コメントを投稿