วิธีการดึงขนาดของมุมมอง?


209

TableLayout, TableRow and TextViewผมมีมุมมองที่สร้างขึ้นจาก ฉันต้องการให้มันดูเหมือนตาราง ฉันต้องการความสูงและความกว้างของกริดนี้ วิธีการgetHeight()และgetWidth()ผลตอบแทน 0 มักเกิดขึ้นเมื่อฉันจัดรูปแบบตารางแบบไดนามิกและเมื่อฉันใช้รุ่น XML

วิธีดึงมิติข้อมูลสำหรับมุมมอง


นี่คือโปรแกรมทดสอบที่ฉันใช้ใน Debug เพื่อตรวจสอบผลลัพธ์:

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

แทนที่จะใช้ getWidth / Height ใช้ getMeasuredWidth / Height หลังจากใช้โครงร่างกับกิจกรรม
bhups

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

2
การโทรgetHeight()/Width()เข้าonResume()ไม่ได้ให้ค่า> 0 สำหรับฉัน
Darpan

@ClassStacker แล้ว Fragment ล่ะ?
zdd

@zdd โปรดถามคำถามใหม่ที่สมบูรณ์และโพสต์การอ้างอิงที่นี่ในความคิดเห็น
รถ stacker ระดับ

คำตอบ:


418

ฉันเชื่อว่า OP หายไปนานแล้ว แต่ในกรณีที่คำตอบนี้สามารถช่วยผู้ค้นหาในอนาคตได้ฉันคิดว่าฉันจะโพสต์โซลูชันที่พบ ฉันได้เพิ่มรหัสนี้เป็นonCreate()วิธีการของฉัน:

แก้ไข: 07/05/11 ที่จะรวมรหัสจากความคิดเห็น:

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

});

ก่อนอื่นฉันจะได้รับการอ้างอิงขั้นสุดท้ายของฉันTextView(เพื่อเข้าถึงในonGlobalLayout()วิธีการ) ต่อไปฉันได้รับViewTreeObserverมาจากของฉันTextViewและเพิ่มการOnGlobalLayoutListenerเอาชนะonGLobalLayout(ดูเหมือนจะไม่มีวิธี superclass เพื่อเรียกที่นี่ ... ) และเพิ่มรหัสของฉันซึ่งต้องรู้การวัดมุมมองในการฟังนี้ ทั้งหมดทำงานได้ตามที่คาดไว้สำหรับฉันดังนั้นฉันหวังว่านี่จะช่วยได้


23
@ George Bailey: สิ่งหนึ่งที่ฉันเพิ่งเห็นคนอื่นโพสต์ (ฉันไม่ได้ทดสอบตัวเองดังนั้น YMMV) เพื่อเอาชนะการโทรหลายสายคือ (ภายใน onGlobalLayout ()): tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);วิธีที่ผู้ฟังควรถูกลบหลังจากครั้งแรก มันเกิดขึ้น.
Kevin Coppock

3
ฉันต้องการความช่วยเหลือเช่นกัน คุณอาจต้องทำสิ่งที่ชอบ: mView.getViewTreeObserver () removeGlobalOnLayoutListener (globalLayoutListener); หากคุณต้องการปรับขนาดมุมมองของคุณเพียงครั้งเดียว หวังว่าความช่วยเหลือนี้
Henry Sou

7
คำถามนั้นเก่ามาก แต่ใช้addOnLayoutChangeListenerงานด้วย! :)
FX

7
นอกจากนี้โปรดทราบว่าตั้งแต่ API 16 คุณต้อง: if (android.os.Build.VERSION.SDK_INT> = 16) {view.getViewTreeObserver () removeOnGlobalLayoutListener (นี่); } else {view.getViewTreeObserver (). removeGlobalOnLayoutListener (นี้); }
Edison

2
เนื่องจาก removeGlobalOnLayoutListener เลิกใช้แล้วจะยังคงให้คำเตือนใน Eclipse เป็นเรื่องปกติหรือไม่ เมื่อเวลาผ่านไปคำเตือนรหัสที่เลิกใช้จะท่วมท้นไปทั่ว ...
จอนนี่

82

ฉันจะเพิ่มโซลูชันทางเลือกแทนที่วิธี onWindowFocusChanged ของกิจกรรมของคุณและคุณจะสามารถรับค่าของ getHeight (), getWidth () จากที่นั่น

@Override
public void onWindowFocusChanged (boolean hasFocus) {
        // the height will be set at this point
        int height = myEverySoTallView.getMeasuredHeight(); 
}

4
ในการอ้างอิงเอกสาร "นี่เป็นตัวบ่งชี้ที่ดีที่สุดว่าผู้ใช้สามารถมองเห็นกิจกรรมนี้ได้หรือไม่"
Cameron Lowell Palmer

6
ViewTreeObserverนี้ไม่ได้ทำงานในกรณีของเศษสำหรับการที่คุณต้องใช้
Mihir

OP ถามโดยเฉพาะว่าจะดูอย่างไร
JustDanyul

ความสูงทั้งหมดของ scrollview และสูงสุด Scrollview.getScrolly () จะเหมือนกันหรือไม่ เช่นในกรณีของฉันฉันได้ความสูงด้วยวิธีการข้างต้นคือ 702 และฉันได้รับมูลค่าสูงสุดจาก getScrolly () คือ 523
Hitesh Dhamshaniya

วิธีนี้ยังใช้ไม่ได้เมื่อรวมมุมมองของคุณจากไฟล์ xml อื่น
Jay Donga

19

คุณกำลังพยายามที่จะรับความกว้างและความสูงขององค์ประกอบที่ยังไม่ได้วาด

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

และฉันอาจจะผิด แต่setWidth()ไม่ได้รับการเคารพเสมอLayoutวางเป็นลูก ๆ และตัดสินใจว่าจะวัดพวกเขาอย่างไร (เรียกchild.measure()) ดังนั้นถ้าคุณตั้งค่าsetWidth()คุณจะไม่รับประกันว่าจะได้ความกว้างนี้หลังจากที่องค์ประกอบถูกดึงออกมา

สิ่งที่คุณต้องการคือการใช้getMeasuredWidth()(การวัดล่าสุดของมุมมองของคุณ) ที่ใดที่หนึ่งหลังจากที่ได้รับมุมมองจริง ๆ แล้ว

ดูActivityวงจรชีวิตเพื่อค้นหาช่วงเวลาที่ดีที่สุด

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

ฉันเชื่อว่าวิธีปฏิบัติที่ดีคือการใช้OnGlobalLayoutListenerเช่นนี้

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

คุณสามารถวางรหัสนี้โดยตรงonCreate()และมันจะถูกเรียกใช้เมื่อมีการวางมุมมอง


น่าเสียดายที่มันไม่ทำงานใน OnResume เช่นกัน ขนาดสุดท้ายถูกตั้งค่าไว้ที่ไหนสักแห่งหลังจากการโทร OnResume อย่างน้อยก็ในโปรแกรมจำลอง
jki

นั่นเป็นวิธีปฏิบัติที่น่ากลัว !!! ผู้ฟังนี้จะถูกเรียกซ้ำแล้วซ้ำอีกและจะฆ่านักแสดงของคุณ แทนที่จะใช้แฟล็กให้ยกเลิกการลงทะเบียนผู้ฟังเมื่อทำเสร็จแล้ว
bluewhile

15

ใช้วิธีการโพสต์ของ View เช่นนี้

post(new Runnable() {   
    @Override
    public void run() {
        Log.d(TAG, "width " + MyView.this.getMeasuredWidth());
        }
    });

1
ใช้งานได้ แต่จริงๆแล้วไม่มีการอภิปรายว่าทำไม Btw ใช้งานได้เนื่องจาก runnable ไม่สามารถรันได้จนกว่าโครงร่างจะเกิดขึ้น
นอร์แมน H

7

ฉันพยายามใช้onGlobalLayout()การจัดรูปแบบที่กำหนดเองของ a TextViewแต่ขณะที่ @George Bailey สังเกตว่าonGlobalLayout()มีการเรียกสองครั้ง: หนึ่งครั้งบนเส้นทางเค้าโครงเริ่มต้นและครั้งที่สองหลังจากแก้ไขข้อความ

View.onSizeChanged()ทำงานได้ดีขึ้นสำหรับฉันเพราะถ้าฉันแก้ไขข้อความที่นั่นวิธีการที่เรียกว่าเพียงครั้งเดียว (ในช่วงที่ผ่านรูปแบบ) จำเป็นต้องมีการจัดหมวดหมู่ย่อยTextViewแต่ในระดับ API 11+ View. addOnLayoutChangeListener()สามารถใช้เพื่อหลีกเลี่ยงการจัดกลุ่มย่อยได้

สิ่งหนึ่งที่มากขึ้นเพื่อให้ได้ความกว้างที่ถูกต้องของมุมมองในการView.onSizeChanged()ที่layout_widthควรจะกำหนดให้ไม่ได้match_parentwrap_content


2

คุณกำลังพยายามที่จะรับขนาดในนวกรรมิกหรือวิธีการอื่นที่รันก่อนที่คุณจะได้ภาพจริงหรือไม่?

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

ลองรับค่าหลังจาก onSizeChanged () (แม้ว่าจะสามารถเรียกได้ด้วยศูนย์) หรือเพียงแค่รอเมื่อคุณจะได้รับภาพที่แท้จริง


ฉันอัปเดตคำถามและรหัสต้นฉบับของฉันให้ชัดเจนขึ้น ขอบคุณ.

2

ตามที่กล่าวถึง FX คุณสามารถใช้OnLayoutChangeListenerมุมมองที่คุณต้องการติดตามตัวเอง

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

คุณสามารถลบ listener ใน callback ถ้าคุณต้องการเค้าโครงเริ่มต้นเท่านั้น


2

ฉันเดาว่านี่คือสิ่งที่คุณต้องดู: ใช้onSizeChanged()มุมมองของคุณ ต่อไปนี้เป็นข้อมูลโค้ด EXTENDED เกี่ยวกับวิธีใช้onSizeChanged()เพื่อรับเลย์เอาต์ความสูงและความกว้างของมุมมองแบบไดนามิกhttp://syedrakibalhasan.blogspot.com/2011/02/how-to-get-width-and-height-dimensions.html


1

ViewTreeObserverและonWindowFocusChanged()ไม่จำเป็นเลย

ถ้าคุณขยายTextViewเป็นรูปแบบและ / หรือใส่เนื้อหาบางอย่างในนั้นและตั้งค่าLayoutParamsแล้วคุณสามารถใช้และgetMeasuredHeight()getMeasuredWidth()

แต่คุณต้องระวังด้วยLinearLayouts (อาจเป็นอย่างอื่นViewGroups) ปัญหาคือคุณสามารถรับความกว้างและความสูงหลังจากนั้นonWindowFocusChanged()แต่ถ้าคุณพยายามที่จะเพิ่มมุมมองบางอย่างในนั้นคุณจะไม่สามารถรับข้อมูลนั้นได้จนกว่าทุกอย่างจะถูกดึงออกมา ผมพยายามที่จะเพิ่มหลายTextViewsที่จะLinearLayoutsไปเลียนแบบFlowLayout(ลักษณะการตัด) และอื่น ๆ Listenersอาจจะไม่ได้ใช้งาน เมื่อกระบวนการเริ่มต้นขึ้นก็ควรดำเนินการต่อไปพร้อมกัน ดังนั้นในกรณีเช่นนี้คุณอาจต้องการเก็บความกว้างในตัวแปรเพื่อใช้ในภายหลังเช่นในระหว่างการเพิ่มมุมมองให้กับเค้าโครงคุณอาจจำเป็นต้องใช้มัน


1

แม้ว่าวิธีการที่นำเสนอนี้ใช้งานได้ แต่อาจไม่ใช่วิธีที่ดีที่สุดสำหรับทุกกรณีเพราะอ้างอิงจากเอกสารสำหรับ ViewTreeObserver.OnGlobalLayoutListener

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

ซึ่งหมายความว่าจะได้รับการเรียกหลายครั้งและไม่ได้วัดทุกมุมมองเสมอ (มีการกำหนดความสูงและความกว้าง)

อีกทางเลือกหนึ่งคือการใช้ViewTreeObserver.OnPreDrawListenerที่ได้รับการเรียกเมื่อมุมมองพร้อมที่จะดึงและมีการวัดทั้งหมด

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

});


0

คุณสามารถใช้การออกอากาศที่เรียกว่าใน OnResume ()

ตัวอย่างเช่น:

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

}

คุณไม่สามารถรับความสูงของมุมมองใน OnCreate (), onStart () หรือแม้แต่ใน onResume () ด้วยเหตุผลที่ kcoppock ตอบสนอง


0

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

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

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


-1

คำตอบง่ายๆ: สิ่งนี้ใช้ได้สำหรับฉันโดยไม่มีปัญหา ดูเหมือนว่ากุญแจสำคัญคือการตรวจสอบให้แน่ใจว่ามุมมองมีโฟกัสก่อนที่คุณจะ getHeight ฯลฯ ทำได้โดยใช้วิธีการ hasFocus () จากนั้นใช้วิธี getHeight () ตามลำดับนั้น ต้องการรหัสเพียง 3 บรรทัด

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

int myButtonHeight = myImageButton1.getHeight();

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

หวังว่ามันจะช่วย


-3

ใช้ getMeasuredWidth () และ getMeasuredHeight () สำหรับมุมมองของคุณ

คู่มือผู้พัฒนา: ดู


4
ตามที่ระบุไว้ก่อนวิธีการเหล่านี้เพียง แต่กลับเป็นผลที่ถูกต้องefterขนาดที่ได้รับการ meassured ภายในกิจกรรมนี้ถือเป็นจริงเมื่อเรียกใช้เมธอด onWindowFocusChanged os
slott

-8

การแก้ไข: ฉันพบว่าทางออกข้างต้นนั้นแย่มาก โดยเฉพาะอย่างยิ่งเมื่อโทรศัพท์ของคุณช้า และที่นี่ฉันพบวิธีแก้ไขปัญหาอื่น: คำนวณค่า px ขององค์ประกอบรวมถึงระยะขอบและช่องว่าง: dp ถึง px: https://stackoverflow.com/a/6327095/1982712

หรือ dimens.xml ถึง px: https://stackoverflow.com/a/16276351/1982712

sp to px: https://stackoverflow.com/a/9219417/1982712 (กลับด้านโซลูชัน)

หรือหรี่ลงเป็น px: https://stackoverflow.com/a/16276351/1982712

และนั่นคือมัน


10
โดยปกติแล้วมันเป็นความคิดที่ไม่ดีที่จะหวังว่าทุกอย่างจะถูกตั้งค่าอย่างถูกต้องหลังจากจำนวนมิลลิวินาทีที่เลือกโดยพลการ อุปกรณ์อาจช้ากระบวนการ / เธรดอื่น ๆ อาจทำงานอยู่อาจไม่ทำงานในดีบั๊กเกอร์อาจไม่ทำงานใน OS เวอร์ชันถัดไป ...
Kristopher Johnson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.