Android的自定义长按

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xxdddail/article/details/90024773

android的弹出菜单,使用activity来实现,但是长按的时间太短,容易与其他view的触摸逻辑相冲突,代码如下

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_draw);
        //其他代码
        _view.setOnCreateContextMenuListener(this);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        menu.add(1, 10001, 0, "添加");
        menu.add(1, 10002, 1, "删除");
        menu.add(1, 10003, 2, "切换");
        super.onCreateContextMenu(menu, v, menuInfo);
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case 10001: {
                //添加
                break;
            }
            case 10002: {
                //删除
                break;
            }
            case 10003: {
                //切换;
                break;
            }
            default:
                break;
        }
        return super.onContextItemSelected(item);
    }

为此,我们需要对长按进行额外的处理,于是需要对_view的onTouch事件进行相应的处理,为了便于调用,将长按的检测逻辑封装成了相应的类,代码如下

/**
     * 长按工作器
     */
    class  LongTouchWorker{

        /**
         * 长按时两次点差的最大偏移量,超过此偏移量则不是长按
         */
        private int _longTouchOffset = 50;
        private int _lastMotionX;
        private int _lastMotionY;
        private int _longTouchDelay_ms;
        private LongTouchCallback _longTouchCallback=null;

        public LongTouchWorker(int longTouchDelay_ms,LongTouchCallback longTouchCallback){
            _longTouchDelay_ms =longTouchDelay_ms;
            _longTouchCallback=longTouchCallback;
        }


        private Handler _longTouchHandle=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.d(TAG,"LongTouchHandle");
            }
        };

        private Runnable _longTouchRunnable=new Runnable() {
            @Override
            public void run() {
                if(_longTouchCallback!=null)
                {
                    _longTouchCallback.longTouch(_lastMotionX,_lastMotionY);
                }
            }
        };

        public void onTouch(View v, MotionEvent event) {
            int x = (int) event.getX();
            int y = (int) event.getY();
            int pointerCount = event.getPointerCount();
            if(pointerCount>1){
                //多点触控,直接取消长按
                cancelLongTouch();
            }
            else {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_UP:
                        // 弹起时,移除已有Runnable回调,弹起就算长按结束了(不需要考虑用户是否长按了超过预设的时间)
                        cancelLongTouch();
                        Log.d(TAG, "LongTouch UP");
                        break;
                    case MotionEvent.ACTION_MOVE:
                        if (Math.abs(_lastMotionX - x) > _longTouchOffset
                                || Math.abs(_lastMotionY - y) > _longTouchOffset) {
                            // 移动误差阈值
                            // xy方向判断
                            // 移动超过阈值,则表示移动了,就不是长按(看需求),移除 已有的Runnable回调
                            cancelLongTouch();
                        }
                        Log.d(TAG, "LongTouch Move");
                        break;
                    case MotionEvent.ACTION_DOWN:
                        // 每次按下重新计时
                        // 按下前,先移除 已有的Runnable回调,防止用户多次单击导致多次回调长按事件的bug
                        cancelLongTouch();
                        _lastMotionX = x;
                        _lastMotionY = y;
                        // 按下时,开始计时
                        _longTouchHandle.postDelayed(_longTouchRunnable, _longTouchDelay_ms);
                        Log.d(TAG, "LongTouch Down");
                        break;
                }
            }
        }

        private void cancelLongTouch(){
            _longTouchHandle.removeCallbacks(_longTouchRunnable);
            if(_longTouchCallback!=null){
                _longTouchCallback.cancelLongTouch();
            }
        }
    }

    /**
     * 长按的回调
     */
    public interface LongTouchCallback{
        /**
         * 取消长按
         */
        void cancelLongTouch();

        /**
         * 长按
         */
        void longTouch(int x,int y);
    }

在LongTouchWorker中通过onTouch的检测来判断是否为长按,判断过程要注意几点

1.弹起(UP)、按下(DOWN)、移动(MOVE)的逻辑,尤其是MOVE时要设置感应的偏移量,因为手指按下时,点的位置是有可能偏移的。

2.Handle和Runnable的配合,remove和postDelay的控制

3.通过接口LongTouchCallback来回调,触发相应的longTouch和cancelLongTouch事件。

那要如何使用LongTouchWorker呢?假如我们的view是自定义的,比如LongTouchView,在LongTouchView的onTouch事件调用LongTouchWorker,示例代码如下

/**
 * 自定义LongTouchView
 */
public class LongTouchView extends View {

 private LongTouchWorker _longTouchWorker=null;

  /**
     * 设置长按工作器
     * @param longTouchDelay_ms 长按的延迟时间(毫秒)
     * @param longTouchCallback 长按的回调
     */
    public void set_longTouchWorker(int longTouchDelay_ms,LongTouchCallback longTouchCallback){
        _longTouchWorker=new LongTouchWorker(longTouchDelay_ms,longTouchCallback);
    }

@Override
    public boolean onTouchEvent(MotionEvent event) {
       if( _longTouchWorker!=null)
        {
            _longTouchWorker.onTouch(this,event);
        }
        return super.onTouchEvent(event);
    }

}

有了LongTouchWorker之后,我们就可以解决前面弹出菜单时,长按时间太短的问题,示例代码如下

  _longTouchView.set_longTouchWorker(_longTouchDelay_ms, new LongTouchCallback() {
            @Override
            public void cancelLongTouch() {

            }

            @Override
            public void longTouch(int x, int y) {
                showPopupMenu(x, y);
            }
        });

对于showPopupMenu弹出菜单的位置控制,可以参看《PopupMenu弹出位置的控制

猜你喜欢

转载自blog.csdn.net/xxdddail/article/details/90024773
今日推荐