在查看我的应用程序中的性能问题时,我发现每次按下按钮都会触发对完整onMeasure()/ layout()循环的调用。没有理由我可以尝试重新布置整个应用程序;没有添加或删除任何内容,并且没有任何内容改变我可以看到的大小。
当布局非常拥挤时,问题往往会发生,并且底部的按钮行可能超出屏幕边缘一两个像素。
有没有人有这方面的经验?有没有办法确定为什么触发了布局周期?
如果屏幕上没有任何TextFields被修改,似乎不会触发布局(参见Finding the cause of a layout request in a ViewGroup)。修改TextField是否总是触发重新布局?我可以以某种方式锁定它以防止这种情况吗?令人沮丧的是,认为在屏幕上的任何地方更改任何TextField都会导致整个测量/布局周期级联整个应用程序;这是在扼杀我的表现。
容器是一个自定义ViewGroup类,但我不认为这是问题所在。我不知道我可以采取哪些不同的方式来阻止它被召唤。
我正在考虑在我的小部件中添加“锁定”方法,以防止在初始布局后进行任何进一步的布局更改。这将提高性能,但我宁愿解决潜在的问题。
这是我调用onMeasure()方法时的堆栈:
Gridbox.onMeasure(int,int)行:217
Gridbox(View).measure(int,int)行:8171
FrameLayout(ViewGroup).measureChildWithMargins(View,int,int,int,int)行:3132
FrameLayout.onMeasure(int,int)行:245
FrameLayout(View).measure(int,int)行:8171
PhoneWindow $ DecorView(ViewGroup).measureChildWithMargins(View,int,int,int,int)行:3132
PhoneWindow $ DecorView(FrameLayout).onMeasure(int,int)行:245
PhoneWindow $ DecorView(View).measure(int,int)行:8171
ViewRoot.performTraversals()行:801
ViewRoot.handleMessage(消息)行:1727
ViewRoot(Handler).dispatchMessage(Message)行:99
Looper.loop()行:123
ActivityThread.main(String [])行:4627
Method.invokeNative(Object,Object [],Class,Class [],Class,int,boolean)line:not available [native method]
Method.invoke(Object,Object ...)行:521
ZygoteInit $ MethodAndArgsCaller.run()行:858
ZygoteInit.main(String [])行:616
NativeStart.main(String [])行:不可用[本机方法]
答案 0 :(得分:5)
有没有人有这方面的经验?有没有办法确定触发布局周期的原因?
有一种方法可以确定触发布局周期的确切原因 转到要调试的屏幕布局,并使用仅覆盖一个方法的自定义容器替换最顶层的容器: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(字体)。
通过恢复程序并等待断点再次触发,您可以快速确定在特定情况下触发重新布局的所有方法。
但请注意,界面滞后几乎总是由多次测量调用引起,而不是多个布局周期!
在复杂的布局(滚动视图,带权重的线性布局等)中,单个布局传递可能需要在单个视图上多次调用onMeasure,有时每个布局周期多达500次。要检查这是否是您的问题,请覆盖其中一个底部视图的onMeasure和onLayout方法(其中一个视图离rootView更远;请注意onLayout to onMeasure ratio对于您屏幕上的不同视图会有所不同) 。可能很难确切地确定哪个视图具有最差的onLayout to onMeasure比率,但是在LinearLayout内的LinearLayout内的LinearLayout内的LinearLayout内部的任何视图都是一个好的起点。
如果onMeasure每次onLayout被调用超过16次,那么你就有问题了。尝试使视图层次结构更加平坦,使用weigthSum属性和ScrollViews删除LinearLayouts。
答案 1 :(得分:3)
更改TextView(其文本)的内容将触发重新布局。但是,这只会导致树的一部分的度量/布局。如果这种情况不时发生(例如当用户点击按钮时),请不要担心。
答案 2 :(得分:1)
根据爱德华的评论,他设法通过隔离自己的LinearLayout中的TextView来阻止整个布局树的重新布局。这对我来说不起作用。我已经在下面列出了对于那些有同样问题的人有用的东西。
当我将TextClock(从TextView派生)添加到我的布局时,我遇到了类似的重新布局问题:这将导致每分钟更新整个布局。
通过将TextClock置于其自身的LinearLayout或RelativeLayout中并使用固定的 layout_width / height 并使用此布局上设置的 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 =&#34; x&#34; 和 lines =&#34; y&#34; 将宽度和高度固定为 x &#34;最宽的&#34;当 layout_width / height 设置为&#34; wrap_content&#34; 时,字符和 y 行。当然你也可以在dp(或sp,我会假设)中使用固定尺寸。