How to vertically center a scaled View? - android

I'm trying to vertically center a rotated and scaled View. It doesn't seem to matter if it is rotated or not (if I keep the scale at 1.0 and rotate the view, and change the x position of the view by using setX it works as expected).
I know the center of my container, and I also know the width of the rectangle that holds the View in the container.
I am setting X of the View to be: the middle of the container - view/2.
This works if I don't scale the view up or down, however if I scale the image up the image doesn't appear in the center, it is always off a noticeable amount.
// Get center of container
val rect = Rect()
containerView.getHitRect(rect)
val width = rect.right - rect.left
val centerContainerView = rect.left + width/2
// At this point assume myView is rotated and scaled up (using any one of the many translate, scale, rotate on touch libraries)
val viewRect = Rect()
myView.getHitRect(viewRect)
val viewWidth = viewRect.right - viewRect.left
val newX = centerContainerView - viewWidth/2
// Tap a button and run the below to center myView
myView.x = newX.toFloat()
SetX

Related

Center of the view after rotation or scale

I am trying to find the center of a view. For non-rotated view the value is correct, but for rotated view it's not correct as the centre(0,0) is rotating. In the case of scaling the width and height is keep constant even after zooming
I am using the following formula to calculate the center.
int[] location = new int[2];
childView.getLocationOnScreen(location);
int xLocation = location[0] + childView.getWidth() / 2;
int yLocation = location[1] + childView.getHeight() / 2
from getGlobalVisibleRect i get the rect of visible part only
https://developer.android.com/reference/android/view/View#getHitRect(android.graphics.Rect)
This method will return you Rect(considering rotation and scale) within its parent view

Automatically scrolling to top left corner in Android TouchImageView

I'm using a TouchImageView in my app in which I have an image which is larger than the screensize. When I start the activity that contains my TouchImageView it currently automatically shows the center of my image in the middle of the screen. I have to manually (using a drag gesture) make the top left-corner visibe. However, I would like to have the top-left corner of the image visible in the top left corner of my screen by default.
I tried imgView.setScrollPosition(0,0), but without any result.
I also tried setting the scaletype to "matrix", but this zooms out on the image, while I want the image to be shown in it's original size. Scaletypes fitStart and fitEnd are not supported by TouchImageView.
How can I scroll to the top left corner of my TouchImageView?
Here's my XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.frankd.wttv.TouchImageView
android:id="#+id/imageView"
android:layout_width = "wrap_content"
android:layout_height ="wrap_content"
android:scaleType="matrix"/>
</LinearLayout>
And here is how I open the layout and set the image.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myLayout);
//set timetable image
TouchImageView imgView = (TouchImageView)findViewById(R.id.imageView);
imgView.setImageResource(R.drawable.myImage);
//TODO: scroll to top left corner of image
}
The cause of the problem is that scrollToPosition(x,y) in TouchImageView doens't use the x and y pixels as input, but instead a number between 0 and 1 that reflects a proportion of the image size.
Also the scrollToPosition(x,y) sets a point of the image in the center of the TouchImageView. So if you call scrollToPosition(0.5,0.5) on the TouchImageView, the center of the image is being displayed at the center of the TouchImageView. We need to calculate which point of the image needs to be placed in the center of the TouchImageView to get it aligned nicely.
I created the function scrollToTopLeft() in TouchImageView.java, which only works if you call it at the end of onMeasure() from within the TouchImageView.java file. If you call it earlier the getWidth() and getHeight() will return 0 as the view hasn't been sized yet.
public void scrollToTopLeft() {
try {
float x, y, viewWidth, viewHeight, viewCenterX, viewCenterY, imageWidth, imageHeight;
//these calls will only work if called after (or at the end of) onMeasure()
viewWidth = this.getWidth();
viewHeight = this.getHeight();
// get center of view
viewCenterX = viewWidth / 2;
viewCenterY = viewHeight / 2;
// get image height and width
imageWidth = getImageWidth();
imageHeight = getImageHeight();
//calculate the x and y pixels that need to be displayed in at the center of the view
x = viewWidth / imageWidth * viewCenterX;
y = viewHeight / imageHeight * viewCenterY;
//calculate the value of the x and y pixels relative to the image
x = x / imageWidth;
y = y / imageHeight;
setScrollPosition(x, y);
} catch (Exception E) {
Log.v(TAG, E.toString());
}
}

how to get bounds of a scaled view?

It seems that setScaleX or setScaleY don't actually change left,top,right,bottom properties. getX and getY remain unchanged too.
So if I scale a view whats the easiest way to get 4 corner coordinates of the newly scaled view?
I tried getHitRect but that doesn't give me the right answer. I am trying to avoid manually calculating the new bounds based on existing transformations (rotation and scale with pivots factored in).
After exploring the view api, it looks like there is no direct API method that does this.
However you can easily get the new points by grabbing the transform matrix of the view and using that to get the new bounds.
Something like this:
Matrix m = view.getMatrix();
Rect bbox = new Rect();
view.getDrawingRect(bbox);
m.mapRect(bbox);
If you want to operate on (x,y) coordiantes directly there is a matrix.mapPoints that will achieve the same result.
I believe if you get the width and height and multiply it by the scales, you'll get the scaled width and height.
int scaledWidth = getWidth() * getScaleX();
int scaledHeight = getHeight() * getScaleY();
int newLeft = getLeft() + (scaledWidth / 2);
int newRight = newLeft + scaledWidth;
int newTop = getTop() + (scaledHeight / 2);
int newBottom = newTop + scaledHeight;
This is assuming that you scaled with a pivot x and y at the center of the view. Things gets far more complicated if you have pivots in strange areas.

Touch coordinates in a centered and scaled ImageView

I have a classic ImageView that is resized to fit its parent with match_parent on both axes and scaleType is fitCenter.
The coordinates I get out of an onTouch event, however, are relative to the bounds of the ImageView. How can I convert them to be relative to the bounds of the image itself?
UPDATE
I ended up doing this manually. This is in Scala, but probably easy to translate into Java :
// Long press on the screen adds a new interaction at that position
screenView onTouch {
(v: View, ev: MotionEvent) => {
// Create the image transformation matrix
val m = screenView.getImageMatrix
val d = screenView.getDrawable
val drawableRect = new RectF(0, 0, d.getIntrinsicWidth, d.getIntrinsicHeight)
val viewRect = new RectF(0, 0, screenView.getWidth, screenView.getHeight)
m.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.CENTER)
m.invert(m)
// Convert the points inside the image
val points = Array(ev.getX, ev.getY)
m mapPoints points
// Compute normalized coordinates
current_x = points(0) / d.getIntrinsicWidth
current_y = points(1) / d.getIntrinsicHeight
// Only continue if we touched inside the image
!(current_x >= 0 && current_x <= 1 &&
current_y >= 0 && current_y <= 1)
}
}
You can calculate it mathematically.
Compute the ratio between height & width of the image, then calculate the actual width of the image as displayed.
Subtract the image's actual width from the imageview's width, and divide by two.
This will give you the width of the grey area left of the image. Simply subtract this number from the X coordinates you got, and you will get the relative X position from the actual image.

How to do hit test on a rotated image in Android?

I am trying to do hit test on a rotated image object drawn on the canvas in surface view.
When image is not rotate we can directly use left, top , width and height as boundaries to check whether the point lies within image rectangle. But how to do it when the object is rotated to some angle?
I am using: canvas.rotate(angle, pivotX ,pivotY); to draw the rotated image.
I could not get the rotated left and top of the image object. I tried to take original left and top of the image and when i tap on the screen i rotate the touch point back with same angle using:
angledTouchX = (float) (eventX * Math.cos(-objectAngle) - eventY * Math.sin(-objectAngle));
angledTouchY = (float) (eventY * Math.sin(-objectAngle) + eventX * Math.cos(-objectAngle));
It does not work because it rotates the point wrt (0,0), but i want it wrt center of the image object.
Was working on similar problem. In my case, I want to do a hottest between touchpoint and rotated and scaled view. So I transform touch point co-ordinate system according to view's rotation and check if its inside non-rotated view boundary or not.
Step
Get touch point in screen co-ordinate system which received from event.getRawX() and event.getRawY()
Target view location in screen location which received from
int location[] = new int[2];
view.getLocationOnScreen(location);
int viewX = location[0];
int viewY = location[1];
Create vector as if view origin is the space origin
float[] vec = new float[2];
vec[0] = event.getRawX() - viewX;
vec[1] = event.getRawY() - viewY;
Rotate the vector
Matrix matrix = new Matrix();
matrix.setRotate(-1 * view.getRotation());
matrix.mapVectors(vec);
Change rotated vector to screen space
vec[0] = vec[0] + viewX;
vec[1] = vec[1] + viewY;
Check if rotated touch co-ordinate is in view unrotated boundary
if(( vec[0] > viewX
&& vec[0] < (viewX + (view.getWidth() * view.getScaleX()) ))
&&( vec[1] > viewY
&& vec[1] < (viewY + (view.getHeight() * view.getScaleY()) ))){
isPointInTextView = true;
} else {
isPointInTextView = false;
}
You need to work with it mathematically. the best way to do is before you rotating the canvas change the position by width/2, height/2. Then apply rotation, finally move back to the previous location by width/2, height/2. Then your picture will be always rotated from the center. But still after rotating your new image will have a new size which fits as a rectagle align to screen sides.

Categories

Resources