뷰의 절대 좌표를 얻는 방법


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

이 코드는 모든 초기화가 완료된 후에 추가되었습니다. 이미지가에 포함 된 ImageView 배열 (cells [] 배열)로 분할되었습니다 TableLayout. Cells [0]은 왼쪽 상단 ImageView이며 중간에 어딘가에 있으므로 [4] 셀을 선택했으며 좌표는 (0,0)이 아니어야합니다.

위에 표시된 코드는 여전히 로그에 0을 모두 제공합니다. 여러 퍼즐 조각이 올바르게 표시되기 때문에 실제로 이해할 수 없습니다. (TableLayoutCorners 및 기본 가시성에 대해 public int를 시도했지만 동일한 결과를 얻었습니다.)

이것이 중요한지 모르겠지만 ImageView원래 크기는 주어지지 않았습니다. ImageViews 의 크기는 표시 할 이미지를 제공 할 때 View에 의해 자동으로 초기화하는 동안 결정됩니다. 로깅 코드에 이미지가 제공된 후 자동으로 크기가 조정 된 후에도 값이 0이 될 수 있습니까? 잠재적으로 그것을 반박하기 위해 tableLayout.requestLayout()위 의 코드 를 추가 했지만 도움이되지 않았습니다.

답변:


632

View.getLocationOnScreen()및 / 또는을 사용하십시오 getLocationInWindow().


3
이것이 내가 찾은 기능의 종류입니다. 불행히도, 나는 그것을 올바르게 사용해서는 안됩니다. getLocationOnScreen이 Android v1.5 (내가 코딩중인 플랫폼)에서 null 포인터 예외를 제공하는 것은 알려진 버그 였지만 v2.1에서의 테스트는 더 이상 작동하지 않습니다. 두 방법 모두 모든 것에 대해 0을 주었다. 위의 코드 스 니펫을 포함 시켰습니다.
Steve Haley

76
레이아웃이 발생한 후에 만 호출 할 수 있습니다 . 뷰가 화면에 배치되기 전에 메소드를 호출합니다.
Romain Guy

5
아하, 당신은 내가 그 일을하고 있는지 분명하지 않지만 맞습니다. 감사! 자세한 내용이 궁금한 사람은 의미하는 바를 설명하는 답변을 추가했습니다.
Steve Haley

9
ViewTreeObserver와 OnGlobalLayoutListener를 의미합니까? 시작하려면 View.getViewTreeObserver ()를 참조하십시오.
Romain Guy

8
@RomainGuy 그래, 그런 식이지만 등록 (그리고 등록 취소 ...)하는 것보다 덜 혼란 스럽습니다. SDK 측면에서 iOS가 Android보다 나은 영역입니다. 매일 두 가지를 모두 사용하면 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

먼저보기의 localVisible 사각형을 가져와야합니다.

예를 들어 :

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의 값을 제공합니다. 내가 잘못한 것을 알아내는 데 도움이되는 경우 위의 코드 스 니펫을 포함 시켰습니다. 다시 감사합니다.
Steve Haley

1
내 다른 의견을 참조하십시오; Views가 레이아웃을 마치기 전에 실수로 이것을 호출했습니다. 이것은 이제 감사합니다.
Steve Haley

@Naveen Murthy 그것은 특정보기에 좌표를 제공합니다, 나는 루트 레이아웃에서 클릭보기의 좌표가 필요합니다.
Aniket

20
부모에 상대적인 위치를 반환합니다. 사용 getGlobalVisibleRect()절대 좌표에 대한.
Jeffrey Blattman

3
@SteveHaley, 나는 당신과 같은 문제에 직면 해 왔으며 어떤 이유로 든 시도하는 모든 방법에서 0을 제공합니다. 왜 제로를 주는지 알아 낸 것 같습니다. 레이아웃이 여전히로드 된 후 뷰 좌표를 얻으려고하는데 왜 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);
 }

onGlobalLayout () {...} 내부 (끝)에서 OnGlobalLayoutListener를 제거하는 것을 잊지 마십시오. 또한, 확실하게 전달할 수 있는지 확인하십시오! = 0; 리스너는 w = 0 / h = 0의 값으로 한 번, 두 번째로 실제 정확한 값으로 두 번 호출 될 수 있습니다.
MacD

이 방법으로 메인 레이아웃을 팽창시킨 다음 사용 setContentView()하면 Activity내 앱 의 특수 문제가 발생했습니다 ! 대화 활동이었습니다 ( windowFrame:@null, windowActionBar:false, windowIsFloating:true, windowNoTitle:true). 메인 레이아웃 (a LinearLayout)은 결정된 직계 자식이 아닙니다 layout_width(모두 match_parent또는 wrap_content).
Mir-Ismaili

17

Romain Guy의 의견에 따라 수정 방법은 다음과 같습니다. 이 문제가있는 다른 사람에게 도움이 되길 바랍니다.

나는 그들이 화면에 배치되기 전에 실제로보기의 위치를 ​​얻으려고 노력했지만 전혀 일어나지 않았습니다. 이 코드는 초기화 코드가 실행 된 후에 배치되었으므로 모든 것이 준비되었다고 가정했습니다. 그러나이 코드는 여전히 onCreate ()에있었습니다. Thread.sleep ()을 실험하여 onResume ()까지 onCreate ()가 끝날 때까지 레이아웃이 실제로 마무리되지 않았다는 것을 알았습니다. 실제로 레이아웃이 화면에 배치되기 전에 코드를 실행하려고했습니다. 코드를 OnClickListener (또는 다른 리스너)에 추가하면 레이아웃이 완료된 후에 만 ​​실행될 수 있으므로 올바른 값을 얻습니다.


아래 줄은 커뮤니티 편집으로 제안되었습니다.

사용하시기 바랍니다 onWindowfocuschanged(boolean hasFocus)


따라서 setContentView (my_layout); 모든 뷰를 배치하지는 않습니다. 모든 뷰는 onCreate ()가 완료된 후에 만 ​​배치됩니까?
Ashwin

5
좀 빠지는. 모든보기는 이후에 '존재' setContentView(R.layout.something)하지만 시각적으로로드되지 않았습니다. 크기 나 위치가 없습니다. 첫 번째 레이아웃 패스가 완료 될 때까지는 발생하지 않으며, 이는 향후 특정 시점 (일반적으로 onResume () 이후)에서 발생합니다.
Steve Haley

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)
Tuan Chau

5

내 유틸은 GET보기 위치 기능, 그것은 반환 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()

그 후, xy뷰의 왼쪽 상단 모서리에 있어야한다. 루트 레이아웃이 대화 상자에서와 같이 화면보다 작은 경우getLocationInWindow 전체 화면이 아니라 컨테이너를 기준으로 됩니다.

자바 솔루션

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

뷰를 조정하기 위해 일부 값을 더하거나 뺍니다. 전체 선이 팽창 된 후에 만이 선을 수행하십시오.


"보기를 조정하기 위해 일부 값을 더하거나 뺍니다." 왜??
ToolmakerSteve

내 요구 사항 (위치)에 따라 뷰를 조정하기 위해 값을 더하고 뺍니다.
Tarun

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 십니까?

가져 오려는보기와 관련된 정보는 화면에보기가 배치 된 후에 만 ​​사용할 수 있습니다. this (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];
            }
        });  
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.