我什么时候可以第一次测量视图?
因此,我对尝试设置视图显示时的背景可绘制对象感到有点困惑。该代码依赖于了解视图的高度,因此我无法从 onCreate()
或 onResume()
调用它,因为 getHeight()
code> 返回 0。 onResume()
似乎是我能得到的最接近的结果。我应该在哪里放置如下代码,以便背景在向用户显示时发生变化?
TextView tv = (TextView)findViewById(R.id.image_test);
LayerDrawable ld = (LayerDrawable)tv.getBackground();
int height = tv.getHeight(); //when to call this so as not to get 0?
int topInset = height / 2;
ld.setLayerInset(1, 0, topInset, 0, 0);
tv.setBackgroundDrawable(ld);
So I have a bit of confusion with trying to set the background drawable of a view as it is displayed. The code relies upon knowing the height of the view, so I can't call it from onCreate()
or onResume()
, because getHeight()
returns 0. onResume()
seems to be the closest I can get though. Where should I put code such as the below so that the background changes upon display to the user?
TextView tv = (TextView)findViewById(R.id.image_test);
LayerDrawable ld = (LayerDrawable)tv.getBackground();
int height = tv.getHeight(); //when to call this so as not to get 0?
int topInset = height / 2;
ld.setLayerInset(1, 0, topInset, 0, 0);
tv.setBackgroundDrawable(ld);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
我不知道
ViewTreeObserver.addOnPreDrawListener()
,我在测试项目中尝试过。使用您的代码,它看起来像这样:
在我的测试项目中
onPreDraw()
已被调用两次,我认为在您的情况下它可能会导致无限循环。您可以尝试仅在
TextView
的高度发生变化时调用setBackgroundDrawable()
:但这对于您想要实现的目标来说听起来有点复杂,而且不太好表现。
由 kcoppock 编辑
这是我最终根据这段代码所做的事情。戈蒂埃的回答让我明白了这一点,所以我宁愿接受这个答案并进行修改,也不愿自己回答。我最终使用 ViewTreeObserver 的 addOnGlobalLayoutListener() 方法来代替,就像这样(这是在 onCreate() 中):
似乎工作完美;我检查了 LogCat,没有发现任何异常活动。希望就是这样!谢谢!
I didn't know about
ViewTreeObserver.addOnPreDrawListener()
, and I tried it in a test project.With your code it would look like this:
In my test project
onPreDraw()
has been called twice, and I think in your case it may cause an infinite loop.You could try to call the
setBackgroundDrawable()
only when the height of theTextView
changes :But that sounds a bit complicated for what you are trying to achieve and not really good for performance.
EDIT by kcoppock
Here's what I ended up doing from this code. Gautier's answer got me to this point, so I'd rather accept this answer with modification than answer it myself. I ended up using the ViewTreeObserver's addOnGlobalLayoutListener() method instead, like so (this is in onCreate()):
Seems to work perfectly; I checked LogCat and didn't see any unusual activity. Hopefully this is it! Thanks!
关于视图树观察者:
即使它是一个短暂的引用(仅使用一次来测量视图并执行与您正在做的相同的操作),我也发现它不再“活着”并抛出异常。事实上,ViewTreeObserver 上的每个函数如果不再“活动”,都会抛出 IllegalStateException,因此您始终必须检查“isAlive”。我必须这样做:
这对我来说似乎很脆弱。很多事情——尽管很少——似乎都会出错。
所以我开始这样做:当您向视图发布消息时,只有在视图完全初始化(包括正在测量)后才会传递消息。如果您只希望测量代码运行一次,并且实际上不想在视图的生命周期中观察布局变化(因为它没有调整大小),那么我只需将测量代码放在这样的帖子中:
我的视图发布策略没有遇到任何问题,也没有看到任何屏幕闪烁或其他您预计可能会出现问题的情况(但是......)
我遇到崩溃了在生产代码中,因为我的全局布局观察器没有被删除,或者因为当我尝试访问它时布局观察器不是“活动的”
Concerning the view tree observer:
Even if its a short lived reference (only used once to measure the view and do the same sort of thing you're doing) I've seen it not be "alive" anymore and throw an exception. In fact, every function on ViewTreeObserver will throw an IllegalStateException if its no longer 'alive', so you always have to check 'isAlive'. I had to do this:
This seems pretty brittle to me. Lots of things - albeit rarely - seem to be able to go wrong.
So I started doing this: when you post a message to a View, the messages will only be delivered after the View has been fully initialized (including being measured). If you only want your measuring code to run once, and you don't actually want to observe layout changes for the life of the view (cause it isn't being resized), then I just put my measuring code in a post like this:
I haven't had any problems with the view posting strategy, and I haven't seen any screen flickers or other things you'd anticipate might go wrong (yet...)
I have had crashes in production code because my global layout observer wasn't removed or because the layout observer wasn't 'alive' when I tried to access it
通过使用全局侦听器,您可能会遇到无限循环。
我通过
在侦听器内调用 onGlobalLayout 方法内部来修复此问题。
by using the global listener, you may run into an infinite loop.
i fixed this by calling
inside of the onGlobalLayout method within the listener.
在我的项目中,我使用以下代码片段:
因此,在提供的 Runnable#run 内,您可以确保测量了 View 并执行相关代码。
In my projects I'm using following snippet:
So, inside provided
Runnable#run
you can be sure thatView
was measured and execute related code.您可以重写 TextView 的 onMeasure:
我几乎确定您也可以在没有任何代码行的情况下做到这一点。我认为有机会创建图层列表。
You can override onMeasure for TextView:
I am almost sure that you can do it without any line of code also. I think there is a chance to create layer-list.
综上所述,有 3 种处理视图大小的方法......嗯,也许是这样!
1)正如@Gautier Hayoun提到的,使用OnGlobalLayoutListener侦听器。但是,请记住在侦听器中的第一个代码处调用:
listViewProduct.getViewTreeObserver().removeOnGlobalLayoutListener(this);
以确保此侦听器不会无限调用。还有一件事,有时这个侦听器根本不会调用,因为您的视图已绘制在您不知道的地方。因此,为了安全起见,让我们在onCreate()
方法(或片段中的onViewCreated()
)中声明视图后立即注册此侦听器2)如果您想在视图绘制并显示后立即执行某些操作,只需使用
post()
方法(当然您可以在此处获取大小,因为您的视图已经完全绘制)。例如,此外,您可以使用
postDelay()
达到相同的目的,增加一点延迟时间。我们自己尝试一下吧。有时你会需要它。例如,当您想在将更多项目添加到滚动视图中后自动滚动滚动视图,或者通过动画展开或使更多视图从滚动视图中的消失状态可见时(这意味着这个过程需要一点时间才能显示所有内容)。它肯定会改变scroll view
的大小,所以在绘制scroll view
之后,我们还需要等待一段时间才能在添加更多内容后获得准确的大小查看它。现在是postDelay()
方法发挥作用的时候了!3) 如果你想设置自己的视图,你可以创建一个类并从任何视图扩展,就像所有视图都有
onMeasure()
方法来定义大小的观点。So summarily, 3 ways to handle the size of view.... hmmm, maybe so!
1) As @Gautier Hayoun mentioned, using OnGlobalLayoutListener listener. However, please remember to call at the first code in listener:
listViewProduct.getViewTreeObserver().removeOnGlobalLayoutListener(this);
to make sure this listener won't call infinitely. And one more thing, sometime this listener won't invoke at all, it because your view has been drawn somewhere you don't know. So, to be safe, let's register this listener right after you declare a view inonCreate()
method (oronViewCreated()
in fragment)2) If you want to do something right after the view has been drawn and shown up, just use
post()
method (of course you can get the size here since your view was drawn completely already). For example,Moreover, you can use
postDelay()
with the same purpose, adding a little delay time. Let's try it yourself. You will need it sometime. For prompt example, when you want to scroll the scroll view automatically after adding more items intoscroll view
or expand or make some more view visible from gone status inscroll view
with animation (it means this process will take a little time to show up everything). It will definitely change thescroll view
's size, so after thescroll view
is drawn, we also need to wait a little time to get an exact size after it adds more view into it. This's a high time forpostDelay()
method to come to the rescue!3) And if you want to set up your own view, you can create a class and
extend
from any View yo like which all haveonMeasure()
method to define size of the view.您还可以使用此视图扩展:
You can also use this view extension:
您可以在 onCreate() 方法下面的类主活动的方法中获取所有视图度量:
这样您就可以重绘、排序...您的视图。 :)
You can get all of view measures in method of class main activity below onCreate() method:
So you can redrawing, sorting... your views. :)
使用
OnPreDrawListener()
而不是addOnGlobalLayoutListener()
,因为它被调用得更早。Use
OnPreDrawListener()
instead ofaddOnGlobalLayoutListener()
, because it is called earlier.