mark-fragment中获取控件的时机

上一篇文章中说到在activity中,onStart, onResume, onCreate都不是真正visible的时间点,真正的visible时间点是onWindowFocusChanged()函数被执行时。问题来了,与activity十分相似的fragment中真正的visible是在什么时候呢?或者怎样才能保证万无一失的获取view控件的长宽参数呢?

ps:急于解决问题的同学可直接参考

方法3:将一个runnable添加到Layout队列中:View.post()【亲测可用,简单暴力的解决问题】

简单地说,只要用View.post()一个runnable就可以了。runnable对象中的方法会在View的measure、layout等事件后触发,具体的参考Romain Guy

UI事件队列会按顺序处理事件。在setContentView()被调用后,事件队列中会包含一个要求重新layout的message,所以任何你post到队列中的东西都会在Layout发生变化后执行。

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

进入正题。。。。。。。。。。

带着这个问题查找了一部分资料

思路一:当fragment可见的状态下,就可以获取控件高宽(主要受activity onWindowFocusChanged方法的影响)

1、说法一来自 农民伯伯

Fragment虽然有onResume和onPause的,但是这两个方法是Activity的方法,调用时机也是与Activity相同,当和ViewPager搭配使用时,通过setUserVisibleHint  参数isVisibleToUser判断fragment是否可见。好像这个地方只是来说明真正意义的onResume和onPause,即便是fragment可见了,也不见得这个时候可以真正获取控件高宽
view.getHeight()。(未验证)

<span style="font-size:14px;"> @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
            //相当于Fragment的onResume
        } else {
            //相当于Fragment的onPause
        }
    }</span>

2、通过判断fragment 的onHiddenChanged(boolean)方法参数

本以为onHiddenChanged和activity的onWindowFocusChanged方法会有异曲同工之处,还专门科普了下isHidden与!isvisible的区别:
光从名字上看,isHidden就是!isVisible,但其实这两方法功能还是有区别的。isVisible,只有这个Fragment显示了,用户看到了,才返回true,但是isHidden,如果这个Fragment刚刚初始化,还没有添加,也会返回false,这个时候isVisible返回false,isHidden也返回false. 但是实际测试才发现, onHiddenChanged根本无法执行,细看发现该方法是和show()  hide()方法配合使用的

思路二:不局限与view的可见与否

要想获取view的width和height 并且值!=0;

方法1: 在oncreat方法中加延时,只要延时适当等view加载完毕后必然会获取所要的值(鬼知道要延时多长时间,不靠谱)

方法2:监听Draw/Layout事件:ViewTreeObserver(未测试)

ViewTreeObserver监听很多不同的界面绘制事件。一般来说OnGlobalLayoutListener就是可以让我们获得到view的width和height的地方.下面onGlobalLayout内的代码会在View完成Layout过程后调用。
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            mScrollView.post(new Runnable() {
                public void run() {
                    view.getHeight(); //height is ready
                }
            });
        }
});

但是要注意这个方法在每次有些view的Layout发生变化的时候被调用(比如某个View被设置为Invisible),所以在得到你想要的宽高后,记得移除onGlobleLayoutListener:

在 SDK Lvl < 16时使用
public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)

在 SDK Lvl >= 16时使用
public void removeOnGlobalLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)

方法3:将一个runnable添加到Layout队列中:View.post()【亲测可用,简单暴力的解决问题】

简单地说,只要用View.post()一个runnable就可以了。runnable对象中的方法会在View的measure、layout等事件后触发,具体的参考Romain Guy

UI事件队列会按顺序处理事件。在setContentView()被调用后,事件队列中会包含一个要求重新layout的message,所以任何你post到队列中的东西都会在Layout发生变化后执行。

final View view=//smth;
...
view.post(new Runnable() {
            @Override
            public void run() {
                view.getHeight(); //height is ready
            }
        });
参考老外: http://stackoverflow.com/questions/3602026/linearlayout-height-in-oncreate-is-0/3602144#3602144
方法4:重写View的onLayout方法

这个方法只在某些场景中实用,比如当你所要执行的东西应该作为他的内在逻辑被内聚、模块化在view中,否者这个解决方案就显得十分冗长和笨重

view = new View(this) {
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        view.getHeight(); //height is ready
    }
};
需要注意的是onLayout方法会调用很多次,所以要考虑好在这个方法中要做什么,或者在第一次执行后禁用掉你的代码

附加:获取固定宽高

如果你要获取的view的width和height是固定的,那么你可以直接使用:

1 View.getMeasureWidth()
2 View.getMeasureHeight()

但是要注意,这两个方法所获取的width和height可能跟实际draw后的不一样。官方文档解释了不同的原因

View的大小由width和height决定。一个View实际上同时有两种width和height值。

第一种是measure width和measure height。他们定义了view想要在父View中占用多少width和height(详情见Layout)。measured height和width可以通过getMeasuredWidth() 和 getMeasuredHeight()获得。

第二种是width和height,有时候也叫做drawing width和drawing height。这些值定义了view在屏幕上绘制和Layout完成后的实际大小。这些值有可能跟measure width和height不同。width和height可以通过getWidth()和getHeight获得。





评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值