Làm thế nào để có được tọa độ tuyệt đối của chế độ xem


390

Tôi đang cố gắng để có được tọa độ pixel màn hình tuyệt đối của góc trên cùng bên trái của chế độ xem. Tuy nhiên, tất cả các phương pháp tôi có thể tìm thấy getLeft()getRight()không hoạt động vì tất cả chúng đều có vẻ tương đối với cha mẹ của chế độ xem, do đó mang lại cho tôi 0. Cách thích hợp để làm điều này là gì?

Nếu nó hữu ích, đây là một trò chơi 'đưa hình ảnh trở lại theo thứ tự'. Tôi muốn người dùng có thể vẽ một hộp để chọn nhiều mảnh. Giả định của tôi là cách dễ nhất để làm điều đó là để getRawX()getRawY()từ MotionEventvà sau đó so sánh những giá trị so với góc trên cùng bên trái của bố trí giữ miếng. Biết kích thước của các mảnh, sau đó tôi có thể xác định có bao nhiêu mảnh đã được chọn. Tôi nhận ra rằng tôi có thể sử dụng getX()getY()trên MotionEvent, nhưng điều đó trả về một vị trí tương đối khiến cho việc xác định phần nào được chọn khó khăn hơn. (Không phải là không thể, tôi biết, nhưng nó có vẻ phức tạp không cần thiết).

Chỉnh sửa: Đây là mã tôi đã sử dụng để cố gắng lấy kích thước của hộp chứa, theo một trong các câu hỏi. TableLayoutlà cái bàn chứa tất cả các mảnh ghép.

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

Chỉnh sửa 2: Đây là mã tôi đã thử, sau nhiều câu trả lời được đề xuất.

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

Mã này đã được thêm vào sau khi tất cả các khởi tạo được thực hiện. Hình ảnh đã được chia thành một mảng ImageViews (các ô [] mảng) có trong a TableLayout. Các ô [0] là trên cùng bên trái ImageViewvà tôi đã chọn các ô [4] vì nó nằm ở giữa và chắc chắn không nên có tọa độ là (0,0).

Mã được hiển thị ở trên vẫn cung cấp cho tôi tất cả 0 trong nhật ký, điều mà tôi thực sự không hiểu vì các mảnh ghép khác nhau được hiển thị chính xác. (Tôi đã thử int int cho bảngLayoutCorners và khả năng hiển thị mặc định, cả hai đều cho kết quả như nhau.)

Tôi không biết điều này có ý nghĩa gì không, nhưng ImageViewban đầu chúng không được cung cấp kích thước. Kích thước của ImageViews được xác định trong quá trình khởi tạo tự động bởi Chế độ xem khi tôi cung cấp cho nó một hình ảnh để hiển thị. Điều này có thể đóng góp cho giá trị của chúng là 0, mặc dù mã đăng nhập là sau khi chúng được cung cấp hình ảnh và tự động thay đổi kích thước? Để có khả năng chống lại điều đó, tôi đã thêm mã tableLayout.requestLayout()như được hiển thị ở trên, nhưng điều đó không giúp được gì.

Câu trả lời:


632

3
Bây giờ đó là loại chức năng tôi mong đợi để tìm thấy. Thật không may, tôi không được sử dụng nó đúng cách. Tôi nhận ra rằng đó là một lỗi đã biết mà getLocationOnScreen đưa ra ngoại lệ con trỏ null trong Android v1.5 (nền tảng tôi đang mã hóa), nhưng thử nghiệm trong phiên bản 2.1 không hoạt động tốt hơn. Cả hai phương pháp đều cho tôi 0s cho mọi thứ. Tôi đã bao gồm một đoạn mã ở trên.
Steve Haley

76
Bạn chỉ có thể gọi nó SAU bố trí đã xảy ra. Bạn đang gọi phương thức trước khi các khung nhìn được định vị trên màn hình.
Romain Guy

5
Aha, bạn nói đúng mặc dù rõ ràng là tôi đã làm điều đó. Cảm ơn! Đối với bất kỳ ai tò mò để biết thêm chi tiết, tôi đã thêm một câu trả lời giải thích ý tôi là gì.
Steve Haley

9
Bạn có nghĩa là như ViewTreeObserver và OnGlobalLayoutListener của nó? Xem View.getViewTreeObserver () để bắt đầu.
Romain Guy

8
@RomainGuy Vâng, đại loại như vậy, nhưng ít gây nhầm lẫn hơn là phải đăng ký (và sau đó hủy đăng ký). Đây là một lĩnh vực mà iOS làm điều đó tốt hơn Android về SDK. Sử dụng cả hai trên cơ sở hàng ngày, tôi có thể nói iOS có rất nhiều điều khó chịu, nhưng việc xử lý UIView không phải là một trong số đó. :)
Martin Marconcini

127

Câu trả lời được chấp nhận không thực sự cho biết làm thế nào để có được vị trí, vì vậy đây là chi tiết hơn một chút. Bạn chuyển vào một intmảng có độ dài 2 và các giá trị được thay thế bằng tọa độ (x, y) của khung nhìn (của góc trên cùng, bên trái).

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

Ghi chú

  • Thay thế getLocationOnScreenbằng getLocationInWindowsẽ cho kết quả tương tự trong hầu hết các trường hợp (xem câu trả lời này ). Tuy nhiên, nếu bạn đang ở trong một cửa sổ nhỏ hơn như Hộp thoại hoặc bàn phím tùy chỉnh, thì bạn sẽ cần phải chọn cái nào phù hợp hơn với nhu cầu của mình.
  • Bạn sẽ nhận được (0,0)nếu bạn gọi phương thức này onCreatevì chế độ xem chưa được đặt ra. Bạn có thể sử dụng a ViewTreeObserverđể lắng nghe khi bố trí xong và bạn có thể lấy tọa độ đo. (Xem câu trả lời này .)

86

Trước tiên, bạn phải có được hình chữ nhật localVisible của khung nhìn

Ví dụ:

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

Hy vọng điều này sẽ giúp


3
Điều đó cũng có thể đã hoạt động ... Tuy nhiên, nó cũng mang lại cho tôi giá trị 0. Tôi đã bao gồm một đoạn mã ở trên nếu điều đó giúp bạn tìm ra những gì tôi đã làm sai. Cảm ơn một lần nữa.
Steve Haley

1
Xem ý kiến ​​khác của tôi; Tôi đã vô tình gọi điều này trước khi Lượt xem hoàn thành. Điều này làm việc tốt bây giờ cảm ơn.
Steve Haley

@Naveen Murthy nó ​​cung cấp tọa độ cho chế độ xem cụ thể, tôi cần tọa độ của chế độ xem được nhấp trong bố cục gốc ..
Aniket

20
cái này trả về vị trí tương đối với cha mẹ. sử dụng getGlobalVisibleRect()cho coords tuyệt đối.
Jeffrey Blattman

3
@SteveHaley, tôi đã phải đối mặt với cùng một vấn đề như bạn đã làm, vì một số lý do, nó mang lại cho tôi 0 trong mọi phương pháp tôi thử. Có vẻ như bạn đã tìm ra lý do tại sao nó cho không. Tôi đang cố gắng để có được tọa độ xem sau khi bố trí được tải vẫn không nhận được tại sao lại như vậy? Cảm ơn sự giúp đỡ của bạn
Pankaj Nimgade

46

Sử dụng một trình lắng nghe bố cục toàn cầu luôn làm việc tốt cho tôi. Nó có lợi thế là có thể làm hài lòng mọi thứ nếu bố cục bị thay đổi, ví dụ nếu một cái gì đó được đặt thành View.GONE hoặc các chế độ xem con được thêm / xóa.

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

Đừng quên xóa OnGlobalLayoutListener bên trong (ở cuối) onGlobalLayout () {...}. Ngoài ra, để chắc chắn, hãy kiểm tra xem những gì đã qua! = 0; người nghe có thể được gọi hai lần, một lần với giá trị w = 0 / h = 0, lần thứ hai với các giá trị thực tế, chính xác.
MacD

Cách này để tăng bố cục chính và sau đó sử dụng setContentView()đã gây ra sự cố đặc biệt Activitytrong ứng dụng của tôi! Đó là một hoạt động hộp thoại ( windowFrame:@null, windowActionBar:false, windowIsFloating:true, windowNoTitle:true). Bố cục chính của nó (a LinearLayout) không có bất kỳ đứa trẻ trực tiếp nào được xác định layout_width(Tất cả chúng đều match_parenthoặc wrap_content).
Mir-Ismaili

17

Theo bình luận của Romain Guy, đây là cách tôi sửa nó. Hy vọng nó sẽ giúp được bất cứ ai khác cũng gặp phải vấn đề này.

Tôi thực sự đã cố gắng để có được vị trí của các lượt xem trước khi chúng được trình bày trên màn hình nhưng rõ ràng điều đó không xảy ra. Những dòng đó đã được đặt sau khi mã khởi tạo chạy, vì vậy tôi cho rằng mọi thứ đã sẵn sàng. Tuy nhiên, mã này vẫn còn trong onCreate (); bằng cách thử nghiệm với Thread.s ngủ () tôi phát hiện ra rằng bố cục không thực sự được hoàn thiện cho đến khi onCreate () tất cả các cách để onResume () đã thực hiện xong. Vì vậy, thực sự, mã đã cố chạy trước khi bố trí xong được định vị trên màn hình. Bằng cách thêm mã vào OnClickListener (hoặc một số Trình nghe khác), các giá trị chính xác đã thu được vì nó chỉ có thể được kích hoạt sau khi bố cục kết thúc.


Dòng dưới đây được đề xuất là chỉnh sửa cộng đồng:

vui lòng sử dụng onWindowfocuschanged(boolean hasFocus)


Vì vậy, bạn có nghĩa là để nói rằng setContentView (my_layout); sẽ không đặt tất cả các quan điểm. Tất cả các khung nhìn sẽ chỉ được đặt sau khi onCreate () kết thúc?
Ashwin

5
Không hẳn. Tất cả các chế độ xem "tồn tại" sau đó setContentView(R.layout.something), nhưng chúng không được tải lên một cách trực quan. Họ không có kích thước, hoặc vị trí. Điều đó không xảy ra cho đến khi bố trí vượt qua đầu tiên kết thúc, điều này xảy ra tại một thời điểm nào đó trong tương lai (thường là sau onResume ()).
Steve Haley

14

Cách thứ nhất:

Trong Kotlin, chúng ta có thể tạo một tiện ích mở rộng đơn giản để xem:

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

Và chỉ cần lấy tọa độ:

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

Cách thứ hai:

Cách thứ hai đơn giản hơn:

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

và chỉ cần lấy X tuyệt đối view.absX()và Y bằngview.absY()


6
Phiên bản ngắn hơn cho mẫu đầu tiên của bạn:val location = IntArray(2).apply(::getLocationOnScreen)
Tuần Châu

5

Hàm utils của tôi để có được vị trí xem, nó sẽ trả về một Pointđối tượng với x valuey value

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

Sử dụng

Point viewALocation = getLocationOnScreen(viewA);

5

Bạn có thể nhận tọa độ của Chế độ xem bằng cách sử dụng getLocationOnScreen()hoặcgetLocationInWindow()

Sau đó, xyphải là góc trên cùng bên trái của chế độ xem. Nếu bố cục gốc của bạn nhỏ hơn màn hình (như trong Hộp thoại), việc sử dụng getLocationInWindowsẽ liên quan đến vùng chứa của nó, chứ không phải toàn bộ màn hình.

Giải pháp Java

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

LƯU Ý: Nếu giá trị luôn là 0, bạn có thể thay đổi chế độ xem ngay trước khi yêu cầu vị trí.

Để đảm bảo chế độ xem đã có cơ hội cập nhật, hãy chạy yêu cầu vị trí của bạn sau khi bố cục mới của Chế độ xem được tính bằng cách sử dụng 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];
});

~ ~

Giải pháp Kotlin

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

LƯU Ý: Nếu giá trị luôn là 0, bạn có thể thay đổi chế độ xem ngay trước khi yêu cầu vị trí.

Để đảm bảo chế độ xem đã có cơ hội cập nhật, hãy chạy yêu cầu vị trí của bạn sau khi bố cục mới của Chế độ xem được tính bằng cách sử dụng view.post:

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

Tôi khuyên bạn nên tạo một chức năng mở rộng để xử lý việc này:

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

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

Và nếu bạn yêu cầu độ tin cậy, cũng thê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)
    }
}

0

Sử dụng mã này để tìm các chính xác X và 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);

Tôi đã thêm / bớt một số giá trị để điều chỉnh quan điểm của mình. Vui lòng thực hiện những dòng này chỉ sau khi toàn bộ khung nhìn đã bị thổi phồng.


"Tôi đã thêm / bớt một số giá trị để điều chỉnh chế độ xem của mình." Tại sao??
ToolmakerSteve

Ý tôi là theo yêu cầu của tôi (Vị trí của chế độ xem), tôi đã thêm và trừ các giá trị để điều chỉnh chế độ xem của mình.
Tarun

0

Nhận cả vị trí xem và kích thước trên màn hình

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

Chỉ cần ngoài các câu trả lời trên, cho câu hỏi bạn nên gọi ở đâu và khi getLocationOnScreennào?

Trong bất kỳ thông tin nào liên quan đến bất kỳ chế độ xem nào bạn muốn nhận, nó sẽ chỉ khả dụng sau khi chế độ xem được hiển thị trên màn hình. Bạn có thể dễ dàng thực hiện điều này bằng cách đặt mã của bạn phụ thuộc vào việc tạo chế độ xem bên trong này (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];
            }
        });  
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.