Làm thế nào để lấy lại kích thước của một khung nhìn?


209

Tôi có một cái nhìn bao gồm TableLayout, TableRow and TextView. Tôi muốn nó trông giống như một lưới. Tôi cần phải có được chiều cao và chiều rộng của lưới này. Các phương thức getHeight()getWidth()luôn trả về 0. Điều này xảy ra khi tôi định dạng lưới một cách linh hoạt và cả khi tôi sử dụng phiên bản XML.

Làm thế nào để lấy kích thước cho một khung nhìn?


Đây là chương trình thử nghiệm của tôi, tôi đã sử dụng trong Debug để kiểm tra kết quả:

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

thay vì sử dụng getWidth / Chiều cao, hãy sử dụng getMeasuredWidth / Chiều cao sau khi bố cục được áp dụng cho hoạt động.
bhups

9
Các bạn, tất cả những gì phải làm là gọi getHeight()getWidth()sau khi vòng đời Activity đã yêu cầu các quan điểm tự đo, nói cách khác, hãy thực hiện loại công cụ này onResume()và đó là điều đó. Bạn không nên mong đợi một đối tượng chưa được đặt ra để biết kích thước của nó, đó là toàn bộ mánh khóe. Không cần phép thuật đề xuất dưới đây.
lớp xếp chồng

2
Gọi getHeight()/Width()vào onResume()không mang lại giá trị> 0 cho tôi.
Darpan

@ClassStacker Còn về Fragment?
zdd

@zdd Xin hỏi một câu hỏi hoàn toàn mới và gửi một tài liệu tham khảo ở đây trong một bình luận.
lớp xếp chồng

Câu trả lời:


418

Tôi tin rằng OP đã qua lâu, nhưng trong trường hợp câu trả lời này có thể giúp những người tìm kiếm trong tương lai, tôi nghĩ tôi sẽ đăng một giải pháp mà tôi đã tìm thấy. Tôi đã thêm mã này vào onCreate()phương thức của mình :

EDITED: 07/05/11 để bao gồm mã từ ý kiến:

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

});

Đầu tiên tôi nhận được một tài liệu tham khảo cuối cùng cho tôi TextView(để truy cập trong onGlobalLayout()phương thức). Tiếp theo, tôi lấy ViewTreeObservertừ của tôi TextViewvà thêm một ghi OnGlobalLayoutListenerđè onGLobalLayout(dường như không có phương thức siêu lớp nào để gọi ở đây ...) và thêm mã của tôi yêu cầu biết các phép đo của chế độ xem vào trình nghe này. Tất cả các công việc như mong đợi cho tôi, vì vậy tôi hy vọng rằng điều này có thể giúp đỡ.


23
@George Bailey: Một điều gần đây tôi đã thấy một người khác đăng bài (tôi đã tự mình kiểm tra nó, vì vậy YMMV) để khắc phục nhiều cuộc gọi là (trong onGlobalLayout ()): tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);cách đó người nghe nên loại bỏ sau lần đầu tiên nó xảy ra.
Kevin Coppock

3
Tôi cũng muốn giúp đỡ. Bạn có thể cần phải làm một cái gì đó như: mView.getViewTreeObserver (). RemoveGlobalOnLayoutListener (globalLayoutListener); nếu bạn muốn thay đổi kích thước khung nhìn của bạn chỉ một lần. Hy vọng sự giúp đỡ này
Henry Sou

7
Câu hỏi rất cũ, nhưng sử dụng addOnLayoutChangeListenercông việc là tốt! :)
FX

7
Ngoài ra, lưu ý rằng kể từ API 16, bạn cần: if (android.os.Build.VERSION.SDK_INT> = 16) {view.getViewTreeObserver (). RemoveOnGlobalLayoutListener (this); } other {view.getViewTreeObserver (). removeGlobalOnLayoutListener (this); }
Edison

2
Khi removeGlobalOnLayoutListener không được dùng nữa, nó vẫn đưa ra các cảnh báo trong Eclipse. Điều này có bình thường không? Theo thời gian, các cảnh báo mã không dùng nữa sẽ tràn ngập khắp nơi ...
Jonny

82

Tôi sẽ chỉ thêm một giải pháp thay thế, ghi đè phương thức onWindowF FocusChanged của hoạt động của bạn và bạn sẽ có thể nhận các giá trị của getHeight (), getWidth () từ đó.

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

4
Để trích dẫn tài liệu, "Đây là chỉ số tốt nhất về việc liệu hoạt động này có hiển thị cho người dùng hay không."
Cameron Lowell Palmer

6
Điều này không hoạt động trong trường hợp Mảnh vỡ mà bạn phải sử dụng ViewTreeObserver.
Mihir

OP đặc biệt hỏi làm thế nào để có được nó để xem mặc dù
JustDanyul

Tổng chiều cao cuộn và tối đa Scrollview.getScrolly () sẽ giống nhau? tức là trong trường hợp của tôi, tôi có Chiều cao với phương pháp trên là 702 và tôi nhận được giá trị tối đa từ getScrolly () là 523,
Hitesh Dhamshaniya

Phương pháp này cũng không hoạt động khi Chế độ xem của bạn được đưa vào từ một tệp xml khác.
Jay Donga

19

Bạn đang cố gắng để có được chiều rộng và chiều cao của một yếu tố, chưa được vẽ.

Nếu bạn sử dụng gỡ lỗi và dừng tại một số điểm, bạn sẽ thấy rằng màn hình thiết bị của bạn vẫn trống, đó là do các yếu tố của bạn chưa được vẽ, vì vậy bạn không thể có chiều rộng và chiều cao của thứ gì đó, chưa hiện hữu.

Và, tôi có thể sai, nhưng setWidth()không phải lúc nào cũng được tôn trọng, Layoutđưa ra đó là trẻ em và quyết định cách đo chúng (gọi điện child.measure()), vì vậy, nếu bạn đặt setWidth(), bạn không được đảm bảo lấy chiều rộng này sau khi phần tử sẽ được rút ra.

Những gì bạn cần, là sử dụng getMeasuredWidth()(thước đo gần đây nhất của Chế độ xem của bạn) ở đâu đó sau khi chế độ xem thực sự được vẽ.

Nhìn vào Activityvòng đời để tìm thời điểm tốt nhất.

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

Tôi tin rằng một thực hành tốt là sử dụng OnGlobalLayoutListenernhư thế này:

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

Bạn có thể đặt mã này trực tiếp vào onCreate(), và nó sẽ được gọi khi các khung nhìn sẽ được đặt ra.


Thật không may, nó cũng không hoạt động trong OnResume. Kích thước cuối cùng được đặt ở đâu đó sau cuộc gọi OnResume, ít nhất là trong trình giả lập.
jki

Đó là một thực tế khủng khiếp !!! Người nghe này sẽ được gọi đi gọi lại và sẽ giết chết màn trình diễn của bạn. Thay vì sử dụng cờ, hủy đăng ký người nghe một lần.
bluewhile

15

Sử dụng phương pháp bài của View như thế này

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

1
Điều đó làm việc, nhưng thực sự, không có cuộc thảo luận về lý do tại sao? Btw, điều này hoạt động vì runnable không được chạy cho đến khi bố trí đã xảy ra.
Norman H

7

Tôi đã cố sử dụng onGlobalLayout()để thực hiện một số định dạng tùy chỉnh của a TextView, nhưng như @George Bailey nhận thấy, onGlobalLayout()thực sự được gọi hai lần: một lần trên đường dẫn bố cục ban đầu và lần thứ hai sau khi sửa đổi văn bản.

View.onSizeChanged()hoạt động tốt hơn đối với tôi bởi vì nếu tôi sửa đổi văn bản ở đó, phương thức chỉ được gọi một lần (trong quá trình bố trí). Điều này yêu cầu phân loại phụ TextView, nhưng trên API Cấp 11+ View. addOnLayoutChangeListener()có thể được sử dụng để tránh phân loại phụ.

Một điều nữa, để có được độ rộng chính xác của chế độ xem View.onSizeChanged(), layout_widthnên đặt thành match_parentkhông, không wrap_content.


2

Bạn đang cố gắng để có được kích thước trong một hàm tạo, hoặc bất kỳ phương thức nào khác đang chạy TRƯỚC KHI bạn có được hình ảnh thực tế?

Bạn sẽ không nhận được bất kỳ kích thước nào trước khi tất cả các thành phần thực sự được đo (vì xml của bạn không biết về kích thước hiển thị, vị trí của cha mẹ và bất cứ điều gì)

Hãy thử nhận các giá trị sau onSizeChanged () (mặc dù nó có thể được gọi bằng 0) hoặc chỉ đơn giản là chờ đợi khi bạn nhận được một hình ảnh thực tế.


Tôi cập nhật câu hỏi và mã ban đầu của tôi để rõ ràng hơn. Cảm ơn bạn.

2

Như FX đã đề cập, bạn có thể sử dụng một OnLayoutChangeListenerchế độ xem mà bạn muốn theo dõi chính nó

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

Bạn có thể loại bỏ người nghe trong cuộc gọi lại nếu bạn chỉ muốn bố trí ban đầu.



1

ViewTreeObserveronWindowFocusChanged()không quá cần thiết ở tất cả.

Nếu bạn thổi phồng TextViewbố cục dưới dạng và / hoặc đặt một số nội dung vào đó và đặt LayoutParamsthì bạn có thể sử dụng getMeasuredHeight()getMeasuredWidth().

NHƯNG bạn phải cẩn thận vớiLinearLayouts (có thể khác ViewGroups). Vấn đề ở đây là, bạn có thể có được chiều rộng và chiều cao sau đó onWindowFocusChanged()nhưng nếu bạn cố gắng thêm một số chế độ xem trong đó, thì bạn không thể có được thông tin đó cho đến khi mọi thứ được rút ra. Tôi đã cố gắng thêm nhiều TextViewsđể LinearLayoutsbắt chước một FlowLayout(kiểu gói) và vì vậy không thể sử dụng Listeners. Khi quá trình được bắt đầu, nó sẽ tiếp tục đồng bộ. Vì vậy, trong trường hợp như vậy, bạn có thể muốn giữ chiều rộng trong một biến để sử dụng nó sau này, vì trong khi thêm chế độ xem vào bố cục, bạn có thể cần nó.


1

Mặc dù giải pháp đề xuất hoạt động, nó có thể không phải là giải pháp tốt nhất cho mọi trường hợp vì dựa trên tài liệu cho ViewTreeObserver.OnGlobalLayoutListener

Định nghĩa giao diện cho một cuộc gọi lại được gọi khi trạng thái bố cục toàn cục hoặc khả năng hiển thị của các khung nhìn trong cây xem thay đổi.

có nghĩa là nó được gọi nhiều lần và không phải lúc nào tầm nhìn cũng được đo (nó có chiều cao và chiều rộng được xác định)

Một cách khác là sử dụng ViewTreeObserver.OnPreDrawListenerchỉ được gọi khi chế độ xem sẵn sàng được vẽ và có tất cả các phép đo của nó.

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

Bạn có thể sử dụng một chương trình phát được gọi trong OnResume ()

Ví dụ:

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

}

Bạn không thể có được chiều cao của chế độ xem trong OnCreate (), onStart () hoặc thậm chí trong onResume () vì lý do kcoppock phản hồi


0

Chiều cao và chiều rộng bằng không vì chế độ xem chưa được tạo vào thời điểm bạn yêu cầu chiều cao và chiều rộng. Một giải pháp đơn giản nhất là

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

Phương pháp này là tốt so với các phương pháp khác vì nó ngắn và sắc nét.


-1

Phản hồi đơn giản: Điều này làm việc cho tôi không có vấn đề. Có vẻ như điều quan trọng là đảm bảo rằng Chế độ xem có tiêu điểm trước khi bạn có được Hightight, v.v. Thực hiện việc này bằng cách sử dụng phương thức hasF Focus (), sau đó sử dụng phương thức getHeight () theo thứ tự đó. Chỉ cần 3 dòng mã.

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

int myButtonHeight = myImageButton1.getHeight();

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

Hy vọng nó giúp.


-3

Sử dụng getMeasuredWidth () và getMeasuredHeight () cho chế độ xem của bạn.

Hướng dẫn dành cho nhà phát triển: Xem


4
Như đã trình bày trước các phương pháp này chỉ trả về một kết quả hợp lệ efter kích thước đã được meassured. Trong một Hoạt động, điều này đúng khi phương thức onWindowF FocusChanged os được gọi.
slott

-8

ĐÚNG: Tôi phát hiện ra rằng giải pháp trên là khủng khiếp. Đặc biệt là khi điện thoại của bạn chậm. Và ở đây, tôi đã tìm thấy một giải pháp khác: tính ra giá trị px của phần tử, bao gồm cả lề và phần đệm: dp đến px: https://stackoverflow.com/a/6327095/1982712

hoặc dimens.xml thành px: https://stackoverflow.com/a/16276351/1982712

sp đến px: https://stackoverflow.com/a/9219417/1982712 (đảo ngược giải pháp)

hoặc giảm xuống px: https://stackoverflow.com/a/16276351/1982712

và đó là nó.


10
Đó thường là một ý tưởng tồi để hy vọng rằng mọi thứ sẽ được đặt chính xác sau một số mili giây được chọn tùy ý. Thiết bị có thể chậm, các tiến trình / luồng khác có thể đang chạy, có thể không hoạt động trong trình gỡ lỗi, có thể không hoạt động trong phiên bản tiếp theo của HĐH, ...
Kristopher Johnson
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.