web-dev-qa-db-ja.com

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

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

それが助けになるならば、これは「写真を元に戻す」ゲームのためのものです。複数の部分を選択するためのボックスを描くことができるようにしたいです。これを行う最も簡単な方法は、MotionEventからgetRawX()getRawY()を選び、それらの値をレイアウトを保持しているレイアウトの左上隅と比較することです。ピースのサイズがわかれば、選択したピースの数を判断できます。私はMotionEventgetX()getY()を使うことができることに気づきました、しかしそれはどの部分が選ばれたか決定することをより困難にする相対位置を返す。 (不可能ではない、私は知っているが、それは不必要に複雑に思える)。

編集:これは私が質問の一つとして、保持コンテナのサイズを取得しようとするために使用したコードです。 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]);

このコードは、すべての初期化が完了した後に追加されました。画像はTableLayout内に含まれるImageViewの配列(cells []配列)に分割されています。 Cells [0]は左上のImageViewです、そして私はcells [4]を中央のどこかにあるので選びました。そして間違いなく(0,0)の座標を持つべきではありません。

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

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

359
Steve Haley

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

579
Romain Guy

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

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

  • getLocationOnScreengetLocationInWindow に置き換えても、ほとんどの場合同じ結果が得られます(を参照)。 この答え )。しかし、Dialogやカスタムキーボードのように小さなウィンドウを開いている場合は、用途に合わせてどちらを選択する必要がありますか。
  • ビューがまだレイアウトされていないため、onCreateでこのメソッドを呼び出すと(0,0)が返されます。 ViewTreeObserverを使用して、レイアウトが完了した時点を監視し、測定座標を取得できます。 (この答えを 参照してください 。)
92
Suragch

まずビューの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));

これが役立つことを願っています

68
Naveen Murthy

グローバルレイアウトリスナを使うことは私にとっていつもうまくいっています。レイアウトが変更された場合、物事を再測定できるという利点があります。何かが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);
 }
44
Simon

Romain Guyのコメントに続いて、これを修正しました。うまくいけば、それはまたこの問題を抱えていた他の誰かを助けるでしょう。

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


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

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

17
Steve Haley

ビューの場所を取得するための私のutils関数、それはx valuey valuePointオブジェクトを返します

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
Phan Van Linh

最初の方法:

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番目の方法:

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

そして単にview.absX()で絶対Xを、そしてview.absY()で絶対Yを得る