ビューの寸法を取得する方法は?


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を使用します。
bhups

9
みんな、電話getHeight()getWidth()し、アクティビティライフサイクルがビューに自分自身を測定するように要求した後、つまり、この種onResume()のことを実行するだけです。まだレイアウトされていないオブジェクトがその寸法を知っていると期待すべきではありません。それが全体のトリックです。以下に示す魔法の必要はありません。
クラススタッカー2013年

2
呼び出してもgetHeight()/Width()onResume()値が0を超えることはありませんでした。
Darpan

@ClassStackerフラグメントはどうですか?
zdd

@zdd完全に新しい質問をして、ここにコメントを投稿してください。
クラススタッカー2016

回答:


418

OPはもうなくなっていると思いますが、この回答が将来の検索者を助けることができる場合は、見つけた解決策を投稿したいと思いました。私はこのコードを私のonCreate()メソッドに追加しました:

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

});

最初に、TextViewonGlobalLayout()メソッドでアクセスするための)myへの最終参照を取得します。次に、私が手ViewTreeObserverに私からTextView、および追加OnGlobalLayoutListener上書きし、onGLobalLayout(ここで起動するために、スーパークラスのメソッドがあるようには思えない...)と、このリスナーにビューの測定を知る必要が私のコードを追加します。すべてが期待どおりに機能するので、これが役立つことを願っています。


23
@George Bailey:私が最近他の誰かが投稿したのを見たことがあります(私は自分でテストしていないので、YMMV)複数の呼び出しを克服するために(onGlobalLayout()内で):tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);リスナーは最初に削除する必要がありますそれが発生します。
Kevin Coppock、2011年

3
私も助けたいです。あなたは次のようなことをする必要があるかもしれません:mView.getViewTreeObserver()。removeGlobalOnLayoutListener(globalLayoutListener); ビューのサイズを一度だけ変更したい場合。このヘルプを願っています
Henry Sou

7
質問は非常に古いですが、使用addOnLayoutChangeListenerしても機能します!:)
FX

7
また、API 16以降、次のものが必要であることに注意してください:if(android.os.Build.VERSION.SDK_INT> = 16){view.getViewTreeObserver()。removeOnGlobalLayoutListener(this); } else {view.getViewTreeObserver()。removeGlobalOnLayoutListener(this); }
エジソン

2
removeGlobalOnLayoutListenerは非推奨であるため、Eclipseで引き続き警告が表示されます。これは正常ですか?時間の経過とともに、廃止されたコードの警告が氾濫する...
ジョニー

82

代わりのソリューションを追加して、アクティビティのonWindowFocusChangedメソッドをオーバーライドすると、そこからgetHeight()、getWidth()の値を取得できるようになります。

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

4
ドキュメントを引用すると、「これは、このアクティビティがユーザーに表示されるかどうかの最良の指標です。」
Cameron Lowell Palmer

6
フラグメントを使用する必要がある場合、これは機能しませんViewTreeObserver
Mihir 2013年

OPは、ビューのためにそれを取得する方法を具体的に尋ねました
JustDanyul

scrollviewの合計の高さと最大のScrollview.getScrolly()は同じになりますか?つまり、私の場合、上記の方法でHeightを取得したのは702で、getScrolly()から最大値を取得したのは523です
Hitesh Dhamshaniya '26

このメソッドは、ビューが別の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

それは恐ろしい習慣です!!! このリスナーは何度も呼び出され、あなたのパフォーマンスを殺します。フラグを使用する代わりに、完了したらリスナーの登録を解除してください。
bluewhile 2015年

15

このようにビューのpostメソッドを使用します

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

1
それは機能しますが、実際には、その理由についての議論はありませんか?ところで、レイアウトが発生するまでランナブルは実行されないため、これは機能します。
ノーマンH

7

を使用onGlobalLayout()してカスタム書式設定を実行しようとしましたTextViewが、@ George Baileyが気づいたように、onGlobalLayout()実際には2回呼び出されています。

View.onSizeChanged()そこでテキストを変更した場合、メソッドは(レイアウトパス中に)1回だけ呼び出されるので、私にとってはうまくいきます。これにはのサブクラス化が必要でしたTextViewが、APIレベル11+ではView. addOnLayoutChangeListener()サブクラス化を回避するために使用できます。

もう1つ、ビューの正しい幅を取得するにはView.onSizeChanged()layout_widthmatch_parentではなくに設定する必要がありwrap_contentます。


2

実際の画像を取得する前に実行されるコンストラクタやその他のメソッドでサイズを取得しようとしていますか?

すべてのコンポーネントが実際に測定されるまで、ディメンションは取得されません(xmlが表示サイズ、親の位置などを知らないため)

onSizeChanged()の後に値を取得するか(ゼロで呼び出すこともできます)、または実際の画像が取得されるのを待つだけです。


元の質問とコードをわかりやすく更新しました。ありがとうございました。

2

FXで述べたように、自分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
    }
});

初期レイアウトのみが必要な場合は、コールバックでリスナーを削除できます。



1

ViewTreeObserverそしてonWindowFocusChanged()、まったく必要ありません。

TextViewasレイアウトを拡張したり、コンテンツを配置して設定しLayoutParamsたりする場合は、getMeasuredHeight()およびを使用できますgetMeasuredWidth()

しかし、あなたがする必要がとの慎重なLinearLayouts(多分、他ViewGroups)。問題は、後で幅と高さを取得できることですが、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);

}

kcoppockが応答した理由により、OnCreate()、onStart()、またはonResume()でもビューの高さを取得できません


0

ビューの高さと幅を要求するまでにビューが作成されていないため、高さと幅はゼロです。最も簡単な解決策は

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

この方法は短くてカリッとするため、他の方法と比較して優れています。


-1

単純な応答: これは問題なく動作しました。重要なのは、getHeightなどの前にビューにフォーカスがあることを確認することです。hasFocus()メソッドを使用してこれを実行し、次にgetHeight()メソッドをこの順序で使用します。必要なコードはわずか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
これらのメソッドの前に述べたように、唯一の有効な結果を返すefterサイズはmeassuredされました。アクティビティ内では、onWindowFocusChangedメソッドが呼び出されたときにこれが当てはまります。
スロット

-8

修正: 上記の解決策はひどいことがわかりました。特にあなたの電話が遅いとき。そしてここで、私は別の解決策を見つけました:マージンとパディングを含む要素のpx値を計算します:dpからpx:https : //stackoverflow.com/a/6327095/1982712

またはdimens.xmlからpx:https ://stackoverflow.com/a/16276351/1982712

spからpx:https : //stackoverflow.com/a/9219417/1982712(ソリューションを逆にする)

またはdimensからpx:https ://stackoverflow.com/a/16276351/1982712

以上です。


10
通常、ミリ秒数を任意に選択した後、すべてが正しく設定されることを期待するのは悪い考えです。デバイスは...、遅い、他のプロセス/スレッドが実行されている可能性があることが、デバッガで動作しない可能性があります、OSの次期バージョンでは動作しない場合があります
のKristopherジョンソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.