问题
So this seems like a very simple question, but I can't figure out how to get the proper location after scaling a view.
I'm trying to line up an imageView to the bottom right corner of another imageView. This code works fine if I don't modify firstImageView
.
secondImageView.setX(firstImageView.getRight());
secondImageView.setY(firstImageView.getBottom());
But then after I apply a translation or scale to the firstImageView, the code above doesn't seem to work correctly (doesn't line up at corners). I'm guessing that scaling doesn't affect the actual size of the view, even though it's clearly resized. So how would I go about setting the secondImageView to the bottom right of the firstImageView even after scale, rotation and/or translation have been modified?
EDIT: two views at a corner code
firstImageView.setOnTouchListener(new View.OnTouchListener() {
float startX, startY;
float translationX, translationY;
float startMoveX, startMoveY;
public boolean onTouch(View v, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
secondImageView.setX(imageView.getRight());
secondImageView.setY(imageView.getBottom());
secondImageView.setVisibility(View.VISIBLE);
startX = e.getRawX();
startY = e.getRawY();
startMoveX = firstImageView.getX();
startMoveY = firstImageView.getY();
} else if (e.getAction() == MotionEvent.ACTION_MOVE) {
translationX = e.getRawX() - startX + startMoveX;
translationY = e.getRawY() - startY + startMoveY;
firstImageView.setTranslationX(translationX);
firstImageView.setTranslationY(translationY);
secondImageView.setTranslationX(firstImageView.getX()+firstImageView.getMeasuredWidth());
secondImageView.setTranslationY(firstImageView.getY()+firstImageView.getMeasuredHeight());
} else if (e.getAction() == MotionEvent.ACTION_UP) {
}
return true;
}
});
}
scaling code:
secondImageView.setScaleX(2);
secondImageView.setScaleY(2);
回答1:
You are correct in your assumption. Translation does not affect the bounds of the view. However if you calculate the position offset (delta) of the second view from the first at the beginning of the gesture, you can translate both views at once. Here's a working sample that you can use.
activity_test.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">
<ImageView
android:id="@+id/photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="false"
android:layout_marginTop="0dp"
android:src="@drawable/photo"/>
<ImageView
android:id="@+id/photo_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/photo"
android:layout_alignBottom="@+id/photo"
android:src="@drawable/ic_launcher"/>
</RelativeLayout>
TestActivity.java
public class TestActivity extends Activity {
private static String TAG = "TestActivity";
private ImageView mPhoto;
private ImageView mPhotoTwo;
private float mStartX;
private float mStartY;
private float mDeltaX;
private float mDeltaY;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
Log.d(TAG, "onCreate");
mPhoto = (ImageView)findViewById(R.id.photo);
mPhotoTwo = (ImageView)findViewById(R.id.photo_two);
mPhoto.setOnTouchListener(new View.OnTouchListener(){
@Override
public boolean onTouch(View view, MotionEvent e) {
boolean result = false;
int action = e.getAction();
if(action == MotionEvent.ACTION_DOWN){
mStartX = e.getX();
mStartY = e.getY();
mDeltaX = mPhoto.getWidth() - mPhotoTwo.getWidth();
mDeltaY = mPhoto.getHeight() - mPhotoTwo.getHeight();
result = true;
}else if(action == MotionEvent.ACTION_MOVE){
float x = e.getX();
float y = e.getY();
float deltaX = x - mStartX;
float deltaY = y - mStartY;
float viewX = view.getX();
float viewY = view.getY();
float valueX = deltaX + viewX;
float valueY = deltaY + viewY;
mPhoto.setX(valueX);
mPhoto.setY(valueY);
float valueTwoX = valueX + mDeltaX;
float valueTwoY = valueY + mDeltaY;
mPhotoTwo.setX(valueTwoX);
mPhotoTwo.setY(valueTwoY);
result = true;
}
return result;
}
});
}
}
UPDATE
Per request I'm modifying the code to remove all layout_align properties from the layout files. All positioning (including the initial rendering of the view) will be done programatically.
activity_test.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relativelayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/photo"/>
<ImageView
android:id="@+id/photo_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher"/>
</RelativeLayout>
TestActivity.java
public class TestActivity extends Activity {
private static String TAG = "TestActivity";
private RelativeLayout mRelativeLayout;
private ImageView mPhoto;
private ImageView mPhotoTwo;
private float mStartX;
private float mStartY;
private float mDeltaX;
private float mDeltaY;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
Log.d(TAG, "onCreate");
mRelativeLayout = (RelativeLayout)findViewById(R.id.relativelayout);
mPhoto = (ImageView)findViewById(R.id.photo);
mPhotoTwo = (ImageView)findViewById(R.id.photo_two);
ViewTreeObserver observer = mRelativeLayout.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
float viewX = mPhoto.getX();
float viewY = mPhoto.getY();
mDeltaX = mPhoto.getWidth() - mPhotoTwo.getWidth();
mDeltaY = mPhoto.getHeight() - mPhotoTwo.getHeight();
float valueTwoX = viewX + mDeltaX;
float valueTwoY = viewY + mDeltaY;
mPhotoTwo.setX(valueTwoX);
mPhotoTwo.setY(valueTwoY);
}
});
mPhoto.setOnTouchListener(new View.OnTouchListener(){
@Override
public boolean onTouch(View view, MotionEvent e) {
boolean result = false;
int action = e.getAction();
if(action == MotionEvent.ACTION_DOWN){
mStartX = e.getX();
mStartY = e.getY();
result = true;
}else if(action == MotionEvent.ACTION_MOVE){
float x = e.getX();
float y = e.getY();
float deltaX = x - mStartX;
float deltaY = y - mStartY;
float viewX = view.getX();
float viewY = view.getY();
float valueX = deltaX + viewX;
float valueY = deltaY + viewY;
mPhoto.setX(valueX);
mPhoto.setY(valueY);
float valueTwoX = valueX + mDeltaX;
float valueTwoY = valueY + mDeltaY;
mPhotoTwo.setX(valueTwoX);
mPhotoTwo.setY(valueTwoY);
result = true;
}
return result;
}
});
}
}
回答2:
Initially the layout was drawn using the first image scale. Later on scale changes you need to redraw the layout to effect the changes you made.
on scale change run requestLayout() or invalidate()
findViewById(android.R.id.content).invalidate();
If in the course of processing the event, the view's bounds may need to be changed, the view will call requestLayout().
Similarly, if in the course of processing the event the view's appearance may need to be changed, the view will call invalidate().
If either requestLayout() or invalidate() were called, the framework will take care of measuring, laying out, and drawing the tree as appropriate.
来源:https://stackoverflow.com/questions/23159579/programmatically-line-up-two-views-at-a-corner