Androidでコントロールのサイズを取得する方法

18047 ワード

1.問題を発見する
GPS関連のアプリケーションを作る過程で地球コントロールのサイズを測定する必要があり、getWidth、getMeasuredWidthなどを呼び出す方法を試みたところ、結果はいずれも0であることが分かった.繰り返しチェックしても間違いが見つからなかったのはなぜですか?
2.原因を究明する.
問題を発見したら、もちろん原因を分析しなければなりません.調べてみると、onCreateでは、私たちのコントロールはまだ描かれていません.言い換えれば、onCreateメソッドが実行されると、私たちが定義したコントロールがメジャーになるので、onCreateメソッドではviewを通じています.getWidth()取得コントロールの幅は0に違いありません.
3.テスト
この问题に対して、ネット上で多くの解决方法があって、本当に理解したいならば、自分ももちろん试してみて、やっと効果がどのようですかを知っていて、その方法はもっと良いです.次のコードレイアウトは簡単です.ImageViewです.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView 
        android:id="@+id/img"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:src="@drawable/bbb"
        android:background="#000000"
        android:layout_centerInParent="true"/>

RelativeLayout>

以下は、画像サイズをそれぞれテストする方法と、1~5つの方法でImageViewコントロールのサイズをテストする方法がいくつかあります.
package com.wuxianxi.test;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;

public class MainActivity extends ActionBarActivity {
    private ImageView img;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        img = (ImageView) findViewById(R.id.img);

        getImageSize();
        getSizeOne();
        getSizeTwo();
        getSizeThree();
        getSizeFour();
        getSizeFive();
    }

    private void getImageSize() {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bbb);
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Log.d("wxx", "image: width = " + width + ", height = " + height);
    }

    private void getSizeOne() {
        int width = img.getWidth();
        int height = img.getHeight();
        Log.d("wxx", "One: width = " + width + ", height = " + height);
    }

    private void getSizeTwo() {
        int width = img.getMeasuredWidth();
        int height = img.getMeasuredHeight();
        Log.d("wxx", "Two: width = " + width + ", height = " + height);
    }

    private void getSizeThree() {
        int w = View.MeasureSpec.makeMeasureSpec(0,
                View.MeasureSpec.UNSPECIFIED);
        int h = View.MeasureSpec.makeMeasureSpec(0,
                View.MeasureSpec.UNSPECIFIED);
        img.measure(w, h);
        int width = img.getMeasuredWidth();
        int height = img.getMeasuredHeight();
        Log.d("wxx", "Three: width = " + width + ", height = " + height);
    }

    private void getSizeFour() {
        final ViewTreeObserver vto = img.getViewTreeObserver();
        vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            public boolean onPreDraw() {
                int width = img.getMeasuredWidth();
                int height = img.getMeasuredHeight();
                Log.d("wxx", "Four: width = " + width + ", height = " + height);
                return true;
            }
        });

    }

    private void getSizeFive() {
        ViewTreeObserver vto = img.getViewTreeObserver();   
                vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 
                    @Override  
                    public void onGlobalLayout() { 
                        img.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
                        int width = img.getWidth();
                        int height = img.getHeight();
                        Log.d("wxx", "Five: width = " + width + ", height = " + height);
                    }   
                });

    }

}

印刷されたログは次のとおりです.
01-20 10:27:34.856: D/wxx(2357): image: width = 312, height = 440
01-20 10:27:34.856: D/wxx(2357): One: width = 0, height = 0
01-20 10:27:34.856: D/wxx(2357): Two: width = 0, height = 0
01-20 10:27:34.861: D/wxx(2357): Three: width = 312, height = 440
01-20 10:27:35.001: D/wxx(2357): Five: width = 900, height = 900
01-20 10:27:35.001: D/wxx(2357): Four: width = 900, height = 900
01-20 10:27:35.021: D/wxx(2357): Four: width = 900, height = 900

レイアウトでImageViewのサイズを変更し、もう一度テストして比較します.
    <ImageView 
        android:id="@+id/img"
        android:layout_width="100dp"
        android:layout_height="200dp"
        android:src="@drawable/bbb"
        android:background="#000000"
        android:layout_centerInParent="true"/>

印刷されたログは次のとおりです.
01-20 10:31:54.491: D/wxx(5233): image: width = 312, height = 440
01-20 10:31:54.491: D/wxx(5233): One: width = 0, height = 0
01-20 10:31:54.491: D/wxx(5233): Two: width = 0, height = 0
01-20 10:31:54.491: D/wxx(5233): Three: width = 312, height = 440
01-20 10:31:54.626: D/wxx(5233): Five: width = 300, height = 600
01-20 10:31:54.626: D/wxx(5233): Four: width = 300, height = 600
01-20 10:31:54.646: D/wxx(5233): Four: width = 300, height = 600

4.結論
1)getWidth,getMeasuredWidthを用いた結果はいずれも0であるため,どちらの方法も利用できない.2)上の3つ目の方法を見る(推奨しない)
    private void getSizeThree() {
        int w = View.MeasureSpec.makeMeasureSpec(0,
                View.MeasureSpec.UNSPECIFIED);
        int h = View.MeasureSpec.makeMeasureSpec(0,
                View.MeasureSpec.UNSPECIFIED);
        img.measure(w, h);
        int width = img.getMeasuredWidth();
        int height = img.getMeasuredHeight();
        Log.d("wxx", "Three: width = " + width + ", height = " + height);
    }

この方法は自分で測定する方法ですが、出力の結果から、ImageViewというコントロールのサイズではなく、コントロールの画像のサイズを測定します.この結果は意外だったが、ネットで調べてみると、多くの大神がこの方法を推薦している.3)第四の方法を見る:(利用可能)
    private void getSizeFour() {
        final ViewTreeObserver vto = img.getViewTreeObserver();
        vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            public boolean onPreDraw() {
                int width = img.getMeasuredWidth();
                int height = img.getMeasuredHeight();
                Log.d("wxx", "Four: width = " + width + ", height = " + height);
                return true;
            }
        });

    }

この方法では、ViewTreeObserverのリスニングコールバックを登録する必要があります.onPreDraw()から、このリスニングコールバックは、図面を専門にリスニングするものであり、リスニング図面である以上、測定値を自然に取得することができます.また、リスニング前にremoveの前回のリスニングを繰り返してリスニングを避けることができます.4)5つ目の方法を見る:(利用可能、推奨)
    private void getSizeFive() {
        ViewTreeObserver vto = img.getViewTreeObserver();   
                vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 
                    @Override  
                    public void onGlobalLayout() { 
                        img.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
                        int width = img.getWidth();
                        int height = img.getHeight();
                        Log.d("wxx", "Five: width = " + width + ", height = " + height);
                    }   
                });

    }

この方法は第4の方法とあまり差がなく,いずれもリスナーによってコールバックを傍受するが,ここではグローバルなレイアウト変更リスナーであり,この方法が最も推奨される.
ここまで、上の最後の2つの方法で正解が得られるので、お勧めします.これだけ書いておこう~~