Android 获取View的高度和宽度

##View宽高值为什么是0
如果我们想获取View的高度和宽度,通过在onCreate()或者onStart()或者onResume()等生命周期中直接获取,像下面这样处理会获取到View的高度吗?

package com.wjj.imagepull;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ScrollView;
import android.widget.TextView;

public class MainActivity extends Activity
{
    ScrollView scrollView;
    public static final String TAG = "VIEW_PARAM";

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        scrollView = (ScrollView) findViewById(R.id.scroll_view);

        getHeightAndWidth("onCreate()");
    }

    private void getHeightAndWidth(String method)
    {
        int height = 0;
        int width = 0;

        if (scrollView != null)
        {
            height = scrollView.getHeight();
            width = scrollView.getWidth();
        }

        Log.i(MainActivity.TAG, method + " has been called: " + System.currentTimeMillis() + " height: " + height + " width: " + width);
    }

    @Override
    protected void onStart()
    {
        super.onStart();

        getHeightAndWidth("onStart()");
    }

    @Override
    protected void onResume()
    {
        super.onResume();

        getHeightAndWidth("onResume()");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings)
        {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

接下来在自定义的组件PullScrollView中适当的方法中加入一些测试log:

public class PullScrollView extends ScrollView
{

    View view;
    int sacTopMargin;
    float lastY;
    float offsetY;

    public PullScrollView(Context context, AttributeSet attrs)
    {
        super(context, attrs); 
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        Log.i(MainActivity.TAG, "onMeasure() has been called: " + System.currentTimeMillis());
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);

        Log.i(MainActivity.TAG, "onDraw() has been called: " + System.currentTimeMillis());
    }
    ...
}

然后运行后看一下Log
此处输入图片的描述

通过上面的方法,显然不能获取到View的高度和宽度,关于为什么获取不到View的这些参数值,原因很简单,因为当前Activity中持有的WindowPhone实例还尚未将View添加到DecorView上来,所以无法获取View的高度和宽度,一般在onResume生命周期过后300ms左右时间才会将需要显示的View添加到DecorView上去,代码像下面这样处理:

##如何获取View宽高值

@Override
protected void onResume()
{
    super.onResume();

    getHeightAndWidth("onResume()");

    scrollView.postDelayed(new Runnable()
    {
        @Override
        public void run()
        {
            getHeightAndWidth("onResume()_postDelayed()");
        }
    }, 300);
}

此处输入图片的描述
这样就获取了View的宽高值,当然这个方法不太保险,因为你不能确保在onResume之后300ms 就能将View添加到DecorView中去,那么保险的方式是什么呢?相信读过Android源代码的同学对ViewTreeObserver.OnGlobalLayoutListener并不陌生,当一个view的布局加载完成或者布局发生改变时OnGlobalLayoutListener可以监听到,利用这点我们可以在布局加载完成的瞬间获得一个view的宽高。

public class PullScrollView extends ScrollView implements ViewTreeObserver.OnGlobalLayoutListener
{
    public PullScrollView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        ViewTreeObserver observer = getViewTreeObserver();
        if (null != observer)
            observer.addOnGlobalLayoutListener(this);
    }
    
    @Override
    public void onGlobalLayout()
    {
        MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
        sacTopMargin = -params.topMargin;
        Log.d(MainActivity.TAG, "" + sacTopMargin);
        getViewTreeObserver()
                .removeGlobalOnLayoutListener(this);
    }
    ...
}

OnGlobalLayoutListener的onGlobalLayout被回调之前是没有值的。由于布局状态可能会发生多次改变,因此OnGlobalLayoutListener的onGlobalLayout可能被回调多次,所以我们在 第一次获得值之后就将listener注销掉

当然关于如何获取View的高度和宽度值,还有很多方法,这里仅仅测试了两种比较靠谱的方法,感兴趣的同学可以继续深究。

你可能感兴趣的:(Android)