Получение координат представления относительно корневого макета


Ответы:


139

Это одно из решений, хотя, поскольку API со временем меняются, и могут быть другие способы сделать это, обязательно проверьте другие ответы. Один утверждает, что он быстрее, а другой утверждает, что он проще.

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());
}

Дай мне знать, если это работает.

Он должен рекурсивно просто добавлять верхнюю и левую позиции из каждого родительского контейнера. Вы также можете реализовать его, Pointесли хотите.


getRelativeLeft (), это нужно добавить, но когда я добавляю (View) или (button) ((Object) myView.getParent ()). getRelativeTop (), это тоже неправильно
pengwang

Я сделал что-то очень похожее, но я проверил корневое представление с помощью getId == R.id.myRootView.
fhucho 02

1
Log.i ("RelativeLeft", "" + getRelativeLeft (findViewById (R.id.tv))); Log.i ("RelativeTop", "" + getRelativeTop (findViewById (R.id.tv))); у меня все равно 0; textView в макете выглядит следующим образом: <TextView android: layout_width = "fill_parent" android: layout_height = "wrap_content" android: text = "@ string / hello" android: id = "@ + id / tv" android: layout_marginBottom = "69dip" android: layout_marginLeft = "69dip" />
pengwang

3
API Android уже предоставляет относительные координаты корня. смотрите здесь stackoverflow.com/a/36740277/2008214
carlo.marinangeli 01

1
Каждый раз, когда я получаю возвращаемое значение 0 из обоих методов, у меня есть один TextView внутри RelativeLayout.
Рохит Бандил,

166

Android API уже предоставляет способ для этого. Попробуй это:

Rect offsetViewBounds = new Rect();
//returns the visible bounds
childView.getDrawingRect(offsetViewBounds);
// calculates the relative coordinates to the parent
parentViewGroup.offsetDescendantRectToMyCoords(childView, offsetViewBounds); 

int relativeTop = offsetViewBounds.top;
int relativeLeft = offsetViewBounds.left;

Вот документ


16
Это должен быть принятый ответ. Нет возможности переполнения стека и математических ошибок. Благодарность!
GLee

Я согласен, это должен быть принятый ответ - осталось всего 92 голоса за.
ошеломлен

Я хотел найти координаты вида на панели инструментов. Это единственный метод, который последовательно работает для учета android: fitsSystemWindows и различных версий API от 19 до 27. Так что спасибо! Это правильный ответ.
Дэвид Ферран

Сработало безупречно
Рава

Работает очень хорошо, просто хочу отметить, что это parentViewGroupможет быть любой родительский элемент в иерархии представлений, поэтому он отлично работает и для ScrollView.
Сира Лам

94

Пожалуйста, используйте view.getLocationOnScreen(int[] location);(см. Javadocs ). Ответ находится в целочисленном массиве (x = location[0]и y = location[1]).


13
Это возвращает положение относительно экрана, а не корневого макета Activity.
fhucho

11
Также существует View.getLocationInWindow (int []), который возвращает позицию представления относительно окна.
Rubicon

Подскажите, пожалуйста, как именно работает этот метод. Он возвращает пустоту, как мы можем получить выходные данные или значения x, y представления на экране
user1106888

6
Чтобы получить позицию относительно корневого макета, просто вызовите getLocationInWindow для корневого макета и элемента и используйте силу вычитания. Это намного проще и, вероятно, быстрее, чем принятый ответ.
Блейк Миллер

1
это просто место, относящееся к экрану. не корень. читайте здесь относительно root: stackoverflow.com/a/36740277/2008214
carlo.marinangeli 01

36
View rootLayout = view.getRootView().findViewById(android.R.id.content);

int[] viewLocation = new int[2]; 
view.getLocationInWindow(viewLocation);

int[] rootLocation = new int[2];
rootLayout.getLocationInWindow(rootLocation);

int relativeLeft = viewLocation[0] - rootLocation[0];
int relativeTop  = viewLocation[1] - rootLocation[1];

Сначала я получаю корневой макет, а затем вычисляю разницу координат с видом.
Вы также можете использовать getLocationOnScreen()вместо getLocationInWindow().


3
какой вид масштабируется и поворачивается?
DKV

Это единственный ответ, который мне подходит. Это должен быть принятый ответ
neoexpert

36

Не нужно рассчитывать вручную.

Просто используйте getGlobalVisibleRect так:

Rect myViewRect = new Rect();
myView.getGlobalVisibleRect(myViewRect);
float x = myViewRect.left;
float y = myViewRect.top;

Также обратите внимание, что для координат центра, а не что-то вроде:

...
float two = (float) 2
float cx = myViewRect.left + myView.getWidth() / two;
float cy = myViewRect.top + myView.getHeight() / two;

Вы можете просто сделать:

float cx = myViewRect.exactCenterX();
float cy = myViewRect.exactCenterY();

Глядя на исходный код Android, все getGlobalVisibleRectделается для того, globalOffset.set(-mScrollX, -mScrollY);чтобы вы могли просто получить эти значения, getScollX/Yесли вам нужны только относительные coodinates.
Xander

2
Это должен быть принятый ответ. getLocationOnScreen работает так же, как getGlobalVisibleRect. Но getGlobalVisibleRect предоставляет немного больше информации, и нет необходимости работать с необработанными массивами. Поэтому я бы выбрал getGlobalVisibleRect. Использование простое. Rect positionRect = new Rect (); view.getGlobablVisibleRect (positionRect); // x = positionRect.left // y = positionRect.top. ура
JacksOnF1re 01

если какая-то часть ребенка находится снаружи, что не будет учитываться в этом случае. как решить такой случай?
DKV

Отличный ответ! Спасибо огромное!
Рохит Кумар

8

Вы можете использовать `

view.getLocationOnScreen (int [] местоположение)

; `, чтобы правильно определить местоположение вашего обзора.

Но есть одна загвоздка: если вы используете его до того, как макет был увеличен, вы получите неправильную позицию.

Решение этой проблемы заключается в ViewTreeObserverследующем: -

Объявить глобально массив для хранения позиции xy вашего представления

 int[] img_coordinates = new int[2];

а затем добавьте ViewTreeObserverв свой родительский макет, чтобы получить обратный вызов для инфляции макета, и только затем выберите положение обзора, иначе вы получите неправильные координаты xy

  // set a global layout listener which will be called when the layout pass is completed and the view is drawn
            parentViewGroup.getViewTreeObserver().addOnGlobalLayoutListener(
                    new ViewTreeObserver.OnGlobalLayoutListener() {
                        public void onGlobalLayout() {
                            //Remove the listener before proceeding
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                                parentViewGroup.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                            } else {
                                parentViewGroup.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                            }

                            // measure your views here
                            fab.getLocationOnScreen(img_coordinates);
                        }
                    }
            );

а затем используйте это так

xposition = img_coordinates[0];
yposition =  img_coordinates[1];

3

Я написал себе два служебных метода, которые, кажется, работают в большинстве условий, обрабатывая прокрутку, перевод и масштабирование, но не вращение. Я сделал это после попытки использовать offsetDescendantRectToMyCoords () во фреймворке, который имел непоследовательную точность. В некоторых случаях это сработало, но в других дало неверные результаты.

«point» - это массив с плавающей запятой с двумя элементами (координаты x и y), «ancestor» - это группа просмотра где-то над «потомком» в древовидной иерархии.

Сначала метод, который переходит от координат потомка к предку:

public static void transformToAncestor(float[] point, final View ancestor, final View descendant) {
    final float scrollX = descendant.getScrollX();
    final float scrollY = descendant.getScrollY();
    final float left = descendant.getLeft();
    final float top = descendant.getTop();
    final float px = descendant.getPivotX();
    final float py = descendant.getPivotY();
    final float tx = descendant.getTranslationX();
    final float ty = descendant.getTranslationY();
    final float sx = descendant.getScaleX();
    final float sy = descendant.getScaleY();

    point[0] = left + px + (point[0] - px) * sx + tx - scrollX;
    point[1] = top + py + (point[1] - py) * sy + ty - scrollY;

    ViewParent parent = descendant.getParent();
    if (descendant != ancestor && parent != ancestor && parent instanceof View) {
        transformToAncestor(point, ancestor, (View) parent);
    }
}

Далее обратное, от предка к потомку:

public static void transformToDescendant(float[] point, final View ancestor, final View descendant) {
    ViewParent parent = descendant.getParent();
    if (descendant != ancestor && parent != ancestor && parent instanceof View) {
        transformToDescendant(point, ancestor, (View) parent);
    }

    final float scrollX = descendant.getScrollX();
    final float scrollY = descendant.getScrollY();
    final float left = descendant.getLeft();
    final float top = descendant.getTop();
    final float px = descendant.getPivotX();
    final float py = descendant.getPivotY();
    final float tx = descendant.getTranslationX();
    final float ty = descendant.getTranslationY();
    final float sx = descendant.getScaleX();
    final float sy = descendant.getScaleY();

    point[0] = px + (point[0] + scrollX - left - tx - px) / sx;
    point[1] = py + (point[1] + scrollY - top - ty - py) / sy;
}

2

Если кто-то все еще пытается в этом разобраться. Это, как вы получите центр X и Y из view.

    int pos[] = new int[2];
    view.getLocationOnScreen(pos);
    int centerX = pos[0] + view.getMeasuredWidth() / 2;
    int centerY = pos[1] + view.getMeasuredHeight() / 2;

1

Я только что нашел здесь ответ

В нем говорится: можно получить местоположение представления, вызвав методы getLeft () и getTop (). Первый возвращает левую или X координату прямоугольника, представляющего вид. Последний возвращает координату вершины или Y прямоугольника, представляющего вид. Оба эти метода возвращают расположение представления относительно его родителя. Например, когда getLeft () возвращает 20, это означает, что представление расположено на 20 пикселей справа от левого края своего прямого родителя.

так что используйте:

view.getLeft(); // to get the location of X from left to right
view.getRight()+; // to get the location of Y from right to left

1
Это дает расположение относительно непосредственного родителя представления, а не корневого представления, которое может быть родителем родительского представления и т. Д.
Руперт Роунсли

какой вид масштабируется и поворачивается?
DKV

0

Вы можете использовать следующее, чтобы узнать разницу между родителем и интересующим вас представлением:

private int getRelativeTop(View view) {
    final View parent = (View) view.getParent();
    int[] parentLocation = new int[2];
    int[] viewLocation = new int[2];

    view.getLocationOnScreen(viewLocation);
    parent.getLocationOnScreen(parentLocation);

    return viewLocation[1] - parentLocation[1];
}

Не забудьте вызвать его после отрисовки представления:

 timeIndicator.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
        final int relativeTop = getRelativeTop(timeIndicator);
    });
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.