2011-06-28 46 views
4

在我的應用程序中查看性能問題時,我發現每次按下按鈕都會觸發完整的onMeasure()/ layout()循環。我沒有理由再嘗試從頭開始整個應用程序;沒有增加或刪除任何東西,也沒有改變我能看到的尺寸。TextField更改觸發完整佈局週期

當佈局非常擁擠時,問題往往會發生,並且底部的一排按鈕可能超出屏幕邊緣一兩個像素。

有沒有人有這方面的經驗?有什麼方法可以確定爲什麼佈局週期被觸發?

如果屏幕上沒有任何TextField被修改(見Finding the cause of a layout request in a ViewGroup),似乎佈局不會被觸發。修改TextField是否會觸發重新佈局?我可以鎖定它以防止這種情況發生?令人沮喪的是,認爲在屏幕上的任何位置更改任何TextField都會導致整個度量/佈局週期在整個應用程序中級聯;它正在屠殺我的表現。

該容器是一個自定義ViewGroup類,但我不認爲這是問題所在。我沒有看到我可以採取什麼不同的做法來阻止它被調用。

我正在考慮爲我的小部件添加一個「鎖定」方法,以防止初始佈局後發生任何進一步的佈局更改。這會提高性能,但我寧願解決潛在的問題。

這裏是當時的堆棧我onMeasure()方法被調用:

Gridbox.onMeasure(INT,INT)線:217
Gridbox(查看).measure(INT,INT)線:8171
的FrameLayout(ViewGroup中).measureChildWithMargins(查看,INT,INT,INT,INT)線:3132 FrameLayout.onMeasure(INT,INT)線:245
的FrameLayout(查看).measure(INT,INT)線:8171
PhoneWindow $ DecorView(ViewGroup).measureChildWithMargins(View,int,int,int,int)line:3132
PhoneWind流$ DecorView(FrameLayout裏).onMeasure(INT,INT)線:245
PhoneWindow $ DecorView(查看).measure(INT,INT)線:8171
ViewRoot.performTraversals()線:801
ViewRoot.handleMessage(消息)線:1727
的ViewRoot(處理程序).dispatchMessage(消息)線:99 Looper.loop()線:123 ActivityThread.main(字符串[])線:4627
Method.invokeNative(對象,對象[ ZygoteInit $ MethodAndArgsCaller.run():line:不可用[本機方法]
Method.invoke(Object,Object ...)line:521
ZygoteInit $ MethodAndArgsCaller.run()line: 858
ZygoteInit.main(字符串[])線:616 NativeStart.main(字符串[])行:不可用[本機方法]

回答

3

更改一個TextView的內容(它的文本)將觸發重新佈局。然而,這隻會導致部分樹的度量/佈局。如果這種情況不時發生(例如用戶點擊一個按鈕),請不要擔心。

+0

是的,不幸的是,在我的情況下,它發生在*每個按鈕按下並生成整個應用程序的重新佈局。我收到了來自用戶的關於性能的投訴,所以我必須處理它。 –

+0

這裏有兩個問題。 1:按下按鈕不應該觸發*整個*應用程序的重新佈局,該框架可以防止出現這種情況。可能有某些特定於您的應用程序導致此行爲。 2:重新佈置應該不會太長,以至於用戶會注意到它。我不知道您有多少視圖或您的自定義ViewGroup的功能,但似乎應該對您的應用進行配置,以瞭解爲什麼需要這麼長時間。 –

+0

該應用程序是RpnCalc。它有5個TextFields,39個按鈕和30多個標籤。我的自定義視圖組基本上是我自己的GridBag實現。我很樂意與任何想看看它的人分享代碼。我是第一個承認它效率不高的人,我只是很樂意在不需要的時候阻止它被調用。我確實介紹了我的應用該配置文件告訴我,大部分時間都被我的佈局小部件以及所有對onMeasure()調用的各種組件所佔用。 –

1

根據Edward的評論,他設法阻止整個佈局樹的重新佈局在他們自己的LinearLayout中隔離TextViews。這對我來說不起作用。我列出了以下針對任何有同樣問題的人的工作。

當我向Layout添加TextClock(從TextView派生)時,我遇到了類似的重新佈局問題:這會導致整個佈局每分鐘更新一次。

通過將其在自己的LinearLayout或RelativeLayout的使用這個佈局layout_alignParent集並沒有改變完全重新佈局的每一分鐘都固定layout_width /高度和固定定位隔離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」線=「Y」固定寬度和高度X 「最寬」 的字符和ÿ線,當layout_width /高度被設置爲「WRAP_CONTENT」。當然,你也可以在dp(或sp,我會假設)中使用固定尺寸。

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的工作方式是,當任何viewg羣接收佈局請求,它也會調用它的直接父對象的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到onMeasure比率對於您屏幕上的不同視圖會有所不同)。可能很難確定哪個視圖具有最糟糕的onLayout到onMeasure比率,但LinearLayout內部的LinearLayout內的LinearLayout內部的LinearLayout內部的任何視圖都是一個很好的開始位置。

如果onMeasure每個onLayout被調用超過16次,那麼你有問題。嘗試使視圖層次更平坦,使用weigthSum屬性和ScrollViews刪除LinearLayouts。