###前提・実現したいこと
表示した画像をピンチで拡大縮小できるようにcustomViewを作成しています。
OnScaleGestureListenerでピンチ操作を取得しようとしています。
以下のデバッグログのようにonScale()の呼び出しが一度のみですぐonScaleEnd()が呼ばれているためか、
スケーリングがうまくいきません。
kotlin
1D/CustomScaleImageView: onScaleBegin() 2D/CustomScaleImageView: onScale() 3D/CustomScaleImageView: updateMatrix() mCurrentScale = 1.6776869 mCurrentX = 103.1402 mCurrentY = 77.35513 4D/CustomScaleImageView: onScaleEnd()
どのようにしたらonScale()が連続で呼ばれ、スケーリング処理が正常に行えるでしょうか?
###該当のソースコード
以下はImageViewを継承した自作クラスCustomScaleImageViewのイベントリスナ部分とanimator部分です。
kotlin
1override fun onTouchEvent(event: MotionEvent?): Boolean { 2 scaleGestureDetector.onTouchEvent(event) 3 if(!scaleGestureDetector.isInProgress) { 4 gestureDetector.onTouchEvent(event) 5 } 6 7 return true 8} 9 10private inner class GestureListener : GestureDetector.SimpleOnGestureListener() { 11 override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float): Boolean { 12 if(scaling) { 13 return false 14 } 15 // 前の座標 16 val previousCurrentX = currentX 17 val previousCurrentY = currentY 18 19 currentX = getAdjustedPos(currentX - distanceX, currentScale, imageWidth, width, horizontalFitType) 20 currentY = getAdjustedPos(currentY - distanceY, currentScale, imageHeight, height, verticalFitType) 21 22 Log.d(TAG, "onScroll() mCurrentX = $currentX mCurrentY = $currentY") 23 24 if(previousCurrentX != currentX || previousCurrentY != currentY) { 25 // 変更あり 26 updateMatrix() 27 } 28 return true 29 } 30 31 override fun onDoubleTap(e: MotionEvent?): Boolean { 32 Log.d(TAG, "onDoubleTap()") 33 34 var targetScale = 0.0F 35 if(currentScale == fitScale) { 36 // オリジナル画像のサイズ 37 targetScale = 1.0F 38 } else { 39 targetScale = fitScale 40 } 41 if(e != null) { 42 animator.startScaleAnimation(targetScale, e.x, e.y) 43 } 44 45 return true 46 } 47 48} 49 50private inner class ScaleGestureListener : OnScaleGestureListener { 51 52 // 前のフォーカスの座標 53 private var previousFocusX = 0.0F 54 private var previousFocusY = 0.0F 55 56 override fun onScaleBegin(p0: ScaleGestureDetector?): Boolean { 57 Log.d(TAG, "onScaleBegin()") 58 59 scaling = true 60 61 // 最初のフォーカス座標を記憶 62 p0?.let { 63 previousFocusX = p0.focusX 64 previousFocusY = p0.focusY 65 } 66 67 return true 68 } 69 70 override fun onScale(p0: ScaleGestureDetector?): Boolean { 71 Log.d(TAG, "onScale()") 72 73 // 前回のイベントでの2点間の距離との比率 74 var scaleFactor : Float 75 p0?.let { 76 scaleFactor = when { 77 (currentScale >= maxScale) -> 1F + (p0.scaleFactor - 1F) * maxScale / currentScale * SCALE_RESTRICT_FACTOR 78 (currentScale <= minScale) -> 1F + (p0.scaleFactor - 1F) * currentScale / minScale * SCALE_RESTRICT_FACTOR 79 else -> p0.scaleFactor 80 } 81 82 currentScale *= scaleFactor 83 84 val focusX = p0.focusX 85 val focusY = p0.focusY 86 87 currentX = focusX + (currentX - this.previousFocusX) * scaleFactor 88 currentY = focusY + (currentY - this.previousFocusY) * scaleFactor 89 updateMatrix() 90 91 previousFocusX = focusX 92 previousFocusY = focusY 93 } 94 95 return true 96 } 97 98 override fun onScaleEnd(p0: ScaleGestureDetector?) { 99 Log.d(TAG, "onScaleEnd()") 100 101 val targetScale = ensureRange(currentScale, minScale, maxScale) 102 animator.startScaleAnimation(targetScale, previousFocusX, previousFocusY) 103 } 104} 105 106private inner class TransformAnimator { 107 // アニメーション開始位置 108 private var startScale = 0.0F 109 private var startX = 0.0F 110 private var startY = 0.0F 111 112 // アニメーション目標位置 113 private var targetScale = 0.0F 114 private var targetX = 0.0F 115 private var targetY = 0.0F 116 117 // 開始と目標の差 118 private var diffScale = 0.0F 119 private var diffX = 0.0F 120 private var diffY = 0.0F 121 122 // アニメーション実行時間 123 private var duration = ANIMATION_DURATION 124 // アニメーション開始時間 125 private var startTime : Long = 0 126 // アニメーション実行状態 127 private var isAnimating = false 128 129 internal fun startAnimation(targetScale: Float, targetX : Float, targetY : Float) { 130 if(isAnimating) { 131 // すでにアニメーションが実行中の場合はなにもしません 132 return 133 } 134 135 if(targetScale == currentScale && targetX == currentX && targetY == currentY) { 136 // 現在の位置とスケーリング後の位置が同じ場合はなにもしません 137 return 138 } 139 140 // アニメーションの開始位置を設定 141 startScale = currentScale 142 startX = currentX 143 startY = currentY 144 145 // アニメーションの目標位置を設定 146 this.targetScale = targetScale 147 this.targetX = targetX 148 this.targetY = targetY 149 150 // 開始位置と目標位置の差分を取得 151 diffScale = targetScale - startScale 152 diffX = targetX - startX 153 diffY = targetY - startY 154 155 // 現在時刻(ミリ秒単位)を開始時刻に設定 156 startTime = System.currentTimeMillis() 157 158 // アニメーション実行状態 159 isAnimating = true 160 161 // 再描画する(onDrawを呼ぶ) 162 invalidate() 163 164 Log.d(TAG, "TransformAnimator.startAnimation() invalidate()") 165 } 166 167 internal fun startScaleAnimation(targetScale : Float, pivotX : Float, pivotY : Float) { 168 // スケーリング後の位置を計算 169 val diffScaleFactor = targetScale / currentScale - 1.0F 170 var targetX = currentX + (currentX - pivotX) * diffScaleFactor 171 var targetY = currentY + (currentY - pivotY) * diffScaleFactor 172 // 調整後の位置を取得 173 targetX = getAdjustedPos(targetX, targetScale, imageWidth, width, horizontalFitType) 174 targetY = getAdjustedPos(targetY, targetScale, imageHeight, height, verticalFitType) 175 176 // アニメーションを開始する 177 startAnimation(targetScale, targetX, targetY) 178 } 179 180 internal fun animate() { 181 if(!isAnimating) { 182 // アニメーション実行状態でない場合 183 return 184 } 185 186 val diffTime = System.currentTimeMillis() - startTime 187 if(diffTime >= duration) { 188 // アニメーション実行時間を過ぎた場合はアニメーションを終了させる 189 190 isAnimating = false 191 currentScale = targetScale 192 currentX = targetX 193 currentY = targetY 194 } else { 195 // アニメーション実行時間内の場合 196 197 // 進捗度 198 val progress = diffTime.toFloat() / duration 199 // 進捗状況に合わせて移動位置を計算します 200 currentScale = startScale + (diffScale * progress) 201 currentX = startX + (diffX * progress) 202 currentY = startY + (diffY * progress) 203 } 204 205 Log.d(TAG, "TransformAnimator.animate() mCurrentScale = $currentScale mCurrentX = $currentX mCurrentY = $currentY") 206 // 移動位置を反映させる 207 updateMatrix() 208 // 再描画する 209 invalidate() 210 } 211} 212 213private fun updateMatrix() { 214 // 初期イメージのcurrentScale倍にする 215 _matrix.setScale(currentScale, currentScale) 216 // 移動する 217 _matrix.postTranslate(currentX, currentY) 218 // imegeViewにmatrixを設定する 219 imageMatrix = _matrix 220 Log.d(TAG, "updateMatrix() mCurrentScale = $currentScale mCurrentX = $currentX mCurrentY = $currentY") 221}
###補足情報(言語/FW/ツール等のバージョンなど)
android studio 3.0.1
kotlin 1.2.10
あなたの回答
tips
プレビュー