Como obter as coordenadas absolutas de uma visualização

390

Estou tentando obter as coordenadas absolutas de pixels da tela no canto superior esquerdo de uma visualização. No entanto, todos os métodos que posso encontrar como getLeft()e getRight()não funcionam, pois todos parecem ser relativos ao pai da visualização, fornecendo-me assim 0. Qual é a maneira correta de fazer isso?

Se ajudar, isso é para um jogo de 'colocar a foto de volta em ordem'. Quero que o usuário possa desenhar uma caixa para selecionar várias peças. Minha suposição é que a maneira mais fácil de fazer isso é ir getRawX()e getRawY()vir de MotionEvente comparar esses valores com o canto superior esquerdo do layout que contém as peças. Conhecendo o tamanho das peças, posso determinar quantas peças foram selecionadas. Sei que posso usar getX()e getY()no MotionEvent, mas como isso retorna uma posição relativa que torna mais difícil determinar quais peças foram selecionadas. (Não é impossível, eu sei, mas parece desnecessariamente complicado).

Edit: Este é o código que eu usei para tentar obter o tamanho do contêiner de retenção, conforme uma das perguntas. TableLayouté a mesa que contém todas as peças do quebra-cabeça.

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

Edit 2: Aqui está o código que eu tentei, seguindo mais das respostas sugeridas.

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]);

Este código foi adicionado após toda a inicialização. A imagem foi dividida em uma matriz de ImageViews (a matriz de células []) contida em a TableLayout. As células [0] estão no canto superior esquerdo ImageViewe eu escolhi as células [4], pois estão em algum lugar no meio e, definitivamente, não devem ter coordenadas de (0,0).

O código mostrado acima ainda me dá todos os 0s nos logs, o que eu realmente não entendo porque as várias peças do quebra-cabeça são exibidas corretamente. (Tentei public int para tableLayoutCorners e visibilidade padrão, ambos dando o mesmo resultado.)

Não sei se isso é significativo, mas os ImageViews não recebem um tamanho originalmente. O tamanho dos ImageViews é determinado durante a inicialização automaticamente pela Visualização quando eu mostro uma imagem a ser exibida. Isso poderia contribuir para que seus valores fossem 0, mesmo que o código de registro ocorra depois de receberem uma imagem e se redimensionarem automaticamente? Para potencialmente combater isso, adicionei o código tableLayout.requestLayout()como mostrado acima, mas isso não ajudou.

Steve Haley
fonte

Respostas:

632

Use View.getLocationOnScreen()e / ou getLocationInWindow().

Romain Guy
fonte
3
Agora esse é o tipo de função que eu esperava encontrar. Infelizmente, não devo usá-lo corretamente. Sei que foi um bug conhecido que getLocationOnScreen fornece uma exceção de ponteiro nulo no Android v1.5 (a plataforma pela qual estou codificando), mas o teste na v2.1 não funcionou melhor. Ambos os métodos me deram 0s para tudo. Incluí um trecho de código acima.
Steve Haley
76
Você pode invocá-lo APÓS o layout acontecer. Você está chamando o método antes que as visualizações sejam posicionadas na tela.
Romain Guy
5
Aha, você está certo, embora não fosse óbvio que eu estava fazendo isso. Obrigado! Para qualquer pessoa curiosa para obter mais detalhes, adicionei uma resposta que explica o que quero dizer.
Steve Haley
9
Você quer dizer como o ViewTreeObserver e seu OnGlobalLayoutListener? Consulte View.getViewTreeObserver () para começar.
Romain Guy
8
@RomainGuy Sim, mais ou menos assim, mas menos confuso do que ter que se registrar (e depois cancelar o registro ...). Essa é uma área em que o iOS faz melhor que o Android em termos de SDK. Usando os dois diariamente, posso dizer que o iOS tem muitas coisas irritantes, mas o manuseio do UIView não é um deles. :)
Martin Marconcini
127

A resposta aceita não revelou como obter o local, então aqui está um pouco mais detalhadamente. Você passa em uma intmatriz de comprimento 2 e os valores são substituídos pelas coordenadas da vista (x, y) (no canto superior esquerdo).

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

Notas

  • Substituir getLocationOnScreenpor getLocationInWindowdeve fornecer os mesmos resultados na maioria dos casos (consulte esta resposta ). No entanto, se você estiver em uma janela menor como um Diálogo ou teclado personalizado, use-o para escolher qual deles melhor se adequa às suas necessidades.
  • Você receberá (0,0)se você chamar esse método onCreateporque a visualização ainda não foi definida. Você pode usar a ViewTreeObserverpara ouvir quando o layout estiver concluído e obter as coordenadas medidas. (Veja esta resposta .)
Suragch
fonte
86

Primeiro você precisa obter o retângulo localVisible da visualização

Por exemplo:

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

Espero que isso ajude

Naveen Murthy
fonte
3
Isso também deveria ter funcionado ... No entanto, está me dando valores de 0 também. Incluí um trecho de código acima, se isso ajudar você a descobrir o que fiz de errado. Obrigado novamente.
Steve Haley
11
Veja meus outros comentários; Eu estava acidentalmente chamando isso antes que os Views terminassem de se expor. Isso funciona bem agora, obrigado.
Steve Haley
@Naveen Murthy dá coordenadas para vista específico, eu preciso de coordenadas de vista clicado no layout raiz ..
Aniket
20
isso retorna a localização relativa ao pai. use getGlobalVisibleRect()para cordas absolutas.
Jeffrey Blattman
3
@ SteveHaley, eu tenho enfrentado o mesmo problema que você, por algum motivo, ele me dá 0 em cada método que eu tento. Parece que você descobriu porque está dando zero. Eu estou tentando obter coordenadas de visão depois que o layout é carregado ainda recebo zero por que é isso? Obrigado pela sua ajuda
Pankaj Nimgade
46

Usar um ouvinte de layout global sempre funcionou bem para mim. Ele tem a vantagem de poder avaliar novamente as coisas se o layout for alterado, por exemplo, se algo estiver definido como View.GONE ou visualizações filho forem adicionadas / removidas.

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);
 }
Simon
fonte
Não se esqueça de remover o OnGlobalLayoutListener dentro (no final de) do onGlobalLayout () {...}. Além disso, apenas para ter certeza, verifique se o que foi passado! = 0; o ouvinte pode ser chamado duas vezes, uma vez com um valor de w = 0 / h = 0, a segunda vez com os valores reais e corretos.
MacD
Dessa forma, para inflar o layout principal e, em seguida, o uso setContentView()causou um problema em um especial Activityno meu aplicativo! Essa foi uma atividade de diálogo ( windowFrame:@null, windowActionBar:false, windowIsFloating:true, windowNoTitle:true). Seu layout principal (a LinearLayout) não tem nenhum filho direto com determinado layout_width(todos eles eram match_parentou wrap_content).
Mir-Ismaili
17

Após o comentário de Romain Guy, aqui está como eu o consertei. Espero que ajude qualquer pessoa que também tenha esse problema.

Na verdade, eu estava tentando obter as posições das vistas antes que elas fossem exibidas na tela, mas não era óbvio o que estava acontecendo. Essas linhas foram colocadas após a execução do código de inicialização, então presumi que tudo estivesse pronto. No entanto, esse código ainda estava em onCreate (); experimentando Thread.sleep (), descobri que o layout não é realmente finalizado até depois que onCreate () todo o caminho até onResume () terminar de executar. Então, de fato, o código estava tentando executar antes que o layout terminasse de ser posicionado na tela. Ao adicionar o código a um OnClickListener (ou a algum outro ouvinte), os valores corretos foram obtidos, pois só podiam ser acionados após a conclusão do layout.


A linha abaixo foi sugerida como uma edição da comunidade:

Por favor, use onWindowfocuschanged(boolean hasFocus)

Steve Haley
fonte
Então você quer dizer que setContentView (my_layout); não colocará todas as visualizações. Todas as visualizações serão colocadas somente após a conclusão de onCreate ()?
Ashwin
5
Nem tanto. Todas as visualizações 'existem' depois setContentView(R.layout.something), mas não foram carregadas visualmente. Eles não têm tamanho ou posição. Isso não acontece até que a primeira passagem de layout seja concluída, o que acontece em algum momento no futuro (geralmente após onResume ()).
21412 Steve
14

Primeira maneira:

No Kotlin , podemos criar uma extensão simples para visualização:

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

E simplesmente obtenha coordenadas:

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

Segunda maneira:

A segunda maneira é mais simples:

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]
}

e simplesmente obtenha X absoluto view.absX()e Y porview.absY()

Amir Hossein Ghasemi
fonte
6
Uma versão mais curta para sua 1ª amostra:val location = IntArray(2).apply(::getLocationOnScreen)
Tuan Chau
5

Meus utils funcionam para obter a localização da visualização, ele retornará um Pointobjeto com x valueey value

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

Usando

Point viewALocation = getLocationOnScreen(viewA);
Phan Van Linh
fonte
5

Você pode obter as coordenadas de uma vista usando getLocationOnScreen()ougetLocationInWindow()

Depois, xe ydeve ser o canto superior esquerdo da exibição. Se o layout raiz for menor que a tela (como em um Diálogo), o uso getLocationInWindowserá relativo ao contêiner, não à tela inteira.

Solução Java

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

NOTA: Se o valor for sempre 0, é provável que você altere a exibição imediatamente antes de solicitar o local.

Para garantir que a visualização tenha a chance de atualizar, execute sua solicitação de local após o novo layout da visualização ter sido calculado usando 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];
});

~~

Solução Kotlin

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

NOTA: Se o valor for sempre 0, é provável que você altere a exibição imediatamente antes de solicitar o local.

Para garantir que a visualização tenha a chance de atualizar, execute sua solicitação de local após o novo layout da visualização ter sido calculado usando view.post:

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

Eu recomendo criar uma função de extensão para lidar com isso:

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

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

E se você precisar de confiabilidade, adicione também:

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)
    }
}
Gibolt
fonte
0

Use este código para encontrar cordinatos X e Y exatos:

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

Eu adicionei / subtraí alguns valores para ajustar minha visão. Faça essas linhas somente após a exibição completa ter sido inflada.

Tarun
fonte
"Eu adicionei / subtraí alguns valores para ajustar minha visão." Por quê??
Home
Quero dizer, conforme minha exigência (posicionamento da visualização), adicionei e subtraí valores para ajustar minha visualização.
Tarun
0

Obter posição e dimensão da vista na tela

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]
                }

            }
        });
    }
Hitesh Sahu
fonte
0

Além das respostas acima, para a pergunta de onde e quando você deve ligar getLocationOnScreen?

Em qualquer informação relacionada a qualquer visualização que você deseja obter, ela estará disponível apenas após a visualização na tela. Você pode fazer isso facilmente colocando seu código, que depende da criação da view dentro deste (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];
            }
        });  
Suraj Vaishnav
fonte