किसी दृश्य के आयामों को कैसे पुनः प्राप्त करें?


209

मेरा एक दृष्टिकोण है TableLayout, TableRow and TextView। मैं चाहता हूं कि यह ग्रिड की तरह दिखे। मुझे इस ग्रिड की ऊंचाई और चौड़ाई प्राप्त करने की आवश्यकता है। विधियाँ getHeight()और getWidth()हमेशा वापस आती हैं 0. यह तब होता है जब मैं ग्रिड को गतिशील रूप से प्रारूपित करता हूं और जब मैं XML संस्करण का उपयोग करता हूं।

एक दृश्य के लिए आयाम कैसे प्राप्त करें?


यहाँ मेरा परीक्षण कार्यक्रम है जिसका उपयोग मैंने डिबग में परिणामों की जाँच के लिए किया था:

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

इसके बजाय getWidth / Height का उपयोग करने के बजाय getMeasuredWidth / Height का उपयोग करें जब लेआउट गतिविधि पर लागू होता है।
भूप्स

9
दोस्तों, सभी को कॉल करना है getHeight()और getWidth()गतिविधि के बाद जीवनचक्र ने विचारों को खुद को मापने के लिए कहा है, दूसरे शब्दों में, इस तरह का सामान इसमें करें onResume()और यही है। आपको इसके आयामों को जानने के लिए एक नहीं-अभी तक रखी गई वस्तु की उम्मीद नहीं करनी चाहिए, यह पूरी चाल है। नीचे दिए गए जादू के लिए कोई ज़रूरत नहीं है।
कक्षा स्टेकर

2
कॉलिंग getHeight()/Width()में onResume()उपज नहीं था> 0 मूल्य मेरे लिए।
डारपैन

@ClassStacker फ्रैगमेंट के बारे में क्या?
1:05

@zdd कृपया एक नया प्रश्न पूछें और यहाँ एक टिप्पणी में एक संदर्भ पोस्ट करें।
कक्षा स्टेकर

जवाबों:


418

मेरा मानना ​​है कि ओपी लंबा चला गया है, लेकिन अगर यह उत्तर भविष्य के खोजकर्ताओं की मदद करने में सक्षम है, तो मैंने सोचा कि मैं एक समाधान पोस्ट करूंगा जो मुझे मिला है। मैंने इस कोड को अपनी onCreate()विधि में जोड़ा है :

EDITED : 07/05/11 टिप्पणी से कोड शामिल करने के लिए:

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

});

पहले मुझे अपना अंतिम संदर्भ मिलता है TextView( onGlobalLayout()विधि में उपयोग करने के लिए)। अगला, मैं अपने ViewTreeObserverसे प्राप्त करता हूं TextView, और एक जोड़ देता हूं , OnGlobalLayoutListenerओवरराइडिंग onGLobalLayout(यहां आह्वान करने के लिए एक सुपरक्लास विधि नहीं लगती है ...) और मेरे कोड को जोड़ना जो इस श्रोता में दृश्य के माप को जानने की आवश्यकता है। सभी मेरे लिए उम्मीद के मुताबिक काम करते हैं, इसलिए मुझे उम्मीद है कि यह मदद करने में सक्षम है।


23
@ जॉर्ज बेली: एक चीज़ जो मैंने हाल ही में किसी और की पोस्ट पर देखी है (मैंने इसे स्वयं परीक्षण नहीं किया है, इसलिए YMMV) कई कॉल्स को दूर करने के लिए था (ऑनग्लोबलआउट () के भीतर): tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);पहली बार के बाद श्रोता को हटा दिया जाना चाहिए ऐसा होता है।
केविन कोपॉक

3
मैं वापस भी मदद करना चाहता हूं। आपको कुछ ऐसा करने की आवश्यकता हो सकती है: mView.getViewTreeObserver ()। RemoveGlobalOnLayoutListener (globalLayoutListener); यदि आप केवल एक बार अपना दृष्टिकोण बदलना चाहते हैं। आशा है कि इस मदद
हेनरी Sou

7
सवाल बहुत पुराना है, लेकिन addOnLayoutChangeListenerसाथ ही साथ काम करता है! :)
FX

7
यह भी ध्यान दें कि एपीआई 16 के बाद से, आपको चाहिए: if (android.os.Build.VERSION.SDK_INT> = 16) {view.getViewTreeObserver ()। RemoveOnGlobalLayoutListener (यह); } और {view.getViewTreeObserver ()। removeGlobalOnLayoutListener (यह); }
एडिसन

2
के रूप में removeGlobalOnLayoutListener पदावनत है यह अभी भी ग्रहण में चेतावनी देता है। क्या यह सामान्य है? समय के साथ, पदावनत की गई कोड चेतावनियाँ भर
जाएँगी

82

मैं बस एक वैकल्पिक समाधान जोड़ूंगा, आपकी गतिविधि के ऑनवॉन्डफॉक्ड चेंज विधि को ओवरराइड करूंगा और आप वहां से getHeight (), getWidth () के मान प्राप्त कर सकेंगे।

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

4
प्रलेखन को उद्धृत करने के लिए, "यह सबसे अच्छा संकेतक है कि क्या यह गतिविधि उपयोगकर्ता को दिखाई देती है।"
कैमरन लोवेल पामर

6
यह आपके लिए उपयोग किए जाने वाले फ़्रैगमेंट के मामले में काम नहीं करता है ViewTreeObserver
मिहिर

ओपी ने विशेष रूप से पूछा कि इसे देखने के लिए कैसे प्राप्त किया जाए
JustDanyul

स्क्रॉलव्यू कुल ऊँचाई और अधिकतम स्क्रॉलव्यू.गेटस्क्रॉलली () समान होगी? यानी मेरे मामले में मुझे उपरोक्त विधि के साथ ऊँचाई 702 है और मुझे
getScrolly

जब आपका दृश्य किसी अन्य xml फ़ाइल से शामिल किया जा रहा हो तो यह विधि भी काम नहीं करती है।
जे डोंगा

19

आप तत्वों की चौड़ाई और ऊंचाई प्राप्त करने की कोशिश कर रहे हैं, जो अभी तक तैयार नहीं हुए थे।

यदि आप डिबग का उपयोग करते हैं और किसी बिंदु पर रुकते हैं, तो आप देखेंगे कि आपकी डिवाइस स्क्रीन अभी भी खाली है, ऐसा इसलिए है क्योंकि आपके तत्व अभी तक नहीं खींचे गए हैं, इसलिए आपको किसी चीज़ की चौड़ाई और ऊँचाई नहीं मिल सकती है, जो अभी तक नहीं है मौजूद।

और, मैं गलत हो सकता हूं, लेकिन setWidth()हमेशा सम्मानित नहीं होता है, Layoutयह बच्चों को देता है और फैसला करता है कि उन्हें कैसे मापना है (कॉलिंग child.measure()), इसलिए यदि आप सेट करते हैंsetWidth() करते हैं, तो आपको इस चौड़ाई को प्राप्त करने की गारंटी नहीं है क्योंकि तत्व तैयार किया जाएगा।

आपको getMeasuredWidth()वास्तव में आकर्षित किए जाने के बाद कहीं और (अपने दृश्य के सबसे हाल के उपाय) का उपयोग करना है।

Activityसबसे अच्छा पल खोजने के लिए जीवनचक्र में देखें ।

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

मेरा मानना ​​है कि एक अच्छा अभ्यास OnGlobalLayoutListenerइस तरह का उपयोग करना है:

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

आप इस कोड को सीधे इसमें रख सकते हैं onCreate(), और इसे तब लागू किया जाएगा जब विचारों को रखा जाएगा।


दुर्भाग्य से यह OnResume में भी काम नहीं कर रहा है। अंतिम आयाम OnResume कॉल के बाद कहीं सेट किए जाते हैं, कम से कम एमुलेटर में।
jki

यह एक भयानक अभ्यास है !!! इस श्रोता को बार-बार बुलाया जाएगा और आपके प्रदर्शन को मार देगा। झंडे का उपयोग करने के बजाय, श्रोता को एक बार अपंजीकृत करें।
ब्लूव्हील

15

इस तरह से देखें की पोस्ट विधि का उपयोग करें

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

1
यह काम करता है, लेकिन वास्तव में, क्यों की कोई चर्चा नहीं? Btw, यह काम करता है क्योंकि रनवेबल तब तक नहीं चलता है जब तक कि लेआउट नहीं हुआ है।
नॉर्मन एच

7

मैंने onGlobalLayout()कुछ कस्टम स्वरूपण करने के लिए उपयोग करने की कोशिश की TextView, लेकिन जैसा कि @George बेली ने देखा, onGlobalLayout()वास्तव में दो बार कहा जाता है: एक बार प्रारंभिक लेआउट पथ पर, और दूसरी बार पाठ को संशोधित करने के बाद।

View.onSizeChanged()मेरे लिए बेहतर काम करता है क्योंकि अगर मैं वहां पाठ को संशोधित करता हूं, तो विधि को केवल एक बार (लेआउट पास के दौरान) कहा जाता है। इसके लिए उप-क्लासिंग की आवश्यकता होती है TextView, लेकिन एपीआई स्तर 11+ View. addOnLayoutChangeListener()का उपयोग उप-वर्गीकरण से बचने के लिए किया जा सकता है।

एक और बात, दृश्य की सही चौड़ाई प्राप्त करने के लिए , इसे सेट View.onSizeChanged()किया layout_widthजाना चाहिए match_parent, नहीं wrap_content


2

क्या आप एक निर्माता, या किसी अन्य विधि से आकार प्राप्त करने की कोशिश कर रहे हैं जो आपको वास्तविक चित्र प्राप्त करने से पहले चलाया जाता है?

सभी घटकों को वास्तव में मापा जाने से पहले आपको कोई आयाम नहीं मिलेगा (चूंकि आपका xml आपके प्रदर्शन आकार, माता-पिता की स्थिति और जो कुछ भी नहीं जानता है)

OnSizeChanged () के बाद मान प्राप्त करने का प्रयास करें (हालांकि इसे शून्य के साथ कहा जा सकता है), या बस प्रतीक्षा कर रहा है कि आपको वास्तविक छवि कब मिलेगी।


मैंने अपना मूल प्रश्न और कोड साफ़ कर दिया है। धन्यवाद।

2

जैसा कि एफएक्स ने उल्लेख किया है, आप OnLayoutChangeListenerउस दृश्य का उपयोग कर सकते हैं जिसे आप स्वयं ट्रैक करना चाहते हैं

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

आप कॉलबैक में श्रोता को हटा सकते हैं यदि आप केवल प्रारंभिक लेआउट चाहते हैं।


2

मुझे लगता है कि यह वही है जो आपको देखने की जरूरत है: onSizeChanged()आपके विचार का उपयोग । यहाँ onSizeChanged()अपने लेआउट या दृश्य की ऊँचाई और चौड़ाई को गतिशील रूप से प्राप्त करने के लिए उपयोग करने के बारे में एक विस्तृत कोड स्निपेट है http://syedrakibalhasan.blogspot.com/2011/02/how-to-get-width-and-height-dimensions.html


1

ViewTreeObserverऔर onWindowFocusChanged()यह बहुत जरूरी नहीं है।

यदि आप TextViewलेआउट के रूप में फुलाते हैं और / या उसमें कुछ सामग्री डालते हैं और सेट करते हैं LayoutParamsतो आप उपयोग कर सकते हैं getMeasuredHeight()और getMeasuredWidth()

लेकिन आपको (शायद अन्य भी ) सावधान रहना होगाLinearLayoutsViewGroups । वहाँ मुद्दा यह है, कि आप चौड़ाई और ऊँचाई प्राप्त कर सकते हैं, onWindowFocusChanged()लेकिन यदि आप इसमें कुछ विचार जोड़ने की कोशिश करते हैं, तो आप उस जानकारी को प्राप्त नहीं कर सकते जब तक कि सब कुछ खींच नहीं लिया गया हो। मैं एक (रैपिंग स्टाइल) की नकल TextViewsकरने के LinearLayoutsलिए कई जोड़ने की कोशिश कर रहा था FlowLayoutऔर इसलिए इसका उपयोग नहीं कर सका Listeners। एक बार प्रक्रिया शुरू होने के बाद, इसे समकालिक रूप से जारी रखना चाहिए। तो ऐसे मामले में, आप बाद में उपयोग करने के लिए एक चर में चौड़ाई रखना चाह सकते हैं, जैसा कि लेआउट में विचार जोड़ने के दौरान, आपको इसकी आवश्यकता हो सकती है।


1

भले ही प्रस्तावित समाधान काम करता है, यह हर मामले के लिए सबसे अच्छा समाधान नहीं हो सकता है क्योंकि प्रलेखन के आधार पर ViewTreeObserver.OnGlobalLayoutListener

कॉलबैक के लिए इंटरफ़ेस परिभाषा को वैश्विक लेआउट स्थिति या दृश्य ट्री परिवर्तन के भीतर दृश्य की दृश्यता को लागू करने के लिए आमंत्रित किया जाता है।

जिसका अर्थ है कि इसे कई बार कहा जाता है और हमेशा दृश्य को मापा नहीं जाता है (इसकी ऊंचाई और चौड़ाई निर्धारित होती है)

एक विकल्प का उपयोग करना ViewTreeObserver.OnPreDrawListenerहै जिसे केवल तभी बुलाया जाता है जब दृश्य तैयार होने के लिए तैयार होता है और इसके सभी माप होते हैं।

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

आप एक प्रसारण का उपयोग कर सकते हैं जिसे OnResume () में कहा जाता है

उदाहरण के लिए:

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

}

आप OnCreate (), onStart (), या यहां तक ​​कि onResume () में उस कारण के लिए नहीं देख सकते हैं जिस कारण से kcoppock ने जवाब दिया


0

ऊँचाई और चौड़ाई शून्य है क्योंकि दृश्य उस समय तक नहीं बनाया गया है जब आप अनुरोध कर रहे हैं कि यह ऊँचाई और चौड़ाई है। एक सबसे सरल उपाय है

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

यह विधि अन्य विधियों की तुलना में अच्छी है क्योंकि यह छोटी और कुरकुरी है।


-1

सिंपल रिस्पांस: इसने मेरे लिए बिना किसी समस्या के काम किया। ऐसा लगता है कि कुंजी यह सुनिश्चित करने के लिए है कि दृश्य आपके पास फोकस है इससे पहले कि आप मिलें आदि हैशोक () विधि का उपयोग करके ऐसा करें, फिर उस क्रम में गेटहाइट () विधि का उपयोग करें। कोड की सिर्फ 3 लाइनों की आवश्यकता है।

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

int myButtonHeight = myImageButton1.getHeight();

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

आशा करता हूँ की ये काम करेगा।


-3

अपने दृश्य के लिए getMeasuredWidth () और getMeasuredHeight () का उपयोग करें।

डेवलपर गाइड: देखें


4
जैसा कि इन तरीकों से पहले कहा गया है कि केवल एक वैध परिणाम लौटाया जाता है , उसके बाद आकार माप लिया गया है। एक गतिविधि के भीतर यह सही है जब विधि onWindowFocusChanged os कहलाती है।
slott

-8

सुधार: मुझे पता चला कि उपरोक्त समाधान भयानक है। खासकर तब जब आपका फोन धीमा हो। और यहां, मुझे एक और समाधान मिला: तत्व के px मान की गणना करें, जिसमें मार्जिन और पेडिंग शामिल हैं: dp से px: https://stackoverflow.com/a/6327095/1982712

or dimens.xml to px: https://stackoverflow.com/a/16276351/1982712

sp to px: https://stackoverflow.com/a/9219417/1982712 (समाधान को उल्टा करें)

या px को डिमन्स: https://stackoverflow.com/a/16276351/1982712

और बस।


10
आमतौर पर यह आशा करना एक बुरा विचार है कि कुछ मनमाने ढंग से चुने गए मिलीसेकंड के बाद सब कुछ सही ढंग से सेट हो जाएगा। डिवाइस धीमा हो सकता है, अन्य प्रक्रियाएं / थ्रेड चल सकते हैं, डिबगर में काम नहीं कर सकते हैं, ओएस के अगले संस्करण में काम नहीं कर सकते हैं ...
क्रिस्टोफर जॉनसन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.