1. 程式人生 > >android 實現圖片旋轉,移動,縮放,並且記錄變化值,用另外一張圖片顯示出來

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();
		}
	}

}

免費專案的原始碼:下載匯入eclipse,直接執行,studio也很方便,就把程式碼拷進studio的專案就好了,之所以不用studio是因為本人在弄unity3D,unity3D專案只能匯入eclipse中.

原始碼地址:http://download.csdn.net/detail/tangpengtp/9370559

親們,給我點個贊或者評論算是對我的支援,謝謝