TextField更改触发整个布局循环



在查看应用程序中的性能问题时,我发现每次按下按钮都会触发对整个onMeasure()/layout()循环的调用。我看不出有什么理由试图重新布局整个应用程序;没有添加或删除任何内容,也没有改变我所看到的大小。

当布局非常拥挤时,问题往往会发生,并且底部的一排按钮可能超出了屏幕边缘一两个像素。

有人有这方面的经验吗?是否有任何方法可以确定触发布局周期的原因?

如果屏幕上的TextFields都没有被修改,布局似乎不会被触发(请参阅在ViewGroup中查找布局请求的原因)。修改TextField是否总是触发重新布局?我能把它锁起来防止这种情况发生吗?令人沮丧的是,在屏幕上的任何地方更改任何TextField都会导致整个测量/布局循环在整个应用程序中级联;这毁了我的表演。

容器是一个自定义ViewGroup类,但我认为这不是问题所在。我看不出我能做些什么不同的事情来阻止它被召唤。

我正在考虑在我的小部件中添加一个"锁定"方法,以防止在初始布局之后发生任何进一步的布局更改。这将提高性能,但我宁愿解决根本问题。

以下是调用onMeasure()方法时的堆栈:

Gridbox.onMeasure(int,int)行:217
网格框(视图).measure(int,int)行:8171
FrameLayout(ViewGroup).measureChildWithMargins(View,int,int,整型)行:3132FrameLayout.onMeasure(int,int)行:245
FrameLayout(View).measure(int,int)line:8171
PhoneWindow$DecorView(ViewGroup).measureChildWithMargins(View,int,int,整型)行:3132
PhoneWindow$DecorView(FrameLayout).onMeasure(int,int)行:245
PhoneWindow$DecorView(View).measure(int,int)line:8171
ViewRoot.performTraversals()行:801
ViewRoot.handleMessage(消息)行:1727
ViewRoot(处理程序).dispatchMessage(消息)行:99Looper.loop()行:123ActivityThread.main(String[])行:4627
Method.invokeNative(Object,Object[],Class,Class[],Class、int,boolean)行:不可用[native Method]
方法.调用(Object,Object…)行:521
ZygoteInit$MethodAndArgsCaller.run()行:858
ZygoteInit.main(String[])行:616NativeStart.main(String[])行:不可用[本地方法]

有人有这方面的经验吗?是否有任何方法可以确定触发布局周期的原因?

有一种方法可以确定触发布局循环的确切原因
转到要调试的屏幕布局,用只覆盖一个方法的自定义容器替换最上面的容器:requestLayout()。

例如,如果你的布局看起来像这样:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- omitted for brevity -->
</android.support.v4.widget.DrawerLayout>

您首先需要创建一个新的自定义类,它是容器类的子类:

public class RootView extends DrawerLayout {
    public RootView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public void requestLayout() {
        super.requestLayout();
    }
}

然后你需要修改你的xml:

<?xml version="1.0" encoding="utf-8"?>
<com.example.RootView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- omitted for brevity -->
</com.example.RootView>

现在,android的工作方式是,当任何视图组收到布局请求时,它也会调用其直接父级的requestLayout()。这意味着,每当在屏幕内进行任何requestLayout()调用时,RootView的requestLayout)也会被调用。

因此,启动一个调试会话,在RootView.requestLayout()方法中放置一个断点,然后执行一个您认为会导致布局循环的操作。看看堆栈跟踪。它总是这样:

RootView.requestLayout() line: 15   
RelativeLayout(View).requestLayout() line: 17364    
RelativeLayout.requestLayout() line: 360    
FrameLayout(View).requestLayout() line: 17364   
... a dozen of other calls to requestLayout() ...
TimeCell(View).requestLayout() line: 17364  
TextViewPlus(View).requestLayout() line: 17364  
TextViewPlus(TextView).setTypeface(Typeface) line: 2713 
... more methods ...

第一个不是requestLayout()的方法是导致布局循环发生的原因
在上面的例子中,它是TextViewPlus.setTypeface(Typeface).

通过恢复程序并等待断点再次触发,您可以快速确定在特定情况下触发重新连接的所有方法。

但是,请注意,接口滞后几乎总是由多个度量值调用引起的,而不是由多个布局周期引起的

在复杂的布局(滚动视图、带权重的线性布局等)中,单个布局过程可能需要在单个视图上多次调用onMeasure,有时每个布局周期最多调用500次,甚至更多。若要检查这是否是您的问题,请覆盖其中一个底部视图(离rootView更远的视图之一;请注意,屏幕上不同视图的onLayout与onMeasure的比例不同)的onMeasure和onLayout方法。可能很难准确确定哪个视图的onLayout与onMeasure之比最差,但任何位于LinearLayout内部的视图。。。是一个很好的起点。

如果每个onLayout调用onMeasure超过16次,那么您就有问题了。尝试使视图层次结构更加平坦,删除具有weightSum属性的LinearLayouts和ScrollViews。

更改TextView(其文本)的内容将触发重新发布。然而,这只会导致对树的一部分进行测量/布局。如果这种情况只是偶尔发生(例如,当用户单击按钮时),请不要担心。

p>根据Edward的评论,他通过在自己的LinearLayout中隔离TextViews来防止整个布局树的重新布局。这对我不起作用。我在下面列出了对有同样问题的人起作用的方法。

当我在布局中添加TextClock(源自TextView)时,我遇到了类似的重新布局问题:这将导致每分钟更新一次整个布局。

通过将TextClock放在自己的LinearLayout或RelativeLayout中并同时设置固定的layout_width/height和使用此layout上设置的layout_alignParent进行固定定位来隔离TextClock,并没有每分钟改变完整的重新布局有帮助的是简单地固定TextClock的大小,即独立于其实际内容:

    <TextClock
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:ems="4"
    android:lines="1"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true"
    />

其中ems="x"lines="y"fix width and height tox"width"characters andylines,当layout_width/height设置为"wrap_content"

最新更新