Cách ủy quyền để ghi đè onMeasure ()?


88

Cách chính xác để ghi đè onMeasure () là gì? Tôi đã thấy nhiều cách tiếp cận khác nhau. Ví dụ: Professional Android Development sử dụng MeasureSpec để tính toán các thứ nguyên, sau đó kết thúc bằng lệnh gọi setMeasuredDimension (). Ví dụ:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
}

Mặt khác, theo bài đăng này , cách "đúng" là sử dụng MeasureSpec, gọi setMeasuredDimensions (), tiếp theo là một lệnh gọi setLayoutParams () và kết thúc bằng một lệnh gọi tới super.onMeasure (). Ví dụ:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Vậy cách nào là đúng? Cả hai cách tiếp cận đều không hiệu quả 100% đối với tôi.

Tôi đoán thực sự những gì tôi đang hỏi là có ai biết hướng dẫn giải thích onMeasure (), bố cục, kích thước của chế độ xem con, v.v. không?


15
bạn có thấy câu trả lời hữu ích / đúng không? tại sao không đánh dấu nó như là các câu trả lời?
superjos

Câu trả lời:


68

Các giải pháp khác không toàn diện. Chúng có thể hoạt động trong một số trường hợp và là một nơi tốt để bắt đầu, nhưng chúng có thể không đảm bảo hiệu quả.

Khi onMeasure được gọi, bạn có thể có hoặc không có quyền thay đổi kích thước. Các giá trị được chuyển đến onMeasure ( widthMeasureSpec, heightMeasureSpec) của bạn chứa thông tin về những gì chế độ xem con bạn được phép làm. Hiện tại có ba giá trị:

  1. MeasureSpec.UNSPECIFIED - Bạn có thể lớn như bạn muốn
  2. MeasureSpec.AT_MOST- Lớn như bạn muốn (tối đa kích thước thông số kỹ thuật), Đây là parentWidthtrong ví dụ của bạn.
  3. MeasureSpec.EXACTLY- Không có lựa chọn. Cha mẹ đã chọn.

Điều này được thực hiện để Android có thể thực hiện nhiều lần vượt qua để tìm ra kích thước phù hợp cho từng mục, xem tại đây để biết thêm chi tiết.

Nếu bạn không tuân theo các quy tắc này, cách tiếp cận của bạn không được đảm bảo hoạt động.

Ví dụ: nếu bạn muốn kiểm tra xem bạn có được phép thay đổi kích thước hay không, bạn có thể làm như sau:

final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
boolean resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
boolean resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;

Sử dụng thông tin này, bạn sẽ biết liệu bạn có thể sửa đổi các giá trị như trong mã của mình hay không. Hoặc nếu bạn được yêu cầu làm điều gì đó khác biệt. Một cách nhanh chóng và dễ dàng để giải quyết kích thước mong muốn của bạn là sử dụng một trong các phương pháp sau:

int ResolutionSizeAndState (int size, int MeasureSpec, int childMeasuredState)

int ResolutionSize (int size, int MeasureSpec)

Trong khi cái đầu tiên chỉ có trên Honeycomb, cái thứ hai có sẵn trên tất cả các phiên bản.

Lưu ý: Bạn có thể thấy điều đó resizeWidthhoặc resizeHeightluôn sai. Tôi thấy đây là trường hợp nếu tôi đang yêu cầu MATCH_PARENT. Tôi đã có thể khắc phục điều này bằng cách yêu cầu WRAP_CONTENTtrên bố cục mẹ của mình và sau đó trong giai đoạn UNSPECIFIED yêu cầu kích thước là Integer.MAX_VALUE. Làm như vậy mang lại cho bạn kích thước tối đa mà cha mẹ bạn cho phép trong lần chuyển tiếp theo qua onMeasure.


39

Tài liệu là cơ quan có thẩm quyền về vấn đề này: http://developer.android.com/guide/topics/ui/how-android-draws.htmlhttp://developer.android.com/guide/topics/ui/custom -components.html

Tóm lại: ở cuối onMeasurephương thức ghi đè, bạn nên gọi setMeasuredDimension.

Bạn không nên gọi super.onMeasuresau khi gọi setMeasuredDimension, điều đó sẽ chỉ xóa bất cứ điều gì bạn đã đặt. Trong một số tình huống, bạn có thể muốn gọi super.onMeasuređầu tiên và sau đó sửa đổi kết quả bằng cách gọi setMeasuredDimension.

Đừng gọi setLayoutParamsvào onMeasure. Bố cục xảy ra trong lần vượt qua thứ hai sau khi đo.


Kinh nghiệm của tôi là nếu tôi ghi đè onMeasure mà không gọi super.onMeasure, thì OnLayout không được gọi trên các lượt xem con.
William Jockusch

2

Tôi nghĩ rằng nó phụ thuộc vào phụ huynh mà bạn đang ghi đè.

Ví dụ: nếu bạn đang mở rộng một ViewGroup (như FrameLayout), khi bạn đã đo kích thước, bạn nên gọi như bên dưới

super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));

bởi vì bạn có thể muốn ViewGroup thực hiện công việc còn lại (thực hiện một số nội dung trên chế độ xem con)

Nếu bạn đang mở rộng một Chế độ xem (như ImageView), bạn chỉ có thể gọi this.setMeasuredDimension(width, height);, bởi vì lớp cha sẽ chỉ làm một việc giống như bạn đã làm.

Nói một cách dễ hiểu, nếu bạn muốn một số tính năng mà lớp cha của bạn cung cấp miễn phí, bạn nên gọi super.onMeasure()(thông thường thông số kỹ thuật đo chế độ MeasureSpec.EXACTLY), nếu không thì gọi this.setMeasuredDimension(width, height);là đủ.


1

đây là cách tôi giải quyết vấn đề:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        ....

        setMeasuredDimension( measuredWidth, measuredHeight );

        widthMeasureSpec = MeasureSpec.makeMeasureSpec( measuredWidth, MeasureSpec.EXACTLY );
        heightMeasureSpec = MeasureSpec.makeMeasureSpec( measuredHeight, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

Hơn nữa, nó cần thiết cho thành phần ViewPager


3
Đây không phải là giải pháp thích hợp. super.onMeasuresẽ xóa các thứ nguyên được đặt bằng cách sử dụng setMeasuredDimension.
tomrozb

@tomrozb nó không phải là humptydevelopers.com/2013/05/… và bạn có thể kiểm tra nguồn android
Yuli Reiri

@YuliReiri: Giải pháp hoàn hảo!
Shayan Tabatabaee

0

Nếu thay đổi kích thước khung nhìn bên trong onMeasuretất cả những gì bạn cần là setMeasuredDimensioncuộc gọi. Nếu bạn đang thay đổi kích thước bên ngoài onMeasurebạn cần phải gọi setLayoutParams. Ví dụ: thay đổi kích thước của dạng xem văn bản khi văn bản được thay đổi.


Bạn vẫn có thể gọi setMinWidth () và setMaxWidth () và có onMeasure tiêu chuẩn xử lý nó. Tôi nhận thấy rằng tốt hơn là không gọi setLayoutParams từ bên trong của một lớp Chế độ xem tùy chỉnh. Nó thực sự được sử dụng cho các Nhóm Xem hoặc Hoạt động để ghi đè hành vi xem trẻ em.
Dorrin

0

Phụ thuộc vào điều khiển bạn đang sử dụng. Các hướng dẫn trong tài liệu này phù hợp với một số điều khiển (TextView, Button, ...), nhưng không phù hợp với những điều khiển khác (LinearLayout, ...). Cách hoạt động rất hiệu quả đối với tôi là gọi siêu cấp khi tôi làm xong. Dựa vào bài viết trong liên kết dưới đây.

http://humptydevelopers.blogspot.in/2013/05/android-view-overriding-onmeasure.html


-1

Tôi đoán, setLayoutParams và tính toán lại các phép đo là một cách giải quyết để thay đổi kích thước các chế độ xem con một cách chính xác, vì điều này thường được thực hiện trong onMeasure của lớp dẫn xuất.

Tuy nhiên, điều này hiếm khi hoạt động chính xác (vì bất kỳ lý do gì ...), tốt hơn nên gọi hàm MeasureChildren (khi tạo ra một ViewGroup) hoặc thử một cái gì đó tương tự khi cần thiết.


-5

bạn có thể lấy đoạn mã này làm ví dụ về onMeasure () ::

public class MyLayerLayout extends RelativeLayout {

    public MyLayerLayout(Context context) {
        super(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);

        int currentChildCount = getChildCount();
        for (int i = 0; i < currentChildCount; i++) {
            View currentChild = getChildAt(i);

            //code to find information

            int widthPercent = currentChildInfo.getWidth();
            int heightPercent = currentChildInfo.getHeight();

//considering we will pass height & width as percentage

            int myWidth = (int) Math.round(parentWidth * (widthPercent / 100.0));
            int myHeight = (int) Math.round(parentHeight * (heightPercent / 100.0));

//Considering we need to set horizontal & vertical position of the view in parent

            AlignmentTraitValue vAlign = currentChildInfo.getVerticalLocation() != null ? currentChildlayerInfo.getVerticalLocation() : currentChildAlignmentTraitValue.TOP;
            AlignmentTraitValue hAlign = currentChildInfo.getHorizontalLocation() != null ? currentChildlayerInfo.getHorizontalLocation() : currentChildAlignmentTraitValue.LEFT;
            int topPadding = 0;
            int leftPadding = 0;

            if (vAlign.equals(currentChildAlignmentTraitValue.CENTER)) {
                topPadding = (parentHeight - myHeight) / 2;
            } else if (vAlign.equals(currentChildAlignmentTraitValue.BOTTOM)) {
                topPadding = parentHeight - myHeight;
            }

            if (hAlign.equals(currentChildAlignmentTraitValue.CENTER)) {
                leftPadding = (parentWidth - myWidth) / 2;
            } else if (hAlign.equals(currentChildAlignmentTraitValue.RIGHT)) {
                leftPadding = parentWidth - myWidth;
            }
            LayoutParams myLayoutParams = new LayoutParams(myWidth, myHeight);
            currentChildLayoutParams.setMargins(leftPadding, topPadding, 0, 0);
            currentChild.setLayoutParams(myLayoutParams);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

6
Mã này về cơ bản là sai. Đầu tiên, nó không tính đến chế độ bố cục (xem câu trả lời của Grimmace). Điều đó thật tệ nhưng bạn sẽ không thấy tác dụng của điều đó vì bạn cũng gọi super.onMeasure () ở cuối, nó sẽ ghi đè các giá trị bạn đặt (xem câu trả lời của satur9nine) và đánh bại mục đích ghi đè onMeasure ().
spaaarky21
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.