Як отримати абсолютні координати погляду


390

Я намагаюся отримати абсолютні координати пікселів екрана у верхньому лівому куті зображення. Однак усі методи, які я можу знайти, такі як getLeft()і getRight()не працюють, оскільки всі вони, мабуть, відносяться до батьківського погляду, таким чином даючи мені 0. Який правильний спосіб це зробити?

Якщо це допомагає, це для гри «привести картинку в порядок». Я хочу, щоб користувач міг намалювати вікно, щоб вибрати кілька фрагментів. Моє припущення полягає в тому, що найпростіший спосіб зробити це - до getRawX()і getRawY()з, MotionEventа потім порівняти ці значення з лівим верхнім кутом макета, утримуючи шматки. Знаючи розмір шматочків, я можу потім визначити, скільки штук було відібрано. Я усвідомлюю, що я можу використовувати getX()і getY()на MotionEvent, але тому повертає відносну позицію, яка ускладнює визначення того, які фрагменти були вибрані складніше. (Не знаю, я знаю, але це здається зайвим складним).

Редагувати: Це код, який я використовував, щоб спробувати отримати розмір контейнера для зберігання, відповідно до одного з питань. TableLayoutце стіл, на якому розміщені всі частини головоломки.

TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);
Log.d(LOG_TAG, "Values " + tableLayout.getTop() + tableLayout.getLeft());

Редагувати 2: Ось код, який я спробував, дотримуючись більше запропонованих відповідей.

public int[] tableLayoutCorners = new int[2];
(...)

TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);
tableLayout.requestLayout();
Rect corners = new Rect();
tableLayout.getLocalVisibleRect(corners);
Log.d(LOG_TAG, "Top left " + corners.top + ", " + corners.left + ", " + corners.right
            + ", " + corners.bottom);

cells[4].getLocationOnScreen(tableLayoutCorners);
Log.d(LOG_TAG, "Values " + tableLayoutCorners[0] + ", " + tableLayoutCorners[1]);

Цей код було додано після всієї ініціалізації. Зображення було поділено на масив ImageViews (масив комірок []), що міститься в TableLayout. Клітини [0] - лівий верхній бік ImageView, і я вибрав осередки [4], оскільки він знаходиться десь посередині і, безумовно, не повинен мати координат (0,0).

Код, показаний вище, все ще дає мені всі 0 в журналах, які я насправді не розумію, оскільки різні частини головоломки правильно відображаються. (Я спробував public int для tableLayoutCorners і видимості за замовчуванням, і ті й інші дають однаковий результат.)

Я не знаю, чи це важливо, але ImageViewспочатку s не мають розміру. Розмір ImageViews визначається під час ініціалізації автоматично Переглядом, коли я даю йому зображення для відображення. Чи може це сприяти тому, щоб їх значення були 0, навіть незважаючи на те, що код реєстрації відбувається після того, як вони отримали зображення та автоматично змінили розмір? Щоб потенційно протистояти цьому, я додав код, tableLayout.requestLayout()як показано вище, але це не допомогло.

Відповіді:


632

Використання View.getLocationOnScreen()та / або getLocationInWindow().


3
Тепер саме таку функцію я очікував знайти. На жаль, я не повинен його правильно використовувати. Я розумію, що це була відома помилка, що getLocationOnScreen дає нульовий виняток вказівника в Android v1.5 (платформа, яку я кодую), але тестування в v2.1 не працювало краще. Обидва методи дали мені 0s за все. Я включив фрагмент коду вище.
Стів Хейлі

76
Ви можете викликати його лише ПІСЛЯ того, як трапилось розташування Ви викликаєте метод до того, як представлення будуть розміщені на екрані.
Ромен Гай

5
Ага, ти маєш рацію, хоча було не очевидно, що я це роблю. Дякую! Для тих, хто цікавиться детальніше, я додав відповідь, яка пояснює, що я маю на увазі.
Стів Хейлі

9
Ви маєте на увазі, як ViewTreeObserver та його OnGlobalLayoutListener? Для початку див. Розділ View.getViewTreeObserver ().
Ромен Гай

8
@RomainGuy Так, подібне, але менш заплутане, ніж реєструватися (а потім скасувати реєстрацію…). Це область, де iOS робить це краще, ніж Android в плані SDK. Щодня використовуючи і те, і інше, я можу сказати, що в iOS є багато дратівливих речей, але обробка UIView не одна з таких. :)
Martin Marconcini

127

Прийнята відповідь насправді не розповіла, як отримати місцеположення, тому ось трохи детальніше. Ви передаєте intмасив довжиною 2, і значення замінюються координатами подання (x, y) (верхнього, лівого кута).

int[] location = new int[2];
myView.getLocationOnScreen(location);
int x = location[0];
int y = location[1];

Примітки

  • Заміна getLocationOnScreenз getLocationInWindowповинен давати однакові результати в більшості випадків (див цієї відповіді ). Однак якщо ви знаходитесь у меншому вікні, наприклад, діалоговому вікні чи спеціальній клавіатурі, тоді для використання вам потрібно буде вибрати, яке краще відповідає вашим потребам.
  • Ви отримаєте, (0,0)якщо зателефонуєте до цього методу, onCreateоскільки представлення ще не було викладено. Ви можете використовувати a ViewTreeObserverдля прослуховування, коли оформлення зроблено, і ви можете отримати виміряні координати. (Дивіться цю відповідь .)

86

Спочатку ви повинні отримати локальний видний прямокутник виду

Наприклад:

Rect rectf = new Rect();

//For coordinates location relative to the parent
anyView.getLocalVisibleRect(rectf);

//For coordinates location relative to the screen/display
anyView.getGlobalVisibleRect(rectf);

Log.d("WIDTH        :", String.valueOf(rectf.width()));
Log.d("HEIGHT       :", String.valueOf(rectf.height()));
Log.d("left         :", String.valueOf(rectf.left));
Log.d("right        :", String.valueOf(rectf.right));
Log.d("top          :", String.valueOf(rectf.top));
Log.d("bottom       :", String.valueOf(rectf.bottom));

Сподіваюсь, це допоможе


3
Це теж повинно було попрацювати ... Однак це також дає мені значення 0. Я включив фрагмент коду вище, якщо це допоможе вам зрозуміти, що я зробив не так. Знову дякую.
Стів Хейлі

1
Дивіться інші мої коментарі; Я випадково зателефонував до цього, перш ніж Погляди закінчили викладати. Зараз це добре працює, дякую.
Стів Хейлі

@Naveen Murthy він дає координати конкретному виду, мені потрібні координати клацнутого виду в кореневому макеті ..
Aniket

20
це повертає розташування відносно батьківського. використання getGlobalVisibleRect()для абсолютних координат.
Джефрі Блатман

3
@SteveHaley, я зіткнувся з тим же питанням, що і ви, чомусь це дає мені 0 у кожному методі, який я намагаюся. Здається, ви зрозуміли, чому це дає нуль. Я намагаюся отримати координати зору після завантаження макета, я все одно отримую нуль, чому це так? Дякуємо за допомогу
Pankaj Nimgade

46

Використання глобального прослуховувача макетів для мене завжди добре працювало. Він має перевагу в тому, що можна переоцінювати речі, якщо макет змінено, наприклад, якщо щось встановлено на View.GONE або дочірні подання додаються / видаляються.

public void onCreate(Bundle savedInstanceState)
{
     super.onCreate(savedInstanceState);

     // inflate your main layout here (use RelativeLayout or whatever your root ViewGroup type is
     LinearLayout mainLayout = (LinearLayout ) this.getLayoutInflater().inflate(R.layout.main, null); 

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

               // measure your views here
          }
       }
     );

     setContentView(mainLayout);
 }

Не забудьте видалити OnGlobalLayoutListener всередині (в кінці) onGlobalLayout () {...}. Крім того, щоб переконатися, перевірте, чи пройшло! = 0; слухача можна викликати двічі, один раз зі значенням w = 0 / h = 0, другий раз із фактичними, правильними, значеннями.
MacD

Цей спосіб надути основний макет, а потім використовувати, setContentView()викликав проблему в спеціальному Activityв моєму додатку! Це була діалогічна діяльність ( windowFrame:@null, windowActionBar:false, windowIsFloating:true, windowNoTitle:true). Її основний макет (а LinearLayout) не має жодної прямої дитини з рішучістю layout_width(усі вони були match_parentчи wrap_content).
Мір-Ісмаїлі

17

Після коментаря Ромена Гая, ось як я це виправив. Сподіваємось, це допоможе всім, хто також мав цю проблему.

Я справді намагався отримати позиції поглядів до того, як вони були викладені на екран, але це зовсім не очевидно, що відбувається. Ці рядки були розміщені після запуску коду ініціалізації, тож я припустив, що все готово. Однак цей код все ще був у onCreate (); експериментуючи з Thread.sleep (), я виявив, що макет фактично не доопрацьований, поки після того, як onCreate () весь шлях до onResume () закінчив виконання. Таким чином, код намагався запустити до того, як макет закінчився розміщенням на екрані. Додавши код до OnClickListener (або якогось іншого слухача), були отримані правильні значення, оскільки його можна було запустити лише після закінчення компонування.


Рядок нижче запропоновано як редагування спільноти:

будь ласка, використовуйте onWindowfocuschanged(boolean hasFocus)


Отже, ви хочете сказати, що setContentView (my_layout); не розмістить усіх поглядів. Усі перегляди будуть розміщені лише після завершення onCreate ()?
Ешвін

5
Не зовсім. Усі перегляди "існують" після setContentView(R.layout.something), але вони не завантажуються візуально. Вони не мають розміру чи положення. Це не відбувається до тих пір, поки не завершиться перший пропуск макета, що станеться в якийсь момент у майбутньому (як правило, після onResume ()).
Стів Хейлі

14

Перший шлях:

У Котліні ми можемо створити просте розширення для перегляду:

fun View.getLocationOnScreen(): Point
{
    val location = IntArray(2)
    this.getLocationOnScreen(location)
    return Point(location[0],location[1])
}

І просто отримайте координати:

val location = yourView.getLocationOnScreen()
val absX = location.x
val absY = location.y

Другий шлях:

Другий спосіб простіший:

fun View.absX(): Int
{
    val location = IntArray(2)
    this.getLocationOnScreen(location)
    return location[0]
}

fun View.absY(): Int
{
    val location = IntArray(2)
    this.getLocationOnScreen(location)
    return location[1]
}

і просто отримати абсолютний X на view.absX()і Y поview.absY()


6
Скорочена версія для вашого першого зразка:val location = IntArray(2).apply(::getLocationOnScreen)
Туан Чау

5

Мої утиліти для отримання місця перегляду, він поверне Pointоб'єкт з x valueіy value

public static Point getLocationOnScreen(View view){
    int[] location = new int[2];
    view.getLocationOnScreen(location);
    return new Point(location[0], location[1]);
}

Використання

Point viewALocation = getLocationOnScreen(viewA);

5

Ви можете отримати координати представлення за допомогою getLocationOnScreen()абоgetLocationInWindow()

Після цього xі yповинен бути верхній лівий кут зору. Якщо ваш кореневий макет менший за екран (як у діалоговому вікні), використовуючиgetLocationInWindow буде відносно його контейнера, а не всього екрана.

Рішення Java

int[] point = new int[2];
view.getLocationOnScreen(point); // or getLocationInWindow(point)
int x = point[0];
int y = point[1];

ПРИМІТКА. Якщо значення завжди дорівнює 0, ви, ймовірно, змінили подання безпосередньо перед тим, як подати запит на місцезнаходження.

Щоб переконатися, що перегляд отримав можливість оновити, запустіть свій запит про місцезнаходження після того, як буде розраховано новий макет Перегляду, використовуючи view.post:

view.post(() -> {
    // Values should no longer be 0
    int[] point = new int[2];
    view.getLocationOnScreen(point); // or getLocationInWindow(point)
    int x = point[0];
    int y = point[1];
});

~~

Рішення Котліна

val point = IntArray(2)
view.getLocationOnScreen(point) // or getLocationInWindow(point)
val (x, y) = point

ПРИМІТКА. Якщо значення завжди дорівнює 0, ви, ймовірно, змінили подання безпосередньо перед тим, як подати запит на місцезнаходження.

Щоб переконатися, що перегляд отримав можливість оновити, запустіть свій запит про місцезнаходження після того, як буде розраховано новий макет Перегляду, використовуючи view.post:

view.post {
    // Values should no longer be 0
    val point = IntArray(2)
    view.getLocationOnScreen(point) // or getLocationInWindow(point)
    val (x, y) = point
}

Я рекомендую створити функцію розширення для управління цим:

// To use, call:
val (x, y) = view.screenLocation

val View.screenLocation get(): IntArray {
    val point = IntArray(2)
    getLocationOnScreen(point)
    return point
}

А якщо вам потрібна надійність, також додайте:

view.screenLocationSafe { x, y -> Log.d("", "Use $x and $y here") }

fun View.screenLocationSafe(callback: (Int, Int) -> Unit) {
    post {
        val (x, y) = screenLocation
        callback(x, y)
    }
}

0

Використовуйте цей код, щоб знайти точні кордони X і Y:

int[] array = new int[2];
ViewForWhichLocationIsToBeFound.getLocationOnScreen(array);
if (AppConstants.DEBUG)
    Log.d(AppConstants.TAG, "array  X = " + array[0] + ", Y = " + array[1]);
ViewWhichToBeMovedOnFoundLocation.setTranslationX(array[0] + 21);
ViewWhichToBeMovedOnFoundLocation.setTranslationY(array[1] - 165);

Я додав / відніс деякі значення, щоб відкоригувати свій погляд. Будь ласка, виконайте ці рядки лише після того, як весь перегляд завищений.


Msgstr "Я додав / відніс деякі значення для коригування моєї точки зору." Чому ??
ToolmakerSteve

Я маю на увазі відповідно до моєї вимоги (Розміщення подання), я додав і відняв значення, щоб відкоригувати свій погляд.
Тарун

0

Отримати на екрані позицію та розмір перегляду

val viewTreeObserver: ViewTreeObserver = videoView.viewTreeObserver;

    if (viewTreeObserver.isAlive) {
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                //Remove Listener
                videoView.viewTreeObserver.removeOnGlobalLayoutListener(this);

                //View Dimentions
                viewWidth = videoView.width;
                viewHeight = videoView.height;

                //View Location
                val point = IntArray(2)
                videoView.post {
                    videoView.getLocationOnScreen(point) // or getLocationInWindow(point)
                    viewPositionX = point[0]
                    viewPositionY = point[1]
                }

            }
        });
    }

0

Просто на додаток до вищезазначених відповідей, на питання, куди і коли слід телефонувати getLocationOnScreen?

Будь-яка інформація, пов’язана з будь-яким представленням, який ви хочете отримати, вона буде доступна лише після того, як вид буде викладений на екрані. Ви можете легко зробити це, вставивши свій код, який залежить від створення перегляду всередині цього (view.post (Runnable)):

view.post(new Runnable() {
            @Override
            public void run() {

               // This code will run view created and rendered on screen

               // So as the answer to this question, you can put
               int[] location = new int[2];
               myView.getLocationOnScreen(location);
               int x = location[0];
               int y = location[1];
            }
        });  
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.