วิธี Authorative เพื่อแทนที่ onMeasure ()?


89

วิธีที่ถูกต้องในการแทนที่ onMeasure () คืออะไร? ฉันได้เห็นแนวทางต่างๆ ตัวอย่างเช่นProfessional Android Developmentใช้ MeasureSpec ในการคำนวณขนาดจากนั้นจึงจบลงด้วยการเรียก setMeasuredDimension () ตัวอย่างเช่น:

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

ในทางกลับกันตามโพสต์นี้วิธีที่ "ถูกต้อง" คือการใช้ MeasureSpec เรียก setMeasuredDimensions () ตามด้วยการเรียก setLayoutParams () และลงท้ายด้วยการเรียก super.onMeasure () ตัวอย่างเช่น:

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

วิธีไหนคือวิธีที่ถูกต้อง? ทั้งสองวิธีไม่ได้ผล 100% สำหรับฉัน

ฉันเดาว่าสิ่งที่ฉันถามจริงๆคือไม่มีใครรู้เกี่ยวกับบทช่วยสอนที่อธิบายเกี่ยวกับ onMeasure () เค้าโครงขนาดของมุมมองเด็ก ฯลฯ


16
คุณพบว่าคำตอบมีประโยชน์ / ถูกต้องหรือไม่? ทำไมไม่ทำเครื่องหมายเป็นคำตอบ?
superjos

คำตอบ:


69

วิธีแก้ปัญหาอื่น ๆ ไม่ครอบคลุม อาจใช้งานได้ในบางกรณีและเป็นจุดเริ่มต้นที่ดี แต่อาจไม่รับประกันว่าจะได้ผล

เมื่อเรียก onMeasure คุณอาจมีหรือไม่มีสิทธิ์เปลี่ยนขนาด ค่าที่ส่งผ่านไปยัง onMeasure ( widthMeasureSpec, heightMeasureSpec) ของคุณมีข้อมูลเกี่ยวกับสิ่งที่มุมมองลูกของคุณได้รับอนุญาตให้ทำ ปัจจุบันมีสามค่า:

  1. MeasureSpec.UNSPECIFIED - คุณสามารถมีขนาดใหญ่เท่าที่คุณต้องการ
  2. MeasureSpec.AT_MOST- ใหญ่เท่าที่คุณต้องการ (ถึงขนาดข้อมูลจำเพาะ) นี่คือparentWidthตัวอย่างของคุณ
  3. MeasureSpec.EXACTLY- ไม่มีทางเลือก. ผู้ปกครองเลือกแล้ว

นี้จะกระทำเพื่อให้ Android สามารถทำให้ผ่านไปหลายที่จะหาขนาดที่เหมาะสมสำหรับแต่ละรายการให้ดูที่นี่สำหรับรายละเอียดเพิ่มเติม

หากคุณไม่ปฏิบัติตามกฎเหล่านี้แนวทางของคุณจะไม่ได้ผล

ตัวอย่างเช่นหากคุณต้องการตรวจสอบว่าคุณได้รับอนุญาตให้เปลี่ยนขนาดหรือไม่คุณสามารถทำสิ่งต่อไปนี้:

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

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

int editSizeAndState (ขนาด int, int measureSpec, int childMeasuredState)

int resolutionSize (ขนาด int, int measureSpec)

แม้ว่ารุ่นแรกจะมีให้บริการบน Honeycomb เท่านั้น แต่เวอร์ชันที่สองมีให้บริการในทุกเวอร์ชัน

หมายเหตุ: คุณอาจพบว่าresizeWidthหรือresizeHeightอยู่เสมอเท็จ MATCH_PARENTฉันพบนี้จะเป็นกรณีที่ถ้าผมขอ ฉันสามารถแก้ไขสิ่งนี้ได้โดยการร้องขอWRAP_CONTENTในเค้าโครงหลักของฉันจากนั้นในช่วง UNSPECIFIED ขอขนาดInteger.MAX_VALUEไฟล์. เพื่อให้คุณได้ขนาดสูงสุดที่ผู้ปกครองของคุณอนุญาตในการส่งผ่าน onMeasure ครั้งต่อไป


39

เอกสารเป็นผู้มีอำนาจในเรื่องนี้: http://developer.android.com/guide/topics/ui/how-android-draws.htmlและhttp://developer.android.com/guide/topics/ui/custom -components.html

สรุป: ในตอนท้ายของonMeasureวิธีการที่คุณลบล้างคุณควรโทรหาsetMeasuredDimensionวิธีที่คุณควรจะเรียก

คุณไม่ควรโทรsuper.onMeasureหลังจากโทรsetMeasuredDimensionซึ่งจะลบสิ่งที่คุณตั้งไว้ ในบางสถานการณ์ที่คุณอาจต้องการที่จะเรียกครั้งแรกแล้วปรับเปลี่ยนผลโดยการเรียกsuper.onMeasuresetMeasuredDimension

อย่าโทรsetLayoutParamsเข้าonMeasureมา เค้าโครงเกิดขึ้นในรอบที่สองหลังจากการวัด


ประสบการณ์ของฉันคือถ้าฉันแทนที่ onMeasure โดยไม่เรียก super.onMeasure จะไม่เรียก OnLayout ในมุมมองเด็ก
William Jockusch

2

ฉันคิดว่ามันขึ้นอยู่กับผู้ปกครองที่คุณเอาชนะ

ตัวอย่างเช่นหากคุณกำลังขยาย ViewGroup (เช่น FrameLayout) เมื่อคุณวัดขนาดแล้วคุณควรเรียกใช้ดังนี้

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

เนื่องจากคุณอาจต้องการให้ ViewGroup ทำงานที่เหลือ (ทำบางอย่างในมุมมองเด็ก)

หากคุณกำลังขยาย View (เช่น ImageView) คุณสามารถโทรหาthis.setMeasuredDimension(width, height);ได้เพราะคลาสหลักจะทำบางอย่างเหมือนที่คุณเคยทำ

กล่าวได้ว่าถ้าคุณต้องการคุณสมบัติบางอย่างที่คลาสแม่ของคุณเสนอให้ฟรีคุณควรโทรหาsuper.onMeasure()(โดยปกติแล้วจะต้องใช้โหมด MeasureSpec.EXACTLY) มิฉะนั้นการโทรthis.setMeasuredDimension(width, height);ก็เพียงพอแล้ว


1

นี่คือวิธีที่ฉันแก้ไขปัญหา:

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

}

ยิ่งไปกว่านั้นจำเป็นสำหรับส่วนประกอบ ViewPager


3
นี่ไม่ใช่วิธีแก้ปัญหาที่เหมาะสม จะชัดเจนขนาดที่กำหนดใช้super.onMeasure setMeasuredDimension
tomrozb

@tomrozb ไม่ใช่humptydevelopers.com/2013/05/…และคุณสามารถตรวจสอบแหล่งที่มาของ Android ได้
Yuli Reiri

@ YuliReiri: โซลูชั่นที่สมบูรณ์แบบ!
Shayan Tabatabaee

0

หากเปลี่ยนขนาดมุมมองภายในonMeasureสิ่งที่คุณต้องการคือการsetMeasuredDimensionโทร หากคุณกำลังเปลี่ยนขนาดนอกonMeasureคุณต้องโทรsetLayoutParams. ตัวอย่างเช่นการเปลี่ยนขนาดของมุมมองข้อความเมื่อข้อความมีการเปลี่ยนแปลง


คุณยังคงสามารถเรียก setMinWidth () และ setMaxWidth () และให้มาตรฐาน onMeasure จัดการได้ ฉันพบว่าการไม่เรียก setLayoutParams จากภายในคลาส View ที่กำหนดเองจะดีกว่า ใช้สำหรับ ViewGroups หรือ Activities เพื่อลบล้างพฤติกรรมการดูเด็ก
Dorrin

0

ขึ้นอยู่กับการควบคุมที่คุณใช้ คำแนะนำในเอกสารประกอบใช้ได้กับตัวควบคุมบางตัว (TextView, Button, ... ) แต่ไม่ใช่สำหรับตัวควบคุมอื่น ๆ (LinearLayout, ... ) วิธีที่ได้ผลดีสำหรับฉันคือโทรหาซุปเปอร์เมื่อฉันทำเสร็จแล้ว อ้างอิงจากบทความในลิงค์ด้านล่าง

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


-1

ฉันเดาว่า setLayoutParams และการคำนวณการวัดใหม่เป็นวิธีแก้ปัญหาในการปรับขนาดมุมมองของเด็กให้ถูกต้องเนื่องจากโดยปกติจะทำใน onMeasure ของคลาสที่ได้รับ

อย่างไรก็ตามสิ่งนี้ทำงานได้ไม่ถูกต้อง (ด้วยเหตุผลใดก็ตาม ... ) ควรเรียกใช้ measureChildren (เมื่อได้รับ ViewGroup) หรือลองสิ่งที่คล้ายกันเมื่อจำเป็น


-5

คุณสามารถใช้โค้ดนี้เป็นตัวอย่างของ 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
รหัสนี้ผิดโดยพื้นฐาน ประการแรกไม่ได้คำนึงถึงโหมดเค้าโครง (ดูคำตอบของ Grimmace) แย่มาก แต่คุณจะไม่เห็นผลของสิ่งนั้นเพราะคุณเรียก super.onMeasure () ในตอนท้ายซึ่งจะแทนที่ค่าที่คุณตั้งไว้ (ดูคำตอบของ satur9nine) และเอาชนะจุดประสงค์ของการแทนที่ onMeasure () เลย
spaaarky21
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.