android 实现图片旋转,移动,缩放,并且记录变化值,用另外一张图片显示出来

最近公司要做一个戒指试戴的功能,就是把戒指通过手势移动到你指定的手指处,并且保存状态,方便下次进入时显示,可以参考APP“钻石快线”的试戴功能,

图片网上有很多的教程教你怎么把图片旋转,移动,缩放,等等,却没有教你保存状态,而且网上的教程都乱七八糟,都是复制粘贴,都不是自己想要的,所以有时间自己写一个,现在把实现步骤,代码,源码贴出来,共同学习!

 * 图片的变化主要是matrix的变化,对matrix不懂的可以先了解下matrxi.
 * 
 * 产品试戴的地方,需要保存戒指的位置,之前用TouchImageView(可以去网上找找,我项目里面也自带了)来处理,因为是别人写好的,要理解里面意思很难,想了很多种方法,比如保存当前的戒指图片位置,获取图片的四个点坐标, 画出正方形,在用图片来填充正方形,后来只能去记录移动,旋转,缩放的每一次事件,还是没 有解决,到了第四天用google在搜了一下类似的,总于找到一个,而且是我想要的已对角线的中 点为坐标,就很方便,也不会卡,现在说下思路
 * 
 * 1.不要在ondraw()里面创建对象,做任何复杂的计算和操作。因为ondraw()被调用执行的次数 量大的惊人。所以在外面创建对象 new Matrix();new Paint (); 
 * 2.ontouchEvent的状态判断,event有很多种类型,要熟知。 
 * 3.已bitmap中心点坐标为中心
 * 4.postTranslate///偏移量,只要相加就好了,postRotate//需要计算角度,,mScale大于1就是放大,少于1就是缩小
 * 5.我这里把偏移量等数据放在本地,ShareFileUtils
 * 6.在重新给戒指确定位置的时候,排序有要求按postTranslate,postRotate,postScale去做, 不然后果就很清楚了
 * 7.// 旋轉,不能以当时旋转的对角线中心点,要以最后的对角线中心位置(通过偏移量计算),因为移动过程中可能没有去旋转,
 * //以对角线为中心,在哪里都是可以的,所以:::: 当前的坐标=偏移量+最开始的对角线的坐标


布局文件和实例化控件的activity就不写了,主要就是2个自定义的view,一个做手势,一个显示。

FunnyView

public class FunnyView extends View {
	private static final String TAG = "FUNNYVIEW";

	/*
	 * 手指按下时可能是移动 也可能是拖动
	 */
	private static final int ZOOM = -1;
	private static final int DRAG = 1;
	private int mode = 0;
	// 第一个触控点
	private PointF startPointF = new PointF();
	// 第二个触控点
	private PointF mCurMovePointF = new PointF();

	private float mDegree;
	private float mScale;

	private Bitmap mBitmap = null;
	private Matrix mMatrix = null;
	private Paint mPaint = null;
	// bitmap的中心点
	private PointF mCenterPoint = new PointF();

	float scale_sx = 1;
	float scale_sy = 1;


	float rotate_degrees = 0;
	

	float translate_dx = 0;
	float translate_dy = 0;

	boolean isload=false;//只让他在第二遍执行 ondraw()
	
	public FunnyView(Context context) {
		this(context, null, 0);
	}

	public FunnyView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
		Log.i(TAG, "context, attrs");
	}

	public FunnyView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
		
	}

	public void getPicBitmap(Bitmap bitmap) {
		// TODO Auto-generated method stub
		mBitmap=bitmap;
		Log.i(TAG, "getPicBitmap");
		isload=true;
		invalidate();
		
	}
	
	private void init(Context context) {
		Log.i(TAG, "init");
		ShareFileUtils.setContext(context);

		mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.diamond);
		mMatrix = new Matrix();
		mPaint = new Paint();
		mPaint.setColor(Color.BLUE);
		mPaint.setStrokeWidth(10);
		mPaint.setAntiAlias(true);
		// 计算bitmap中心点坐标
		mCenterPoint.set(mBitmap.getWidth() / 2, mBitmap.getHeight() / 2);
		
		ShareFileUtils.setFloat("getTryDiamandWidth", mBitmap.getWidth() / 2);// 将试戴的图片的宽高保存在数组里面
		ShareFileUtils.setFloat("getTryDiamandHeight", mBitmap.getHeight() / 2);
		
	}

	@Override
	protected void onDraw(Canvas canvas) {
		if (isload) {
		canvas.drawBitmap(mBitmap, mMatrix, mPaint);
		}
		super.onDraw(canvas);
	}

	/*
	 * 计算角度和缩放
	 * 
	 * 这里使用余弦定理计算 容易理解 如果对android矩阵matrix底层原理清楚的同学 可以使用matrix计算出旋转角度和缩放量
	 */
	private void getRotationScale() {
		// 角度
		double a = distance4PointF(mCenterPoint, startPointF);
		double b = distance4PointF(startPointF, mCurMovePointF);
		double c = distance4PointF(mCenterPoint, mCurMovePointF);

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

		if (cosb >= 1) {
			cosb = 1f;
		}
		double radian = Math.acos(cosb);
		float newDegree = (float) radianToDegree(radian);

		PointF centerToStartMove = new PointF((startPointF.x - mCenterPoint.x),
				(startPointF.y - mCenterPoint.y));

		PointF centerToCurMove = new PointF(
				(mCurMovePointF.x - mCenterPoint.x),
				(mCurMovePointF.y - mCenterPoint.y));

		// 向量叉乘结果, 如果结果为负数, 表示为逆时针, 结果为正数表示顺时针
		float result = centerToStartMove.x * centerToCurMove.y
				- centerToStartMove.y * centerToCurMove.x;

		if (result < 0) {
			newDegree = -newDegree;
		}

		mDegree = newDegree;
		mScale = (float) (c / a);
	}

	/**
	 * 弧度换算成角度
	 * 
	 * @return
	 */
	public static double radianToDegree(double radian) {
		return radian * 180 / Math.PI;
	}

	/**
	 * 两个点之间的距离
	 */
	private float distance4PointF(PointF pf1, PointF pf2) {
		float disX = pf2.x - pf1.x;
		float disY = pf2.y - pf1.y;
		return FloatMath.sqrt(disX * disX + disY * disY);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
			// 注意getX()和getRawX()的区别
			startPointF.set(event.getX(), event.getY());
			mode = DRAG;
			break;
		case MotionEvent.ACTION_MOVE:
			if (mode == DRAG) {

				float x = event.getX() - startPointF.x;
				float y = event.getY() - startPointF.y;
				mCenterPoint.x = mCenterPoint.x + x;
				mCenterPoint.y = mCenterPoint.y + y;
				startPointF.set(event.getX(), event.getY());

				mMatrix.postTranslate(x, y);// 偏移量,只要相加就好了

				translate_dx = translate_dx + (x);
				translate_dy = translate_dy + (y);

				// Log.i(TAG, "x="+x+"y="+y);

				ShareFileUtils.setFloat("translate_dx", translate_dx);// 将Translate的位置保存在数组里面
				ShareFileUtils.setFloat("translate_dy", translate_dy);

			} else if (mode == ZOOM) {
				mCurMovePointF.set(event.getX(), event.getY());
				getRotationScale();
				/*
				 * 旋转和缩放的中心点都是bitmap的中心点,根据需求可以更改。
				 */
				// 旋转
				mMatrix.postRotate(mDegree, mCenterPoint.x, mCenterPoint.y);// 旋转的角度,X,Y的坐标
				// 缩放
				mMatrix.postScale(mScale, mScale, mCenterPoint.x,mCenterPoint.y);
				
				// mScale大于1就是放大,少于1就是缩小
				scale_sx = scale_sx *mScale;
				scale_sy = scale_sy*mScale;

//				Log.i(TAG, "mScale=" + mScale + " mCenterPoint.x="+ mCenterPoint.x + "mCenterPoint.y=" + mCenterPoint.y);
//				Log.i(TAG, "mDegree=" + mDegree);

				ShareFileUtils.setFloat("scale_sx", scale_sx);// 将缩放的位置保存在数组里面,因为是正方形,所以x,y是一样的
				ShareFileUtils.setFloat("scale_sy", scale_sy);

				rotate_degrees = rotate_degrees + mDegree;//360度为一圈
				ShareFileUtils.setFloat("rotate_degrees", rotate_degrees);// 将Rotate,旋转,的位置保存在数组里面

				// 重新赋值起始点
				startPointF.set(mCurMovePointF);
				
			}
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			mode = ZOOM;
			break;
		case MotionEvent.ACTION_POINTER_UP:
			mode = 0;
			break;
		case MotionEvent.ACTION_CANCEL:
			mode = 0;
			break;
		}
		invalidate();
		// 返回true 事件不再往下分发
		return true;
	}
	
	public  void bitmapRecycle()
	{
		if (mBitmap!=null&& !mBitmap.isRecycled()) {
			Log.i(TAG, "mBitmap.recycle()");
			mBitmap.recycle();
		}
	}
}

SelectedDiamondPosition

public class SelectedDiamondPosition extends View {
	private static final String TAG = "SELECTEDDIAMONDPOSITION";
	private float[] position;
	private Context context;
	int widthScreen;
	int heightScreen;
	Matrix matrix;
	Bitmap mFieldBitmap = null;
	boolean isload=false;//只让他在第二遍执�? ondraw()
	float topHight=0;//标题栏的高度
	 
	public SelectedDiamondPosition(Context context) {
		super(context);
		Log.i(TAG, "context");
	}

	public SelectedDiamondPosition(Context context, AttributeSet attrs,int defStyle) {
		super(context, attrs, defStyle);
		Log.i(TAG, "context, attrs, defStyle)");

	}

	public SelectedDiamondPosition(Context context, AttributeSet attrs) {
		super(context, attrs);
		Log.i(TAG, "context, attrs");
		init(context);
	}

	public void getPicBitmap(Bitmap bitmap) {
		// TODO Auto-generated method stub
		this.mFieldBitmap = bitmap;
		Log.i(TAG, "getPicBitmap");
		isload=true;
		invalidate();//注意
		//Invalidate()之后:
		//...OnPaint()->OnPrepareDC()->OnDraw()
		//所以只是刷新在OnPaint()和OnDraw()函数中的绘图语句。其它地方没有影响。
	}

	public void init(Context context) {
		
		Log.i(TAG, "init");
		ShareFileUtils.setContext(context);
		
		DisplayMetrics dm = new DisplayMetrics();
		((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
		widthScreen = dm.widthPixels;
		heightScreen = dm.heightPixels;
		
		
		
		// mFieldBitmap = BitmapFactory.decodeResource(this.getResources(),
		// R.drawable.diamond);

	}

	@SuppressLint("DrawAllocation")
	@Override
	protected void onDraw(Canvas canvas) {
		try {
			super.onDraw(canvas);
			
			Log.i(TAG, "onDraw");
			
			if (isload) {
			matrix = new Matrix();// 矩阵; 模型;
			// 去除锯齿毛边
			canvas.setDrawFilter(new PaintFlagsDrawFilter(0,
					Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
			canvas.save();

			Log.i(TAG,"translate_dx_ok="+ ShareFileUtils.getFloat("translate_dx", 0));
			Log.i(TAG,"translate_dy_ok="+ ShareFileUtils.getFloat("translate_dy", 0));

			float scale_sx = ShareFileUtils.getFloat("scale_sx", 1);
			float scale_sy = ShareFileUtils.getFloat("scale_sy", 1);

			float rotate_degrees = ShareFileUtils.getFloat("rotate_degrees", 0);

			float translate_dx = ShareFileUtils.getFloat("translate_dx", 0);
			float translate_dy = ShareFileUtils.getFloat("translate_dy", 0);

			float getTryDiamandWidth = ShareFileUtils.getFloat("getTryDiamandWidth", 1);
			float getTryDiamandHeight = ShareFileUtils.getFloat("getTryDiamandHeight", 1);

			Log.i(TAG, "getTryDiamandWidth=" + getTryDiamandWidth+ "getTryDiamandHeight=" + getTryDiamandHeight);
			Log.i(TAG, "translate_dx=" + translate_dx + "translate_dy"+ translate_dy);
			Log.i(TAG, "rotate_degrees=" + rotate_degrees);
			Log.i(TAG, "scale_sx=" + scale_sx + "scale_sy=" + scale_sy);
			Log.i(TAG, "topHight="+topHight);
			
			
			
			
			matrix.postTranslate(translate_dx, translate_dy);// 平移
			matrix.postRotate(rotate_degrees,translate_dx + getTryDiamandWidth, translate_dy+ getTryDiamandHeight);
			// 旋轉,不能以当时旋转的对角线中心点,要以最后的对角线中心位置(通过偏移量计算),因为移动过程中可能没有去旋转,
			// 以对角线为中心,在哪里都是可以的,所以:::�? 当前的坐�?=偏移�?+�?�?始的对角线的坐标+标题�?
			matrix.postScale(scale_sx, scale_sy, translate_dx+ getTryDiamandWidth, translate_dy + getTryDiamandHeight);// 縮放,同�?
			
			//
			// matrix.setScale(1.75f, 1.75f);//三种不同的情�?
			// matrix.resRotate(15);
			// matrix.postTranslate(0, 0);

			canvas.drawBitmap(mFieldBitmap, matrix, null);
			canvas.restore();
			}

		} catch (Exception e) {

			System.out.println("MyImageView  -> onDraw() Canvas: trying to use a recycled bitmap");
		}
//		isload=false;

	}

	public void bitmapRecycle() {
		if (mFieldBitmap != null && !mFieldBitmap.isRecycled()) {
			Log.i(TAG, "mFieldBitmap.recycle()");
			mFieldBitmap.recycle();
		}
	}

}
android 实现图片旋转,移动,缩放,并且记录变化值,用另外一张图片显示出来_第1张图片

免费项目的源码:下载导入eclipse,直接运行,studio也很方便,就把代码拷进studio的项目就好了,之所以不用studio是因为本人在弄unity3D,unity3D项目只能导入eclipse中.

源码地址:http://download.csdn.net/detail/tangpengtp/9370559

亲们,给我点个赞或者评论算是对我的支持,谢谢




你可能感兴趣的:(android 实现图片旋转,移动,缩放,并且记录变化值,用另外一张图片显示出来)