android开发分享手写双向滑动的 ScalableImageView

12_手写双向滑动的 ScalableImageViewScalablelmageView放缩和移动GestrueDetectorGeasureDetector 的默认监听器:OnGestureListener双击监听器:OnDoubleTapListenerOverScrollerScalablelmageView放缩和移动由于有两次移动,一次是初始偏移,另一次是随手指拖动,所以要分开两次写translate()canvas.translate(offsetX * scalingFraction,

12_手写双向滑动的 ScalableImageView

  • ScalablelmageView
    • 放缩和移动
    • GestrueDetector
    • GeasureDetector 的默认监听器:OnGestureListener
    • 双击监听器:OnDoubleTapListener
    • OverScroller

ScalablelmageView

放缩和移动

由于有两次移动,一次是初始偏移,另一次是随手指拖动,所以要分开两次写translate()

canvas.translate(offsetX * scalingFraction, offsetY * scalingFraction); //二次手动偏移 float imageScale = smallImageScale + (bigImageScale - smallImageScale) * scalingFraction; canvas.scale(imageScale, imageScale, getWidth() / 2, getHeight() / 2); //放缩 canvas.translate(originalOffsetX, originalOffsetY); // 初始偏移  canvas.drawBitmap(bitmap, 0, 0, paint); 

GestrueDetector

用于在点击和长按之外,增加其他手势的监听,例如双击、滑动。通过在View.onTouchEvent()里调用GestureDetector.onTouchEvent(),以代理的形式来实现:

@Override public boolean onTouchEvent(MotionEvent event) { 	return gestureDetector.onTouchEvent(event); } 

GeasureDetector 的默认监听器:OnGestureListener

通过构造方法 GeasureDetector(Context, OnGestureListener) 来配置:

gestureDetector = new GestureDetector(context, gestureListener); 

OnGestureListener的几个回调方法:

@Override public boolean onDown(MotionEvent e) { 	//每次ACTION_DOWN事件出现的时候都会被调用,在这里返回true可以保证必然消费掉 事件 	return true; } @Overridepublic void onShowPress(MotionEvent e) { 	//用户按下100ms不松手后会被调用,用于标记「可以显示按下状态了」 } @Override public boolean onSingleTapUp(MotionEvent e) { 	//用户单击时被调用(长按后松手不会调用、双击的第二下时不会被调用) 	return false; } @Override public boolean onScroll(MotionEvent downEvent, MotionEvent event, float distanceX, float distanceY) { 	//用户滑动时被调用 	//第一个事件是用户按下时的ACTION_DOWN事件,第二个事件是当前事件 	//偏移是按下时的位置-当前事件的位置 	return false; } @Override public void onLongPress(MotionEvent e) { 	//用户长按(按下500ms不松手)后会被调用 	// 这个 500ms 在 GestureDetectorCompat 中变成了 600ms (???) } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 	//用于滑动时迅速抬起时被调用,用于用户希望控件进行惯性滑动的场景  	return false; } 

双击监听器:OnDoubleTapListener

通过 GestureDetector.setOnDoubleTapListener(OnDoubleTapListener) 来配置:

gestureDetector.setOnDoubleTapListener(doubleTapListener); 

OnDoubleTapListener的几个回调方法:

@Override public boolean onSingleTapConfirmed(MotionEvent e) { 	//用户单击时被调用 	//和onSingltTapUp()的区别在于,用户的一次点击不会立即调用这个方法,而是在一定 时间后(300ms),确认用户没有进行双击,这个方法才会被调用 	return false; }@Override public boolean onDoubleTap(MotionEvent e) { 	//用户双击时被调用 	//注意:第二次触摸到屏幕时就调用,而不是抬起时 	return false; } @Override public boolean onDoubleTapEvent(MotionEvent e) { 	//用户双击第二次按下时、第二次按下后移动时、第二次按下后抬起时都会被调用 	//常用于「双击拖拽」的场景 	return false; } 

OverScroller

用于自动计算滑动的偏移。

scroller = new OverScroller(context); 

常用于onFling()方法中,调用OverScroller.fling()方法来启动惯性滑动的计算:

@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 	//初始化滑动 	scroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY); 	//下一帧刷新 	postOnAnimation(flingRunner); 	return false; }  @Override public void run() { 	//计算此时的位置,并且如果滑动已经结束,就停止 	if (scroller.computeScrollOffset()) { 		//把此时的位置应用于界面 		offsetX = scroller.getCurrX(); 		offsetY = scroller.getCurrY(); 		invalidate(); 		//下一帧刷新 		postOnAnimation(this); 	} } 

完整代码

public class ScalableImageView extends View {     private static final float IMAGE_WIDTH = Utils.dpToPixel(300);     private static final float OVER_SCALE_FACTOR = 1.5f;      Bitmap bitmap;     Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);      float offsetX;     float offsetY;     float originalOffsetX;     float originalOffsetY;     float smallScale;     float bigScale;     boolean big;     float currentScale;     ObjectAnimator scaleAnimator;     GestureDetectorCompat detector;     HenGestureListener gestureListener = new HenGestureListener();     HenFlingRunner henFlingRunner = new HenFlingRunner();     ScaleGestureDetector scaleDetector;     HenScaleListener henScaleListener = new HenScaleListener();     OverScroller scroller;      public ScalableImageView(Context context, @Nullable AttributeSet attrs) {         super(context, attrs);          bitmap = Utils.getAvatar(getResources(), (int) IMAGE_WIDTH);         detector = new GestureDetectorCompat(context, gestureListener);         scroller = new OverScroller(context);         scaleDetector = new ScaleGestureDetector(context, henScaleListener);     }      @Override     protected void onSizeChanged(int w, int h, int oldw, int oldh) {         super.onSizeChanged(w, h, oldw, oldh);          originalOffsetX = ((float) getWidth() - bitmap.getWidth()) / 2;         originalOffsetY = ((float) getHeight() - bitmap.getHeight()) / 2;          if ((float) bitmap.getWidth() / bitmap.getHeight() > (float) getWidth() / getHeight()) {             smallScale = (float) getWidth() / bitmap.getWidth();             bigScale = (float) getHeight() / bitmap.getHeight() * OVER_SCALE_FACTOR;         } else {             smallScale = (float) getHeight() / bitmap.getHeight();             bigScale = (float) getWidth() / bitmap.getWidth() * OVER_SCALE_FACTOR;         }         currentScale = smallScale;     }      @Override     public boolean onTouchEvent(MotionEvent event) {         boolean result = scaleDetector.onTouchEvent(event);         if (!scaleDetector.isInProgress()) {             result = detector.onTouchEvent(event);         }          return result;     }      @Override     protected void onDraw(Canvas canvas) {         super.onDraw(canvas);          float scaleFraction = (currentScale - smallScale) / (bigScale - smallScale);         canvas.translate(offsetX * scaleFraction, offsetY * scaleFraction);         canvas.scale(currentScale, currentScale, getWidth() / 2f, getHeight() / 2f);         canvas.drawBitmap(bitmap, originalOffsetX, originalOffsetY, paint);     }      private float getCurrentScale() {         return currentScale;     }      private void setCurrentScale(float currentScale) {         this.currentScale = currentScale;         invalidate();     }      private ObjectAnimator getScaleAnimator() {         if (scaleAnimator == null) {             scaleAnimator = ObjectAnimator.ofFloat(this, "currentScale", 0);         }         scaleAnimator.setFloatValues(smallScale, bigScale);         return scaleAnimator;     }      class HenGestureListener extends GestureDetector.SimpleOnGestureListener {          @Override         public boolean onDown(MotionEvent e) {             return true;         }          @Override         public void onShowPress(MotionEvent e) {          }          @Override         public boolean onSingleTapUp(MotionEvent e) {             return false;         }          @Override         public boolean onScroll(MotionEvent down, MotionEvent event, float distanceX, float distanceY) {             if (big) {                 offsetX -= distanceX;                 offsetY -= distanceY;                 fixOffsets();                 invalidate();             }             return false;         }          @Override         public void onLongPress(MotionEvent e) {          }          @Override         public boolean onFling(MotionEvent down, MotionEvent event, float velocityX, float velocityY) {             if (big) {                 scroller.fling((int) offsetX, (int) offsetY, (int) velocityX, (int) velocityY,                         - (int) (bitmap.getWidth() * bigScale - getWidth()) / 2,                         (int) (bitmap.getWidth() * bigScale - getWidth()) / 2,                         - (int) (bitmap.getHeight() * bigScale - getHeight()) / 2,                         (int) (bitmap.getHeight() * bigScale - getHeight()) / 2);                  postOnAnimation(henFlingRunner);             }             return false;         }          @Override         public boolean onSingleTapConfirmed(MotionEvent e) {             return false;         }          @Override         public boolean onDoubleTap(MotionEvent e) {             big = !big;             if (big) {                 offsetX = (e.getX() - getWidth() / 2f) - (e.getX() - getWidth() / 2) * bigScale / smallScale;                 offsetY = (e.getY() - getHeight() / 2f) - (e.getY() - getHeight() / 2) * bigScale / smallScale;                 fixOffsets();                 getScaleAnimator().start();             } else {                 getScaleAnimator().reverse();             }             return false;         }          @Override         public boolean onDoubleTapEvent(MotionEvent e) {             return false;         }     }      private void fixOffsets() {         offsetX = Math.min(offsetX, (bitmap.getWidth() * bigScale - getWidth()) / 2);         offsetX = Math.max(offsetX, - (bitmap.getWidth() * bigScale - getWidth()) / 2);         offsetY = Math.min(offsetY, (bitmap.getHeight() * bigScale - getHeight()) / 2);         offsetY = Math.max(offsetY, - (bitmap.getHeight() * bigScale - getHeight()) / 2);     }      class HenFlingRunner implements Runnable {          @Override         public void run() {             if (scroller.computeScrollOffset()) {                 offsetX = scroller.getCurrX();                 offsetY = scroller.getCurrY();                 invalidate();                 postOnAnimation(this);             }         }     }      class HenScaleListener implements ScaleGestureDetector.OnScaleGestureListener {         float initialScale;          @Override         public boolean onScale(ScaleGestureDetector detector) {             currentScale = initialScale * detector.getScaleFactor();             invalidate();             return false;         }          @Override         public boolean onScaleBegin(ScaleGestureDetector detector) {             initialScale = currentScale;             return true;         }          @Override         public void onScaleEnd(ScaleGestureDetector detector) {          }     } } 
<?xml version="1.0" encoding="utf-8"?> <com.hencoder.a12_scalable.ScalableImageView xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:tools="http://schemas.android.com/tools"     android:layout_width="match_parent"     android:layout_height="match_parent"     tools:context=".MainActivity">  </com.hencoder.a12_scalable.ScalableImageView> 

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请点击右边联系管理员删除。

如若转载,请注明出处:https://www.ctvol.com/addevelopment/890831.html

(0)
上一篇 2021年10月19日
下一篇 2021年10月19日

精彩推荐