About

<#TEMPLATE_INCLUDE_NINEPAGE_ABOUTME#>
  • Oct

    7

    AcheGesture 在安卓设备中 Hold 手势失效的修正方法

    • 2 Comments
    • Flash Platform
    • 发布:古树
    • 引用:0
    • 浏览:

    AcheGesture 有一个长按手势类 HoldGesture,但它在有些安卓设备中无法使用,这并不是 AcheGesture 的 Bug,也不是 Android 系统的 Bug,造成它无法正常使用的主要原因是因为安卓设备制作商太过杂乱,开发者永远都无法知道一家设备制造商与另一家设备制造商除了分辨率、精度与灵敏度等方面的区别外,还有哪些区别。

    而在 AcheGesture 中造成某些安卓设备无法使用 Hold 手势的主要原因是 Starling 中 Touch 事件的 TouchPhase.MOVED 阶段在不同安卓设备制造商对它的“定义”并不相同,比如在索尼 LT22i 移动定制版手机中,只要按住屏幕,既便没有移动手指,touch.phase 输出的值仍然是 moved 阶段:

    trace(t.phase, t.globalX, t.globalY);
    //output
    moved 80 266
    moved 80 266
    moved 80 266
    moved 80 266
    moved 80 266
    moved 80 266

    而 AcheGesture 中 HoldGesture 通过判断 phase 是否为 TouchPhase.MOVED ,进而是否中止计时器 Timer 对象,所以在这样的设备中,AcheGesture 永远不会发生 Hold/长按屏幕事件。

    虽然造成这种问题的原因与分辨率、精度以及灵敏度等无关,但解决方法基本与《Adobe AIR 移动开发,安卓设备触屏精度不准度的解决方法》一文中相同,通过阔值/容差值进行修正。以下是修正以后的类:

    package acheGesture.gestures
    {
        import flash.events.TimerEvent;
        import flash.geom.Point;
        import flash.utils.Timer;
        import acheGesture.GestureManager;
        import acheGesture.utils.GestureConfigKey;
        import acheGesture.utils.GestureType;
        import starling.events.Touch;
        import starling.events.TouchPhase;

        public class HoldGestureRecognizer extends GestureRecognizerPlugin
        {
            private var _timeThreshold:Number = 1000;
            private var _timer:Timer;
            private var _validate:Boolean;

            private var _touchBeganPoint:Point = new Point(-1,-1);//手指刚开始按下时的点坐标
            private var _touchMovePoint:Point = new Point(-1,-1);//手指开始移动的坐标
            private var _distance:Number = 10;//两个点之间的阔值/容差值

            public function HoldGestureRecognizer(priority:int=0, requireGestureRecognizerToFail:Boolean=false)
            {
                super(GestureType.HOLD, priority, requireGestureRecognizerToFail);
            }

            override public function checkGesture(ts:Vector.<Touch>):Boolean
            {
                var t:Touch = ts[0];

                if(t.phase == TouchPhase.BEGAN)
                {
                    _validate = false;
                    if(_timer == null)
                    {
                        _touchBeganPoint.x = t.globalX;
                        _touchBeganPoint.y = t.globalY;


                        _timer = new Timer(_timeThreshold, 1);
                        _timer.addEventListener(TimerEvent.TIMER, onHoldGestureValidate);
                        _timer.reset();
                        _timer.start();
                    }
                    _failed = false;
                }
                if(t.phase == TouchPhase.MOVED) 
                {
                    _touchMovePoint.x = t.globalX;
                    _touchMovePoint.y = t.globalY;
                    //如果两点之间的距离大于容差值
                    if(Point.distance(_touchBeganPoint,_touchMovePoint) > _distance)
                    {
                        _validate = false;
                        removeTimer();
                        if(!_validate) 
                            _failed = true;
                    }
                }
                if(t.phase == TouchPhase.ENDED) 
                {
                    _validate = false;
                    removeTimer();
                    _touchBeganPoint.x = -1;
                    _touchBeganPoint.y = -1;
                }
                return _validate;
            }

            private function removeTimer():void
            {
                if(_timer)
                {
                    _timer.stop();
                    _timer.removeEventListener(TimerEvent.TIMER, onHoldGestureValidate);
                    _timer = null;
                }
            }

            override public function _onInitGesture(callback:Object, config:Object, g:GestureManager):Boolean
            {
                if(config[GestureConfigKey.TIME_THRESHOLD] != null) 
                    _timeThreshold = Number(config[GestureConfigKey.TIME_THRESHOLD]);
                return super._onInitGesture(callback, config, g);
            }

            private function onHoldGestureValidate(e:TimerEvent):void
            {
                if(_g._firstG.r && !_g.allowSimultaneous) 
                    return;
                removeTimer();
                _validate = true;
                _g.gestureRecognizerStateChange(this._gestureType, true);
            }
        }
    }

    备注:不要试图去为安卓用户开发任何基于精度或灵敏度的 APP(除非 APP 是量身定制量,只在固定的厂商和型号的设备上运行),尤其是应用的用户可能会集中在中低端机型或山寨机型情况下。开发者应珍爱生命,远离安卓开发——把所有的时间和精力放在自己的产品算法和业务逻辑上,不要把时间和精力放在各种安卓厂商生产的不同机型的调试和测试过程中。

    Post Comment.
    quote 1.古树悬叶
    2014/10/11 3:43:31
    AcheGesture 最佳使用环境是在 iOS 系统中,iPad 和 iPhone 都已经经过测试,只要手指按住后没有移动位置,是不会生成 touch 事件的(即不会有 touch.phase 为 moved 过程被输出)。

    而在安卓设备中就有可能产生上面这种“奇葩”机型的结果。
    quote 2.古树悬叶
    2015/2/15 1:54:40
    此贴已经过多个安卓设备测试验证。

    并且不少设备需要容差(也被称为“阔值”)值处理,一些开发者自己也给这类设备取了一些搞笑的名字,将它们称为“霍金森病”、“帕金森病”、“抖动症”、“震颤麻痹”等等。tongue

    发表评论