Getting View's Coordinates Relative to the Root Layout

Getting View's coordinates relative to the root layout

This is one solution, though since APIs change over time and there may be other ways of doing it, make sure to check the other answers. One claims to be faster, and another claims to be easier.

private int getRelativeLeft(View myView) {
if (myView.getParent() == myView.getRootView())
return myView.getLeft();
else
return myView.getLeft() + getRelativeLeft((View) myView.getParent());
}

private int getRelativeTop(View myView) {
if (myView.getParent() == myView.getRootView())
return myView.getTop();
else
return myView.getTop() + getRelativeTop((View) myView.getParent());
}

Let me know if that works.

It should recursively just add the top and left positions from each parent container.
You could also implement it with a Point if you wanted.

get position of view with respect to top-most parent in android

To find out absolute view location, you can use view.getLocationOnScreen() or view.getLocationInWindow().

How to get the absolute coordinates of a view

Use View.getLocationOnScreen() and/or getLocationInWindow().

Get the position of each corner of a view, then add a view at the position

Since the top-level layout is a RelativeLayout, you will need to use the view positioning that is available to RelativeLayout to achieve what you want. (See the documentation.)

Here is a mock-up of what you want to achieve in XML. This mock-up will demonstrate how we can approach the actual solution. I am using standard views, but it shouldn't matter. The technique will apply to your custom views. The image is from Android Studio's designer, so no code was used to create the image.

Sample Image

activity_main.xml

<RelativeLayout 
android:id="@+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<RelativeLayout
android:id="@+id/customView"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_centerInParent="true"
android:background="@android:color/holo_green_light" />

<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignStart="@id/customView"
android:layout_alignTop="@id/customView"
android:src="@drawable/circle"
android:translationX="-10dp"
android:translationY="-10dp" />

<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignEnd="@id/customView"
android:layout_alignTop="@id/customView"
android:src="@drawable/circle"
android:translationX="10dp"
android:translationY="-10dp" />

<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignBottom="@id/customView"
android:layout_alignStart="@id/customView"
android:src="@drawable/circle"
android:translationX="-10dp"
android:translationY="10dp" />

<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignBottom="@id/customView"
android:layout_alignEnd="@id/customView"
android:src="@drawable/circle"
android:translationX="10dp"
android:translationY="10dp" />

</RelativeLayout>

circle.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!-- fill color -->
<solid android:color="@android:color/holo_red_light" />
<size
android:width="20dp"
android:height="20dp" />
</shape>

The Actual Solution

Now that we have demonstrated that the mocked-up approach works, we now have to reproduce the effect in code. We will have to add the circle view and position it within the parent RelativeLayout using RelativeLayout view positioning and translations. The following code shows just the top left circle positioned, but the other circles will be positioned in a similar way.

activity_main.java

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Drawable circle = ContextCompat.getDrawable(this, R.drawable.circle);
ImageView imageView = new ImageView(this);
imageView.setImageDrawable(circle);
int circleSize = dpToPx(CIRCLE_SIZE_DP);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(circleSize, circleSize);

// Position top left circle within the custom view.
lp.addRule(RelativeLayout.ALIGN_START, R.id.customView);
lp.addRule(RelativeLayout.ALIGN_TOP, R.id.customView);

// Uncomment these 2 lines to position the top left circle with translation.
imageView.setTranslationX(-circleSize / 2);
imageView.setTranslationY(-circleSize / 2);

// Uncomment these 3 lines to position the top left circle with margins.
// View customView = findViewById(R.id.customView);
// lp.leftMargin = customView.getLeft() - circleSize / 2;
// lp.topMargin = customView.getTop() - circleSize / 2;

((RelativeLayout) findViewById(R.id.relativeLayout)).addView(imageView, lp);
}

private int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}

private static final int CIRCLE_SIZE_DP = 20;
}

The code above uses a shortened layout:

activity_main.xml

<RelativeLayout 
android:id="@+id/relativeLayout"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<RelativeLayout
android:id="@+id/customView"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_centerInParent="true"
android:background="@android:color/holo_green_light" />

</RelativeLayout>

It is also possible to produce the same positioning using margins. The code to use margins is commented out but will work. (I think that negative margins may also work, but I have read that they are not officially supported, so I try to avoid them.)

Android get view location on screen

It might be because you tried to get position before onLayout has happened.

You might try something like this:

ViewTreeObserver vto=view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener(){
@Override public void onGlobalLayout(){
int [] location = new int[2];
view.getLocationOnScreen(location);
x = location[0];
y = location[1];
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}

Also check how to get view's position in coordinates?

Position of view relative to parent

You can try traversing "upwards" from each view, summing each containers position in the process:

/** Returns array of views [x, y] position relative to parent **/
public static int[] getPositionInParent(ViewGroup parent, View view){
int relativePosition[] = {view.getLeft(), view.getTop()};
ViewGroup currentParent = (ViewGroup) view.getParent();
while (currentParent != parent){
relativePosition[0] += currentParent.getLeft();
relativePosition[1] += currentParent.getTop();
currentParent = (ViewGroup) currentParent.getParent();
}
return relativePosition;
}

This has no error handling, but should work even in more nested layouts.

Getting screen coordinates with layout weight property - android

getLeft() returns left border relative to parent. You should recursively add getLeft() of all parents as well.

Explained differently: both b4 and b8 return the same number for getLeft() call. if you want their absolute screen coordinates, see

How to get the absolute coordinates of a view

int pos[] = new int[2];
button.getLocationOnScreen(pos);
int x1 = pos[0], y1 = pos[1];
int x2 = x1 + button.getWidth();
int y2 = y1 + button.getHeight();

JavaFX - Get Coordinates of Node Relative to its Parent

Node.getBoundsInParent returns the bounds of a node in it's parent coordinates. E.g. polygon.getBoundsInParent() would return the bounds in the VBox.

If you need to "go up" one additional step, you can use parent.localToParent to do this. vBox.localToParent(boundsInVbox) returns the bounds in the coordinate system of the AnchorPane.

To get values relative to the size of the image, you simply need to divide by it's size.

The following example only allows you to move the cover regions to in one direction and does not check, if the regions intersect, but it should be sufficient to demonstrate the approach.

The interesting part is the event handler of the button. It restricts the viewport of the second image to the part of the first image that isn't covered.

private static void setSideAnchors(Node node) {
AnchorPane.setLeftAnchor(node, 0d);
AnchorPane.setRightAnchor(node, 0d);
}

@Override
public void start(Stage primaryStage) {
// create covering area
Region topRegion = new Region();
topRegion.setStyle("-fx-background-color: white;");
Polygon topArrow = new Polygon(0, 0, 20, 0, 10, 20);
topArrow.setFill(Color.WHITE);
VBox top = new VBox(topRegion, topArrow);
top.setAlignment(Pos.TOP_CENTER);
topArrow.setOnMouseClicked(evt -> {
topRegion.setPrefHeight(topRegion.getPrefHeight() + 10);
});

// create bottom covering area
Region bottomRegion = new Region();
bottomRegion.setStyle("-fx-background-color: white;");
Polygon bottomArrow = new Polygon(0, 20, 20, 20, 10, 0);
bottomArrow.setFill(Color.WHITE);
VBox bottom = new VBox(bottomArrow, bottomRegion);
bottom.setAlignment(Pos.BOTTOM_CENTER);
bottomArrow.setOnMouseClicked(evt -> {
bottomRegion.setPrefHeight(bottomRegion.getPrefHeight() + 10);
});

Image image = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg/402px-Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg");
ImageView imageView = new ImageView(image);

setSideAnchors(top);
setSideAnchors(bottom);
setSideAnchors(imageView);
AnchorPane.setTopAnchor(top, 0d);
AnchorPane.setBottomAnchor(bottom, 0d);
AnchorPane.setTopAnchor(imageView, 0d);
AnchorPane.setBottomAnchor(imageView, 0d);

AnchorPane container = new AnchorPane(imageView, top, bottom);

ImageView imageViewRestricted = new ImageView(image);

Button button = new Button("restrict");
button.setOnAction(evt -> {
// determine bouns of Regions in AnchorPane
Bounds topBounds = top.localToParent(topRegion.getBoundsInParent());
Bounds bottomBounds = bottom.localToParent(bottomRegion.getBoundsInParent());

// set viewport accordingly
imageViewRestricted.setViewport(new Rectangle2D(
0,
topBounds.getMaxY(),
image.getWidth(),
bottomBounds.getMinY() - topBounds.getMaxY()));
});

HBox root = new HBox(container, button, imageViewRestricted);
root.setFillHeight(false);

Scene scene = new Scene(root);

primaryStage.setScene(scene);
primaryStage.show();
}


Related Topics



Leave a reply



Submit