実行時のAndroidビューのサイズの決定


123

アクティビティが作成された後、Androidアプリのビューにアニメーションを適用しようとしています。これを行うには、ビューの現在のサイズを決定し、現在のサイズから新しいサイズにスケーリングするアニメーションを設定する必要があります。ユーザーからの入力に応じてビューのサイズが異なるため、この部分は実行時に実行する必要があります。私のレイアウトはXMLで定義されています。

これは簡単な仕事のようで、明らかに私の問題を解決したものはありませんが、これに関して多くのSOの質問があります。おそらく私は明らかな何かを見逃しています。私は自分の見解のハンドルを手に入れます:

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

これは罰金を動作しますが、呼び出し時にgetWidth()getHeight()getMeasuredWidth()getLayoutParams().width、など、彼らはすべてのリターン0は、私はまた、呼び出しを手動しようとしたmeasure()の呼び出しに続いてビューにgetMeasuredWidth()、それは効果がありません。

私はこれらのメソッドを呼び出して、私のアクティビティのonCreate()とでデバッガのオブジェクトを検査してみましたonPostCreate()。実行時にこのビューの正確な寸法を把握するにはどうすればよいですか?


1
また、ビュー自体の幅/高さが0でないことにも注意してください。画面に問題なく表示されます。
Nik Reiman

xmlレイアウトから<ImageView ... />タグを投稿できますか?
fhucho

私の場合、ビューのサイズが物理画面と一致していることに気づくまで、私はこの問題に対する多くのSOソリューションと格闘しました(私のアプリは「没入型」であり、ビューとそのすべての親の幅と高さがに設定されていますmatch_parent)。この場合、ビューが描画される前でも(たとえば、アクティビティのonCreate()メソッドから)安全に呼び出すことができる簡単なソリューションは、Display.getSize();を使用することです。詳細については、stackoverflow.com / a / 30929599/5025060をご覧ください。これは@NikReimanが要求したものではないことを知っており、このより簡単な方法を使用できる可能性のある人にメモを残しています。
CODE-REaD

回答:


180

ビューでViewTreeObserverを使用して、最初のレイアウトを待ちます。最初のレイアウトが初めてgetWidth()/ 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
基になるビューがビデオ/サーフェスである場合は継続的に呼び出されるため、この回答には注意してください
Kevin Parker

7
@Kevinについて詳しく説明していただけますか?まず、2つあります。これはリスナーであるため、コールバックは1つであることを期待します。次に、最初のコールバックを取得するとすぐに、リスナーを削除します。私は何か見落としてますか?
Vikram Bodicherla 2011年

4
ViewTreeObserverメソッドを呼び出す前に、isAlive()を確認することをお勧めします。}
Peter Tran

4
に代わるものはremoveGlobalOnLayoutListener(this);?廃止されたためです。
Sibbsギャンブル2013

7
@FarticlePilter、代わりにremoveOnGlobalLayoutListener()を使用してください。それはドキュメントで言及されています。
Vikram Bodicherla 2014年

63

シナリオに応じて、実際には複数のソリューションがあります。

  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. 拡張したカスタムビューがある場合、「onMeasure」メソッドでそのサイズを取得できますが、一部の場合にのみうまく機能すると思います。
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. Kotlinで記述する場合は、next関数を使用できます。これは、裏runJustBeforeBeingDrawnで私が記述したのとまったく同じように機能します。

    view.doOnPreDraw { actionToBeTriggered() }

    これをgradleに追加する必要があることに注意してください(ここで見つかります):

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

1
ソリューション#1が常に機能するとは限りませんOnPreDrawListener。Activityからコードを実行したい場合にテスト済みのケースで、onCreate()すぐにアプリをバックグラウンドで実行します。特に遅いデバイスでは繰り返しが簡単です。に置き換えるOnPreDrawListenerと、OnGlobalLayoutListener同じ問題は発生しません。
質問者

@questionerわかりませんが、結局あなたを助けてくれて嬉しいです。
android開発者

通常はViewTreeObserverまたはを使用しますがpost、私の場合は2番が役立ちました(view.measure
CoolMind 2018

3
@CoolMind Kotlinを使用している場合は、新しいメソッドを使用することもできます。回答も含めて更新しました。
Androidデベロッパー

なぜkotlinはこれに追加の機能を持っているのですか?グーグルはクレイジーです、この言語は役に立たないです(ただJavaにとどまる必要があります)、Swiftよりはるかに古いですが、Swiftだけが十分な人気を得ましたtiobe.com/tiobe-index(Swift 12位とkotlin 38)
user924

18

getWidth()ビューが実際に画面上に配置される前に呼び出しますか?

新しいAndroid開発者がよく犯す間違いは、コンストラクター内でビューの幅と高さを使用することです。ビューのコンストラクターが呼び出されたとき、Androidはビューの大きさをまだ認識していないため、サイズはゼロに設定されます。実際のサイズはレイアウト段階で計算されます。これは、構築後、描画される前に行われます。onSizeChanged()メソッドを使用して、値が既知のときに通知を受けるかgetWidth()getHeight()メソッドなどで後でメソッドを使用できますonDraw()


2
つまりonSizeChange()、実際のサイズを知るためにイベントをキャッチするためだけにImageViewをオーバーライドする必要があるということですか?それは少しやり過ぎのようです...しかし、現時点ではコードを機能させるためだけに必死なので、一撃の価値があります。
Nik Reiman

私はあなたのonCreate()があなたのアクティビティで終了するまで待つようなものだと思っていました。
マークB

1
前述のように、onPostCreate()ビューが完全に作成されて開始された後に実行されるこのコードを入れてみました。
Nik Reiman

1
引用されたテキストはどこにありますか?
複雑化

1
このカスタムビューの見積もりです。ビューのサイズ全般について尋ねる場合は、そのような回答には投稿しないでください
user924

17

@mbairdのアドバイスに基づいて、ImageViewクラスをサブクラス化してオーバーライドすることで実行可能な解決策を見つけましたonLayout()。次に、アクティビティが実装するオブザーバーインターフェイスを作成し、それ自体への参照をクラスに渡しました。これにより、実際にサイズ変更が完了したときにアクティビティに通知できます。

これが最良の解決策であると100%確信しているわけではありませんが(このため、この答えをまだ正しいとマークしていません)、それは機能し、ドキュメントによると、ビューの実際のサイズを見つけることができるのは初めてです。


知ってよかった。ビューをサブクラス化する必要があることを回避できるものを見つけたら、ここに投稿します。onPostCreate()が機能しなかったことに少し驚いています。
マークB

まあ、私はこれを試してみましたが、うまくいくようです。このメソッドは頻繁に呼び出されるため、開始時に1回だけではないため、実際には最善のソリューションとは言えません。:(
トビアス・ライヒ

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
クリックリスナー内にある場合は、postDelayedする必要はありません。ビューがすべて測定されて画面に表示されるまで、何もクリックできません。したがって、それらは、ユーザーがクリックできる時間までに既に測定されています。
afollestad 2015年

このコードは間違っています。ポスト遅延は、次のティックでビューが測定されることを保証しません。
Ian Wong

postDelayed()を使用しないことをお勧めします。発生する可能性は低いですが、ビューが1秒以内に測定されず、このランナブルを呼び出してビューの幅/高さを取得した場合はどうなりますか。それでも結果は0になります。
アレックス

4

私も周りでgetMeasuredWidth()getMeasuredHeight() getHeight()そしてgetWidth()長い間迷っていました..........後で、ビューの幅と高さを取得するonSizeChanged()ことがこれを行う最善の方法であることがわかりました........動的にできますonSizeChanged()メソッドをオーバーライドして、ビューの現在の幅と現在の高さを取得します。

精巧なコードスニペットがあるこれを見てみたいかもしれません。新しいブログ投稿:AndroidでcustomView(ビューを拡張)の幅と高さの寸法を取得する方法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;
        }
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.