Zoomlayout: ν™•λŒ€ν•˜λ €λ©΄ 두 번 νƒ­ν•˜μ„Έμš”.

에 λ§Œλ“  2017λ…„ 09μ›” 29일  Β·  13μ½”λ©˜νŠΈ  Β·  좜처: natario1/ZoomLayout

ν™•λŒ€/μΆ•μ†Œλ₯Ό μ œμ–΄ν•˜κΈ° μœ„ν•΄ 두 번 νƒ­ν•˜λŠ” 데 λ°˜μ‘ν•˜λŠ” 것이 μΌλ°˜μ μž…λ‹ˆλ‹€. 이것은 ZoomEngine 의 옡트인 κΈ°λŠ₯이어야 ν•˜λ©° 기본적으둜 ZoomImageView μ—μ„œ ν™œμ„±ν™”λ©λ‹ˆλ‹€.

enhancement long term

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

μ•ˆλ…•,

더블 νƒ­μ—μ„œ ν™•λŒ€/μΆ•μ†Œ + νŒ¨λ‹μ„ μˆ˜ν–‰ν•˜λŠ” 방법에 λŒ€ν•œ μ†Œμ‹μ΄ μžˆμŠ΅λ‹ˆκΉŒ?

감사 ν•΄μš”,
ν¬λ¦¬μŠ€ν† ν”„

λͺ¨λ“  13 λŒ“κΈ€

@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을 μ‚¬μš©ν•œλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€) 거의 λ™μΌν•œ μž‘μ—…μž…λ‹ˆλ‹€.
  • 이것은 적어도 두 가지 μ˜΅μ…˜("μ—†μŒ" 및 "쀌")이 μžˆμ–΄μ•Ό ν•˜λŠ” XML 속성( 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()
    }
}

μ•ˆλ…•,

λ…Έλ ₯ν•΄ μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€! :)

그것은 ν™•μ‹€νžˆ 그것을 λ‹¬μ„±ν•˜λŠ” μ•½κ°„μ˜ ν•΄ν‚Ή λ°©λ²•μ²˜λŸΌ λ³΄μž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ λ‚˜λŠ” μš°λ¦¬κ°€ μ§€κΈˆ λ‹Ήμž₯은 맀우 μƒμƒν•œ μ• λ‹ˆλ©”μ΄μ…˜μœΌλ‘œ μ‚΄κΈ°λ‘œ κ²°μ •ν•  것이라고 μƒκ°ν•©λ‹ˆλ‹€.

감사 ν•΄μš”,
λ‹ˆμ½œλΌμ΄

μ•ˆλ…•,

더블 νƒ­μ—μ„œ ν™•λŒ€/μΆ•μ†Œ + νŒ¨λ‹μ„ μˆ˜ν–‰ν•˜λŠ” 방법에 λŒ€ν•œ μ†Œμ‹μ΄ μžˆμŠ΅λ‹ˆκΉŒ?

감사 ν•΄μš”,
ν¬λ¦¬μŠ€ν† ν”„

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰