Rotate and resize images with one finger in Android

I am developing an application that has a function that resizes and rotates an image by dragging its button in the lower right corner.

I saw one application that has a function that, if we drag the button of the lower right corner diagonally, reduce the size of the image, or if we drag the button left or right, the image was viewed in the direction. I want to implement this function in my application

I'm struggling to implement a single rotation of the finger, as well as resize the image.

Please guide me correctly. enter image description here


I am trying to use this code and trying to apply scaling and rotation, but I am not able to do it, please help me. clear the code to zoom in and rotate the action of the finger base.

public class ScaleActivity extends Activity { ViewGroup lLayout; static ImageView img, backgrndImg; Canvas mCanvas; float d; private float mAspectQuotient; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lLayout = (FrameLayout) findViewById(R.id.lLayout); final CropView cv = new CropView(this); lLayout.addView(cv); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } public class CropView extends ImageView { private static final int SELECTION_RECT_PAINT_COLOR = 0xFF000000; private static final int SELECTION_RECT_FILL_COLOR = 0x70FFFFFF; private static final int TOUCH_TOLERANCE = 25; private static final int xInc = 25; private static final int yInc = 25; Paint paint = new Paint(); private int initial_size = 200; private Point leftTop, rightBottom, center, previous, currentPoint, rectPos; private Paint fillPaint; private Paint rectPaint; protected Rect selection, dest; private boolean isAffectedBottom = false; Bitmap bitmap, backgroundBitmap; Rect rectf; Rect knobRect; private Context mContext; int width, height; private Matrix matrix = new Matrix(); Bitmap resizedBitmap; // Adding parent class constructors public CropView(Context context) { super(context); mContext = context; backgroundBitmap = BitmapFactory.decodeResource(getContext() .getResources(), R.drawable.toast_bkgrd); bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.aviary_adjust_knob); rectPaint = new Paint(); rectPaint.setStyle(Style.STROKE); rectPaint.setColor(SELECTION_RECT_PAINT_COLOR); fillPaint = new Paint(); fillPaint.setStyle(Style.FILL); fillPaint.setColor(SELECTION_RECT_FILL_COLOR); currentPoint = new Point(getWidth() / 2, getHeight() / 2); width = backgroundBitmap.getWidth(); height = backgroundBitmap.getHeight(); initCropView(); } public CropView(Context context, AttributeSet attrs) { super(context, attrs, 0); initCropView(); } public CropView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initCropView(); } @SuppressLint("DrawAllocation") @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mCanvas = canvas; if (leftTop.equals(0, 0)) resetPoints(); mCanvas.save(); resizedBitmap = Bitmap.createBitmap(backgroundBitmap, 0, 0, backgroundBitmap.getWidth(), backgroundBitmap.getHeight(), matrix, true); // mCanvas.drawBitmap(backgroundBitmap, matrix, rectPaint); mCanvas.drawBitmap(resizedBitmap, null, selection, rectPaint); mCanvas.drawBitmap(bitmap, selection.right - 25, selection.bottom - 25, null); rectPos.set(selection.left, selection.top); mCanvas.restore(); } @Override public boolean onTouchEvent(MotionEvent event) { int eventaction = event.getAction(); switch (eventaction) { case MotionEvent.ACTION_DOWN: touchDown((int) event.getX(), (int) event.getY()); previous.set((int) event.getX(), (int) event.getY()); break; case MotionEvent.ACTION_MOVE: touchMove((int) event.getX(), (int) event.getY()); if (isActionInsideRectangle(event.getX(), event.getY()) && !isAffectedBottom) { drag((int) event.getX(), (int) event.getY()); invalidate(); // redraw rectangle previous.set((int) event.getX(), (int) event.getY()); } previous.set((int) event.getX(), (int) event.getY()); break; case MotionEvent.ACTION_UP: touchUp((int) event.getX(), (int) event.getY()); previous = new Point(); break; } return true; } private void initCropView() { paint.setColor(Color.WHITE); paint.setStyle(Style.STROKE); paint.setStrokeWidth(5); leftTop = new Point(); rightBottom = new Point(); center = new Point(); previous = new Point(); rectPos = new Point(); } public void resetPoints() { center.set(getWidth() / 2, getHeight() / 2); leftTop.set((getWidth() - initial_size) / 2, (getHeight() - initial_size) / 2); rightBottom.set(leftTop.x + initial_size, leftTop.y + initial_size); selection = new Rect(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y); knobRect = new Rect(selection.right, selection.bottom, bitmap.getWidth(), bitmap.getHeight()); dest = selection; } private boolean isActionInsideRectangle(float x, float y) { int buffer = 10; return (x >= (selection.left) && x <= (selection.right) && y >= (selection.top) && y <= (selection.bottom)) ? true : false; } void touchDown(int x, int y) { System.out.println("selection " + selection); int dx = (previous.x - x) / 2; int dy = (previous.y - y) / 2; // d= rotation(dx,dy); currentPoint.set(x, y); if (pointsAreClose(x, y, selection.right, selection.bottom)) { isAffectedBottom = true; System.out.println("isAffectedBottom " + isAffectedBottom); } } void touchMove(int x, int y) { currentPoint.set(x, y); if (isAffectedBottom) { int dx = (previous.x - x) / 2; int dy = (previous.y - y) / 2; double startAngle = getAngle(previous.x, previous.y); double currentAngle = getAngle(x, y); matrix.postRotate((float) (startAngle - currentAngle), selection.width() / 2.0f, selection.height() / 2.0f); // selection.inset(dx, dy); invalidate(); } } void touchUp(int x, int y) { currentPoint.set(x, y); isAffectedBottom = false; } private boolean pointsAreClose(float x1, float y1, float x2, float y2) { return Math.hypot(x1 - x2, y1 - y2) < TOUCH_TOLERANCE; } private void drag(int x, int y) { int movement; movement = x - previous.x; int movementY = y - previous.y; selection.set(selection.left + movement, selection.top + movementY, selection.right + movement, selection.bottom + movementY); selection.sort(); invalidate(); } /** * Calculate the degree to be rotated by. * * @param event * @return Degrees */ // private float rotation(float dx, float dy) { // // // double delta_x = (dx); // // double delta_y = (dy); // double radians = Math.atan2((selection.left) - (previous.y), // (selection.top) - (previous.x)); // double radians2 = Math.atan2((selection.left) - (dy), // (selection.top) - (dx)); // // System.out.println("radians" + radians); // System.out.println("" + radians2); // System.out.println("radians2-radians" + (radians2 - radians)); // System.out.println(Math.toDegrees(radians2 - radians)); // return (float) Math.toDegrees(radians2 - radians); // // } private double getAngle(double xTouch, double yTouch) { double x = xTouch - (getWidth() / 2d); double y = getHeight() - yTouch - (getHeight() / 2d); switch (getQuadrant(x, y)) { case 1: System.out.println("1"); return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI; case 2: case 3: System.out.println("32"); return 180 - (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI); case 4: System.out.println("4"); return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI; default: // ignore, does not happen return 0; } } /** * @return The selected quadrant. */ private int getQuadrant(double x, double y) { if (x >= 0) { return y >= 0 ? 1 : 4; } else { return y >= 0 ? 2 : 3; } } } } 
+6
source share
3 answers

please check the repository on github, I create it.

count the distance from the center point in the rotation and zoom mode to the push point. just use:

  private float getDistance(Point a, Point b) { float v = ((ax - bx) * (ax - bx)) + ((ay - by) * (ay - by)); return ((int) (Math.sqrt(v) * 100)) / 100f; } 

and count OA / OB, that value can count new heights and widths of the view

count the angle AOB, A is the first pushing point, B is the last displacement point, O is the center of the viewpoint.

and then just set a new height and width for viewing and count the left and top for viewing.

suce link: https://github.com/ryanch741/android-view-rotate-zoom-single-finger

code:

  Point pushPoint; int lastImgWidth; int lastImgHeight; int lastImgLeft; int lastImgTop; int lastImgAngle; double lastComAngle; int pushImgWidth; int pushImgHeight; int lastPushBtnLeft; int lastPushBtnTop; private View mView; private Point mViewCenter; private static final double PI = 3.14159265359; public PushBtnTouchListener(View mView) { this.mView = mView; } private FrameLayout.LayoutParams pushBtnLP; private FrameLayout.LayoutParams imgLP; float lastX = -1; float lastY = -1; @Override public boolean onTouch(View pushView, MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { // δΈ»η‚ΉζŒ‰δΈ‹case MotionEvent.ACTION_DOWN: pushBtnLP = (FrameLayout.LayoutParams) pushView.getLayoutParams(); imgLP = (FrameLayout.LayoutParams) mView.getLayoutParams(); pushPoint = getPushPoint(pushBtnLP, event); lastImgWidth = imgLP.width; lastImgHeight = imgLP.height; lastImgLeft = imgLP.leftMargin; lastImgTop = imgLP.topMargin; lastImgAngle = (int) mView.getRotation(); lastPushBtnLeft = pushBtnLP.leftMargin; lastPushBtnTop = pushBtnLP.topMargin; pushImgWidth = pushBtnLP.width; pushImgHeight = pushBtnLP.height; lastX = event.getRawX(); lastY = event.getRawY(); refreshImageCenter(); break; // ε‰―η‚ΉζŒ‰δΈ‹case MotionEvent.ACTION_POINTER_DOWN: break; case MotionEvent.ACTION_UP: { break; } case MotionEvent.ACTION_POINTER_UP: break; case MotionEvent.ACTION_MOVE: float rawX = event.getRawX(); float rawY = event.getRawY(); if (lastX != -1) { if (Math.abs(rawX - lastX) < 5 && Math.abs(rawY - lastY) < 5) { return false; } } lastX = rawX; lastY = rawY; Point O = mViewCenter, A = pushPoint, B = getPushPoint(pushBtnLP, event); float dOA = getDistance(O, A); float dOB = getDistance(O, B); float f = dOB / dOA; int newWidth = (int) (lastImgWidth * f); int newHeight = (int) (lastImgHeight * f); imgLP.leftMargin = lastImgLeft - ((newWidth - lastImgWidth) / 2); imgLP.topMargin = lastImgTop - ((newHeight - lastImgHeight) / 2); imgLP.width = newWidth; imgLP.height = newHeight; mView.setLayoutParams(imgLP); float fz = (((Ax - Ox) * (Bx - Ox)) + ((Ay - Oy) * (By - Oy))); float fm = dOA * dOB; double comAngle = (180 * Math.acos(fz / fm) / PI); if (Double.isNaN(comAngle)) { comAngle = (lastComAngle < 90 || lastComAngle > 270) ? 0 : 180; } else if ((By - Oy) * (Ax - Ox) < (Ay - Oy) * (Bx - Ox)) { comAngle = 360 - comAngle; } lastComAngle = comAngle; float angle = (float) (lastImgAngle + comAngle); angle = angle % 360; mView.setRotation(angle); Point imageRB = new Point(mView.getLeft() + mView.getWidth(), mView.getTop() + mView.getHeight()); Point anglePoint = getAnglePoint(O, imageRB, angle); pushBtnLP.leftMargin = (int) (anglePoint.x - pushImgWidth / 2); pushBtnLP.topMargin = (int) (anglePoint.y - pushImgHeight / 2); pushView.setLayoutParams(pushBtnLP); break; } return false; } private void refreshImageCenter() { int x = mView.getLeft() + mView.getWidth() / 2; int y = mView.getTop() + mView.getHeight() / 2; mViewCenter = new Point(x, y); } private Point getPushPoint(FrameLayout.LayoutParams lp, MotionEvent event) { return new Point(lp.leftMargin + (int) event.getX(), lp.topMargin + (int) event.getY()); } private float getDistance(Point a, Point b) { float v = ((ax - bx) * (ax - bx)) + ((ay - by) * (ay - by)); return ((int) (Math.sqrt(v) * 100)) / 100f; } private Point getAnglePoint(Point O, Point A, float angle) { int x, y; float dOA = getDistance(O, A); double p1 = angle * PI / 180f; double p2 = Math.acos((Ax - Ox) / dOA); x = (int) (Ox + dOA * Math.cos(p1 + p2)); double p3 = Math.acos((Ax - Ox) / dOA); y = (int) (Oy + dOA * Math.sin(p1 + p3)); return new Point(x, y); } 
+4
source

I have a Design Layout layout that can work as your need. Download demo here

Java file

 import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.view.GestureDetector; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.RelativeLayout; public class ClipArt extends RelativeLayout { int baseh; int basew; int basex; int basey; ImageButton btndel; ImageButton btnrot; ImageButton btnscl; RelativeLayout clip; Context cntx; boolean freeze = false; int h; int i; ImageView image; String imageUri; boolean isShadow; int iv; RelativeLayout layBg; RelativeLayout layGroup; RelativeLayout.LayoutParams layoutParams; public LayoutInflater mInflater; int margl; int margt; float opacity = 1.0F; Bitmap originalBitmap; int pivx; int pivy; int pos; Bitmap shadowBitmap; float startDegree; String[] v; public ClipArt(Context paramContext) { super(paramContext); cntx = paramContext; layGroup = this; basex = 0; basey = 0; pivx = 0; pivy = 0; mInflater = ((LayoutInflater) paramContext.getSystemService("layout_inflater")); mInflater.inflate(R.layout.clipart, this, true); btndel = ((ImageButton) findViewById(R.id.del)); btnrot = ((ImageButton) findViewById(R.id.rotate)); btnscl = ((ImageButton) findViewById(R.id.sacle)); layoutParams = new RelativeLayout.LayoutParams(250, 250); layGroup.setLayoutParams(layoutParams); image = ((ImageView) findViewById(R.id.clipart)); image.setImageResource(R.drawable.ic_launcher); setOnTouchListener(new View.OnTouchListener() { final GestureDetector gestureDetector = new GestureDetector(ClipArt.this.cntx, new GestureDetector.SimpleOnGestureListener() { public boolean onDoubleTap(MotionEvent paramAnonymous2MotionEvent) { return false; } }); public boolean onTouch(View paramAnonymousView, MotionEvent event) { if (!ClipArt.this.freeze) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: layGroup.invalidate(); gestureDetector.onTouchEvent(event); layGroup.performClick(); basex = ((int) (event.getRawX() - layoutParams.leftMargin)); basey = ((int) (event.getRawY() - layoutParams.topMargin)); break; case MotionEvent.ACTION_MOVE: int i = (int) event.getRawX(); int j = (int) event.getRawY(); layBg = ((RelativeLayout) getParent()); if ((i - basex > -(layGroup.getWidth() * 2 / 3)) && (i - basex < layBg.getWidth() - layGroup.getWidth() / 3)) { layoutParams.leftMargin = (i - basex); } if ((j - basey > -(layGroup.getHeight() * 2 / 3)) && (j - basey < layBg.getHeight() - layGroup.getHeight() / 3)) { layoutParams.topMargin = (j - basey); } layoutParams.rightMargin = -1000; layoutParams.bottomMargin = -1000; layGroup.setLayoutParams(layoutParams); break; } return true; } return true; } }); this.btnscl.setOnTouchListener(new View.OnTouchListener() { @SuppressLint({ "NewApi" }) public boolean onTouch(View paramAnonymousView, MotionEvent event) { if (!ClipArt.this.freeze) { int j = (int) event.getRawX(); int i = (int) event.getRawY(); layoutParams = (RelativeLayout.LayoutParams) layGroup.getLayoutParams(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: ClipArt.this.layGroup.invalidate(); ClipArt.this.basex = j; ClipArt.this.basey = i; ClipArt.this.basew = ClipArt.this.layGroup.getWidth(); ClipArt.this.baseh = ClipArt.this.layGroup.getHeight(); int[] loaction = new int[2]; layGroup.getLocationOnScreen(loaction); margl = layoutParams.leftMargin; margt = layoutParams.topMargin; break; case MotionEvent.ACTION_MOVE: float f2 = (float) Math.toDegrees(Math.atan2(i - ClipArt.this.basey, j - ClipArt.this.basex)); float f1 = f2; if (f2 < 0.0F) { f1 = f2 + 360.0F; } j -= ClipArt.this.basex; int k = i - ClipArt.this.basey; i = (int) (Math.sqrt(j * j + k * k) * Math.cos(Math.toRadians(f1 - ClipArt.this.layGroup.getRotation()))); j = (int) (Math.sqrt(i * i + k * k) * Math.sin(Math.toRadians(f1 - ClipArt.this.layGroup.getRotation()))); k = i * 2 + ClipArt.this.basew; int m = j * 2 + ClipArt.this.baseh; if (k > 150) { layoutParams.width = k; layoutParams.leftMargin = (ClipArt.this.margl - i); } if (m > 150) { layoutParams.height = m; layoutParams.topMargin = (ClipArt.this.margt - j); } ClipArt.this.layGroup.setLayoutParams(layoutParams); ClipArt.this.layGroup.performLongClick(); break; } return true; } return ClipArt.this.freeze; } }); this.btnrot.setOnTouchListener(new View.OnTouchListener() { @SuppressLint({ "NewApi" }) public boolean onTouch(View paramAnonymousView, MotionEvent event) { if (!ClipArt.this.freeze) { layoutParams = (RelativeLayout.LayoutParams) ClipArt.this.layGroup.getLayoutParams(); ClipArt.this.layBg = ((RelativeLayout) ClipArt.this.getParent()); int[] arrayOfInt = new int[2]; layBg.getLocationOnScreen(arrayOfInt); int i = (int) event.getRawX() - arrayOfInt[0]; int j = (int) event.getRawY() - arrayOfInt[1]; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: ClipArt.this.layGroup.invalidate(); ClipArt.this.startDegree = layGroup.getRotation(); ClipArt.this.pivx = (layoutParams.leftMargin + ClipArt.this.getWidth() / 2); ClipArt.this.pivy = (layoutParams.topMargin + ClipArt.this.getHeight() / 2); ClipArt.this.basex = (i - ClipArt.this.pivx); ClipArt.this.basey = (ClipArt.this.pivy - j); break; case MotionEvent.ACTION_MOVE: int k = ClipArt.this.pivx; int m = ClipArt.this.pivy; j = (int) (Math.toDegrees(Math.atan2(ClipArt.this.basey, ClipArt.this.basex)) - Math.toDegrees(Math.atan2(m - j, i - k))); i = j; if (j < 0) { i = j + 360; } ClipArt.this.layGroup.setRotation((ClipArt.this.startDegree + i) % 360.0F); break; } return true; } return ClipArt.this.freeze; } }); this.btndel.setOnClickListener(new View.OnClickListener() { public void onClick(View paramAnonymousView) { if (!ClipArt.this.freeze) { layBg = ((RelativeLayout) ClipArt.this.getParent()); layBg.performClick(); layBg.removeView(ClipArt.this.layGroup); } } }); } public void disableAll() { this.btndel.setVisibility(4); this.btnrot.setVisibility(4); this.btnscl.setVisibility(4); } public ImageView getImageView() { return this.image; } public void setFreeze(boolean paramBoolean) { this.freeze = paramBoolean; } } 

Layout file

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageButton android:id="@+id/rotate" android:layout_width="50dp" android:layout_height="50dp" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:adjustViewBounds="true" android:background="@android:color/transparent" android:scaleType="fitCenter" android:src="@drawable/rotation"/> <ImageButton android:id="@+id/sacle" android:layout_width="50dp" android:layout_height="50dp" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:adjustViewBounds="true" android:background="@android:color/transparent" android:scaleType="fitCenter" android:src="@drawable/pointer"/> <ImageButton android:id="@+id/del" android:layout_width="50dp" android:layout_height="50dp" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:adjustViewBounds="true" android:background="@android:color/transparent" android:scaleType="fitCenter" android:src="@drawable/close"/> <ImageView android:id="@+id/clipart" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp"/> </RelativeLayout> 

and images placed among available

enter image description here enter image description here enter image description here

+4
source

I assume rotation / scaling comes from the center of the image? In this case, simple trigonometry allows you to find the angle of rotation and size:

Diagram of triangle formed between picture center and finger

Calculate the dx and dy coordinates of the finger minus the center coordinates. Math.atan2(dy, dx) is the rotation angle (in radians), and Math.hypot(dx,dy) can be used for relative size or just double dx / dy and use directly.

+1
source

Source: https://habr.com/ru/post/955571/


All Articles