Bagaimana cara mengambil dimensi tampilan?

209

Saya memiliki pandangan TableLayout, TableRow and TextView. Saya ingin itu terlihat seperti kotak. Saya perlu mendapatkan tinggi dan lebar dari kisi ini. Metode getHeight()dan getWidth()selalu kembali 0. Ini terjadi ketika saya memformat grid secara dinamis dan juga ketika saya menggunakan versi XML.

Bagaimana cara mengambil dimensi untuk tampilan?


Ini adalah program pengujian saya yang saya gunakan di Debug untuk memeriksa hasilnya:

import android.app.Activity;
import android.os.Bundle;
import android.widget.TableLayout;
import android.widget.TextView;

public class appwig extends Activity {  
    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.maindemo);  //<- includes the grid called "board"
      int vh = 0;   
      int vw = 0;

      //Test-1 used the xml layout (which is displayed on the screen):
      TableLayout tl = (TableLayout) findViewById(R.id.board);  
      tl = (TableLayout) findViewById(R.id.board);
      vh = tl.getHeight();     //<- getHeight returned 0, Why?  
      vw = tl.getWidth();     //<- getWidth returned 0, Why?   

      //Test-2 used a simple dynamically generated view:        
      TextView tv = new TextView(this);
      tv.setHeight(20);
      tv.setWidth(20);
      vh = tv.getHeight();    //<- getHeight returned 0, Why?       
      vw = tv.getWidth();    //<- getWidth returned 0, Why?

    } //eof method
} //eof class
Sagar Zala
sumber
alih-alih menggunakan getWidth / Height gunakan getMeasuredWidth / Height setelah tata letak diterapkan pada aktivitas.
bhups
9
Guys, yang harus dilakukan adalah menelepon getHeight()dan getWidth()setelah siklus hidup Activity meminta pandangan untuk mengukur diri mereka sendiri, dengan kata lain, lakukan hal-hal semacam ini onResume()dan hanya itu. Anda seharusnya tidak mengharapkan objek yang belum ditata untuk mengetahui dimensinya, itulah keseluruhan triknya. Tidak perlu sihir yang disarankan di bawah ini.
kelas stacker
2
Memanggil getHeight()/Width()di onResume()tidak menghasilkan> 0 nilai bagi saya.
Darpan
@ClassStacker Bagaimana dengan Fragment?
zdd
@zdd Harap ajukan pertanyaan baru yang lengkap dan kirimkan referensi di sini dalam komentar.
class stacker

Jawaban:

418

Saya percaya OP sudah lama hilang, tetapi jika jawaban ini dapat membantu pencari masa depan, saya pikir saya akan memposting solusi yang saya temukan. Saya telah menambahkan kode ini ke onCreate()metode saya :

Diedit: 07/05/11 untuk memasukkan kode dari komentar:

final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {
        LayerDrawable ld = (LayerDrawable)tv.getBackground();
        ld.setLayerInset(1, 0, tv.getHeight() / 2, 0, 0);
        ViewTreeObserver obs = tv.getViewTreeObserver();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            obs.removeOnGlobalLayoutListener(this);
        } else {
            obs.removeGlobalOnLayoutListener(this);
        }
    }

});

Pertama saya mendapatkan referensi terakhir ke TextView(untuk mengakses dalam onGlobalLayout()metode) saya. Selanjutnya, saya mendapatkan ViewTreeObserverdari saya TextView, dan menambahkan OnGlobalLayoutListener, overriding onGLobalLayout(sepertinya tidak ada metode superclass untuk memanggil di sini ...) dan menambahkan kode saya yang membutuhkan mengetahui pengukuran tampilan ke pendengar ini. Semua berfungsi seperti yang diharapkan untuk saya, jadi saya berharap ini bisa membantu.

Kevin Coppock
sumber
23
@ George Bailey: Satu hal yang baru-baru ini saya lihat posting orang lain (saya belum mengujinya sendiri, jadi YMMV) untuk mengatasi beberapa panggilan adalah (dalam onGlobalLayout ()): dengan tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);begitu pendengar harus dihapus setelah pertama kali ini terjadi.
Kevin Coppock
3
Saya juga ingin membantu. Anda mungkin perlu melakukan sesuatu seperti: mView.getViewTreeObserver (). RemoveGlobalOnLayoutListener (globalLayoutListener); jika Anda ingin mengubah ukuran tampilan hanya sekali. Semoga bantuan ini
Henry Sou
7
Pertanyaannya sudah sangat lama, tetapi menggunakan addOnLayoutChangeListenerkarya juga! :)
FX
7
Juga, perhatikan bahwa sejak API 16, Anda perlu: if (android.os.Build.VERSION.SDK_INT> = 16) {view.getViewTreeObserver (). RemoveOnGlobalLayoutListener (this); } else {view.getViewTreeObserver (). removeGlobalOnLayoutListener (ini); }
Edison
2
Karena removeGlobalOnLayoutListener sudah tidak digunakan lagi, ia masih memberikan peringatan di Eclipse. Apakah ini normal? Seiring berjalannya waktu, peringatan kode yang sudah usang akan membanjiri seluruh ...
Jonny
82

Saya hanya akan menambahkan solusi alternatif, menimpa metode onWindowFocusChanged aktivitas Anda dan Anda akan bisa mendapatkan nilai getHeight (), getWidth () dari sana.

@Override
public void onWindowFocusChanged (boolean hasFocus) {
        // the height will be set at this point
        int height = myEverySoTallView.getMeasuredHeight(); 
}
JustDanyul
sumber
4
Mengutip dokumentasi, "Ini adalah indikator terbaik apakah aktivitas ini dapat dilihat oleh pengguna."
Cameron Lowell Palmer
6
Ini tidak berfungsi untuk Fragmen yang harus Anda gunakan ViewTreeObserver.
Mihir
OP secara khusus bertanya bagaimana cara melihatnya
JustDanyul
scrollview total tinggi dan max Scrollview.getScrolly () akan sama? yaitu dalam kasus saya, saya mendapatkan Ketinggian dengan metode di atas adalah 702 dan saya mendapat nilai maksimum dari getScrolly () adalah 523,
Hitesh Dhamshaniya
Metode ini juga tidak berfungsi ketika View Anda dimasukkan dari file xml lain.
Jay Donga
19

Anda mencoba mendapatkan lebar dan tinggi elemen, yang belum digambar.

Jika Anda menggunakan debug dan berhenti di beberapa titik, Anda akan melihat, bahwa layar perangkat Anda masih kosong, itu karena elemen Anda belum digambar, sehingga Anda tidak bisa mendapatkan lebar dan tinggi sesuatu, itu belum ada.

Dan, saya mungkin salah, tetapi setWidth()tidak selalu dihormati, Layoutmenjabarkan itu anak-anak dan memutuskan bagaimana mengukur mereka (memanggil child.measure()), jadi Jika Anda mengatursetWidth() , Anda tidak dijamin untuk mendapatkan lebar ini setelah elemen akan ditarik.

Yang Anda butuhkan adalah menggunakannya getMeasuredWidth() (ukuran terbaru dari Tampilan Anda) di suatu tempat setelah tampilan tersebut benar-benar dibuat.

Memeriksa Activity siklus hidup untuk menemukan momen terbaik.

http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle

Saya percaya praktik yang baik adalah menggunakan OnGlobalLayoutListenerseperti ini:

yourView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (!mMeasured) {
                // Here your view is already layed out and measured for the first time
                mMeasured = true; // Some optional flag to mark, that we already got the sizes
            }
        }
    });

Anda dapat menempatkan kode ini secara langsung onCreate(), dan akan dipanggil ketika tampilan akan ditata.

Alex Orlov
sumber
Sayangnya itu tidak berfungsi di OnResume juga. Dimensi akhir ditetapkan di suatu tempat setelah panggilan OnResume, setidaknya di emulator.
jki
Itu latihan yang mengerikan !!! Pendengar ini akan dipanggil berulang kali dan akan membunuh kinerja Anda. Alih-alih menggunakan flag, batalkan registrasi pendengar setelah selesai.
bluewhile
15

Gunakan metode posting Lihat seperti ini

post(new Runnable() {   
    @Override
    public void run() {
        Log.d(TAG, "width " + MyView.this.getMeasuredWidth());
        }
    });
Boris Rusev
sumber
1
Itu berhasil, tapi sungguh, tidak ada diskusi mengapa? Btw, ini berfungsi karena runnable tidak dapat dijalankan sampai tata letak telah terjadi.
Norman H
7

Saya mencoba menggunakan onGlobalLayout()untuk melakukan beberapa format kustom TextView, tetapi seperti yang diperhatikan @George Bailey, onGlobalLayout()memang disebut dua kali: sekali di jalur tata letak awal, dan kedua kalinya setelah memodifikasi teks.

View.onSizeChanged()berfungsi lebih baik untuk saya karena jika saya memodifikasi teks di sana, metode ini dipanggil hanya sekali (selama pass tata letak). Subklasifikasi ini diperlukan TextView, tetapi pada API Level 11+ View. addOnLayoutChangeListener()dapat digunakan untuk menghindari subklasifikasi.

Satu hal lagi, untuk mendapatkan lebar tampilan yang benar View.onSizeChanged(), layout_widthharus diatur ke match_parent, bukan wrap_content.

Y2i
sumber
2

Apakah Anda mencoba untuk mendapatkan ukuran dalam konstruktor, atau metode lain yang dijalankan sebelum Anda mendapatkan gambar yang sebenarnya?

Anda tidak akan mendapatkan dimensi apa pun sebelum semua komponen benar-benar diukur (karena xml Anda tidak tahu tentang ukuran tampilan, posisi orang tua, dan apa pun)

Coba dapatkan nilai setelah onSizeChanged () (meskipun dapat dipanggil dengan nol), atau cukup menunggu ketika Anda akan mendapatkan gambar yang sebenarnya.

Alex Orlov
sumber
Saya memperbarui pertanyaan dan kode asli saya menjadi lebih jelas. Terima kasih.
2

Seperti yang disebutkan FX, Anda dapat menggunakan a OnLayoutChangeListeneruntuk tampilan yang ingin Anda lacak sendiri

view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        // Make changes
    }
});

Anda dapat menghapus pendengar di dalam panggilan balik jika Anda hanya menginginkan tata letak awal.

Zeophlite
sumber
1

ViewTreeObserver dan onWindowFocusChanged() sama sekali tidak perlu.

Jika Anda mengembang TextViewtata letak sebagai dan / atau menaruh beberapa konten di dalamnya dan mengatur LayoutParamsmaka Anda dapat menggunakan getMeasuredHeight()dan getMeasuredWidth().

TETAPI Anda harus berhati-hati denganLinearLayouts (mungkin juga yang lain ViewGroups). Masalahnya ada, bahwa Anda bisa mendapatkan lebar dan tinggi setelah onWindowFocusChanged()tetapi jika Anda mencoba untuk menambahkan beberapa tampilan di dalamnya, maka Anda tidak bisa mendapatkan informasi itu sampai semuanya telah diambil. Saya mencoba menambahkan beberapa TextViewsuntuk LinearLayoutsmeniru FlowLayout(gaya pembungkus) dan tidak bisa digunakan Listeners. Setelah proses dimulai, itu harus dilanjutkan secara sinkron. Jadi dalam kasus seperti itu, Anda mungkin ingin menjaga lebar dalam variabel untuk menggunakannya nanti, karena saat menambahkan tampilan ke tata letak, Anda mungkin membutuhkannya.

Genom
sumber
1

Meskipun solusi yang diusulkan berfungsi, itu mungkin bukan solusi terbaik untuk setiap kasus karena berdasarkan pada dokumentasi untuk ViewTreeObserver.OnGlobalLayoutListener

Definisi antarmuka untuk panggilan balik akan dipanggil ketika keadaan tata letak global atau visibilitas tampilan di dalam pohon tampilan berubah.

yang berarti dipanggil berkali-kali dan tidak selalu dilihat diukur (memiliki tinggi dan lebar ditentukan)

Alternatif adalah menggunakan ViewTreeObserver.OnPreDrawListeneryang dipanggil hanya ketika tampilan siap untuk diambil dan memiliki semua pengukurannya.

final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnPreDrawListener(new OnPreDrawListener() {

    @Override
    public void onPreDraw() {
        tv.getViewTreeObserver().removeOnPreDrawListener(this);
        // Your view will have valid height and width at this point
        tv.getHeight();
        tv.getWidth();
    }

});
Kayvan N
sumber
0

Anda dapat menggunakan siaran yang disebut di OnResume ()

Sebagai contoh:

int vh = 0;   
int vw = 0;

public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.maindemo);  //<- includes the grid called "board"

      registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                TableLayout tl = (TableLayout) findViewById(R.id.board);  
                tl = (TableLayout) findViewById(R.id.board);
                vh = tl.getHeight();       
                vw = tl.getWidth();               
            }
      }, new IntentFilter("Test"));
}

protected void onResume() {
        super.onResume();

        Intent it = new Intent("Test");
        sendBroadcast(it);

}

Anda tidak bisa mendapatkan ketinggian tampilan di OnCreate (), onStart (), atau bahkan di onResume () karena alasan kcoppock merespons

Lucas Ferreira Batista
sumber
0

Tinggi dan lebar adalah nol karena tampilan belum dibuat pada saat Anda meminta tinggi dan lebar. Salah satu solusi paling sederhana adalah

view.post(new Runnable() {
    @Override
    public void run() {
        view.getHeight(); //height is ready
        view.getWidth(); //width is ready
    }
});

Metode ini baik dibandingkan dengan metode lain karena pendek dan garing.

Shivam Yadav
sumber
-1

Tanggapan Sederhana: Ini bekerja untuk saya tanpa masalah. Tampaknya kuncinya adalah memastikan bahwa Tampilan memiliki fokus sebelum Anda mendapatkanHeight dll. Lakukan ini dengan menggunakan metode hasFocus (), kemudian gunakan metode getHeight () dalam urutan itu. Diperlukan hanya 3 baris kode.

ImageButton myImageButton1 =(ImageButton)findViewById(R.id.imageButton1); myImageButton1.hasFocus();

int myButtonHeight = myImageButton1.getHeight();

Log.d("Button Height: ", ""+myButtonHeight );//Not required

Semoga ini bisa membantu.

Monk4D
sumber
-3

Gunakan getMeasuredWidth () dan getMeasuredHeight () untuk tampilan Anda.

Panduan pengembang: Lihat

Vladimir Ivanov
sumber
4
Seperti yang dinyatakan sebelumnya metode ini hanya mengembalikan hasil yang valid efter ukuran telah meassured. Dalam sebuah Aktivitas, ini berlaku ketika metode onWindowFocusChanged os dipanggil.
slot
-8

KOREKSI: Saya menemukan bahwa solusi di atas mengerikan. Apalagi saat ponsel Anda lambat. Dan di sini, saya menemukan solusi lain: menghitung nilai px elemen, termasuk margin dan bantalan: dp ke px: https://stackoverflow.com/a/6327095/1982712

atau dimens.xml ke px: https://stackoverflow.com/a/16276351/1982712

sp to px: https://stackoverflow.com/a/9219417/1982712 (membalikkan solusinya)

atau dimens ke px: https://stackoverflow.com/a/16276351/1982712

dan hanya itu.

pimkle
sumber
10
Biasanya ide yang buruk untuk berharap bahwa semuanya akan ditetapkan dengan benar setelah beberapa milidetik yang dipilih secara sewenang-wenang. Perangkat mungkin lambat, proses / utas lainnya mungkin berjalan, mungkin tidak berfungsi dalam debugger, mungkin tidak berfungsi di versi OS berikutnya, ...
Kristopher Johnson