Android中图像变换Matrix的原理、代码验证和应用(三)

时间:2023-02-07 14:55:05

第三部分 应用

在这一部分,我们会将前面两部分所了解到的内容和Android手势结合起来,利用各种不同的手势对图像进行平移、缩放和旋转,前面两项都是在实践中经常需要用到的功能,后一项据说苹果也是最近才加上的,而实际上在Android中,咱们通过自己的双手,也可以很轻松地实现之。

 

首先创建一个Android项目PatImageView,同时创建一个Activity:PatImageViewActivity。完成这一步后, 记得在AndroidManifest.xml中增加如下许可:

<uses-permissionandroid:name="android.permission.VIBRATE"/>

因为我们将要通过短按还是长按,来确定将图片到底是缩放还是旋转。

 

现在来创建一个ImageView的派生类:PatImageView,其代码(PatImageView.java)如下(2011-11-22 revised):

package com.pat.imageview;

import android.app.Service;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Vibrator;
import android.util.FloatMath;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;

public class PatImageView extends ImageView
{
private Matrix matrix;
private Matrix savedMatrix;

private boolean long_touch = false;
private static int NONE = 0;
private static int DRAG = 1;// 拖动
private static int ZOOM = 2;// 缩放
private static int ROTA = 3;// 旋转
private int mode = NONE;

private PointF startPoint;
private PointF middlePoint;

private float oldDistance;
private float oldAngle;

private Vibrator vibrator;

private GestureDetector gdetector;

public PatImageView(final Context context)
{
super(context);

matrix = new Matrix();
savedMatrix = new Matrix();

matrix.setTranslate(0f, 0f);
setScaleType(ScaleType.MATRIX);
setImageMatrix(matrix);

startPoint = new PointF();
middlePoint = new PointF();

oldDistance = 1f;

gdetector = new GestureDetector(context, new GestureDetector.OnGestureListener()
{
@Override
public boolean onSingleTapUp(MotionEvent e)
{
return true;
}

@Override
public void onShowPress(MotionEvent e)
{
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
{
return true;
}

@Override
public void onLongPress(MotionEvent e)
{
long_touch = true;
vibrator = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE);
// 振动50ms,提示后续的操作将是旋转图片,而非缩放图片
vibrator.vibrate(50);
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
return true;
}

@Override
public boolean onDown(MotionEvent e)
{
return true;
}
});

setOnTouchListener(new OnTouchListener()
{
public boolean onTouch(View view, MotionEvent event)
{
switch(event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:// 第一个手指touch
savedMatrix.set(matrix);
startPoint.set(event.getX(), event.getY());
mode = DRAG;
long_touch = false;
break;
case MotionEvent.ACTION_POINTER_DOWN:// 第二个手指touch
oldDistance = getDistance(event);// 计算第二个手指touch时,两指之间的距离
oldAngle = getDegree(event);// 计算第二个手指touch时,两指所形成的直线和x轴的角度
if(oldDistance > 10f)
{
savedMatrix.set(matrix);
middlePoint = midPoint(event);
if(!long_touch)
{
mode = ZOOM;
}
else
{
mode = ROTA;
}
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if(vibrator != null)vibrator.cancel();
if(mode == DRAG)
{
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - startPoint.x, event.getY() - startPoint.y);
}

if(mode == ZOOM)
{
float newDistance = getDistance(event);

if(newDistance > 10f)
{
matrix.set(savedMatrix);
float scale = newDistance / oldDistance;
matrix.postScale(scale, scale, middlePoint.x, middlePoint.y);
}
}

if(mode == ROTA)
{
float newAngle = getDegree(event);
matrix.set(savedMatrix);
float degrees = newAngle - oldAngle;
matrix.postRotate(degrees, middlePoint.x, middlePoint.y);
}
break;
}
setImageMatrix(matrix);
invalidate();
gdetector.onTouchEvent(event);
return true;
}
});
}

// 计算两个手指之间的距离
private float getDistance(MotionEvent event)
{
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}

// 计算两个手指所形成的直线和x轴的角度
private float getDegree(MotionEvent event)
{
return (float)(Math.atan((event.getY(1) - event.getY(0)) / (event.getX(1) - event.getX(0))) * 180f);
}

// 计算两个手指之间,中间点的坐标
private PointF midPoint( MotionEvent event)
{
PointF point = new PointF();
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);

return point;
}
}


 

下面完善PatImageViewActivity.java的代码,使之如下:

package com.pat.imageview;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class PatImageViewActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);

PatImageView piv = new PatImageView(this);
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.sophie);

piv.setImageBitmap(bmp);

setContentView(piv);
}
}


 

由于有些手势在模拟器上无法模拟,所以就不上运行结果的图片了。本人在真机上运行后(照片就不拍了,有点累啦),可以轻松做到:

1.     很方便地拖动图片(比如,单指按住屏幕进行拖动)

2.     很方便地缩放图片(比如,双指按住屏幕进行分开或者并拢操作,可分别实现放大或者缩小图片的功能)

3.     长按出现振动后,可以很方便地旋转图片(一个手指固定,另外一个手指围绕那个固定的手指运动)。