ビューの絶対座標を取得する方法


390

ビューの左上隅の絶対スクリーンピクセル座標を取得しようとしています。しかし、すべてのメソッドは、私のような見つけることができますgetLeft()し、getRight()これを私に与えて、彼らはすべてのビューの親に対するように見えるように動作しません0。これを行う適切な方法は何ですか?

それが役立つ場合、これは「画像を順番に戻す」ゲーム用です。ユーザーがボックスを描いて複数のピースを選択できるようにしたい。私の仮定は、それを行うための最も簡単な方法はにあるということであるgetRawX()getRawY()からMotionEvent、その後の作品を保持したレイアウトの左上隅に対するそれらの値を比較します。ピースのサイズがわかったら、選択されたピースの数を判断できます。私が使用することができ実現getX()getY()MotionEvent、それ戻る片がより困難に選択されたかを決定することができる相対位置として。(不可能ではない、私は知っている、しかしそれは不必要に複雑に思われる)。

編集:これは、質問の1つに従って、保持コンテナーのサイズを取得するために使用したコードです。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の配列(cells []配列)に分割されていますTableLayout。Cells [0]は左上でImageView、cells [4]を選択しました。これは真ん中のどこかにあり、間違いなく(0,0)の座標を持つべきではないためです。

上記のコードでは、ログにすべて0が表示されますが、さまざまなパズルのピースが正しく表示されているため、理解できません。(私はtableLayoutCornersとデフォルトの可視性の両方でpublic intを試しましたが、どちらも同じ結果が得られます。)

これが重要かどうかはわかりませんが、ImageViewsには元々サイズが与えられていません。ImageViews のサイズは、初期化中に、表示する画像を指定すると、ビューによって自動的に決定されます。そのロギングコードは、画像が与えられて自動的にサイズが変更された後でも、これはそれらの値が0になる原因になりますか?これに対抗するために、tableLayout.requestLayout()上記のコードを追加しましたが、効果がありませんでした。

回答:


632

View.getLocationOnScreen()および/またはを使用しgetLocationInWindow()ます。


3
これが、私が期待していた機能です。残念ながら、私はそれを適切に使用してはなりません。getLocationOnScreenがAndroid v1.5(私がコーディングしているプラ​​ットフォーム)でnullポインター例外を発生させるのは既知のバグでしたが、v2.1でのテストはうまく機能しませんでした。どちらの方法でも、すべてに0が与えられました。上記のコードスニペットを含めました。
スティーブヘイリー

76
あなたはそれを呼び出すことができAFTERレイアウトが起こっています。ビューが画面に配置される前にメソッドを呼び出しています。
Romain Guy

5
ああ、私がそうしていることは明らかではありませんでしたが、あなたは正しいです。ありがとう!詳細について知りたい人のために、私が何を意味するかを説明する回答を追加しました。
スティーブヘイリー

9
ViewTreeObserverとそのOnGlobalLayoutListenerが好きですか?開始するには、View.getViewTreeObserver()を参照してください。
Romain Guy

8
@RomainGuyええ、そのようなものですが、登録する必要がある(そして次に登録を解除する)よりも混乱が少ないです。これは、SDKに関してiOSがAndroidよりも優れている領域です。両方を日常的に使用すると、iOSには多くの迷惑な点がありますが、UIViewの処理はその1つではありません。:)
Martin Marconcini 2013年

127

受け入れられた答えは実際に場所を取得する方法を示していなかったので、ここでもう少し詳しく説明します。int長さ2の配列を渡し、値はビューの(x、y)座標(左上隅)で置き換えられます。

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

ノート

  • で置き換えるgetLocationOnScreengetLocationInWindow、ほとんどの場合に同じ結果が得られます(この回答を参照)。ただし、ダイアログやカスタムキーボードのような小さなウィンドウの場合は、ニーズに合ったものを選択する必要があります。
  • あなたは得るでしょう(0,0)あなたがこのメソッドを呼び出すと、onCreateビューはまだレイアウトされていないため。を使用し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の値も与えています。私が間違ったことを理解するのに役立つ場合は、上のコードスニペットを含めました。再度、感謝します。
スティーブヘイリー

1
私の他のコメントを参照してください。ビューがレイアウトを完了する前に、誤ってこれを呼び出していました。これは今おかげで正常に動作します。
スティーブヘイリー

@Naveen Murthy特定のビューに座標を提供します。ルートレイアウトでクリックしたビューの座標が必要です
。–

20
これは、親を基準にした相対位置を返します。getGlobalVisibleRect()絶対座標に使用します。
Jeffrey Blattman 2014

3
@SteveHaley、私はあなたがしたのと同じ問題に直面してきました、何らかの理由で、私が試みるすべての方法で0を与えます。なぜそれがゼロを与えるのか、あなたはそれを理解したようです。レイアウトが読み込まれた後にビューの座標を取得しようとしていますが、それでもゼロになるのはなぜですか?あなたの助けをありがとう
パンカジ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; リスナーは2回呼び出すことができます。1回目はw = 0 / h = 0の値で、2回目は実際の正しい値で呼び出します。
MacD 2013

メインレイアウトを膨らませて使用するこの方法は、私のアプリのsetContentView()スペシャルActivityで問題を引き起こしました!これは対話アクティビティ(windowFrame:@null, windowActionBar:false, windowIsFloating:true, windowNoTitle:true)でした。そのメインレイアウト(a LinearLayout)には、決定済みの直接の子はありませんlayout_width(それらはすべてmatch_parentor でしたwrap_content)。
Mir-Ismaili 2017

17

Romain Guyのコメントに続き、ここで私はそれを修正しました。うまくいけば、この問題を抱えていた他の人にも役立つでしょう。

実際に、ビューが画面に配置される前にビューの位置を取得しようとしましたが、それが起こっていることはまったく明らかではありませんでした。これらの行は初期化コードが実行された後に配置されていたので、すべてが準備できていると思いました。ただし、このコードはまだonCreate()にありました。Thread.sleep()を試してみると、onResume()までのすべての方法が実行を完了するまで、レイアウトが実際には確定されていないことがわかりました。そのため、実際には、レイアウトが画面上に配置される前にコードが実行されていました。OnClickListener(またはその他のリスナー)にコードを追加することで、レイアウトが完了した後にのみ起動できるため、正しい値が取得されました。


以下の行はコミュニティ編集として提案されました:

使ってください onWindowfocuschanged(boolean hasFocus)


つまり、setContentView(my_layout); すべてのビューを配置するわけではありません。すべてのビューはonCreate()が終了した後にのみ配置されますか?
Ashwin 2012

5
結構です。すべてのビューは後setContentView(R.layout.something)に「存在」しますが、視覚的に読み込まれていません。サイズや位置はありません。これは、最初のレイアウトパスが完了するまで発生しません。これは、将来のある時点で発生します(通常、onResume()の後)。
スティーブヘイリー

14

最初の方法:

Kotlin我々は、ビューのための単純な拡張機能を作成することができます。

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

二番目の方法:

2番目の方法はより簡単です。

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

私utilsのは、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()

その後、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];
});

~~

Kotlinソリューション

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 2018年

つまり、私の要件(ビューの配置)に従って、値を加算および減算してビューを調整しています。
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.