zoukankan      html  css  js  c++  java
  • 【Android

      在Android的自定义View中,往往需要处理一系列的事件,如触摸事件、双击事件、缩放事件等。本文将这些事件及其处理进行总结。本文将持续更新,将我在自定义View的实践中用到的事件及其处理进行总结。

    1、触摸事件

      触摸事件用于处理用户在触摸屏幕时触发的事件。

      触摸事件通常在 onTouchEvent() 方法中处理,有时也会实现 OnTouchListener 进行处理。

      用户的触摸可以分为三类:手指按下、手指滑动和手指抬起,对应 MotionEvent 类中的事件常量依次为 ACTION_DOWN 、 ACTION_MOVE 和 ACTION_UP 。

      以下是一个处理触摸事件的示例,在这个例子中,我们通过分析用户的触摸来让布局控件滚动。代码如下:

    /**
     * 滚动事件的处理,当布局可以滚动(内容高度大于测量高度)时,对手势操作进行处理
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 只有当布局可以滚动的时候(内容高度大于测量高度的时候),才会对手势操作进行处理
        if (scrollable) {
            int currY = (int) event.getY();
            switch (event.getAction()) {
                // 因为ACTION_DOWN手势可能是为了点击布局中的某个子元素,因此在onInterceptTouchEvent()方法中没有拦截这个手势
                // 因此,在这个事件中不能获取到startY,也因此才将startY的获取移动到第一次滚动的时候进行
                case MotionEvent.ACTION_DOWN:
                    break;
                // 当第一次触发ACTION_MOVE事件时,视为手指按下;以后的ACTION_MOVE事件才视为滚动事件
                case MotionEvent.ACTION_MOVE:
                    // 用pointerDown标志位只是手指是否已经按下
                    if (!pointerDown) {
                        startY = currY;
                        pointerDown = true;
                    } else {
                        offsetY = startY - currY; // 下滑大于0
                        // 布局中的内容跟随手指的滚动而滚动
                        // 用scrolledHeight记录以前的滚动事件中滚动过的高度(因为不一定每一次滚动都是从布局的最顶端开始的)
                        this.scrollTo(0, scrolledHeight + offsetY);
                    }
                    break;
                // 手指抬起时,更新scrolledHeight的值;
                // 如果滚动过界(滚动到高于布局最顶端或低于布局最低端的时候),设置滚动回到布局的边界处
                case MotionEvent.ACTION_UP:
                    scrolledHeight += offsetY;
                    if (scrolledHeight + offsetY < 0) {
                        this.scrollTo(0, 0);
                        scrolledHeight = 0;
                    } else if (scrolledHeight + offsetY + measuredHeight > realHeight) {
                        this.scrollTo(0, realHeight - measuredHeight);
                        scrolledHeight = realHeight - measuredHeight;
                    }
                    // 手指抬起后别忘了重置这个标志位
                    pointerDown = false;
                    break;
            }
        }
        return super.onTouchEvent(event);
    }

    2、加载完成事件

      某些时候,我们需要在这个控件加载完成之后做一些事情,比如,我们需要在一个ImageView加载完成之后拿到它加载的图片并获取其宽高,此时,我们就需要处理控件加载完成的事件。

      加载完成的事件通常需要实现 ViewTreeObserver.OnGlobalLayoutListener 接口并在 onGlobalLayout() 方法中处理,然后在 onAttachedToWindow() 方法中注册这个事件,在 onDetachedFromWindow() 方法中移除这个事件。

      以下是一个处理加载完成的事件的示例,在这个示例中,我们在ImageView加载完成之后获取其加载的图片,然后让图片居中铺满屏幕的宽或高。代码如下:

    /**
     * 当视图被加载到Activity的Window中时,添加监听器
     */
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }
    
    /**
     * 当视图从Activity的Window中移除时,移除监听器
     */
    @SuppressLint("NewApi")
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
    
    /**
     * 视图加载完成后获取图片的大小
     * 在onMeasure()、onLayout()、onDraw()方法或构造方法中都获取不到ImageView中设置的图片的大小
     * 因此需要实现ViewTreeObserver.OnGlobalLayoutListener接口,在onGlobalLayout()方法中实现
     * 注意:这个方法在程序运行期间可能执行多次,我们可以控制它只执行一次(设置一个标识符)
     */
    @Override
    public void onGlobalLayout() {
        if (!once) {
            // 得到控件的宽高
            int width = getWidth();
            int height = getHeight();
            // 得到控件中加载的图片及其宽高
            Drawable drawable = getDrawable();
            if (drawable == null) {
                return;
            }
            int dw = drawable.getIntrinsicWidth();
            int dh = drawable.getIntrinsicHeight();
            // 获取到图片缩放比例的几个阈值
            initScale = Math.min(width * 1.0f / dw, height * 1.0f / dh);
            maxScale = initScale * 2f;
            // 将图片居中在控件中显示
            scaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);
            scaleMatrix.postScale(initScale, initScale, width / 2, height / 2);
            setImageMatrix(scaleMatrix);
            once = true;
        }
    }

    3、缩放事件

      缩放事件通常用于处理用户多点触控的缩放事件。当用户用两个或多个手指在屏幕上进行放大或缩小的动作时,我们就需要处理缩放事件。

      缩放事件通常需要实现 ScaleGestureDetector.OnScaleGestureListener 接口并实现 onScaleBegin() 、 onScale() 和 onScaleEnd() 三个方法。这三个方法中都有一个 ScaleGestureDetector 类型的参数,通过这个参数我们可以得到在这个缩放瞬间用户缩放的倍数,以及缩放的中心点等。

      以下是一个处理缩放事件的示例。在这个示例中,我们对一个ImageView中的图片进行缩放,并在用户松开手指后回缩图片大小。代码如下:

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        float factor = detector.getScaleFactor(); // 缩放的比例(相对于手指按下时的大小的缩放比例)
        if (getDrawable() == null) {
            return true;
        }
        scaleMatrix.postScale(factor, factor, detector.getFocusX(), detector.getFocusY());
        checkBorderAndCenterWhileScale();
        setImageMatrix(scaleMatrix);
        return true;
    }
    
    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }
    
    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        float currScale = getScale();
        float factor = detector.getScaleFactor(); // 缩放的比例(相对于手指按下时的大小的缩放比例)
        if (currScale <= initScale) {
            factor = initScale / currScale;
        } else if (currScale >= maxScale) {
            factor = maxScale / currScale;
        }
        scaleMatrix.postScale(factor, factor, detector.getFocusX(), detector.getFocusY());
        checkBorderAndCenterWhileScale();
        setImageMatrix(scaleMatrix);
    }

    4、双击事件

      当用户在很短的一段时间内在控件上点击了两下,我们就需要对用户的双击事件进行处理了。

      双击事件和其他的一些列事件(如:长按事件、滚动事件、单机事件、惯性滑动事件等)都可以通过实现 GestureDetector.SimpleOnGestureListener 类并重写其中的相应方法来实现,例如,处理双击事件需要重写 onDoubleTap() 方法。

      下面是一个处理用户双击事件的示例。在这个示例中,当用户双击图片后,将图片放大到最大大小或缩小到初始大小。代码如下:

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        final float x = e.getX();
        final float y = e.getY();
        final float finalScale;
        float scale = getScale();
        if (scale < maxScale) {
            finalScale = maxScale / scale;
        } else {
            finalScale = initScale / scale;
        }
        ZoomImageView.this.post(new Runnable() {
            @Override
            public void run() {
                if (finalScale > 1) {
                    if (getScale() * LARGER_SCALE < maxScale) {
                        scaleMatrix.postScale(LARGER_SCALE, LARGER_SCALE, x, y);
                        checkBorderAndCenterWhileScale();
                        setImageMatrix(scaleMatrix);
                        postDelayed(this, 15);
                    } else {
                        scaleMatrix.postScale(maxScale / getScale(), maxScale / getScale(), x, y);
                        checkBorderAndCenterWhileScale();
                        setImageMatrix(scaleMatrix);
                    }
                } else {
                    if (getScale() * SMALLER_SCALE > initScale) {
                        scaleMatrix.postScale(SMALLER_SCALE, SMALLER_SCALE, x, y);
                        checkBorderAndCenterWhileScale();
                        setImageMatrix(scaleMatrix);
                        postDelayed(this, 15);
                    }
                }
            }
        });
        return super.onDoubleTap(e);
    }
  • 相关阅读:
    The Lobo Project: Home of Lobo (Java Web Browser) and Cobra (HTML Rendering Engine)
    基于DOM树的网页相似度研究与应用《大连理工大学》2011年硕士论文
    学习用 c/c++写crawler
    终于用上gcc4.1编译的系统了
    Android 查找SDCard 下面的文件 函数
    对HTML5 Device API相关规范的解惑
    Windows Phone开发(14):数据模板
    Windows Phone开发(13):如何规范用户的输入行为
    InputScope的62个值
    转:Windows Phone 7 设计简介
  • 原文地址:https://www.cnblogs.com/itgungnir/p/6844059.html
Copyright © 2011-2022 走看看