【问题标题】:How to add long click listener to multitouch gesture detection library如何将长按监听器添加到多点触控手势检测库
【发布时间】:2017-03-26 17:41:58
【问题描述】:

Almeros library 中包含示例应用程序(如下所示以及我的补充)。它允许用户执行多点触控手势以在不变的背景上移动、调整大小、旋转和推动对象:

我想实现一个长按监听器,但这样做很明显(添加 implements View.OnLongClickListener 并覆盖 public boolean onLongClick(View v) 不起作用(该方法永远不会被调用)。

我尝试做的是通过长按将地球变成篮球(两张图片都已包含在 github 上的示例中)。

我的问题:如何在保留库提供的现有手势功能的同时使用长点击事件?

这是the sample code 以及我添加的一些内容(已标记为20160326)。

/**
 * Test activity for testing the different GestureDetectors.
 * 
 * @author Almer Thie (code.almeros.com)
 * Copyright (c) 2013, Almer Thie (code.almeros.com)
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 
 *  in the documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 */
// 20170326 - Commented 1, Added 1
//public class TouchActivity extends Activity implements OnTouchListener {
public class TouchActivity extends AppCompatActivity implements View.OnTouchListener, View.OnLongClickListener {
    private Matrix mMatrix = new Matrix();
    private float mScaleFactor = .4f;
    private float mRotationDegrees = 0.f;
    private float mFocusX = 0.f;
    private float mFocusY = 0.f;  
    private int mAlpha = 255;
    private int mImageHeight, mImageWidth;

    private ScaleGestureDetector mScaleDetector;
    private RotateGestureDetector mRotateDetector;
    private MoveGestureDetector mMoveDetector;
    private ShoveGestureDetector mShoveDetector; 

    @SuppressWarnings("deprecation")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Determine the center of the screen to center 'earth'
        Display display = getWindowManager().getDefaultDisplay();
        mFocusX = display.getWidth()/2f;
        mFocusY = display.getHeight()/2f;

        // Set this class as touchListener to the ImageView
        ImageView view = (ImageView) findViewById(R.id.imageToMove);
        view.setOnTouchListener(this);
        //20160326 - Added 1
        view.setOnLongClickListener(this);

        // Determine dimensions of 'earth' image
        Drawable d      = this.getResources().getDrawable(R.drawable.earth);
        mImageHeight    = d.getIntrinsicHeight();
        mImageWidth     = d.getIntrinsicWidth();

        // View is scaled and translated by matrix, so scale and translate initially
        float scaledImageCenterX = (mImageWidth*mScaleFactor)/2; 
        float scaledImageCenterY = (mImageHeight*mScaleFactor)/2;

        mMatrix.postScale(mScaleFactor, mScaleFactor);
        mMatrix.postTranslate(mFocusX - scaledImageCenterX, mFocusY - scaledImageCenterY);
        view.setImageMatrix(mMatrix);

        // Setup Gesture Detectors
        mScaleDetector  = new ScaleGestureDetector(getApplicationContext(), new ScaleListener());
        mRotateDetector = new RotateGestureDetector(getApplicationContext(), new RotateListener());
        mMoveDetector   = new MoveGestureDetector(getApplicationContext(), new MoveListener());
        mShoveDetector  = new ShoveGestureDetector(getApplicationContext(), new ShoveListener());
    }

    @SuppressWarnings("deprecation")
    public boolean onTouch(View v, MotionEvent event) {
        mScaleDetector.onTouchEvent(event);
        mRotateDetector.onTouchEvent(event);
        mMoveDetector.onTouchEvent(event);
        mShoveDetector.onTouchEvent(event);

        float scaledImageCenterX = (mImageWidth*mScaleFactor)/2;
        float scaledImageCenterY = (mImageHeight*mScaleFactor)/2;

        mMatrix.reset();
        mMatrix.postScale(mScaleFactor, mScaleFactor);
        mMatrix.postRotate(mRotationDegrees,  scaledImageCenterX, scaledImageCenterY);
        mMatrix.postTranslate(mFocusX - scaledImageCenterX, mFocusY - scaledImageCenterY);

        ImageView view = (ImageView) v;
        view.setImageMatrix(mMatrix);
        view.setAlpha(mAlpha);

        return true; // indicate event was handled
    }

    // 20170326 - Added method
    @Override
    public boolean onLongClick(View v) {
        Log.v("20170326", "onLongClick called <<<<<<<<<<<<<<<<<<<<<<<<<<<");
        ImageView view = (ImageView) findViewById(R.id.imageToMove);
        view.setImageDrawable(this.getResources().getDrawable(R.drawable.basketball));
        return true;
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor(); // scale change since previous event

            // Don't let the object get too small or too large.
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f)); 

            return true;
        }
    }

    private class RotateListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
        @Override
        public boolean onRotate(RotateGestureDetector detector) {
            mRotationDegrees -= detector.getRotationDegreesDelta();
            return true;
        }
    }   

    private class MoveListener extends MoveGestureDetector.SimpleOnMoveGestureListener {
        @Override
        public boolean onMove(MoveGestureDetector detector) {
            PointF d = detector.getFocusDelta();
            mFocusX += d.x;
            mFocusY += d.y;     

            // mFocusX = detector.getFocusX();
            // mFocusY = detector.getFocusY();
            return true;
        }
    }       

    private class ShoveListener extends ShoveGestureDetector.SimpleOnShoveGestureListener {
        @Override
        public boolean onShove(ShoveGestureDetector detector) {
            mAlpha += detector.getShovePixelsDelta();
            if (mAlpha > 255)
                mAlpha = 255;
            else if (mAlpha < 0)
                mAlpha = 0;

            return true;
        }
    }   

}

【问题讨论】:

    标签: android multi-touch ontouchlistener onlongclicklistener android-event


    【解决方案1】:

    正如用户 mudit pant 所指出的,同时拥有 TouchClick 侦听器是一个问题。所以方法应该是从原来的sample code重新开始,和其他监听器一样实现一个LongPressListener私有类:

    // 20160327 - Added class
    private class LongPressListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public void onLongPress(MotionEvent e) {
            super.onLongPress(e);
            ImageView mView = (ImageView) findViewById(R.id.imageView);
            mView.setImageDrawable(getApplicationContext().getResources().getDrawable(R.drawable.basketball));
        }
    }
    

    然后,以同样的方式将其他检测器集成到onTouch() 方法中,添加新的侦听器:

    public boolean onTouch(View v, MotionEvent event) {
        mScaleDetector.onTouchEvent(event);
        mRotateDetector.onTouchEvent(event);
        mShoveDetector.onTouchEvent(event);
        mMoveDetector.onTouchEvent(event);
    
        // 20160327 - Added 1
        mLongPressDetector.onTouchEvent(event);
        .
        .
        .
    }
    

    同样,在onCreate() 创建另一个检测器:

    public void onCreate(Bundle savedInstanceState) {
        .
        .
        .
    
        // Setup Gesture Detectors
        .
        .
        .
        // 20170327 - Added 1
        mLongPressDetector = new GestureDetector(getApplicationContext(), new LongPressListener());
        .
        .
        .
    }
    

    长按后,图像变为篮球:

    【讨论】:

      【解决方案2】:

      问题在于您同时拥有 Touch 和 clicklisteners。一旦您触摸设备,就会调用触摸侦听器,并且由于您从那里返回 true,因此此事件不会传播到单击侦听器。因此您的代码没有执行。处理此问题的最佳方法是在触地时启动计时器以模仿长按行为。您应该在触摸/移动事件中取消此计时器。如果您的计时器到期触发长点击事件工作。希望这可以帮助。 :)

      【讨论】:

        【解决方案3】:

        answer here 会触发不需要的长按事件。换句话说,如果用户获取的次数超过了 GestureDetector 的 LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(),则触发长按事件,即使用户可能只是试图移动、旋转或调整大小。快速移动效果很好,当他们试图精确时,长按事件会介入。我的第一个想法是增加超时时间,但它似乎适用于所有视图的系统范围,设置为 500 毫秒,并且不可自定义。

        因此,一个非常简单且不需要或使用GestureDetector替代解决方案是使用Handler.postDelayed()Runnable

        public boolean onTouch(View v, MotionEvent event) {
            mScaleDetector.onTouchEvent(event);
            mRotateDetector.onTouchEvent(event);
            mMoveDetector.onTouchEvent(event);
            mShoveDetector.onTouchEvent(event);
        
            // 20170329 - removed 1, added 'if' block
            //mLongPressDetector.onTouchEvent(event);
            if(event.getAction() == MotionEvent.ACTION_DOWN) {
                // Start a timer on each ACTION_DOWN
                handler.postDelayed(mLongPressed, 1000);
            }
            .
            .
            .
        }
        

        在每个sample code onTouch() 方法中,计时器被删除。在除onMove() 之外的所有情况下,回调都会被无条件移除。但是在 MoveListener 的情况下,即使偏移量为零,它也会被触发。所以我这里只粘贴修改后的 MoveListener,但是你需要把handler.removeCallbacks(mLongPressed); 放到所有的监听器中:

        private class MoveListener extends MoveGestureDetector.SimpleOnMoveGestureListener {
            @Override
            public boolean onMove(MoveGestureDetector detector) {
                PointF d = detector.getFocusDelta();
                mFocusX += d.x;
                mFocusY += d.y;
        
                // 20170329 - Added 'if' block - remove listener if there is non-zero offset
                if ((d.x + d.y) != 0f) {
                    handler.removeCallbacks(mLongPressed);
                    Log.v(TAG, "onMove removed " + d.x + ", " + d.y);
                }
                return true;
            }
        }       
        

        最后,在您的活动中,您实例化Handler 并实现Runnable

        // 20170329 - Added Handler and Runnable
        final Handler handler = new Handler();
        Runnable mLongPressed = new Runnable() {
            public void run() {
                Log.v(TAG, "mLongPressed Runnable triggered.");
                ImageView mView = (ImageView) findViewById(R.id.imageView);
                mView.setImageDrawable(getApplicationContext().getResources().getDrawable(R.drawable.basketball));
            }
        };
        

        这个解决方案来自this MSquare answer

        【讨论】:

          猜你喜欢
          • 2020-03-09
          • 1970-01-01
          • 2018-10-27
          • 1970-01-01
          • 2020-08-17
          • 1970-01-01
          • 2016-01-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多