Android 正确设置图像缩放的焦点

Android 正确设置图像缩放的焦点,android,scaling,surfaceview,multi-touch,Android,Scaling,Surfaceview,Multi Touch,我有一个SurfaceView,负责绘制位图作为背景,另一个用作覆盖。因此,我决定使用一个矩阵进行所有转换,该矩阵可以用于两个位图,因为(我认为)这是不用OpenGL进行转换的最快方法之一 我已经能够实现左右平移和缩放,但我遇到了一些问题: 我找不到一种方法来集中注意力在这两个问题的中心 手指缩放时,图像始终重置为其初始状态 (也就是说,没有平移或缩放)在创建新的缩放之前 应用除了看起来不对之外,这也不允许用户缩放 缩小以查看整个图像,然后放大显示的部分 重要的 缩放操作完成后,图像将不会在

我有一个
SurfaceView
,负责绘制
位图作为背景,另一个用作覆盖。因此,我决定使用一个
矩阵
进行所有转换,该矩阵可以用于两个位图,因为(我认为)这是不用OpenGL进行转换的最快方法之一

我已经能够实现左右平移和缩放,但我遇到了一些问题:

  • 我找不到一种方法来集中注意力在这两个问题的中心 手指缩放时,图像始终重置为其初始状态 (也就是说,没有平移或缩放)在创建新的缩放之前 应用除了看起来不对之外,这也不允许用户缩放 缩小以查看整个图像,然后放大显示的部分 重要的
  • 缩放操作完成后,图像将不会在 新绘制过程后的相同位置,因为平移值将 要与众不同
有没有一种方法可以通过使用矩阵来实现这一点,或者有没有其他解决方案

代码如下(我在单独的线程中使用SurfaceHolder锁定SurfaceView画布并调用其doDraw方法):

公共类MapSurfaceView扩展了SurfaceView,实现了SurfaceHolder.Callback{
公共虚空doDraw(画布){
画布。drawColor(颜色。黑色);
画布.绘图位图(mBitmap、MTTransformationMatrix、MPainTA);
}
@凌驾
公共布尔onTouchEvent(运动事件){
开关(event.getAction()&MotionEvent.ACTION\u掩码){
case MotionEvent.ACTION\u指针\u向下:{
if(event.getPointerCount()==2){
mOriginalDistance=MathUtils.distanceBetween(event.getX(0)、event.getX(1)、event.getY(0)、event.getY(1));
mScreenMidpoint=MathUtils.midpoint(event.getX(0)、event.getX(1)、event.getY(0)、event.getY(1));
mImageMidpoint=MathUtils.midpoint((mXPosition+event.getX(0))/mScale,(mXPosition+event.getX(1))/mScale,(mYPosition+event.getY(0))/mScale,(mYPosition+event.getY(1))/mScale);
mOriginalScale=mScale;
}
}
case MotionEvent.ACTION\u DOWN:{
mOriginalTouchPoint=新点((int)event.getX(),(int)event.getY());
mOriginalPosition=新点(mXPosition,mYPosition);
打破
}
case MotionEvent.ACTION\u移动:{
if(event.getPointerCount()==2){
最终的double currentDistance=MathUtils.DistanceBeween(event.getX(0)、event.getX(1)、event.getY(0)、event.getY(1));
如果(变焦错误| |当前距离-mOriginalDistance>mPinchToZoomTolerance | | mOriginalDistance-当前距离>mPinchToZoomTolerance){
最终浮动距离比率=(浮动)(当前距离/原始距离);
float tempZoom=mOriginalScale*距离比;
mScale=Math.min(10,Math.max(Math.min((float)getHeight()/(float)mBitmap.getHeight(),(float)getWidth()/(float)mBitmap.getWidth()),tempZoom));
mScale=(float)MathUtils.roundToDecimals(mScale,1);
放大错误=正确;
MTTransformationMatrix=新矩阵();
mtTransformationMatrix.setScale(mScale,mScale);/,mimageMiddpoint.x,mimageMiddpoint.y);
}否则{
System.out.println(“拖动”);
变焦=错误;
final int deltaX=(int)((int)(mOriginalTouchPoint.x-event.getX());
final int deltaY=(int)((int)(mOriginalTouchPoint.y-event.getY());
mXPosition=mOriginalPosition.x+deltaX;
mYPosition=mOriginalPosition.y+deltaY;
验证位置();
MTTransformationMatrix=新矩阵();
MTTransformationMatrix.setScale(mScale,mScale);
mtTransformationMatrix.postTranslate(-mXPosition,-mYPosition);
}
}
打破
}
case MotionEvent.ACTION\u UP:
case MotionEvent.ACTION\u指针\u向上:{
变焦=错误;
验证位置();
MTTransformationMatrix=新矩阵();
MTTransformationMatrix.setScale(mScale,mScale);
mtTransformationMatrix.postTranslate(-mXPosition,-mYPosition);
}
}
返回true;
}
私有void validatePositions(){
//右下角
mXPosition=Math.min(mXPosition,(int)((mBitmap.getWidth()*mScale)-getWidth());
mYPosition=Math.min(mYPosition,(int)((mBitmap.getHeight()*mScale)-getHeight());
//左上角
mXPosition=数学最大值(mXPosition,0);
mYPosition=Math.max(mYPosition,0);
//图像小于容器,应居中

如果(mBitmap.getWidth()*mScale,而不是每次使用new matrix()重置转换矩阵,请尝试使用post*()更新它。这样,您只执行与屏幕相关的操作。更容易用“缩放到屏幕上的这一点”来思考

现在是一些代码。在缩放部分计算了mScale后:

...
mScale = (float) MathUtils.roundToDecimals(mScale, 1);
float ratio = mScale / mOriginalScale;
mTransformationMatrix.postScale(ratio, ratio, mScreenMidpoint.x, mScreenMidpoint.y);
在每个缩放触摸事件上重新计算MSCreenMiddpoint可能更好。这将允许用户在缩放时稍微更改焦点。对我来说,这比在头两次手指触摸后冻结焦点更自然

在拖动过程中,可以使用deltaX和deltaY而不是绝对点进行平移:

mTransformationMatrix.postTranslate(-deltaX, -deltaY);
当然,现在您必须将validatePositions()方法更改为:

  • 确保deltaX和deltaY不会使图像移动过多,
    mTransformationMatrix.postTranslate(-deltaX, -deltaY);
    
    void validate() {
    
        mTransformationMatrix.mapRect(new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()));
    
        float height = rect.height();
        float width = rect.width();
    
        float deltaX = 0, deltaY = 0;
    
        // Vertical delta
        if (height < mScreenHeight) {
            deltaY = (mScreenHeight - height) / 2 - rect.top;
        } else if (rect.top > 0) {
            deltaY = -rect.top;
        } else if (rect.bottom < mScreenHeight) {
            deltaY = mScreenHeight - rect.bottom;
        }
    
        // Horziontal delta
        if (width < mScreenWidth) {
            deltaX = (mScreenWidth - width) / 2 - rect.left;
        } else if (rect.left > 0) {
            deltaX = -rect.left;
        } else if (rect.right < mScreenWidth) {
            deltaX = mScreenWidth - rect.right;
        }
    
        mTransformationMatrix.postTranslate(deltaX, deltaY)
    }