Cara mendapatkan koordinat mutlak tampilan

390

Saya mencoba untuk mendapatkan koordinat piksel layar absolut dari sudut kiri atas tampilan. Namun, semua metode yang saya dapat temukan seperti getLeft()dan getRight()tidak berfungsi karena semuanya tampaknya relatif terhadap pandangan orang tua, sehingga memberi saya 0. Apa cara yang tepat untuk melakukan ini?

Jika ini membantu, ini adalah untuk permainan 'kembalikan gambar ke urutan'. Saya ingin pengguna dapat menggambar kotak untuk memilih beberapa bagian. Asumsi saya adalah bahwa cara termudah untuk melakukan itu adalah ke getRawX()dan getRawY()dari MotionEventdan kemudian membandingkan nilai-nilai itu terhadap sudut kiri atas tata letak memegang potongan-potongan. Mengetahui ukuran potongan, saya kemudian dapat menentukan berapa banyak potongan yang telah dipilih. Saya menyadari bahwa saya dapat menggunakan getX()dan getY()pada MotionEvent, tetapi karena itu mengembalikan posisi relatif yang membuat menentukan potongan mana yang dipilih lebih sulit. (Bukan tidak mungkin, saya tahu, tetapi tampaknya tidak perlu rumit).

Sunting: Ini adalah kode yang saya gunakan untuk mencoba mendapatkan ukuran wadah penampung, sesuai salah satu pertanyaan. TableLayoutadalah meja yang menampung semua potongan puzzle.

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

Sunting 2: Ini kode yang sudah saya coba, mengikuti lebih banyak jawaban yang disarankan.

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

Kode ini ditambahkan setelah semua inisialisasi dilakukan. Gambar telah dibagi menjadi array dari ImageViews (array sel []) yang terkandung dalam a TableLayout. Sel [0] adalah kiri atas ImageView, dan saya mengambil sel [4] karena berada di suatu tempat di tengah dan pasti tidak memiliki koordinat (0,0).

Kode yang ditunjukkan di atas masih memberi saya semua 0s dalam log, yang saya benar-benar tidak mengerti karena berbagai potongan puzzle ditampilkan dengan benar. (Saya mencoba int publik untuk tableLayoutCorners dan visibilitas default, keduanya memberikan hasil yang sama.)

Saya tidak tahu apakah ini signifikan, tetapi ImageViews awalnya tidak diberi ukuran. Ukuran ImageViews ditentukan selama inisialisasi secara otomatis oleh Tampilan ketika saya memberikan gambar untuk ditampilkan. Bisakah ini berkontribusi pada nilainya menjadi 0, meskipun kode pencatatan setelah mereka diberikan gambar dan secara otomatis mengubah ukurannya sendiri? Untuk mencegahnya, saya menambahkan kode tableLayout.requestLayout()seperti yang ditunjukkan di atas, tetapi itu tidak membantu.

Steve Haley
sumber

Jawaban:

632

Gunakan View.getLocationOnScreen()dan / atau getLocationInWindow().

Guy Romain
sumber
3
Nah, itu jenis fungsi yang saya harapkan akan ditemukan. Sayangnya, saya tidak boleh menggunakannya dengan benar. Saya menyadari itu adalah bug yang dikenal yang getLocationOnScreen memberikan pengecualian pointer nol di Android v1.5 (platform yang saya kodekan), tetapi pengujian di v2.1 tidak bekerja lebih baik. Kedua metode memberi saya 0 untuk semuanya. Saya menyertakan cuplikan kode di atas.
Steve Haley
76
Anda hanya dapat menjalankannya SETELAH tata letak telah terjadi. Anda memanggil metode sebelum pandangan diposisikan di layar.
Romain Guy
5
Aha, kamu benar meskipun tidak jelas aku melakukan itu. Terima kasih! Bagi siapa pun yang ingin tahu lebih detail, saya telah menambahkan jawaban yang menjelaskan apa yang saya maksud.
Steve Haley
9
Maksud Anda seperti ViewTreeObserver dan OnGlobalLayoutListener? Lihat View.getViewTreeObserver () untuk memulai.
Romain Guy
8
@ DomainGuy Ya, agak seperti itu, tapi kurang membingungkan daripada harus mendaftar (dan kemudian membatalkan pendaftaran ...). Ini adalah area di mana iOS melakukannya lebih baik daripada Android dalam hal SDK. Menggunakan keduanya setiap hari, saya dapat mengatakan iOS memiliki banyak hal yang menjengkelkan, tetapi penanganan UIView bukan salah satunya. :)
Martin Marconcini
127

Jawaban yang diterima sebenarnya tidak memberi tahu bagaimana cara mendapatkan lokasi, jadi di sini ada sedikit lebih detail. Anda melewatkan intarray dengan panjang 2 dan nilainya diganti dengan koordinat tampilan (x, y) (dari sudut atas, kiri).

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

Catatan

  • Mengganti getLocationOnScreendengan getLocationInWindowharus memberikan hasil yang sama dalam banyak kasus (lihat jawaban ini ). Namun, jika Anda berada di jendela yang lebih kecil seperti Dialog atau papan ketik khusus, maka gunakan Anda harus memilih mana yang lebih sesuai dengan kebutuhan Anda.
  • Anda akan mendapatkan (0,0)jika Anda memanggil metode ini onCreatekarena tampilan belum ditata. Anda dapat menggunakan a ViewTreeObserveruntuk mendengarkan ketika tata letak selesai dan Anda bisa mendapatkan koordinat yang diukur. (Lihat jawaban ini .)
Suragch
sumber
86

Pertama, Anda harus mendapatkan persegi panjang viewVisible lokal

Untuk misalnya:

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

Semoga ini bisa membantu

Naveen Murthy
sumber
3
Itu juga seharusnya bekerja ... Namun itu memberi saya nilai 0 juga. Saya menyertakan cuplikan kode di atas jika itu membantu Anda mengetahui apa yang saya lakukan salah. Terima kasih lagi.
Steve Haley
1
Lihat komentar saya yang lain; Saya tidak sengaja menyebut ini sebelum Views selesai meletakkan diri mereka. Ini berfungsi dengan baik sekarang terima kasih.
Steve Haley
@Naveen Murthy ini memberikan koordinat untuk tampilan tertentu, saya perlu koordinat tampilan yang diklik dalam tata letak root ..
Aniket
20
ini mengembalikan lokasi relatif ke induk. gunakan getGlobalVisibleRect()untuk coord mutlak.
Jeffrey Blattman
3
@SteveHaley, saya telah menghadapi masalah yang sama seperti yang Anda lakukan, untuk beberapa alasan itu memberi saya 0 dalam setiap metode yang saya coba. Tampaknya Anda telah menemukan jawabannya mengapa memberi nol. Saya mencoba untuk mendapatkan koordinat tampilan setelah tata letak dimuat masih saya mendapatkan nol mengapa begitu? Terima kasih atas bantuan Anda
Pankaj Nimgade
46

Menggunakan pendengar tata letak global selalu bekerja dengan baik untuk saya. Ini memiliki keuntungan karena dapat mengukur kembali hal-hal jika tata letak diubah, misalnya jika sesuatu diatur ke View.GONE atau tampilan anak ditambahkan / dihapus.

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
sumber
Jangan lupa untuk menghapus OnGlobalLayoutListener di dalam (di akhir) onGlobalLayout () {...}. Juga, hanya untuk memastikan, periksa apakah yang berlalu! = 0; pendengar dapat dipanggil dua kali, sekali dengan nilai w = 0 / h = 0, kedua kalinya dengan nilai aktual, benar,.
MacD
Cara ini untuk mengembang tata letak utama dan kemudian menggunakan setContentView()menyebabkan masalah khusus Activitydi aplikasi saya! Itu adalah aktivitas dialog ( windowFrame:@null, windowActionBar:false, windowIsFloating:true, windowNoTitle:true). Tata letak utamanya (a LinearLayout) belum ditentukan oleh anak langsung layout_width(Semuanya adalah match_parentatau wrap_content).
Mir-Ismaili
17

Mengikuti komentar Romain Guy, inilah cara saya memperbaikinya. Semoga ini bisa membantu orang lain yang juga punya masalah ini.

Saya memang mencoba untuk mendapatkan posisi pandangan sebelum ditampilkan di layar, tetapi tidak jelas apa yang terjadi. Baris-baris itu ditempatkan setelah kode initilisasi berjalan, jadi saya berasumsi semuanya sudah siap. Namun, kode ini masih di onCreate (); dengan bereksperimen dengan Thread.sleep () saya menemukan bahwa tata letak sebenarnya belum selesai sampai setelah onCreate () semua jalan ke onResume () selesai dieksekusi. Jadi memang, kode itu mencoba dijalankan sebelum tata letak selesai diposisikan di layar. Dengan menambahkan kode ke OnClickListener (atau Listener lain), nilai yang benar diperoleh karena hanya bisa diaktifkan setelah tata letak selesai.


Baris di bawah ini disarankan sebagai edit komunitas:

mohon gunakan onWindowfocuschanged(boolean hasFocus)

Steve Haley
sumber
Jadi Anda bermaksud mengatakan bahwa setContentView (my_layout); tidak akan menempatkan semua tampilan. Semua tampilan akan ditempatkan hanya setelah onCreate () selesai?
Ashwin
5
Tidak terlalu. Semua tampilan 'ada' setelahnya setContentView(R.layout.something), tetapi belum dimuat secara visual. Mereka tidak memiliki ukuran, atau posisi. Itu tidak terjadi sampai pass tata letak pertama selesai, yang terjadi di beberapa titik di masa depan (umumnya setelah onResume ()).
Steve Haley
14

Cara pertama:

Di Kotlin kita dapat membuat ekstensi sederhana untuk tampilan:

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

Dan cukup dapatkan koordinat:

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

Jalan kedua:

Cara kedua lebih sederhana:

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

dan cukup dapatkan X absolut oleh view.absX()dan Y olehview.absY()

Amir Hossein Ghasemi
sumber
6
Versi yang lebih pendek untuk sampel 1 Anda:val location = IntArray(2).apply(::getLocationOnScreen)
Tuan Chau
5

Utils saya berfungsi untuk mendapatkan lokasi tampilan, itu akan mengembalikan Pointobjek dengan x valuedany value

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

Menggunakan

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

Anda bisa mendapatkan koordinat Tampilan menggunakan getLocationOnScreen()ataugetLocationInWindow()

Setelah itu, xdan yharus menjadi sudut kiri atas tampilan. Jika tata letak root Anda lebih kecil dari layar (seperti dalam Dialog), menggunakan getLocationInWindowakan relatif terhadap wadahnya, bukan seluruh layar.

Solusi Java

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

CATATAN: Jika nilainya selalu 0, Anda cenderung mengubah tampilan segera sebelum meminta lokasi.

Untuk memastikan tampilan memiliki kesempatan untuk memperbarui, jalankan permintaan lokasi Anda setelah tata letak baru View telah dihitung dengan menggunakan 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];
});

~~

Solusi Kotlin

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

CATATAN: Jika nilainya selalu 0, Anda cenderung mengubah tampilan segera sebelum meminta lokasi.

Untuk memastikan tampilan memiliki kesempatan untuk memperbarui, jalankan permintaan lokasi Anda setelah tata letak baru View telah dihitung dengan menggunakan view.post:

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

Saya sarankan membuat fungsi ekstensi untuk menangani ini:

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

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

Dan jika Anda membutuhkan keandalan, tambahkan juga:

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
sumber
0

Gunakan kode ini untuk menemukan kordinat X dan Y yang tepat:

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

Saya telah menambahkan / mengurangi beberapa nilai untuk menyesuaikan tampilan saya. Harap lakukan baris-baris ini hanya setelah seluruh tampilan meningkat.

Tarun
sumber
"Saya telah menambahkan / mengurangi beberapa nilai untuk menyesuaikan pandangan saya." Mengapa??
ToolmakerSteve
Maksud saya sesuai kebutuhan saya (Penempatan tampilan), saya telah menambahkan dan mengurangi nilai untuk menyesuaikan tampilan saya.
Tarun
0

Dapatkan Tampilan Posisi dan Dimensi di layar

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
sumber
0

Hanya sebagai tambahan dari jawaban di atas, untuk pertanyaan di mana dan kapan Anda harus menelepon getLocationOnScreen?

Dalam informasi apa pun yang terkait dengan tampilan apa pun yang ingin Anda dapatkan, itu hanya akan tersedia setelah tampilan diletakkan di layar. Anda dapat dengan mudah melakukan ini dengan meletakkan kode Anda yang bergantung pada pembuatan tampilan di dalam ini (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
sumber