जब कोई लेआउट तैयार किया गया है तो आप कैसे बता सकते हैं?


201

मेरे पास एक कस्टम दृश्य है जो स्क्रीन पर स्क्रॉल करने योग्य बिटमैप खींचता है। इसे प्रारंभ करने के लिए, मुझे मूल लेआउट ऑब्जेक्ट के पिक्सेल में आकार में पास करने की आवश्यकता है। लेकिन onCreate और onResume फ़ंक्शन के दौरान, लेआउट अभी तक नहीं खींचा गया है, और इसलिए लेआउट ।getMeasuredHeight () रिटर्न 0।

वर्कअराउंड के रूप में, मैंने एक हैंडलर को एक सेकंड इंतजार करने और फिर मापने के लिए जोड़ा है। यह काम करता है, लेकिन इसकी मैला, और मुझे पता नहीं है कि लेआउट तैयार होने से पहले मैं कितना समय ट्रिम कर सकता हूं।

जो मैं जानना चाहता हूं वह यह है कि जब कोई लेआउट तैयार होता है तो मैं कैसे पता लगा सकता हूं? क्या कोई घटना या कॉलबैक है?

जवाबों:


391

आप लेआउट में ट्री ऑब्जर्वर जोड़ सकते हैं। यह सही चौड़ाई और ऊंचाई वापस करना चाहिए। onCreate()बच्चे के विचारों के लेआउट से पहले किया जाता है। इसलिए चौड़ाई और ऊंचाई की गणना अभी नहीं की गई है। ऊंचाई और चौड़ाई प्राप्त करने के लिए, इसे onCreate()विधि पर रखें:

    final LinearLayout layout = (LinearLayout) findViewById(R.id.YOUR_VIEW_ID);
    ViewTreeObserver vto = layout.getViewTreeObserver(); 
    vto.addOnGlobalLayoutListener (new OnGlobalLayoutListener() { 
        @Override 
        public void onGlobalLayout() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    layout.getViewTreeObserver()
                            .removeOnGlobalLayoutListener(this);
                } else {
                    layout.getViewTreeObserver()
                            .removeGlobalOnLayoutListener(this);
                }
            int width  = layout.getMeasuredWidth();
            int height = layout.getMeasuredHeight(); 

        } 
    });

28
आपका बहुत बहुत धन्यवाद! लेकिन मुझे आश्चर्य है कि ऐसा करने का कोई सरल तरीका नहीं है क्योंकि यह एक आम समस्या है।
फेलिक्स

1
गजब का। मैंने OnLayoutChangeListener की कोशिश की और यह काम नहीं किया। लेकिन OnGlobalLayoutListener ने काम किया। आपको बहुत - बहुत धन्यवाद!
योयोमो

8
हटाने के बजाय एपीआई स्तर 16 में हटा दिया गया है।
त्यूनाबुन

3
एक "गोचा" मैं देख रहा हूं कि यदि आपका दृश्य दिखाई नहीं दे रहा है, तो onGlobalLayout कहा जाता है, लेकिन बाद में जब दृश्य दिखाई देता है तो कॉल किया जाता है, लेकिन onGlobalLayout नहीं ... ugh।
स्टीफन मैककॉर्मिक

1
इसके लिए एक और त्वरित समाधान है view.post(Runnable), यह क्लीनर और यह एक ही काम करता है।
DVDciri

74

वास्तव में एक आसान तरीका यह है कि बस post()अपने लेआउट पर कॉल करें । आपका दृश्य पहले ही बन जाने के बाद, यह आपका कोड अगला चरण चलाएगा।

YOUR_LAYOUT.post( new Runnable() {
    @Override
    public void run() {
        int width  = YOUR_LAYOUT.getMeasuredWidth();
        int height = YOUR_LAYOUT.getMeasuredHeight(); 
    }
});

4
मुझे नहीं पता कि दृश्य के निर्माण के बाद पोस्ट वास्तव में आपका कोड चलाएगी या नहीं। इसकी कोई गारंटी नहीं है, क्योंकि पोस्ट केवल Runnable को जोड़ेगी जो आप RunQueue में बनाते हैं, जिसे UI थ्रेड पर निष्पादित पिछली कतार के बाद निष्पादित किया जाना है।
हेंड्रीडब्ल्यूडी

4
इसके विपरीत, पोस्ट () अगले UI चरण अपडेट के बाद निष्पादित होता है, जो दृश्य बनने के बाद होता है। इसलिए, आपको गारंटी दी जाती है कि यह दृश्य तैयार होने के बाद निष्पादित होगा।
एलिप्टिका

मैंने कई बार इस कोड का परीक्षण किया, यह केवल UI-थ्रेड में सबसे अच्छा काम करता है। पृष्ठभूमि थ्रेड में यह कभी-कभी काम नहीं करता है।
CoolMind

3
@HendraWijayaDjiono, शायद यह पोस्ट जवाब देगी: stackoverflow.com/questions/21937224/…
CoolMind

1
मैं एक ऐसे मुद्दे पर भाग गया, जहाँ हर बार पहला जवाब काम नहीं कर रहा था, जिस पोस्ट को देखने के लिए मुझे स्क्रॉल करने की ज़रूरत थी (एक स्क्रॉल दृश्य) का उपयोग करके, मुझे वांछित स्थिति में सही तरीके से स्क्रॉल करने की अनुमति दी, जब तक कि दृश्य लेने के लिए तैयार था स्क्रोल्टो विधि कॉल।
दानूफ्र

21

पदावनत कोड और चेतावनियों से बचने के लिए:

view.getViewTreeObserver().addOnGlobalLayoutListener(
        new ViewTreeObserver.OnGlobalLayoutListener() {
            @SuppressWarnings("deprecation")
            @Override
            public void onGlobalLayout() {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    view.getViewTreeObserver()
                            .removeOnGlobalLayoutListener(this);
                } else {
                    view.getViewTreeObserver()
                            .removeGlobalOnLayoutListener(this);
                }
                yourFunctionHere();
            }
        });

1
इस कोड का उपयोग करने के लिए, 'दृश्य' को 'अंतिम' घोषित किया जाना चाहिए।
BeccaP

2
इस स्थिति का उपयोग Build.VERSION.SDK_INT <Build.VERSION_CODES.JELLY_BEAN
HendraWD

4

कोटलिन विस्तार कार्यों के साथ बेहतर है

inline fun View.waitForLayout(crossinline yourAction: () -> Unit) {
    val vto = viewTreeObserver
    vto.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            when {
                vto.isAlive -> {
                    vto.removeOnGlobalLayoutListener(this)
                    yourAction()
                }
                else -> viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        }
    })
}

बहुत अच्छा और सुरुचिपूर्ण समाधान जिसका पुन: उपयोग किया जा सकता है।
आयनुत नेगरू


3

सामान्य तरीकों का एक विकल्प दृश्य की ड्राइंग में हुक करना है।

OnPreDrawListenerकिसी दृश्य को प्रदर्शित करते समय कई बार कहा जाता है, इसलिए कोई विशिष्ट पुनरावृत्ति नहीं होती है जहाँ आपके दृश्य की मापी गई चौड़ाई या ऊँचाई होती है। इसके लिए आवश्यक है कि आप शून्य से अधिक view.getMeasuredWidth() <= 0समय तक लगातार जाँचें ( ) या एक सीमा निर्धारित करें measuredWidth

एक मौका यह भी है कि दृश्य कभी भी नहीं खींचा जाएगा, जो आपके कोड के साथ अन्य समस्याओं का संकेत दे सकता है।

final View view = [ACQUIRE REFERENCE]; // Must be declared final for inner class
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw() {
        if (view.getMeasuredWidth() > 0) {     
            view.getViewTreeObserver().removeOnPreDrawListener(this);
            int width = view.getMeasuredWidth();
            int height = view.getMeasuredHeight();
            //Do something with width and height here!
        }
        return true; // Continue with the draw pass, as not to stop it
    }
});

सवाल यह है How can you tell when a layout has been drawn?और आप एक तरीका प्रदान कर रहे हैं जहां आप कॉल करते हैं OnPreDrawListenerऔर don't stop interrogating that view until it has provided you with a reasonable answerइसलिए आपके समाधान पर आपत्तियां स्पष्ट होनी चाहिए।
कार्ट छोड़ दिया

आपको यह जानने की आवश्यकता है कि यह कब खींचा गया है, लेकिन इसे कब मापा गया है। मेरा कोड उस प्रश्न का उत्तर देता है जिसे वास्तव में पूछे जाने की आवश्यकता है।
jwehrle

इस समाधान के साथ कुछ समस्या है? क्या यह सामना की गई समस्या को हल करने में विफल रहता है?
19:19

1
जब तक यह निर्धारित नहीं किया जाता है, तब तक दृश्य को मापा नहीं जाता है, इसलिए यह विकल्प अत्यधिक है और एक निरर्थक जांच करता है। आप प्रश्न को संबोधित नहीं करते हैं, लेकिन आपको क्या लगता है, यह पूछा जाना चाहिए। मूल टिप्पणी ने इन दोनों बिंदुओं को बनाया, लेकिन कोड के साथ कुछ मुद्दे भी हैं जिन्हें एक संपादन के रूप में प्रस्तुत किया गया है।
कार्ट

यह एक अच्छा संपादन है। धन्यवाद। मुझे लगता है कि मैं जो पूछ रहा हूं, क्या आपको लगता है कि इस उत्तर को हटा दिया जाना चाहिए या नहीं? मुझे ओपी की समस्या का सामना करना पड़ा। इससे समस्या हल हो गई। मैंने इसे अच्छे विश्वास में पेश किया। क्या वह गलती थी? क्या ऐसा कुछ है जो StackOverflow पर नहीं किया जाना चाहिए?
jwehrle

2

बस अपने लेआउट या दृश्य पर पोस्ट विधि को कॉल करके इसे जांचें

 view.post( new Runnable() {
     @Override
     public void run() {
        // your layout is now drawn completely , use it here.
      }
});

यह सच नहीं है और संभव मुद्दों को छिपा सकता है। postके बाद चलाने के लिए रन करने योग्य कतार होगी, यह सुनिश्चित नहीं है कि दृश्य मापा जाता है, यहां तक ​​कि कम खींचा गया है।
आयनुत नेगरू


@BrunoBieri, आप async फ़ंक्शंस के साथ इस व्यवहार का परीक्षण कर सकते हैं जो दृश्य मापों में परिवर्तन करता है और देखें कि क्या आप प्रत्येक कार्य के बाद कतारबद्ध कार्य चलाया जाता है और अपेक्षित मान प्राप्त करते हैं।
नोनुत नेगरू

1

जब onMeasureकहा जाता है तो दृश्य को इसकी मापा चौड़ाई / ऊंचाई मिलती है। इसके बाद कॉल कर सकते हैं layout.getMeasuredHeight()


4
क्या आपको पता है कि ऑनयर फायर कब होता है?
गीग ऑन ऑन

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