问题
I'm trying to rotate and scale a view based on one "drag" handle in Android. With the layout rotating, and the view resizing, the end result should be that the drag handle follows the users finger where ever it moves.
This is based on combining these two questions:
- How to scale a view in android using a handle?
- Rotating around two points in android not working
The only thing that seems wrong is the rotation code.
Here is my activity code:
public class MainActivity extends Activity {
ImageView imageView;
ImageView dragHandle;
RelativeLayout layout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.imageView1);
imageView.setBackgroundColor(Color.MAGENTA);
dragHandle = (ImageView) findViewById(R.id.imageView2);
dragHandle.setBackgroundColor(Color.CYAN);
layout = (RelativeLayout) findViewById(R.id.relativeLayout2);
layout.setBackgroundColor(Color.YELLOW);
setUpResize();
}
public void setUpResize() {
dragHandle.setOnTouchListener(new View.OnTouchListener() {
float centerX, centerY, startR, startScale, startX, startY;
float startAngle;
float zeroAngle;
int firstPointX;
int firstPointY;
public boolean onTouch(View v, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
// calculate center of image
centerX = (imageView.getLeft() + imageView.getRight()) / 2f;
centerY = (imageView.getTop() + imageView.getBottom()) / 2f;
// recalculate coordinates of starting point
startX = e.getRawX() - dragHandle.getX() + centerX;
startY = e.getRawY() - dragHandle.getY() + centerY;
// get starting distance and scale
startR = (float) Math.hypot(e.getRawX() - startX, e.getRawY() - startY);
startScale = imageView.getScaleX();
/*
* Rotate code
*/
int[] locationOfLayout = new int[2];
int[] locationOfDrag = new int[2];
layout.getLocationOnScreen(locationOfLayout);
dragHandle.getLocationOnScreen(locationOfDrag);
firstPointX = locationOfLayout[0];
firstPointY = locationOfLayout[1];
float secondPointX = e.getRawX();
float secondPointY = e.getRawY();
zeroAngle = findRotation(firstPointX, firstPointY, secondPointX, secondPointY); // remember
// "zero"
// angle
startAngle = layout.getRotation(); // remember angle at
// which layout is
// rotated at the start
} else if (e.getAction() == MotionEvent.ACTION_MOVE) {
// calculate new distance
float newR = (float) Math.hypot(e.getRawX() - startX, e.getRawY() - startY);
// set new scale
float newScale = newR / startR * startScale;
imageView.setScaleX(newScale);
imageView.setScaleY(newScale);
// move handler image
dragHandle.setX(centerX + imageView.getWidth() / 2f * newScale);
dragHandle.setY(centerY + imageView.getHeight() / 2f * newScale);
/*
* Rotate code
*/
layout.setRotation(findRotation(firstPointX, firstPointY, e.getRawX(), e.getRawY()) - zeroAngle
+ startAngle); // rotate relative to start and zero
// angle
} else if (e.getAction() == MotionEvent.ACTION_UP) {
}
return true;
}
});
}
private float findRotation(float firstPointX, float firstPointY, float secondPointX, float secondPointY) {
double delta_x = (secondPointX - firstPointX);
double delta_y = (secondPointY - firstPointY);
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}
}
xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:id="@+id/relativeLayout2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" >
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_launcher" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_below="@+id/imageView1"
android:layout_toRightOf="@+id/imageView1"
android:src="@drawable/meanicons" />
</RelativeLayout>
</RelativeLayout>
回答1:
In case you need to resize and rotate image simultaneously using one handler icon, some trigonometrical calculations should be performed.
It is not so difficult to calculate angle by which image should be rotated, based on its initial angle and angle, by which the vector, that connects center of image and current finger position, is rotated. A little bit more complicated task is to position the handler in a proper place of the screen to make it always connect the corner of main image.
public void setUpResize() {
dragHandle.setOnTouchListener(new View.OnTouchListener() {
float centerX, centerY, startR, startScale, startX, startY, startRotation, startA ;
public boolean onTouch(View v, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
centerX = (imageView.getLeft() + imageView.getRight()) / 2f;
centerY = (imageView.getTop() + imageView.getBottom()) / 2f;
startX = e.getRawX() - dragHandle.getX() + centerX;
startY = e.getRawY() - dragHandle.getY() + centerY;
startR = (float) Math.hypot(e.getRawX() - startX, e.getRawY() - startY);
startA = (float) Math.toDegrees(Math.atan2(e.getRawY() - startY, e.getRawX() - startX));
startScale = imageView.getScaleX();
startRotation = imageView.getRotation();
} else if (e.getAction() == MotionEvent.ACTION_MOVE) {
float newR = (float) Math.hypot(e.getRawX() - startX, e.getRawY() - startY);
float newA = (float) Math.toDegrees(Math.atan2(e.getRawY() - startY, e.getRawX() - startX));
float newScale = newR / startR * startScale;
float newRotation = newA - startA + startRotation;
imageView.setScaleX(newScale);
imageView.setScaleY(newScale);
imageView.setRotation(newRotation);
// ----- this part takes some effort to understand... ------
dragHandle.setX((float) (centerX + Math.hypot(imageView.getWidth(), imageView.getHeight())/2f * newScale
* Math.cos(Math.toRadians(newRotation) + Math.atan2(imageView.getHeight(), imageView.getWidth()))));
dragHandle.setY((float) (centerY + Math.hypot(imageView.getWidth(), imageView.getHeight())/2f * newScale
* Math.sin(Math.toRadians(newRotation) + Math.atan2(imageView.getHeight(), imageView.getWidth()))));
//-----------------------------------------------------------
dragHandle.setPivotX(0);
dragHandle.setPivotY(0);
dragHandle.setRotation(newRotation);
} else if (e.getAction() == MotionEvent.ACTION_UP) {
}
return true;
}
});
}
So, what I'm doing?
Math.hypot(imageView.getWidth(), imageView.getHeight()) / 2f * newScale
--this calculates half length of diagonal of the main image, i. e. distance between its center and corner point
Math.atan2(imageView.getHeight(), imageView.getWidth())
--this is angle by which diagonal was rotated initially (since image must not be square, this angle is not always 45 deg.)
Math.cos(Math.toRadians(newRotation) + Math.atan2(imageView.getHeight(), imageView.getWidth()))
--this gives us projection onto X axis of unit vector, rotated by an angle composed with angle, at which image is rotated, and angle of initial rotation of its diagonal. After multiplying it to half length of diagonal, we get X of image corner.
The same with Y, but using sin
instead of cos
.
来源:https://stackoverflow.com/questions/23115472/rotate-and-scale-a-view-based-on-one-handle-in-android