Android 仿图库,QQ等图片查看手势操作

本文详细介绍了如何在Android中实现类似图库和QQ图片查看的手势操作,包括图片的移动、双击放大缩小、双指拖动等。通过分析move(event)、isdbclick(event)和gesture(event)三个核心方法,讲解了手势操作的实现原理和关键代码。
摘要由CSDN通过智能技术生成

引言
阅览图片是生活中必不可少的事,而这个过程包括了许多的操作比如拖动图片,双击放大缩小,双指拖动固定区域放大等。笔者直接贴出自己写的代码然后逐步分析设置思路。

##设计源码 ##

 public static void setGesture(final ImageView image) {

        imageView = image;

        defeautHeight = imageView.getHeight();    //得到图片默认宽度
	    defeautWidth = imageView.getWidth();     //得到图片默认高度

        imageView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                /**
                 * 先进行双击判断,单个手指操作返回false,执行后续
                 */

                if (gesture(event)) ;

                /***
                 * 单个手指操作,先判断是否双击, 超过规定时间则返回false交给后续
                 */

                else if (isdbclick(event)) {
                    if (i == 0) {
                        Animator.twoClickAnimator(imageView, MAXRATIO);
                        i = 1;
                    } else if (i == 1) {
                        Animator.twoClickAnimator(imageView, MINRATIO);
                        i = 0;
                    }
                }

                /***
                 * 既不是双指操作也不是双击则执行ImageView的move
                 */

                else move(event);

                return true;
            }
        });
    }

setGesture()为唯一外部可调用方法,传入一个ImageView该工具类便为其设置onTouch事件,当然事件里包括了移动双击以及双指的操作。gesture(event)用于判断处理双指操作,isdbclick(event)用于判断处理双击操作,move(event)用于处理滑动操作,也就是说核心分为三步, 笔者用到if else结构意味着这几个操作是独立运行的。也就是在双指放大过程不能相应移动或者双击。从易到难,先了解move(event)方法。

三部曲一 move(event)

    private static void move(MotionEvent event) {

        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            downX = (int) event.getX();    //downX 记录move()方法down事件距ImageView左边距离
            downY = (int) event.getY();   //同上

        }

        if (event.getAction() == MotionEvent.ACTION_MOVE) {


            if (tooAnimalEnd == true) {        //动画执行结束,由于部分动画是我们通过ValuesAnimator定义的,所以在动画执行期间操作使得视觉感很差,所以用此来控制

                difference_X += (int) (event.getX() - downX);    //发生一个move事件记录X方向偏移量
                difference_Y += (int) (event.getY() - downY);    //同上

                if (Math.abs(difference_X) > refreshValues) {      //refreshValues 为自定义的偏移量阀值,超过这个值则刷新ImageView的位置  
                    imageView.setX(imageView.getX() + difference_X);     //刷新ImageView的位置
                    difference_X = 0;      //清空偏移量记录值
                }

                if (Math.abs(difference_Y) > refreshValues) {
                    imageView.setY(imageView.getY() + difference_Y);
                    difference_Y = 0;
                }
            } else {     //执行动画期间,暂不考虑                 

                downX = (int) event.getX();
                downY = (int) event.getY();

            }

        }

        if (event.getAction() == MotionEvent.ACTION_UP) {

            handler.postDelayed(new Runnable() {   
                @Override
                public void run() {
                    decideBigLocationBounds();      //判断完全放大化图片是否偏离屏幕缘边 
                }
            }, 200);     //防止多动画重叠引起数据异常所以延迟200ms执行

            decideLittleLocationBounds();    //判断未完全放大化图片是否脱离屏幕边缘
        }

    }

总结:move(event)方法并不难,只是其他的操作会影响图片的显示效果所以要在内部加上判断、tooAnimalEnd会在所有动画开始的时候值为false,动画结束为true,所以播放动画过程不能移动。而down事件却可以响应并附相关值所以要在
move中用else{}不断匹配down的值,以保证下次操作正常。
为什么会有 decideBigLocationBounds()与 decideLittleLocationBounds()主要是产生的动画效果不同所以笔者还是区分开了。
紧接着move()方法内部看下decideBigLocationBounds()。

**decideBigLocationBounds(); **
这个方法笔者先上效果图
这里写图片描述
当ImageView的宽度或者高度超过屏幕,此时当一方发生偏移后则反弹回原始位置,看源码

  /*
   判断放大后图片位置
    */
    private static void decideBigLocationBounds() {
        if (getWidth() > point.x) {


            if (getLeft() > 0) {

                Animator.locationBounds(imageView, LEFT_BOUNDS);

            }
            if (getRight() < point.x) {

                Animator.locationBounds(imageView, RIGHT_BOUNDS);

            }

        }

        if (getHeight() > point.y) {

            if (getTop() > 0) {

                Animator.locationBounds(imageView, TOP_BOUNDS);

            }

            if (getBottom() < point.y) {

                Animator.locationBounds(imageView, BOTTOM_BOUNDS);

            }
        }
    }

其中point.x point.y封装了屏幕的宽高,当触发条件后则执行 Animator.locationBounds()方法, Animayor类主要负责所有动画操作,当前了解locationBounds()即可

Animator.locationBounds()

 public static void locationBounds(ImageView imageView, int bounds) {

            switch (bounds) {

                case LEFT_BOUNDS:

                    ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(imageView, "translationX", imageView.getTranslationX(), imageView.getTranslationX() - getLeft());
                    objectAnimator1.setDuration(200);
                    objectAnimator1.addListener(new AnimatorAdapter());
                    objectAnimator1.start();

                    break;
                case TOP_BOUNDS:

                    ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(imageView, "translationY", imageView.getTranslationY(), imageView.getTranslationY() - getTop());
                    objectAnimator2.setDuration(200);
                    objectAnimator2.addListener(new AnimatorAdapter());
                    objectAnimator2.start();

                    break;

                case RIGHT_BOUNDS:

                    ObjectAnimator objectAnimator3 = ObjectAnimator.ofFloat(imageView, "translationX", imageView.getTranslationX(), imageView.getTranslationX() + point.x - getRight());
                    objectAnimator3.setDuration(200);
                    objectAnimator3.addListener(new AnimatorAdapter());
                    objectAnimator3.start();

                    break;

                case BOTTOM_BOUNDS:

                    ObjectAnimator objectAnimator4 = ObjectAnimator.ofFloat(imageView, "translationY", imageView.getTranslationY(), imageView.getTranslationY() + point.y - getBottom());
                    objectAnimator4.setDuration(200);
                    objectAnimator4.addListener(new AnimatorAdapter());
                    objectAnimator4.start();

                    break;

            }
        }

其实就是左上右下四中类型的ObjectAnimator, bounds用于控制那种方向的动画,执行动画会使得ImageView移动到屏幕边缘,注意我们添加的Listenner与上述动画开始结束的标志tooAnimalEnd 息息相关,看下AnimatorAdapter

AnimatorAdapter

private static class AnimatorAdapter extends AnimatorListenerAdapter {

        @Override
        public void onAnimationStart(android.animation.Animator animation) {
            super.onAnimationStart(animation);
            tooAnimalEnd = false;
        }

        @Override
        public void onAnimationEnd(android.animation.Animator animation) {
            super.onAnimationEnd(animation);
            tooAnimalEnd = true;
        }
    }

没错,这个适配器仅仅只是提供了动画开始结束的标志。那么decideBigLocationBounds()就到位了,接下来瞧瞧 decideLittleLocationBounds()。

decideLittleLocationBounds()
我们叫它未完全放大化的图片,具体怎样算未放大化我们暂不考虑,只需知道它的大小在某个范围就行,

  private static void decideLittleLocationBounds() {
		  /*
          判断未完全放大化图片位置
          */

        if (getRight() < 0 || getBottom() < 0 || getLeft() > point.x || getTop() > point.y) {

            imageView.setX(point.x / 2 - getWidth() / 2);
            imageView.setY(point.y / 2 - getHeight() / 2);

        }
    }

很简单,当这个ImageView完全脱离屏幕可视区域后将其重新设定为屏幕中心,看下效果。
这里写图片描述

ok这样move(event)方法就到此结束了,紧接着isdbclick(event)方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值