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