写点什么

自定义 View:如何实现双击点放大图片控件

作者:Changing Lin
  • 2021 年 11 月 15 日
  • 本文字数:3595 字

    阅读完需:约 12 分钟

自定义View:如何实现双击点放大图片控件

1.知识点

  • 实现双击缩小图片的居中显示

  • 实现双击放大图片后,根据双击点居中显示

2.原理

  • 查看 View.onDraw 代码:表示 图片的偏移坐标,与缩放系数相关,当用户双击图片缩小时,scaleFraction 会从 1->0,也就意味着图片会移动到 View 的中间

canvas.translate(offsetX*scaleFraction, offsetY*scaleFraction); 
复制代码
  • 如下图所示:


  • 实际上,实现双击放大图片后,根据双击点居中显示,也就意味着在现有偏移上面实现逆向偏移,即把图片从 B 点移动到 A 点即可;思路很简单,根据 A 点坐标,计算出 smallScale 缩放前的图片坐标;再根据 bigScale 计算出缩放后的图片坐标,将两者相减,即为所求的逆向偏移量。需要注意的,原点坐标为(getWidth()/2, getHeight()/2)

3.代码

package com.besmart.components;
import android.animation.ObjectAnimator;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Paint;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;import android.widget.OverScroller;
import androidx.annotation.Nullable;import androidx.core.view.GestureDetectorCompat;
public class ScalableImageView extends View implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, Runnable {
public static final String TAG = "ScalableImageView"; public static final float IMAGE_WIDTH = Util.dp2px(300); // 指定图片的尺寸 public static final float OVER_SCALE_FACTOR = 1.5f;
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); Bitmap bitmap;
float originalOffsetX; float originalOffsetY;
float offsetX, offsetY;
float smallScale; float bigScale;
boolean isBig = false; float scaleFraction = 0f;
ObjectAnimator scaleAnimator;
GestureDetectorCompat detector; OverScroller scroller;
public void setScaleFraction(float scaleFraction) { this.scaleFraction = scaleFraction; invalidate(); }
public float getScaleFraction() { return scaleFraction; }
{ bitmap = Util.getAvatar(getResources(), (int) IMAGE_WIDTH, R.drawable.audi);
}
public ScalableImageView(Context context, @Nullable AttributeSet attrs) { super(context, attrs);
detector = new GestureDetectorCompat(context, this); scroller = new OverScroller(context); }
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh);
originalOffsetX = (getWidth() - bitmap.getWidth()) / 2.0f; originalOffsetY = (getHeight() - bitmap.getHeight()) / 2.0f;
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; }
}
private ObjectAnimator getScaleAnimator() { if (null == scaleAnimator) { scaleAnimator = ObjectAnimator.ofFloat(this, "scaleFraction", 0, 1); scaleAnimator.setStartDelay(0); scaleAnimator.setDuration(500); } return scaleAnimator; }
@Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (null != scaleAnimator) scaleAnimator.cancel(); }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas);
canvas.translate(offsetX*scaleFraction, offsetY*scaleFraction); float scale = smallScale + (bigScale - smallScale) * scaleFraction; canvas.scale(scale, scale, getWidth() / 2, getHeight() / 2); canvas.drawBitmap(bitmap, originalOffsetX, originalOffsetY, paint); }
@Override public boolean onTouchEvent(MotionEvent event) { return detector.onTouchEvent(event); }
@Override public boolean onDown(MotionEvent e) { Log.e(TAG, "onDown: " + e); return true; }
@Override public void onShowPress(MotionEvent e) { Log.e(TAG, "onShowPress: " + e); }
@Override public boolean onSingleTapUp(MotionEvent e) { Log.e(TAG, "onSingleTapUp: " + e); return false; }
@Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { Log.e(TAG, "onScroll: " + distanceX); if (isBig) { offsetX -= distanceX; offsetX = Math.min(offsetX, (bitmap.getWidth() * bigScale - getWidth()) / 2); offsetX = Math.max(offsetX, -(bitmap.getWidth() * bigScale - getWidth()) / 2); offsetY -= distanceY; offsetY = Math.min(offsetY, (bitmap.getHeight() * bigScale - getHeight()) / 2); offsetY = Math.max(offsetY, -(bitmap.getHeight() * bigScale - getHeight()) / 2); invalidate(); } return false; }
@Override public void onLongPress(MotionEvent e) { Log.e(TAG, "onLongPress: " + e); }
// 惯性滑动 @Override public boolean onFling(MotionEvent down, MotionEvent event, float velocityX, float velocityY) { Log.e(TAG, "onFling: " + velocityX);
if (isBig) { 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(this); } return false; }
@Override public boolean onSingleTapConfirmed(MotionEvent e) { Log.e(TAG, "onSingleTapConfirmed: " + e); return false; }
@Override public boolean onDoubleTap(MotionEvent e) { Log.e(TAG, "onDoubleTap: " + e); isBig = !isBig; if (isBig) {
float orgClickSmallX = e.getX()-getWidth()/2; // 这个坐标 其实是 原图坐标*smallScale float orgClickSmallY = e.getY()-getHeight()/2;
orgClickSmallX = orgClickSmallX / smallScale; // 得到图片点的原始坐标 orgClickSmallY = orgClickSmallY / smallScale; // 得到图片点的原始坐标
float finalBigScaleX =orgClickSmallX * bigScale; // 得到放大后坐标 float finalBigScaleY =orgClickSmallY * bigScale;
offsetX = orgClickSmallX - finalBigScaleX; offsetY = orgClickSmallY - finalBigScaleY;
getScaleAnimator().start(); } else { getScaleAnimator().reverse(); } return false; }
@Override public boolean onDoubleTapEvent(MotionEvent e) { Log.e(TAG, "onDoubleTapEvent: " + e); return false; }
@Override public void run() { if (scroller.computeScrollOffset()) { offsetX = scroller.getCurrX(); offsetY = scroller.getCurrY(); invalidate(); postOnAnimation(this); } }}
复制代码


发布于: 58 分钟前阅读数: 3
用户头像

Changing Lin

关注

获得机遇的手段远超于固有常规之上~ 2020.04.29 加入

我能做的,就是调整好自己的精神状态,以最佳的面貌去面对那些未曾经历过得事情,对生活充满热情和希望。

评论

发布
暂无评论
自定义View:如何实现双击点放大图片控件