android两张图片旋转,支持双指旋转的Android超级图片控件

最近项目需要做一个Android端图片浏览的功能,类似于常规的图片浏览器,可以支持缩放,拖拉等操作。由于对Android开发不熟悉,我在网上搜了个具有类似功能的图片浏览程序,其可以支持双指缩放和单指拖拉的基本手势,虽然不算完美,但程序中对Matrix的应用让我学到很多。我在这个程序的基础上进行改进,把它封装成了一个图片控件,完善了原有功能,并增加了双指旋转和双击缩放的功能。

进过测试和修改,这个控件基本上完美的达到了我预期的效果,出乎我的意料。这个图片控件一共支持4种手势操作:双指缩放、双指旋转、单指拖拉和双击缩放。其中双指旋转是一个亮点(目前还不知道有哪个Android图片浏览器实现了这个功能),旋转过程是平滑的,但松手后图片会固定在最接近0度、90度、180度、270度的一个角度上,这样保证了图片不会倾斜。另外这个控件还超级易用,可以和Android原生的ImageView一样使用(控件本身也是继承ImageView,我把它称作SuperImageView):

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:src="@drawable/sample" />

超级控件的源码SuperImageView.java如下,使用者自行修改package名称:package com.c35.nmt.widgets;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.Matrix;

import android.graphics.PointF;

import android.graphics.RectF;

import android.graphics.drawable.Drawable;

import android.util.AttributeSet;

import android.util.FloatMath;

import android.view.MotionEvent;

import android.widget.ImageView;

public class SuperImageView extends ImageView {

static final float MAX_SCALE = 2.0f;

float imageW;

float imageH;

float rotatedImageW;

float rotatedImageH;

float viewW;

float viewH;

Matrix matrix = new Matrix();

Matrix savedMatrix = new Matrix();

static final int NONE = 0;// 初始状态

static final int DRAG = 1;// 拖动

static final int ZOOM = 2;// 缩放

static final int ROTATE = 3;// 旋转

static final int ZOOM_OR_ROTATE = 4; // 缩放或旋转

int mode = NONE;

PointF pA = new PointF();

PointF pB = new PointF();

PointF mid = new PointF();

PointF lastClickPos = new PointF();

long lastClickTime = 0;

double rotation = 0.0;

float dist = 1f;

public SuperImageView(Context context) {

super(context);

init();

}

public SuperImageView(Context context, AttributeSet attrs) {

super(context, attrs);

init();

}

public SuperImageView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

init();

}

private void init() {

setScaleType(ImageView.ScaleType.MATRIX);

}

public void setImageBitmap(Bitmap bm) {

super.setImageBitmap(bm);

setImageWidthHeight();

}

public void setImageDrawable(Drawable drawable) {

super.setImageDrawable(drawable);

setImageWidthHeight();

}

public void setImageResource(int resId) {

super.setImageResource(resId);

setImageWidthHeight();

}

private void setImageWidthHeight() {

Drawable d = getDrawable();

if (d == null) {

return;

}

imageW = rotatedImageW = d.getIntrinsicWidth();

imageH = rotatedImageH = d.getIntrinsicHeight();

initImage();

}

protected void onSizeChanged(int w, int h, int oldw, int oldh) {

super.onSizeChanged(w, h, oldw, oldh);

viewW = w;

viewH = h;

if (oldw == 0) {

initImage();

} else {

fixScale();

fixTranslation();

setImageMatrix(matrix);

}

}

private void initImage() {

if (viewW <= 0 || viewH <= 0 || imageW <= 0 || imageH <= 0) {

return;

}

mode = NONE;

matrix.setScale(0, 0);

fixScale();

fixTranslation();

setImageMatrix(matrix);

}

private void fixScale() {

float p[] = new float[9];

matrix.getValues(p);

float curScale = Math.abs(p[0]) + Math.abs(p[1]);

float minScale = Math.min((float) viewW / (float) rotatedImageW,

(float) viewH / (float) rotatedImageH);

if (curScale < minScale) {

if (curScale > 0) {

double scale = minScale / curScale;

p[0] = (float) (p[0] * scale);

p[1] = (float) (p[1] * scale);

p[3] = (float) (p[3] * scale);

p[4] = (float) (p[4] * scale);

matrix.setValues(p);

} else {

matrix.setScale(minScale, minScale);

}

}

}

private float maxPostScale() {

float p[] = new float[9];

matrix.getValues(p);

float curScale = Math.abs(p[0]) + Math.abs(p[1]);

float minScale = Math.min((float) viewW / (float) rotatedImageW,

(float) viewH / (float) rotatedImageH);

float maxScale = Math.max(minScale, MAX_SCALE);

return maxScale / curScale;

}

private void fixTranslation() {

RectF rect = new RectF(0, 0, imageW, imageH);

matrix.mapRect(rect);

float height = rect.height();

float width = rect.width();

float deltaX = 0, deltaY = 0;

if (width < viewW) {

deltaX = (viewW - width) / 2 - rect.left;

} else if (rect.left > 0) {

deltaX = -rect.left;

} else if (rect.right < viewW) {

deltaX = viewW - rect.right;

}

if (height < viewH) {

deltaY = (viewH - height) / 2 - rect.top;

} else if (rect.top > 0) {

deltaY = -rect.top;

} else if (rect.bottom < viewH) {

deltaY = viewH - rect.bottom;

}

matrix.postTranslate(deltaX, deltaY);

}

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction() & MotionEvent.ACTION_MASK) {

// 主点按下

case MotionEvent.ACTION_DOWN:

savedMatrix.set(matrix);

pA.set(event.getX(), event.getY());

pB.set(event.getX(), event.getY());

mode = DRAG;

break;

// 副点按下

case MotionEvent.ACTION_POINTER_DOWN:

if (event.getActionIndex() > 1)

break;

dist = spacing(event.getX(0), event.getY(0), event.getX(1),

event.getY(1));

// 如果连续两点距离大于10,则判定为多点模式

if (dist > 10f) {

savedMatrix.set(matrix);

pA.set(event.getX(0), event.getY(0));

pB.set(event.getX(1), event.getY(1));

mid.set((event.getX(0) + event.getX(1)) / 2,

(event.getY(0) + event.getY(1)) / 2);

mode = ZOOM_OR_ROTATE;

}

break;

case MotionEvent.ACTION_UP:

case MotionEvent.ACTION_POINTER_UP:

if (mode == DRAG) {

if (spacing(pA.x, pA.y, pB.x, pB.y) < 50) {

long now = System.currentTimeMillis();

if (now - lastClickTime < 500

&& spacing(pA.x, pA.y, lastClickPos.x,

lastClickPos.y) < 50) {

doubleClick(pA.x, pA.y);

now = 0;

}

lastClickPos.set(pA);

lastClickTime = now;

}

} else if (mode == ROTATE) {

int level = (int) Math.floor((rotation + Math.PI / 4)

/ (Math.PI / 2));

if (level == 4)

level = 0;

matrix.set(savedMatrix);

matrix.postRotate(90 * level, mid.x, mid.y);

if (level == 1 || level == 3) {

float tmp = rotatedImageW;

rotatedImageW = rotatedImageH;

rotatedImageH = tmp;

fixScale();

}

fixTranslation();

setImageMatrix(matrix);

}

mode = NONE;

break;

case MotionEvent.ACTION_MOVE:

if (mode == ZOOM_OR_ROTATE) {

PointF pC = new PointF(event.getX(1) - event.getX(0) + pA.x,

event.getY(1) - event.getY(0) + pA.y);

double a = spacing(pB.x, pB.y, pC.x, pC.y);

double b = spacing(pA.x, pA.y, pC.x, pC.y);

double c = spacing(pA.x, pA.y, pB.x, pB.y);

if (a >= 10) {

double cosB = (a * a + c * c - b * b) / (2 * a * c);

double angleB = Math.acos(cosB);

double PID4 = Math.PI / 4;

if (angleB > PID4 && angleB < 3 * PID4) {

mode = ROTATE;

rotation = 0;

} else {

mode = ZOOM;

}

}

}

if (mode == DRAG) {

matrix.set(savedMatrix);

pB.set(event.getX(), event.getY());

matrix.postTranslate(event.getX() - pA.x, event.getY() - pA.y);

fixTranslation();

setImageMatrix(matrix);

} else if (mode == ZOOM) {

float newDist = spacing(event.getX(0), event.getY(0),

event.getX(1), event.getY(1));

if (newDist > 10f) {

matrix.set(savedMatrix);

float tScale = Math.min(newDist / dist, maxPostScale());

matrix.postScale(tScale, tScale, mid.x, mid.y);

fixScale();

fixTranslation();

setImageMatrix(matrix);

}

} else if (mode == ROTATE) {

PointF pC = new PointF(event.getX(1) - event.getX(0) + pA.x,

event.getY(1) - event.getY(0) + pA.y);

double a = spacing(pB.x, pB.y, pC.x, pC.y);

double b = spacing(pA.x, pA.y, pC.x, pC.y);

double c = spacing(pA.x, pA.y, pB.x, pB.y);

if (b > 10) {

double cosA = (b * b + c * c - a * a) / (2 * b * c);

double angleA = Math.acos(cosA);

double ta = pB.y - pA.y;

double tb = pA.x - pB.x;

double tc = pB.x * pA.y - pA.x * pB.y;

double td = ta * pC.x + tb * pC.y + tc;

if (td > 0) {

angleA = 2 * Math.PI - angleA;

}

rotation = angleA;

matrix.set(savedMatrix);

matrix.postRotate((float) (rotation * 180 / Math.PI),

mid.x, mid.y);

setImageMatrix(matrix);

}

}

break;

}

return true;

}

/**

* 两点的距离

*/

private float spacing(float x1, float y1, float x2, float y2) {

float x = x1 - x2;

float y = y1 - y2;

return FloatMath.sqrt(x * x + y * y);

}

private void doubleClick(float x, float y) {

float p[] = new float[9];

matrix.getValues(p);

float curScale = Math.abs(p[0]) + Math.abs(p[1]);

float minScale = Math.min((float) viewW / (float) rotatedImageW,

(float) viewH / (float) rotatedImageH);

if (curScale <= minScale + 0.01) { // 放大

float toScale = Math.max(minScale, MAX_SCALE) / curScale;

matrix.postScale(toScale, toScale, x, y);

} else { // 缩小

float toScale = minScale / curScale;

matrix.postScale(toScale, toScale, x, y);

fixTranslation();

}

setImageMatrix(matrix);

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值