onMeasure कस्टम दृश्य स्पष्टीकरण


316

मैंने कस्टम कंपोनेंट करने की कोशिश की। मैंने Viewक्लास बढ़ाई और onDrawओवररेटेड तरीके से कुछ ड्राइंग की । मुझे ओवरराइड करने की आवश्यकता क्यों है onMeasure? अगर मैं नहीं था, सब कुछ सही हो देखा। क्या कोई इसे समझा सकता है? मुझे अपनी onMeasureविधि कैसे लिखनी चाहिए ? मैंने युगल ट्यूटोरियल देखे हैं, लेकिन हर एक दूसरे से थोड़ा अलग है। कभी-कभी वे super.onMeasureअंत में कॉल करते हैं, कभी-कभी वे उपयोग करते हैं setMeasuredDimensionऔर इसे कॉल नहीं करते हैं। अंतर कहां है?

आखिरकार मैं कई समान घटकों का उपयोग करना चाहता हूं। मैंने उन घटकों को अपनी XMLफ़ाइल में जोड़ा , लेकिन मुझे नहीं पता कि उन्हें कितना बड़ा होना चाहिए। मैं इसकी स्थिति और आकार बाद में सेट करना चाहता हूं (क्यों मैं जब मैं इसे खींचता हूं onMeasureतो आकार सेट करने की आवश्यकता है onDraw, कस्टम घटक वर्ग में भी काम कर रहा है)। जब वास्तव में मुझे ऐसा करने की आवश्यकता है?

जवाबों:


735

onMeasure()एंड्रॉइड को यह बताने का आपका अवसर है कि आप कितना बड़ा चाहते हैं कि आपका कस्टम दृश्य माता-पिता द्वारा प्रदान की गई लेआउट बाधाओं पर निर्भर हो; यह आपके कस्टम व्यू का अवसर भी है कि उन लेआउट बाधाओं को जानने का अवसर क्या है (यदि आप किसी match_parentस्थिति से अलग स्थिति में व्यवहार करना चाहते हैं wrap_content)। इन बाधाओं को MeasureSpecविधि में पारित किए गए मानों में पैक किया जाता है । यहाँ मोड मूल्यों का एक मोटा सहसंबंध है:

  • वास्तव में इसका मतलब है layout_widthया layout_heightमान एक विशिष्ट मूल्य पर सेट किया गया था। आपको शायद इस आकार को अपना विचार बनाना चाहिए। इसका match_parentउपयोग तब भी किया जा सकता है जब इसका उपयोग किया जाता है, आकार को मूल दृश्य में सेट करने के लिए (यह लेआउट में आश्रित है)।
  • आमतौर पर AT_MOST का अर्थ है कि layout_widthया वह layout_heightमान सेट किया गया था match_parentया wrap_contentजहां अधिकतम आकार की आवश्यकता है (यह फ्रेमवर्क में लेआउट पर निर्भर है), और मूल आयाम का आकार मान है। आपको इस आकार से बड़ा नहीं होना चाहिए।
  • UNSPECIFIED का अर्थ आमतौर पर होता है layout_widthया layout_heightमान wrap_contentबिना किसी प्रतिबंध के सेट किया गया था । आप जो चाहें आकार हो सकते हैं। कुछ लेआउट भी इस कॉलबैक का उपयोग आपके वांछित आकार का पता लगाने से पहले करते हैं कि वास्तव में दूसरे माप अनुरोध में आपको फिर से पास करने के लिए क्या चश्मा है।

इसके साथ मौजूद अनुबंध यह onMeasure()है कि जिस आकार को आप देखना चाहते हैं, उसके आकार के साथ अंत में setMeasuredDimension() MUST को बुलाया जाना चाहिए। इस पद्धति को सभी फ्रेमवर्क कार्यान्वयनों द्वारा बुलाया जाता है, जिसमें पाया गया डिफ़ॉल्ट कार्यान्वयन भी शामिल है View, यही कारण है कि इसके superउपयोग के मामले में फिट होने के बजाय कॉल करना सुरक्षित है ।

दी गई, क्योंकि रूपरेखा एक डिफ़ॉल्ट कार्यान्वयन को लागू करती है, इसलिए आपके लिए इस पद्धति को ओवरराइड करना आवश्यक नहीं हो सकता है, लेकिन आप उन मामलों में कतरन देख सकते हैं, जहाँ आप नहीं देखते हैं, तो दृश्य स्थान आपकी सामग्री से छोटा है, और यदि आप अपना काम करते हैं wrap_contentदोनों दिशाओं में कस्टम दृश्य , आपका दृश्य बिल्कुल भी नहीं दिख सकता क्योंकि फ्रेमवर्क यह नहीं जानता कि यह कितना बड़ा है!

आम तौर पर, यदि आप Viewकिसी अन्य मौजूदा विजेट को ओवरराइड कर रहे हैं और नहीं, तो यह कार्यान्वयन प्रदान करने के लिए एक अच्छा विचार है, भले ही यह कुछ इस तरह से सरल हो:

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

    int desiredWidth = 100;
    int desiredHeight = 100;

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int width;
    int height;

    //Measure Width
    if (widthMode == MeasureSpec.EXACTLY) {
        //Must be this size
        width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        width = Math.min(desiredWidth, widthSize);
    } else {
        //Be whatever you want
        width = desiredWidth;
    }

    //Measure Height
    if (heightMode == MeasureSpec.EXACTLY) {
        //Must be this size
        height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        height = Math.min(desiredHeight, heightSize);
    } else {
        //Be whatever you want
        height = desiredHeight;
    }

    //MUST CALL THIS
    setMeasuredDimension(width, height);
}

उम्मीद है की वो मदद करदे।


1
अरे @Devunwired अच्छा स्पष्टीकरण मैं अब तक पढ़ा सबसे अच्छा है। आपके स्पष्टीकरण से मेरे द्वारा पूछे गए कुछ प्रश्नों के उत्तर मिल गए और कुछ संदेह दूर हो गए, लेकिन अभी भी एक ही है जो है: यदि मेरा कस्टम दृश्य कुछ अन्य लोगों के साथ एक ViewGroup के अंदर है उनके लेआउटप्रधान बाधाओं के लिए हर एक जांच के लिए और प्रत्येक बच्चे को अपनी बाधाओं के अनुसार इसे स्वयं मापने के लिए कहें?
फिरौन

47
ध्यान दें कि यदि आप किसी भी ViewGroup उपवर्ग की ऑनराइड को ओवरराइड करते हैं तो यह कोड काम नहीं करेगा। आपके साक्षात्कार दिखाई नहीं देंगे और सभी का आकार 0x0 होगा। यदि आपको किसी कस्टम व्यूग्रुप की ऑनमोर को ओवरराइड करने की आवश्यकता है, तो चौड़ाई बदलें, चौड़ाई बदलें, चौड़ाई, ऊँचाई और ऊँचाई करें, उन्हें मापने के लिए वापस कंपाइल करें।
एलेक्सी

1
शानदार जवाब। ध्यान दें कि, Google के प्रलेखन के अनुसार, यह पेडिंग को संभालने के लिए व्यू की जिम्मेदारी है।
जोंस्टाफ

4
जटिल सी ** पी पर जो एंड्रॉइड के साथ काम करने के लिए एक दर्दनाक लेआउट सिस्टम बनाता है। वे सिर्फ getParent () प्राप्त कर सकते थे। *** (...)
ओलिवर डिक्सन

2
Viewकक्षा में सहायक विधियाँ होती हैं , जिन्हें कहा जाता है resolveSizeAndStateऔर resolveSize, उन्हें वह करना चाहिए जो 'अगर' खंड करती है - मैंने उन्हें उपयोगी पाया, खासकर यदि आपको उन आईएफएस को अक्सर लिखना पड़ता है।
stan0

5

वास्तव में, आपका उत्तर पूर्ण नहीं है क्योंकि मान भी रैपिंग कंटेनर पर निर्भर करते हैं। रिश्तेदार या रैखिक लेआउट के मामले में, मान इस तरह से व्यवहार करते हैं:

  • बिल्कुल मैच_परेंट, माता-पिता का बिल्कुल सही आकार है
  • AT_MOST मापक में AT_MOST रैप_ कॉन्टेंट परिणाम
  • UNSPECIFIED ने कभी ट्रिगर नहीं किया

क्षैतिज स्क्रॉल दृश्य के मामले में, आपका कोड काम करेगा।


57
यदि आपको लगता है कि यहां कुछ उत्तर अपूर्ण है, तो कृपया आंशिक उत्तर देने के बजाय इसे जोड़ें।
माइकल

1
इस बात को जोड़ने के लिए अच्छा ओनाया कि लेआउट कैसे काम करते हैं, लेकिन मेरे मामले में onMeasure को मेरे कस्टम दृश्य के लिए तीन बार कहा जाता है। विचाराधीन दृश्य में रैप_ कॉन्टेंट ऊंचाई और भारित चौड़ाई (चौड़ाई = 0, वजन = 1) थी। पहले कॉल में UNSPECIFIED / UNSPECIFIED था, दूसरे में AT_MOST / EXACTLY था और तीसरे में EXACTLY / EXACTLY था।
विलियम टी। मल्लार्ड

0

यदि आपको कुछ बदलने की आवश्यकता नहीं है, तो आपको इसे ओवरराइड करने की कोई आवश्यकता नहीं है।

Devunwired कोड (यहां चयनित और सबसे अधिक मत दिया गया उत्तर) लगभग वही है जो एसडीके कार्यान्वयन पहले से ही आपके लिए करता है (और मैंने जाँच की - यह 2009 के बाद से किया था)।

आप यहां ऑनमार्ट विधि की जांच कर सकते हैं :

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

ओवरराइडिंग SDK कोड को ठीक उसी कोड के साथ बदलने के लिए कोई मतलब नहीं है।

यह आधिकारिक डॉक्टर का टुकड़ा जो दावा करता है कि "डिफ़ॉल्ट ऑनमार्ट () हमेशा 100x100 का आकार निर्धारित करेगा" - गलत है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.