νλ/μΆμλ₯Ό μ μ΄νκΈ° μν΄ λ λ² ννλ λ° λ°μνλ κ²μ΄ μΌλ°μ μ
λλ€. μ΄κ²μ ZoomEngine
μ μ΅νΈμΈ κΈ°λ₯μ΄μ΄μΌ νλ©° κΈ°λ³Έμ μΌλ‘ ZoomImageView
μμ νμ±νλ©λλ€.
@natario1
μ§κΈμ μ¬μ©μ μ§μ Gesture Listener ν΄λμ€λ₯Ό μ¬μ©νμ¬ μ΄ κΈ°λ₯μ μ§μ ꡬννμ΅λλ€. λκ΅°κ°κ° νμνλ©΄ μ¬κΈ° λ΄ μ½λ μ‘°κ°μ΄ μμ΅λλ€.
private GestureDetector gestureDetector;
private View.OnTouchListener touchListener;
private ZoomImageView selectedImage;
κ·Έλ° λ€μ λ€μ λ³μλ₯Ό μ΄κΈ°νν©λλ€.
gestureDetector = new GestureDetector(mContext, new MyGestureListener());
touchListener = new View.OnTouchListener() {
<strong i="11">@Override</strong>
public boolean onTouch(View v, MotionEvent event) {
// pass the events to the gesture detector
// a return value of true means the detector is handling it
// a return value of false means the detector didn't
// recognize the event
selectedImage = (ZoomImageView) v;
return gestureDetector.onTouchEvent(event);
}
};
κ·Έλ° λ€μ ν°μΉ 리μ€λλ₯Ό zoomimageview κ°μ²΄μ ν λΉνμμμ€.
ZoomImageView image = layout.findViewById(R.id.imageViewItemImageSlider);
image.setOnTouchListener(touchListener);
λ€μμ MyGestureListener ν΄λμ€μ λλ€.
class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
// <strong i="18">@Override</strong>
// public boolean onDown(MotionEvent event) {
// Log.d("TAG","onDown: ");
//
// // don't return false here or else none of the other
// // gestures will work
// return true;
// }
//
// <strong i="19">@Override</strong>
// public boolean onSingleTapConfirmed(MotionEvent e) {
// Log.i("TAG", "onSingleTapConfirmed: ");
// return true;
// }
//
// <strong i="20">@Override</strong>
// public void onLongPress(MotionEvent e) {
// Log.i("TAG", "onLongPress: ");
// }
<strong i="21">@Override</strong>
public boolean onDoubleTap(MotionEvent e) {
Log.i("TAG", "onDoubleTap: ");
if((selectedImage.getEngine().getZoom() >= 2.75F)) {
selectedImage.getEngine().zoomTo(1F, true);
} else if((selectedImage.getEngine().getZoom() < 1F)) {
selectedImage.getEngine().zoomTo(1F, true);
} else {
selectedImage.getEngine().zoomBy(2F, true);
}
return true;
}
// <strong i="22">@Override</strong>
// public boolean onScroll(MotionEvent e1, MotionEvent e2,
// float distanceX, float distanceY) {
// Log.i("TAG", "onScroll: ");
// return true;
// }
// <strong i="23">@Override</strong>
// public boolean onFling(MotionEvent event1, MotionEvent event2,
// float velocityX, float velocityY) {
// Log.d("TAG", "onFling: ");
// return true;
// }
}
ZoomImageViewμ ν°μΉ μ΄λ²€νΈμ μΆ©λνμ¬ λ¬Έμ λ₯Ό μμ±νλ―λ‘ μ²λ¦¬ν κ²μ΄ μλ onScroll(MUST) λ° κΈ°ν λ°©λ²(λ΄ κ²½μ°μλ μ ν μ¬ν)μ μ¬μ μν λ μννμμμ€.
μ΄κ²μ΄ λμμ΄ λκΈ°λ₯Ό λ°λλλ€.
μ λ νμ¬ μ΄λ²€νΈκ° μλ£λ νλ©΄ μ€μμ λ λ² ννλ κ²μ λ°μνκ³ μμ΅λλ€.
λ€μμ μΆκ°νκΈ°λ§ νλ©΄ λ©λλ€.
private val simpleGestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
//here is the method for double tap
override fun onDoubleTap(e: MotionEvent): Boolean {
Log.d("OnDoubleTapListener", "onDoubleTap")
centerElement(e.x.absoluteValue,e.y.absoluteValue)
return true
}
})
fun centerElement(clickedX: Float, clickedY: Float) {
val offsetX = (width.absoluteValue) / 2
val offsetY = (height.absoluteValue) / 2
val displacedX = engine.panX.absoluteValue
val displacedY = engine.panY.absoluteValue
val x = (displacedX + clickedX / engine.realZoom) - (offsetX / engine.realZoom)
val y = (displacedY + clickedY / engine.realZoom) - (offsetY / engine.realZoom)
val desiredX = if (x > 0) -x else 0f
val desiredY = if (y > 0) -y else 0f
engine.moveTo(engine.zoom, desiredX, desiredY, true)
}
μ΄μ μ¬λ°λ₯΄κ² μ΄λνκ³ νλ/μΆμνλ €κ³ ν©λλ€. μλνκ² νλ €λ©΄ ν 리νμ€νΈλ₯Ό μ΄ μ μμ΅λλ€.
@AlvaroFalcon λ©μ§λ€μ ! λͺ κ°μ§ νμ λ릴 μ μμ΅λλ€.
ZoomEngine
μ μ΄λ―Έ μ€ν¬λ‘€ λ° νλ§μ λ°μνλ μ μ€μ² κ°μ§κΈ°κ° μμ΅λλ€.onScale()
λ΄λΆμ λ
Όλ¦¬λ₯Ό 99% μ¬μ¬μ©ν μ μλ€κ³ μκ°ν©λλ€. μ€μΌμΌ μ μ€μ²μ μ€μ¬ μ’νμ ν¨κ» μλνκ³ νλ/μΆμ μμλ₯Ό μ μ©ν©λλ€. νλ/μΆμ μμλ₯Ό μ§μ ν λΉν΄μΌ νμ§λ§( zoomIn()
λ 1.3μ μ¬μ©νλ€κ³ μκ°ν©λλ€) κ±°μ λμΌν μμ
μ
λλ€.doubleTapBehavior
?)μ ν΅ν΄ ꡬμ±ν μ μμ΄μΌ ν©λλ€. μ΄λ―Έ libλ₯Ό μ¬μ©νκ³ μλ μ¬λμ μν΄ μ΄κ²μ λ³κ²½νμ§ μλλ‘ κΈ°λ³Έκ°μΌλ‘ noneμ μ¬μ©ν©μλ€.@natario1 λ©μ§λ€! μκ°λλ©΄ ν΄λ΄μΌμ§...
μ λ νμ¬ νλ/μΆμ λ μ΄μμμ νμ₯νλ ν΄λμ€λ‘ νλ‘μ νΈμμ μμ
μ€μ΄λ―λ‘ κ±°κΈ°μ λ
Όλ¦¬λ₯Ό μΆκ°ν©λλ€.
λλ κ·Έκ²μ μ¬μ©νμ¬ λμμ μμ§μκ³Ό ν¨κ» νλ/μΆμλ₯Ό μλνκ³ μμ§λ§ νμ¬λ‘μλ μ΄μ΄ μμ΅λλ€.
ν κ°μ¬ν©λλ€!
moveTo() λ©μλμ μμ μ μΆκ°ν μ μμ΅λκΉ? λλλ©΄ νΈμΆνλ λ°©μμ΄ λ μ μ°ν΄μ μ¬λ¬ μΌμ ν μ μμ κ² κ°μμ.
μ, moveTo, zoomBy, zoomTo λ±...
λ€μκ³Ό κ°μ μ μμ΅λλ€.
moveTo(zoom, x, y, action : ()->Unit={})
@AlvaroFalcon Kotlinμμλ κ·Έλ κ² ν κ²μ΄μ§λ§ μ΄κ²μ μ¬μ ν ββμμν Javaμ
λλ€. νμ§λ§ μμΌλ‘ KotlinμΌλ‘ μ΄λν κ²μ
λλ€. νμ¬λ‘μλ λꡬλ μ‘μΈμ€ν μ μλ onIdle()
μ΄ μμ΅λλ€.
@AlvaroFalcon μ€κ³Ό ν¬μ λμμ λ¬μ±ν μ μμμ΅λκΉ? μ½λλ₯Ό 곡μ νμκ² μ΅λκΉ?
λλΈ νκ³Ό νμΉ ν¬ μ€μ λͺ¨λ ꡬννμ΅λλ€. μλ μ루μ μ ν΅μ¬μ ν/λλΈ ν μ μ€μ² κ°μ§κΈ°λ₯Ό νλ/μΆμ λ μ΄μμ λ΄λΆμ μ½ν μΈ λ³΄κΈ°μ μ°κ²°νκ³ ν΄λ¦ κ°λ₯ν μμμ΄ trueλ‘ μ€μ λλλ‘ νλ/μΆμ λ μ΄μμμ ꡬμ±νλ κ²μ λλ€. κ·Έλ° λ€μ μ½ν μΈ λ³΄κΈ°μ onTouchEventμμ trueλ₯Ό λ°ννμ¬ ν°μΉ μ΄λ²€νΈλ₯Ό μ¬μ©νκ³ μ μ€μ² κ°μ§κΈ°λ‘ μ λ¬ν©λλ€. μ΄λ κ² νλ©΄ νμΉ, νλ/μΆμ λ° λ λ² νμ΄ λμμ μλν©λλ€. λ λ² ννμ¬ νλνλ κ²μ λ¬Έμ κ° λμ§ μμ΅λλ€.
κ·Έλ¬λ λ λ² νν μμμΌλ‘ νλ/μΆμλ₯Ό μλνλλ° ZoomEngine.moveTo
κ° κ΅¬νλ λ°©μμ κ°μν λ νλ/μΆμ λ° ν¬μ λμ 보κ°μ΄ μ¬λ°λ₯΄κ² μλνμ§ μκ³ μ΄λ―Έμ§κ° λ€λ₯Έ κ²½λ‘λ₯Ό λ°λΌ μ΄λνμ¬ λλ¬ν©λλ€. λμ μ€ λ° ν¬. μκ°μ μΌλ‘ 보기μ μ’μ§λ μμ§λ§ λ¬΄μ¨ μΌμ΄ μΌμ΄λκ³ μλμ§ μ μ μμ΅λλ€. λ³΄κ° μκ³ λ¦¬μ¦μ μ λλ©μ΄μ
μ μΌλΆκ° μλλΌ μ€μ νλ/μΆμκ° λ¬΄μμΈμ§μ λ°λΌ μ€μ ν¬μ λ€λ₯΄κ² κ³μ°ν΄μΌ νλ€κ³ μκ°ν©λλ€.
λ λ² νν μμ νλ
λ€, κ·Έκ² λ°λ‘ μ κ° νλ €κ³ νλ κ²μ λλ€. @natario1μ μννλ λ°©λ²μ λν μ μ μ¬νμ΄ μμ΅λκΉ?
μλ νμΈμ @mman
λν μ¬μ©μκ° νλ©΄μ νν λ μλμΌλ‘ νλ/μΆμλλ λ°©μκ³Ό μ’ λ£ μμΉλ₯Ό ν₯ν΄ μμ©λμ΄μΉλ ννλ‘ νλλλ μ λλ©μ΄μ μ ꡬννμ΅λλ€.
μ°λ¦¬κ° μ΄ μ λλ©μ΄μ μ μ μ΄ν μ μλ€λ©΄ λ§€μ° μ’μ κ²μ λλ€. λΉμ μ΄λ λ€λ₯Έ μ¬λλ€μ΄ κ·Έκ²μ λν΄ μ΄λ»κ² ν κ²μΈμ§ μ μν μ μμ΅λκΉ?
미리 κ°μ¬λ립λλ€ :)
λλ λλ¬μ΄ ν΄νΉμΌλ‘ κ·Έκ²μ ν μ μμλ€. νμΉκ° μΌμ΄λκ³ μλ€κ³ μκ°νλλ‘ μ€ μμ§μ μμ΄κ³ μμ΅λλ€.
class ReflectionHelper {
<strong i="6">@Nullable</strong>
static ScaleGestureDetector.OnScaleGestureListener getScaleDetectorListenerUsingReflection(ZoomEngine zoomEngine) {
try {
Field mScaleDetector = zoomEngine.getClass().getDeclaredField("mScaleDetector");
mScaleDetector.setAccessible(true);
ScaleGestureDetector detector = (ScaleGestureDetector) mScaleDetector.get(zoomEngine);
Field mListener = detector.getClass().getDeclaredField("mListener");
mListener.setAccessible(true);
return (ScaleGestureDetector.OnScaleGestureListener) mListener.get(detector);
} catch (Exception e) {
return null;
}
}
}
private fun simulatePinch(fromScale: Float, toScale: Float, focusX: Float, focusY: Float) {
class MockDetector(var mockScaleFactor: Float, var mockFocusX: Float, var mockFocusY: Float) : ScaleGestureDetector(context, null) {
override fun getScaleFactor() = mockScaleFactor
override fun getFocusX() = mockFocusX
override fun getFocusY() = mockFocusY
}
val mockDetector = MockDetector(fromScale, focusX, focusY)
ValueAnimator.ofFloat(fromScale, toScale).apply {
addUpdateListener { animation ->
mockDetector.mockScaleFactor = animation.animatedValue as Float
zoomEngineScaleListener?.onScale(mockDetector)
}
doOnEnd { zoomEngineScaleListener?.onScaleEnd(mockDetector) }
start()
}
}
μλ ,
λ Έλ ₯ν΄ μ£Όμ μ κ°μ¬ν©λλ€! :)
κ·Έκ²μ νμ€ν κ·Έκ²μ λ¬μ±νλ μ½κ°μ ν΄νΉ λ°©λ²μ²λΌ 보μ λλ€. κ·Έλ¬λ λλ μ°λ¦¬κ° μ§κΈ λΉμ₯μ λ§€μ° μμν μ λλ©μ΄μ μΌλ‘ μ΄κΈ°λ‘ κ²°μ ν κ²μ΄λΌκ³ μκ°ν©λλ€.
κ°μ¬ ν΄μ,
λμ½λΌμ΄
μλ ,
λλΈ νμμ νλ/μΆμ + ν¨λμ μννλ λ°©λ²μ λν μμμ΄ μμ΅λκΉ?
κ°μ¬ ν΄μ,
ν¬λ¦¬μ€ν ν
κ°μ₯ μ μ©ν λκΈ
μλ ,
λλΈ νμμ νλ/μΆμ + ν¨λμ μννλ λ°©λ²μ λν μμμ΄ μμ΅λκΉ?
κ°μ¬ ν΄μ,
ν¬λ¦¬μ€ν ν