มุมมองของ getWidth () และ getHeight () ส่งคืนค่า 0


513

ฉันกำลังสร้างองค์ประกอบทั้งหมดในโครงการ Android ของฉันแบบไดนามิก ฉันพยายามรับความกว้างและความสูงของปุ่มเพื่อให้สามารถหมุนปุ่มนั้นได้ ฉันแค่พยายามเรียนรู้วิธีทำงานกับภาษา Android อย่างไรก็ตามมันจะคืนค่า 0

ฉันทำวิจัยบางอย่างและฉันเห็นว่าต้องทำที่อื่นนอกเหนือจากonCreate()วิธีการ ถ้ามีคนสามารถยกตัวอย่างให้ฉันฟังได้ว่ามันยอดเยี่ยมมาก

นี่คือรหัสปัจจุบันของฉัน:

package com.animation;

import android.app.Activity;
import android.os.Bundle;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.Button;
import android.widget.LinearLayout;

public class AnimateScreen extends Activity {


//Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LinearLayout ll = new LinearLayout(this);

    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
    layoutParams.setMargins(30, 20, 30, 0);

    Button bt = new Button(this);
    bt.setText(String.valueOf(bt.getWidth()));

    RotateAnimation ra = new RotateAnimation(0,360,bt.getWidth() / 2,bt.getHeight() / 2);
    ra.setDuration(3000L);
    ra.setRepeatMode(Animation.RESTART);
    ra.setRepeatCount(Animation.INFINITE);
    ra.setInterpolator(new LinearInterpolator());

    bt.startAnimation(ra);

    ll.addView(bt,layoutParams);

    setContentView(ll);
}

ความช่วยเหลือใด ๆ ที่ชื่นชม

คำตอบ:


197

คุณโทรgetWidth()เร็วเกินไป UI ยังไม่ได้ปรับขนาดและวางบนหน้าจอ

ฉันสงสัยว่าคุณต้องการทำสิ่งที่คุณทำอยู่แล้ว - วิดเจ็ตที่เป็นภาพเคลื่อนไหวไม่เปลี่ยนพื้นที่ที่สามารถคลิกได้ดังนั้นปุ่มจะยังคงตอบสนองต่อการคลิกในทิศทางเดิมโดยไม่คำนึงถึงการหมุน

ดังที่ได้กล่าวไว้คุณสามารถใช้ทรัพยากรมิติเพื่อกำหนดขนาดปุ่มจากนั้นอ้างอิงทรัพยากรมิตินั้นจากไฟล์เลย์เอาต์และซอร์สโค้ดของคุณเพื่อหลีกเลี่ยงปัญหานี้


82
ngreenwood6 โซลูชันอื่นของคุณคืออะไร
แอนดรู

12
@Andrew - หากคุณต้องการให้ negreenwood6 ได้รับการแจ้งเตือนจากการติดตามของคุณคุณต้องเริ่มต้นข้อความของคุณเหมือนที่ฉันทำกับคุณ (ฉันคิดว่าตัวอักษรสามตัวแรกก็เพียงพอแล้ว) - CommonsWare ได้รับการแจ้งเตือนโดยอัตโนมัติเนื่องจากเขาเขียนคำตอบนี้ นอกเสียจากคุณจะกล่าวถึง
ปีเตอร์ Ajtai

11
@Override โมฆะสาธารณะ onWindowFocusChanged (บูลีน hasFocus) {// TODO วิธีการที่สร้างขึ้นอัตโนมัติต้นขั้ว super.onWindowFocusChanged (hasFocus); // ที่นี่คุณจะได้ขนาด! }
Sana

5
@ ngreenwood6 แล้วทางออกของคุณคืออะไร?
Nima Vaziri

5
ใช้ฟังนี้เพื่อรับขนาดเมื่อหน้าจอของคุณพร้อม view.getViewTreeObserver (). addOnGlobalLayoutListener (ใหม่ ViewTreeObserverOnGlobalLayoutListener () {}
Sinan Dizdarević

815

ปัญหาพื้นฐานคือว่าคุณต้องรอสำหรับขั้นตอนการวาดภาพสำหรับการวัดจริง (โดยเฉพาะอย่างยิ่งที่มีค่าแบบไดนามิกเช่นwrap_contentหรือmatch_parent) onResume()แต่โดยปกติขั้นตอนนี้ยังไม่ได้รับการเสร็จสิ้นถึง ดังนั้นคุณต้องมีวิธีแก้ปัญหาเพื่อรอขั้นตอนนี้ มีวิธีแก้ไขปัญหาต่าง ๆ ที่เป็นไปได้สำหรับสิ่งนี้:


1. ฟังกิจกรรม Draw / Layout: ViewTreeObserver

ViewTreeObserver เริ่มทำงานแล้วสำหรับกิจกรรมการวาดรูปที่แตกต่างกัน โดยทั่วไปOnGlobalLayoutListenerคือสิ่งที่คุณต้องการรับการวัดดังนั้นโค้ดในผู้ฟังจะถูกเรียกหลังจากเฟสเลย์เอาต์ดังนั้นการวัดจึงพร้อม:

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                view.getHeight(); //height is ready
            }
        });

หมายเหตุ: ผู้ฟังจะถูกลบออกทันทีเพราะมิฉะนั้นจะเริ่มทำงานในทุกรูปแบบเหตุการณ์ หากคุณต้องสนับสนุนแอปSDK Lvl <16ใช้สิ่งนี้เพื่อยกเลิกการลงทะเบียนผู้ฟัง:

public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)


2. เพิ่ม runnable ไปยังคิวโครงร่าง: View.post ()

ไม่ค่อยมีใครรู้จักและเป็นทางออกที่ฉันโปรดปราน โดยทั่วไปเพียงใช้วิธีการโพสต์ของ View ด้วย runnable ของคุณเอง โดยทั่วไปจะเป็นการรอคิวโค้ดของคุณหลังจากการวัด, เลย์เอาต์และอื่น ๆ ตามที่ระบุไว้โดยRomain Guy :

คิวกิจกรรม UI จะประมวลผลกิจกรรมตามลำดับ หลังจาก setContentView () ถูกเรียกใช้คิวเหตุการณ์จะมีข้อความขอให้มีการถ่ายทอดใหม่ดังนั้นสิ่งที่คุณโพสต์ลงในคิวจะเกิดขึ้นหลังจากผ่านโครงร่าง

ตัวอย่าง:

final View view=//smth;
...
view.post(new Runnable() {
            @Override
            public void run() {
                view.getHeight(); //height is ready
            }
        });

ข้อดีกว่า ViewTreeObserver :

  • รหัสของคุณจะถูกดำเนินการเพียงครั้งเดียวและคุณไม่จำเป็นต้องปิดการใช้งานผู้สังเกตการณ์หลังจากการดำเนินการซึ่งอาจเป็นเรื่องยุ่งยาก
  • ไวยากรณ์ verbose น้อย

อ้างอิง:


3. เขียนทับวิธี onLayout ของ Views

นี่เป็นเพียงการปฏิบัติในบางสถานการณ์เมื่อตรรกะสามารถห่อหุ้มในมุมมองของตัวเองมิฉะนั้นนี่เป็นไวยากรณ์ที่ค่อนข้างละเอียดและซับซ้อน

view = new View(this) {
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        view.getHeight(); //height is ready
    }
};

นอกจากนี้โปรดทราบว่า onLayout จะถูกเรียกหลายครั้งดังนั้นโปรดคำนึงถึงสิ่งที่คุณทำในวิธีการหรือปิดใช้งานรหัสของคุณหลังจากครั้งแรก


4. ตรวจสอบว่าผ่านขั้นตอนการจัดวางหรือไม่

หากคุณมีรหัสที่รันหลายครั้งในขณะที่สร้าง UI คุณสามารถใช้วิธีการสนับสนุน v4 lib ต่อไปนี้:

View viewYouNeedHeightFrom = ...
...
if(ViewCompat.isLaidOut(viewYouNeedHeightFrom)) {
   viewYouNeedHeightFrom.getHeight();
}

ผลตอบแทนจริงถ้ามุมมองที่ผ่านอย่างน้อยหนึ่งรูปแบบเพราะมันถูกแนบล่าสุดหรือแยกออกจากหน้าต่าง


เพิ่มเติม: การวัดที่กำหนดไว้แบบสแตติก

หากพอเพียงที่จะได้รับความสูง / ความกว้างที่กำหนดไว้แบบคงที่คุณก็สามารถทำได้ด้วย:

แต่โปรดจำไว้ว่านี่อาจแตกต่างกับความกว้าง / ความสูงจริงหลังจากวาด javadoc อธิบายความแตกต่างได้อย่างสมบูรณ์แบบ:

ขนาดของมุมมองแสดงด้วยความกว้างและความสูง มุมมองมีค่าความกว้างและความสูงสองคู่จริง ๆ แล้ว

คู่แรกเรียกว่าความกว้างที่วัดได้และความสูงที่วัดได้ มิติข้อมูลเหล่านี้กำหนดขนาดมุมมองที่ต้องการให้อยู่ในพาเรนต์ (ดูรายละเอียดเพิ่มเติมจากเลย์เอาต์) มิติข้อมูลที่วัดได้โดยการเรียกใช้ getMeasuredWidth () และ getMeasuredHeight ()

คู่ที่สองนั้นรู้จักกันในชื่อความกว้างและความสูงหรือบางครั้งการวาดความกว้างและความสูงของการวาด มิติข้อมูลเหล่านี้กำหนดขนาดที่แท้จริงของมุมมองบนหน้าจอ ณ เวลาวาดและหลังเค้าโครง ค่าเหล่านี้อาจ แต่ไม่จำเป็นต้องแตกต่างจากความกว้างและความสูงที่วัดได้ สามารถรับความกว้างและความสูงได้โดยโทรหา getWidth () และ getHeight ()


1
ฉันใช้ view.post เป็นส่วน ๆ ฉันต้องวัดมุมมองที่ความสูงของผู้ปกครองคือ 0dp และมีชุดน้ำหนัก ทุกสิ่งที่ฉันลองคืนกลับมาจะสูงเป็นศูนย์ (ฉันได้ลองใช้ฟังเค้าโครงทั่วโลกด้วย) เมื่อฉันใส่รหัส view.post ใน onViewCreated ด้วยความล่าช้า 125 มันใช้งานได้ แต่ไม่ล่าช้า ไอเดีย?
Psest328

6
เลวร้ายเกินไปคุณสามารถถอนคำตอบได้เพียงครั้งเดียวคำอธิบายที่ดีจริงๆ ฉันมีปัญหากับการหมุนมุมมองถ้าฉันออกจากแอพและรีสตาร์ทจากแอพล่าสุด ถ้าฉันกวาดจากที่นั่นและรีสตาร์ทใหม่ทุกอย่างก็โอเค The view.post () ช่วยชีวิตฉันไว้!
Matthias

1
ขอบคุณ ฉันมักจะใช้ View.post () (วิธีที่ 2) แต่ถ้ามันถูกเรียกจากเธรดพื้นหลังบางครั้งก็ใช้ไม่ได้ runOnUiThread(new Runnable() {...}ดังนั้นอย่าลืมที่จะเขียน ปัจจุบันผมใช้รูปแบบของวิธีที่ addOnLayoutChangeListener1: อย่าลืมที่จะลบมันแล้วภายใน: removeOnLayoutChangeListener มันทำงานได้จากเธรดพื้นหลังเช่นกัน
CoolMind

2
น่าเสียดายสำหรับฉัน 'View.post ()' ไม่ทำงานตลอดเวลา ฉันมี 2 โครงการที่คล้ายกันมาก ในโครงการหนึ่งใช้งานได้โครงการหนึ่งไม่ทำงาน :(. ในทั้งสองสถานการณ์ฉันเรียกวิธีจาก 'onCreate ()' วิธีจากกิจกรรมข้อเสนอแนะใด ๆ ทำไม?
Adrian Buciuman

1
ฉันพบปัญหา ในโครงการหนึ่งมุมมองที่มีandroid:visibility="gone"และในโครงการอื่น ๆ invisibleคุณสมบัติการแสดงผลเป็น หากการมองเห็นคือgoneความกว้างจะเป็น 0 เสมอ
Adrian Buciuman

262

เราสามารถใช้

@Override
 public void onWindowFocusChanged(boolean hasFocus) {
  super.onWindowFocusChanged(hasFocus);
  //Here you can get the size!
 }

10
แล้วเราจะทำงานในแฟรกเมนต์ได้อย่างไร ใช้งานได้เฉพาะในกิจกรรม
Olkunmustafa

8
สิ่งนี้ควรทำสิ่งนั้น แต่ไม่ควรเป็นคำตอบ ผู้ใช้ต้องการรับมิติในเวลาใดก็ได้แทนที่จะดึงออกมาจากหน้าต่าง นอกจากนี้วิธีการนี้ได้รับการเรียกหลายครั้งหรือทุกครั้งเมื่อมีการเปลี่ยนแปลง (ดูซ่อนหายไปเพิ่มมุมมองใหม่ ฯลฯ ) ในหน้าต่าง โปรดใช้อย่างระมัดระวัง :)
ดร. aNdRO

1
นี่เป็นแฮ็คและดูเหมือนว่าการใช้ ViewTreeObserver น่าจะดีกว่า
i_am_jorf

ไม่ได้หมายถึงเสียงหยาบคาย แต่การแสดงความคิดเห็นโดยอัตโนมัติ (โดยเฉพาะอย่างยิ่งในโค้ด 2 บรรทัด) ไม่ได้ให้ความมั่นใจมากนักว่าคุณรู้ว่าคุณกำลังทำอะไรอยู่
Natix

โชคไม่ดีมันไม่ได้ผลสำหรับฉัน การลองใช้มุมมอง TreeObserver เหมาะกับฉัน
Narendra Singh

109

ฉันใช้โซลูชันนี้ซึ่งฉันคิดว่าดีกว่า onWindowFocusChanged () หากคุณเปิด DialogFragment แล้วหมุนโทรศัพท์ onWindowFocusChanged จะถูกเรียกเฉพาะเมื่อผู้ใช้ปิดกล่องโต้ตอบ):

    yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        @Override
        public void onGlobalLayout() {
            // Ensure you call it only once :
            yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);

            // Here you can get the size :)
        }
    });

แก้ไข: เนื่องจาก removeGlobalOnLayoutListener เลิกใช้แล้วคุณควรทำดังนี้:

@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {

    // Ensure you call it only once :
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
        yourView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
    else {
        yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }

    // Here you can get the size :)
}

4
นี่คือทางออกที่ดีที่สุด! เพราะมันใช้งานได้กับโซลูชันแบบไดนามิกที่ไม่รู้จักและคำนวณความกว้างและความสูง นั่นคือในรูปแบบสัมพัทธ์ตามตำแหน่ง / ขนาดขององค์ประกอบอื่น ๆ ทำได้ดี!
George Pligoropoulos

2
ต้องใช้ API 16 หรือสูงกว่า (Jelly bean)
tomsv

7
มันไม่ได้ร้องขอ API 16! ปัญหาเพียงอย่างเดียวคือเมธอด removeGlobalOnLayoutListener เลิกใช้แล้วจาก API 16 แต่มีวิธีแก้ไขปัญหาที่เข้ากันได้กับระดับ API ทั้งหมด - stackoverflow.com/questions/16189525/ …
gingo

หากคุณต้องการลบผู้ฟังเพื่อที่จะไม่เรียกตัวเองหลายครั้งและคุณมีปัญหากับ API ที่แตกต่างกันฉันแนะนำให้คุณตั้งค่าบูลีนเท็จที่ส่วนหัวของชั้นเรียนจากนั้นหลังจากจับค่าตั้งเป็นจริงเพื่อให้ มันไม่ได้เก็บค่าอีกครั้ง
Mansour Fahad

หากคุณลบผู้ฟังหลังจากใช้งานครั้งแรกคุณอาจได้รับมิติที่ไม่ถูกต้อง: 02-14 10: 18: 53.786 15063-15063 / PuzzleFragment: ความสูง: 1647 ความกว้าง: 996 02-14 10: 18: 53.985 15063-15063 / PuzzleFragment: ความสูง : ความกว้าง 1500: 996
Leos Literak

76

ดังที่ Ian กล่าวไว้ในเธรด Android Developers :

อย่างไรก็ตามข้อตกลงคือเค้าโครงของเนื้อหาของหน้าต่างเกิดขึ้น หลังจากองค์ประกอบทั้งหมดถูกสร้างและเพิ่มไปยังมุมมองหลัก มันจะต้องเป็นเช่นนี้เพราะจนกว่าคุณจะรู้ว่าองค์ประกอบใดที่วิวมีและสิ่งที่มีอยู่และต่อไปเรื่อย ๆ ไม่มีวิธีที่สมเหตุสมผลที่คุณสามารถจัดวางมันได้

บรรทัดล่างถ้าคุณเรียก getWidth () ฯลฯ ใน Constructor มันจะคืนค่าศูนย์ ขั้นตอนคือการสร้างองค์ประกอบมุมมองทั้งหมดของคุณใน Constructor จากนั้นรอให้วิธีการเรียกดู onSizeChanged () ของ View ของคุณ - นั่นคือเมื่อคุณหาขนาดที่แท้จริงของคุณเป็นครั้งแรกดังนั้นเมื่อคุณตั้งค่าขนาดขององค์ประกอบ GUI ของคุณ

โปรดทราบด้วยว่าบางครั้ง onSizeChanged () จะถูกเรียกด้วยพารามิเตอร์ zero - ตรวจสอบกรณีนี้และกลับมาทันที (ดังนั้นคุณจะไม่ได้รับการหารด้วยศูนย์เมื่อคำนวณเค้าโครงของคุณเป็นต้น) ในเวลาต่อมามันจะถูกเรียกด้วยค่าจริง


8
onSizeChanged ทำงานให้ฉัน onWindowFocusedChanged ไม่ได้รับการเรียกในมุมมองที่กำหนดเองของฉัน
aaronmarino

นี่เป็นวิธีแก้ปัญหาที่ดี แต่ฉันควรสร้างมุมมองที่กำหนดเองเพื่อแทนที่ onSizeChanged method อย่างไร
M.ElSaka

Bottom line, if you call getWidth() etc. in a constructor, it will return zero.นั่นคือบิงโก รากของปัญหาของฉัน
MikeNereson

ฉันแนะนำวิธีแก้ปัญหานี้สำหรับแฟรกเมนต์เนื่องจากโซลูชันอื่นคืนค่า 0 ในกรณีของฉัน
WindRider

66

หากคุณต้องการรับความกว้างของวิดเจ็ตบางส่วนก่อนที่จะปรากฏบนหน้าจอคุณสามารถใช้ getMeasuredWidth () หรือ getMeasuredHeight ()

myImage.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
int width = myImage.getMeasuredWidth();
int height = myImage.getMeasuredHeight();

5
ด้วยเหตุผลบางอย่างนี่เป็นคำตอบเดียวที่เหมาะกับฉัน - ขอบคุณ!
Farbod Salamat-Zadeh

เช่นเดียวกับฉันทางออกที่ง่ายที่สุดที่ทำงานกับฉัน
Tima

ทำงานเหมือนจับใจ!
Morales Batovski

1
@CommonsWare วิธีการแก้ปัญหานี้ให้ความสูงและความกว้างก่อนที่ผู้สังเกตการณ์ดูต้นไม้ในการโทรกลับรูปแบบทั่วโลกคำอธิบายจะเป็นประโยชน์
Mightian

22

ฉันอยากจะใช้OnPreDrawListener()แทนaddOnGlobalLayoutListener()เพราะมันถูกเรียกว่าเร็วกว่าผู้ฟังคนอื่นเล็กน้อย

    view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
    {
        @Override
        public boolean onPreDraw()
        {
            if (view.getViewTreeObserver().isAlive())
                view.getViewTreeObserver().removeOnPreDrawListener(this);

            // put your code here
            return true;
        }
    });

3
โปรดทราบว่าreturn falseวิธีการที่จะยกเลิกการผ่านการวาดภาพในปัจจุบัน เพื่อดำเนินการต่อreturn trueแทน
ปาง

9

ส่วนขยาย Kotlin เพื่อสังเกตการณ์โครงร่างส่วนกลางและดำเนินงานตามที่กำหนดเมื่อความสูงพร้อมแบบไดนามิก

การใช้งาน:

view.height { Log.i("Info", "Here is your height:" + it) }

การดำเนินงาน:

fun <T : View> T.height(function: (Int) -> Unit) {
    if (height == 0)
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
                function(height)
            }
        })
    else function(height)
}

6

AndroidX มีฟังก์ชั่นเสริมหลายอย่างที่ช่วยให้คุณทำงานประเภทนี้ได้ androidx.core.view

คุณต้องใช้ Kotlin สำหรับสิ่งนี้

หนึ่งที่เหมาะกับที่นี่ที่สุดคือdoOnLayout:

ดำเนินการตามที่กำหนดเมื่อมุมมองนี้ถูกจัดวาง หากมุมมองถูกวางไว้และไม่ได้ขอเลย์เอาต์การดำเนินการจะถูกดำเนินการทันทีมิฉะนั้นการกระทำจะถูกดำเนินการหลังจากมุมมองถูกวางถัดไป

การดำเนินการจะถูกเรียกใช้เพียงครั้งเดียวในเค้าโครงถัดไปและนำออกแล้ว

ในตัวอย่างของคุณ:

bt.doOnLayout {
    val ra = RotateAnimation(0,360,it.width / 2,it.height / 2)
    // more code
}

การอ้างอิง: androidx.core:core-ktx:1.0.0


นี่เป็นคำตอบที่ถูกต้องเท่านั้น android.googlesource.com/platform/frameworks/support/+/…
hrules6872

1
ส่วนขยายเหล่านี้มีความสำคัญและมีประโยชน์จริงๆ
Ahmed na

5

หนึ่งซับถ้าคุณกำลังใช้RxJava & RxBindings วิธีการที่คล้ายกันโดยไม่ต้องต้นแบบ นอกจากนี้ยังแก้สับคำเตือนปราบในขณะที่คำตอบโดยทิม Autin

RxView.layoutChanges(yourView).take(1)
      .subscribe(aVoid -> {
           // width and height have been calculated here
      });

นี่ไง. ไม่จำเป็นต้องยกเลิกการสมัครแม้ว่าจะไม่เคยโทรมา


4

มันเกิดขึ้นเพราะมุมมองต้องใช้เวลามากขึ้นในการขยายตัว ดังนั้นแทนที่จะโทรview.widthและview.heightบนเธรดหลักคุณควรใช้view.post { ... }เพื่อให้แน่ใจว่าคุณviewได้ถูกทำให้พองเกินจริงแล้ว ใน Kotlin:

view.post{width}
view.post{height}

ใน Java คุณสามารถโทรgetWidth()และgetHeight()วิธีการใน a Runnableและส่งผ่านRunnableไปยังview.post()วิธี

view.post(new Runnable() {
        @Override
        public void run() {
            view.getWidth(); 
            view.getHeight();
        }
    });

นี่ไม่ใช่คำตอบที่ถูกต้องเนื่องจากมุมมองอาจไม่ได้รับการวัดและจัดวางเมื่อโค้ดที่โพสต์เรียกใช้งาน มันจะเป็นกรณีที่มีขอบ แต่.postไม่รับประกันว่ามุมมองที่จะวาง
d.aemon

1
คำตอบนี้ยอดเยี่ยมและใช้ได้สำหรับฉัน ขอบคุณมาก Ehsan
MMG

3

ความสูงและความกว้างเป็นศูนย์เนื่องจากมุมมองไม่ได้ถูกสร้างขึ้นตามเวลาที่คุณร้องขอความสูงและความกว้าง ทางออกหนึ่งที่ง่ายที่สุดคือ

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

วิธีนี้ดีเมื่อเทียบกับวิธีอื่น ๆ เพราะสั้นและคมชัด


3

บางทีนี่อาจช่วยใครซักคน:

สร้างฟังก์ชั่นเสริมสำหรับการเรียนดู

ชื่อไฟล์: ViewExt.kt

fun View.afterLayout(what: () -> Unit) {
    if(isLaidOut) {
        what.invoke()
    } else {
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
                what.invoke()
            }
        })
    }
}

ซึ่งสามารถใช้กับทุกมุมมองด้วย:

view.afterLayout {
    do something with view.height
}

2

Answer with postไม่ถูกต้องเนื่องจากขนาดอาจไม่ถูกคำนวณใหม่
อีกสิ่งที่สำคัญคือการที่มุมมองและทุกบรรพบุรุษมันต้องมองเห็นได้ View.isShownเพื่อที่ฉันจะใช้สถานที่ให้บริการ

นี่คือฟังก์ชั่นค็อตลินของฉันซึ่งสามารถวางไว้ที่ใดก็ได้ใน utils:

fun View.onInitialized(onInit: () -> Unit) {
    viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            if (isShown) {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
                onInit()
            }
        }
    })
}

และการใช้งานคือ:

myView.onInitialized {
    Log.d(TAG, "width is: " + myView.width)
}

1

หากคุณกำลังใช้ Kotlin

  leftPanel.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {

            override fun onGlobalLayout() {

                if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
                    leftPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
                }
                else {
                    leftPanel.viewTreeObserver.removeGlobalOnLayoutListener(this)
                }

                // Here you can get the size :)
                leftThreshold = leftPanel.width
            }
        })

0

มุมมองที่หายไปจะคืนค่าความสูงเป็น 0 หากแอปในพื้นหลัง รหัสของฉัน (1oo% ใช้งานได้)

fun View.postWithTreeObserver(postJob: (View, Int, Int) -> Unit) {
    viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            val widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            val heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            measure(widthSpec, heightSpec)
            postJob(this@postWithTreeObserver, measuredWidth, measuredHeight)
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                @Suppress("DEPRECATION")
                viewTreeObserver.removeGlobalOnLayoutListener(this)
            } else {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        }
    })
}

0

เราต้องรอดูจะถูกดึงออกมา เพื่อจุดประสงค์นี้ใช้ OnPreDrawListener ตัวอย่าง Kotlin:

val preDrawListener = object : ViewTreeObserver.OnPreDrawListener {

                override fun onPreDraw(): Boolean {
                    view.viewTreeObserver.removeOnPreDrawListener(this)

                    // code which requires view size parameters

                    return true
                }
            }

            view.viewTreeObserver.addOnPreDrawListener(preDrawListener)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.