获取屏幕、控件大小方法汇总

    |     2015年7月1日   |   Android UI界面   |     0 条评论   |    1697

在 Android 里量控件宽高,onCreate 里直接 getHeight() / getMeasuredWidth() 经常是 0:界面还没走完 measure / layout。本文汇总控件、状态栏、标题栏、屏幕和内容区的取法,并用 PopupWindow 把这些尺寸串起来。


一、为什么 onCreate 里拿不到尺寸

onCreate 只负责数据初始化,控件还没真正绘制;等到 onDraw 再量又太晚。正确时机是:measure / layout 刚完成、绘制之前。可用 ViewTreeObserver 或 View.post() 卡住这一拍。

方法 时机 注意
OnPreDrawListener 绘制前 可能反复回调,用 hasMeasured 只算一次
OnGlobalLayoutListener 全局 layout 完成 同样可能多次触发,量完应移除监听
View.post(Runnable) 当前 layout 消息之后 只跑一次,语法更简单,一般优先用这个

二、获取控件尺寸

方法一:OnPreDrawListener

View layout = findViewById(R.id.popwindow_btn);
ViewTreeObserver vto = layout.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    public boolean onPreDraw() {
        if (hasMeasured == false) {
            int btWidth = layout.getMeasuredWidth();
            int btHeight = layout.getMeasuredHeight();
            hasMeasured = true;
        }
        return true;
    }
});

方法二:OnGlobalLayoutListener

ViewTreeObserver vto = mPopupBtn.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        if (hasMeasured == false) {
            int btWidth = mPopupBtn.getMeasuredWidth();
            int btHeight = mPopupBtn.getMeasuredHeight();
            hasMeasured = true;
        }
    }
});

方法三:View.post()

setContentView() 之后,UI 事件队列里会有一条要求重新 layout 的消息。你 post 进去的 Runnable 会排在它后面,因此 run 时宽高已经就绪。比 Observer 省事:只执行一次,也不用再禁用监听。

final View view = // smth;
view.post(new Runnable() {
    @Override
    public void run() {
        view.getHeight(); // height is ready
    }
});

三、状态栏、标题栏、屏幕与内容区

状态栏高度取可见窗口矩形的 frame.top:

public int getStatusBarHeight() {
    Rect frame = new Rect();
    getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
    int statusBarHeight = frame.top;
    return statusBarHeight;
}

标题栏:内容区顶部减去状态栏;有 ActionBar 时也可直接 getActionBar().getHeight()(需确认 Title / ActionBar 没有隐藏)。

public int getTitleBarHeight() {
    int statusBarHeight = getStatusBarHeight();
    int contentTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
    int titleBarHeight = contentTop - statusBarHeight;
    return titleBarHeight;
}

public int getTitleBarHeight() {
    return getActionBar().getHeight();
}

屏幕宽高用 DisplayMetrics;内容区高度走 android.R.id.content。

public int[] getScreenSize() {
    DisplayMetrics dm = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(dm);
    int screenWidth = dm.widthPixels;
    int screenHeigh = dm.heightPixels;
    return new int[] { screenWidth, screenHeigh };
}

int contentViewHeight = findViewById(android.R.id.content).getHeight();

四、弹框实例

弹窗宽 = 屏幕宽减两侧边距;高 = 内容区高度减按钮高度再减边距。点击空白关闭需设背景、可聚焦、外部可点。定位用 getLocationOnScreen,location[0] 是 x,location[1] 是 y。

public void showPopupWindow(int buttonHeight) {
    View contentView = getPopWindowContentViewBySimpleAdatper();
    int margin = 50;
    int contentViewHeight = findViewById(android.R.id.content).getHeight();
    int[] screenSize = getScreenSize();
    int screenWidth = screenSize[0];
    int screenHeigh = screenSize[1];
    int titleHeight = getTitleBarHeight();
    int statusBarHeight = getStatusBarHeight();
    int popwindowWidth = screenWidth - 2 * margin;
    int popwindowHeight = contentViewHeight - buttonHeight - margin;
    // int popwindowHeight = screenHeigh - buttonHeight - titleHeight - statusBarHeight - margin;

    mPopwindow = new PopupWindow(contentView, popwindowWidth, popwindowHeight);
    mPopwindow.setBackgroundDrawable(new BitmapDrawable());
    mPopwindow.setFocusable(true);
    mPopwindow.setOutsideTouchable(true);

    mPopupBtn.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mPopwindow.isShowing()) {
                mPopwindow.dismiss();
            } else {
                int[] location = new int[2];
                v.getLocationOnScreen(location);
                Logs.v("location[0] :" + location[0] + " location[1] :" + location[1]);
                mPopwindow.showAtLocation(v, Gravity.NO_GRAVITY, location[0] + 50, location[1] + v.getHeight());
            }
        }
    });
}

一句话总结:onCreate 量不到宽高,等 layout 完成再用 View.post 或 ViewTreeObserver;屏幕、状态栏、标题栏、内容区分开取,弹窗宽高才能算对。

转载请注明来源:获取屏幕、控件大小方法汇总
本文链接地址:https://ai.zhousir.top/?p=813
回复 取消