रनटाइम पर एक Android दृश्य का आकार निर्धारित करना


123

मैं अपनी गतिविधि बनाए जाने के बाद अपने एंड्रॉइड ऐप में एक दृश्य पर एनीमेशन लागू करने का प्रयास कर रहा हूं। ऐसा करने के लिए, मुझे दृश्य के वर्तमान आकार को निर्धारित करने की आवश्यकता है, और फिर वर्तमान आकार से नए आकार में स्केल करने के लिए एक एनीमेशन सेट करें। यह भाग रनटाइम पर किया जाना चाहिए, क्योंकि दृश्य उपयोगकर्ता से इनपुट के आधार पर अलग-अलग आकारों में होता है। मेरा लेआउट XML में परिभाषित किया गया है।

यह एक आसान काम की तरह लगता है, और इस बारे में बहुत सारे एसओ प्रश्न हैं, हालांकि कोई नहीं जिसने मेरी समस्या को हल किया, जाहिर है। इसलिए शायद मुझे कुछ स्पष्ट याद आ रहा है। मुझे मेरे विचार से एक हैंडल मिलता है:

ImageView myView = (ImageView) getWindow().findViewById(R.id.MyViewID);

यह ठीक काम करता है, लेकिन जब बुला getWidth(), getHeight(), getMeasuredWidth(), getLayoutParams().width, आदि, वे सब वापसी 0. मैं भी मैन्युअल बुला की कोशिश की है measure()देखने के लिए एक फोन के बाद पर getMeasuredWidth()है, लेकिन यह कोई प्रभाव नहीं है।

मैंने इन विधियों को कॉल करने की कोशिश की है onCreate()और अपनी गतिविधि में और में डिबगर में ऑब्जेक्ट का निरीक्षण किया है onPostCreate()। मैं रनटाइम के दौरान इस दृश्य के सटीक आयामों का पता कैसे लगा सकता हूं?


1
ओह, भी मैं नोट करना चाहिए कि देखने में ही निश्चित रूप से करता नहीं 0 चौड़ाई / ऊंचाई है। यह स्क्रीन पर ठीक दिखाई देता है।
निक रीमन सेप

क्या आप xml लेआउट से <ImageView ... /> टैग पोस्ट कर सकते हैं?
fhucho

मैं इस समस्या के कई SO समाधानों से तब तक जूझता रहा जब तक मुझे एहसास नहीं हो गया कि मेरे मामले में View का आयाम भौतिक स्क्रीन से मेल खाता है (मेरा ऐप "immersive" है और इसके सभी माता-पिता की चौड़ाई और ऊंचाइयां सेट हैं match_parent)। इस मामले में, एक सरल समाधान जिसे सुरक्षित रूप से देखने से पहले भी कॉल किया जा सकता है (जैसे, आपकी गतिविधि का ऑनक्रिएट () विधि) केवल Display.getSize () का उपयोग करना है; देख stackoverflow.com/a/30929599/5025060 जानकारी के लिए। मुझे पता है कि यह @NikReiman ने नहीं पूछा है, बस उन लोगों के लिए एक नोट छोड़ रहा है जो इस सरल विधि का उपयोग करने में सक्षम हो सकते हैं।
CODE-REaD

जवाबों:


180

पहले लेआउट के लिए प्रतीक्षा करने के लिए दृश्य पर ViewTreeObserver का उपयोग करें। पहले लेआउट के बाद ही मिलेगाWidth () / getHeight () / getMeasuredWidth () / getMeasuredHeight () काम।

ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
  viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
      view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
      viewWidth = view.getWidth();
      viewHeight = view.getHeight();
    }
  });
}

21
इस उत्तर से सावधान रहें क्योंकि यह लगातार कहा जाएगा यदि अंतर्निहित दृश्य एक वीडियो / सतह है
केविन पार्कर

7
क्या आप उस @ केविन के बारे में विस्तार से बता सकते हैं? दो बातें, सबसे पहले, चूंकि यह एक श्रोता है, मैं एक सिंगल कॉलबैक की उम्मीद करूंगा, और अगला, जैसे ही हमें पहला कॉलबैक मिलेगा, हम श्रोता को हटा देते हैं। क्या मैं कुछ भुल गया?
विक्रम बोदीचेरला

4
किसी भी ViewTreeObserver तरीकों को कॉल करने से पहले इसकी जाँच करने की सिफारिश की गई है IAlive (): if (view.getViewTreeObserver ()। IsAlive ()) {view.getViewTreeObserver ()। RemoveGlobalOnLayoutListener (यह) देखें। }
पीटर ट्रान

4
किसी भी विकल्प के लिए removeGlobalOnLayoutListener(this);? क्योंकि यह पदावनत है।
सिबस जुआ

7
इसके बजाय @FarticlePilter, removeOnGlobalLayoutListener () का उपयोग करें। यह डॉक्स में उल्लिखित है।
विक्रम बोदीचेरला

64

परिदृश्य के आधार पर वास्तव में कई समाधान हैं:

  1. सुरक्षित विधि, लेआउट चरण समाप्त होने के बाद दृश्य देखने से पहले काम करेगी:
public static void runJustBeforeBeingDrawn(final View view, final Runnable runnable) {
    final OnPreDrawListener preDrawListener = new OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            view.getViewTreeObserver().removeOnPreDrawListener(this);
            runnable.run();
            return true;
        }
    };
    view.getViewTreeObserver().addOnPreDrawListener(preDrawListener); 
}

नमूना उपयोग:

    ViewUtil.runJustBeforeBeingDrawn(yourView, new Runnable() {
        @Override
        public void run() {
            //Here you can safely get the view size (use "getWidth" and "getHeight"), and do whatever you wish with it
        }
    });
  1. कुछ मामलों में, यह मैन्युअल रूप से दृश्य के आकार को मापने के लिए पर्याप्त है:
view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
int width=view.getMeasuredWidth(); 
int height=view.getMeasuredHeight();

यदि आप कंटेनर का आकार जानते हैं:

    val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxWidth, View.MeasureSpec.AT_MOST)
    val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxHeight, View.MeasureSpec.AT_MOST)
    view.measure(widthMeasureSpec, heightMeasureSpec)
    val width=view.measuredWidth
    val height=view.measuredHeight
  1. यदि आपके पास एक कस्टम दृश्य है जिसे आपने बढ़ाया है, तो आप "ऑनमर्ज़" विधि पर इसका आकार प्राप्त कर सकते हैं, लेकिन मुझे लगता है कि यह केवल कुछ मामलों में अच्छी तरह से काम करता है:
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
    final int newHeight= MeasureSpec.getSize(heightMeasureSpec);
    final int newWidth= MeasureSpec.getSize(widthMeasureSpec);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
  1. यदि आप कोटलिन में लिखते हैं, तो आप अगले फ़ंक्शन का उपयोग कर सकते हैं, जो पर्दे के पीछे ठीक उसी तरह काम करता है जैसे runJustBeforeBeingDrawnमैंने लिखा है:

    view.doOnPreDraw { actionToBeTriggered() }

    ध्यान दें कि आपको इसे ढाल में जोड़ने की आवश्यकता है ( यहां से होकर ):

    implementation 'androidx.core:core-ktx:#.#'

1
लगता है कि समाधान # 1 हमेशा साथ काम नहीं करेगा OnPreDrawListener। जब आप गतिविधि onCreate()और आप तुरंत पृष्ठभूमि ऐप से कोड चलाना चाहते हैं तो परीक्षण किया गया मामला । विशेष रूप से धीमी डिवाइस पर दोहराना आसान। यदि आप के OnPreDrawListenerसाथ प्रतिस्थापित OnGlobalLayoutListener- आप एक ही मुद्दा नहीं देखेंगे।
प्रश्नकर्ता

@questioner मुझे समझ में नहीं आता है, लेकिन मुझे खुशी है कि इससे आपको अंत में मदद मिली।
Android डेवलपर

हालांकि मैं आमतौर पर उपयोग करता हूं ViewTreeObserverया post, मेरे मामले में नंबर 2 ने मदद की ( view.measure)।
कूलमाइंड

3
यदि आप कोटलिन का उपयोग करते हैं तो @CoolMind आप एक नई विधि का उपयोग भी कर सकते हैं। इसे शामिल करने के लिए अद्यतित उत्तर।
एंड्रॉइड डेवलपर

कोटलिन के पास इसके लिए अतिरिक्त कार्य क्यों हैं? Google पागल है, यह भाषा बेकार है (बस जावा के साथ रहना चाहिए), यह स्विफ्ट की तुलना में बहुत पुराना है लेकिन केवल तेजी से पर्याप्त लोकप्रियता मिली tiobe.com/tiobe-index (स्विफ्ट 12 स्थिति और कोटलिन 38)
user924

18

क्या आप getWidth()वास्तव में स्क्रीन पर दृश्य को देखने से पहले कॉल कर रहे हैं ?

नए एंड्रॉइड डेवलपर्स द्वारा की गई एक सामान्य गलती इसके निर्माता के अंदर एक दृश्य की चौड़ाई और ऊंचाई का उपयोग करना है। जब किसी व्यू का कंस्ट्रक्टर कहा जाता है, तो एंड्रॉइड को यह नहीं पता होता है कि व्यू कितना बड़ा होगा, इसलिए साइज को शून्य पर सेट किया गया है। असली आकार की गणना लेआउट चरण के दौरान की जाती है, जो कि निर्माण के बाद होती है लेकिन कुछ भी खींचने से पहले। आप onSizeChanged()ज्ञात होने पर मूल्यों को अधिसूचित करने के लिए विधि का उपयोग कर सकते हैं , या आप बाद में getWidth()और getHeight()विधियों का उपयोग कर सकते हैं , जैसे कि onDraw()विधि में।


2
तो क्या इसका मतलब है कि मुझे onSizeChange()वास्तविक आकार जानने के लिए घटना को पकड़ने के लिए ImageView को ओवरराइड करने की आवश्यकता है ? यह थोड़ा ज्यादा लगता है ... हालांकि इस बिंदु पर मैं सिर्फ कोड काम करने के लिए बेताब हूं, इसलिए यह एक शॉट के लायक है।
निक रीमन

मैं तब तक अधिक सोच रहा था जब तक आपकी गतिविधि में आपकी onCreate () का इंतजार खत्म नहीं हो गया।
मार्क बी

1
जैसा कि उल्लेख किया गया है, मैंने इस कोड को डालने की कोशिश की onPostCreate()जिसमें दृश्य पूरी तरह से निर्मित होने और शुरू होने के बाद चलता है।
निक रीमन

1
उद्धृत पाठ कहाँ से लिया गया है?
प्रातः काल

1
कस्टम दृश्य के लिए यह उद्धरण, ऐसे उत्तर के लिए पोस्ट न करें जब हम सामान्य रूप से दृश्य आकार के बारे में पूछते हैं
user924

17

@ Mbaird की सलाह के आधार पर, मैंने कक्षा को ओवरक्लाइड करके ImageViewऔर ओवरराइड करके एक व्यावहारिक समाधान पायाonLayout() । मैंने तब एक ऑब्जर्वर इंटरफ़ेस बनाया, जिसे मेरी गतिविधि ने लागू किया और खुद को क्लास के लिए एक संदर्भ दिया, जिसने इसे गतिविधि को बताने की अनुमति दी जब यह वास्तव में साइज़िंग समाप्त हो गया था।

मैं 100% आश्वस्त नहीं हूं कि यह सबसे अच्छा समाधान है (इसलिए मेरे इस जवाब को अभी तक सही नहीं चिह्नित कर रहा है), लेकिन यह काम करता है और प्रलेखन के अनुसार पहली बार है जब कोई किसी दृश्य का वास्तविक आकार पा सकता है।


जानकार अच्छा लगा। अगर मुझे ऐसा कुछ भी मिलता है, जो आपको यहां देखे जाने वाले उपवर्ग को प्राप्त करने की अनुमति देगा तो मैं इसे यहां पोस्ट करूंगा। मैं थोड़ा हैरान हूँ onPostCreate () काम नहीं किया।
मार्क बी

खैर, मैंने यह कोशिश की और यह काम करने लगता है। बस वास्तव में सबसे अच्छा समाधान नहीं है क्योंकि यह विधि केवल एक बार शुरू होने पर अक्सर कॉल नहीं होती है। :(
टोबियास रिइच

10

यदि API <11 (API 11 में View.OnLayoutChangedListener सुविधा शामिल है) को देखने के माध्यम से लेआउट प्राप्त करने के लिए कोड है:

public class CustomListView extends ListView
{
    private OnLayoutChangedListener layoutChangedListener;

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

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {
        if (layoutChangedListener != null)
        {
            layoutChangedListener.onLayout(changed, l, t, r, b);
        }
        super.onLayout(changed, l, t, r, b);
    }

    public void setLayoutChangedListener(
        OnLayoutChangedListener layoutChangedListener)
    {
        this.layoutChangedListener = layoutChangedListener;
    }
}
public interface OnLayoutChangedListener
{
    void onLayout(boolean changed, int l, int t, int r, int b);
}


5

नीचे दिए गए कोड का उपयोग करें, यह देखने का आकार देता है।

@Override
public void onWindowFocusChanged(boolean hasFocus) {
       super.onWindowFocusChanged(hasFocus);
       Log.e("WIDTH",""+view.getWidth());
       Log.e("HEIGHT",""+view.getHeight());
}

5

यह मेरे लिए मेरे में काम करता है onClickListener:

yourView.postDelayed(new Runnable() {               
    @Override
    public void run() {         
        yourView.invalidate();
        System.out.println("Height yourView: " + yourView.getHeight());
        System.out.println("Width yourView: " + yourView.getWidth());               
    }
}, 1);

4
जब यह एक क्लिक श्रोता में हो तो आपको पोस्टडेलड करने की आवश्यकता नहीं है। आप तब तक कुछ भी क्लिक नहीं कर सकते जब तक कि सभी ने खुद को मापा और स्क्रीन पर दिखाई नहीं दिया। इस प्रकार जब तक आप उन्हें क्लिक करने में सक्षम नहीं हो जाते, तब तक उन्हें पहले ही मापा जा सकता है।
23

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

मैं पोस्टडेलिड () के लिए नहीं जाने की सलाह दूंगा। यह होने की संभावना नहीं है लेकिन क्या होगा यदि आपका दृश्य 1 सेकंड के भीतर मापा नहीं जाता है और आप इस चौड़ाई को ऊंचाई / ऊंचाई प्राप्त करने के लिए कहते हैं। यह अभी भी 0. परिणाम देगा। संपादित करें: ओह और btw कि 1 मिलीसेकंड में है, इसलिए यह सुनिश्चित करने में विफल होगा।
एलेक्स

4

मैं भी चारों ओर खो गया था getMeasuredWidth()और getMeasuredHeight() getHeight()और getWidth()एक लंबे समय के लिए .......... बाद में मैंने पाया कि में देखने के चौड़ाई और ऊंचाई हो रही onSizeChanged()यह करने के लिए सबसे अच्छा तरीका है ........ आप गतिशील रूप से कर सकते हैं ओवरराइड करके अपने दृश्य की अपनी चौड़ाई और चौड़ाई को प्राप्त करेंonSizeChanged( ) विधि ।

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


0

आप स्क्रीन पर दृश्य की स्थिति और आयाम दोनों प्राप्त कर सकते हैं

 val viewTreeObserver: ViewTreeObserver = videoView.viewTreeObserver;

if (viewTreeObserver.isAlive) {
    viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            //Remove Listener
            videoView.viewTreeObserver.removeOnGlobalLayoutListener(this);

            //View Dimentions
            viewWidth = videoView.width;
            viewHeight = videoView.height;

            //View Location
            val point = IntArray(2)
            videoView.post {
                videoView.getLocationOnScreen(point) // or getLocationInWindow(point)
                viewPositionX = point[0]
                viewPositionY = point[1]
            }

        }
    });
}

-1

मेरे लिए परफेक्ट काम करता है:

 protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
        CTEditor ctEdit = Element as CTEditor;
        if (ctEdit == null) return;           
        if (e.PropertyName == "Text")
        {
            double xHeight = Element.Height;
            double aHaight = Control.Height;
            double height;                
            Control.Measure(LayoutParams.MatchParent,LayoutParams.WrapContent);
            height = Control.MeasuredHeight;
            height = xHeight / aHaight * height;
            if (Element.HeightRequest != height)
                Element.HeightRequest = height;
        }
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.