2011-06-09 54 views
9

回答將視圖添加到ViewGroup時,可以跳過onMeasure?

我有一個RelativeLayout的其中我動態垂直或水平添加視圖當用戶滾動。我已經推出了自己的ViewRecycler,因爲可能有成千上萬的視圖可以組成整個可以滾動的視圖,但我在任何時候都只顯示30個左右。想象一個放大的日曆視圖。

當我添加即將看到的視圖時,我遇到了性能問題,在RelativeLayout上調用onMeasure並將其級聯到onMeasure,從而調用它的所有子視圖。我已經計算出RelativeLayout有多大的計算大小,並且已經在LayoutParameters上設置了它,所以測量ViewGroup並不是必要的,也不需要重新測量已經添加了最終大小和最近添加的視圖增加的觀點對這些觀點沒有影響。

演示此問題的簡單示例是向ViewLayout添加/刪除視圖,並觀察onMeasure被調用,儘管它不影響RelativeLayout的大小或其他視圖的位置。

main.xml中

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/shell" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"> 
    <Button 
     android:id="@+id/button" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content"> 
</LinearLayout> 

MyActivity.java

public class MyActivity extends Activity 
{ 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

     ViewGroup shell = (ViewGroup) findViewById(R.id.shell); 

     final RelativeLayout container = new RelativeLayout(this) { 
      @Override 
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
       super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
       Log.d("MyActvity", "onMeasure called on map"); 
      } 
     }; 
     container.setBackgroundColor(Color.rgb(255, 0, 0)); 
     ViewGroup.LayoutParams containerParams = new ViewGroup.LayoutParams(300, 300); 

     final TextView childView = new TextView(this); 
     childView.setBackgroundColor(Color.rgb(0, 255, 0)); 
     childView.setText("Child View"); 

     Button viewToggle = (Button) findViewById(R.id.button); 
     viewToggle.setText("Add/Remove Child View"); 

     viewToggle.setOnClickListener(new View.OnClickListener() { 

      public void onClick(View view) { 
       if (childView.getParent() == null) { 
        container.addView(childView, 400, 30); 
       } else { 
        container.removeView(childView); 
       } 
      } 
     }); 

     shell.addView(container, containerParams); 
    } 
} 

運行這個,你會看到2初始(預期)調用onMeasure,然後每一個您添加的時間/刪除點擊按鈕查看視圖。這顯然運行良好,但你可以看到,當你有一個複雜的嵌套視圖佈局可能會有問題的地方不斷調用onMeasure。

是否有推薦的方法來繞過這些onMeasure調用或至少onMeasure調用measureChildren?

+1

回答android-developers:http://groups.google.com/group/android-developers/browse_frm/thread/5951029333032455 – CommonsWare 2011-06-09 22:54:02

+0

感謝馬克的交叉鏈接。我用我的更快/更髒的解決方案更新了原始帖子。 – 2011-06-10 15:47:38

+1

你能以一種可以幫助別人的方式回答你自己的問題嗎?只需從您的問題中編輯出「解決方案」,並將其添加爲新答案。一旦你這樣做,你可以選擇你的正確答案。這看起來很奇怪,但它是處理這種情況的首選方式。 – Will 2011-07-15 15:35:35

回答

0

而是我自己的滾動佈局管理器(我仍然可以做在未來)的,我改變了onMeasure到:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    int count = getChildCount(); 
    for (int i = 0; count > i; i++) { 
     View v = getChildAt(i); 

     if (v.getVisibility() != GONE) { 
      if (v.getMeasuredWidth() <= 0 || v.getMeasuredHeight() <= 0) { 
       measureChild(v, 
        MeasureSpec.makeMeasureSpec(v.getLayoutParams().width, 
         MeasureSpec.AT_MOST), 
        MeasureSpec.makeMeasureSpec(v.getLayoutParams().height, 
         MeasureSpec.AT_MOST)); 
      } 
     } 
    } 

    setMeasuredDimension(resolveSize(staticContainerWidth, widthMeasureSpec), 
     resolveSize(staticContainerHeight, heightMeasureSpec)); 
} 

...並增加了一個sudo的硬編碼的高度和寬度容器作爲變量。將這些設置爲您所期望的不在本解決方案的範圍內。

int staticContainerHeight = 300; 
int staticContainerWidth = 300; 
+0

它不適合我,整個視圖甚至不顯示。 :/ – 2012-03-28 14:36:23

+0

使用scrollView作爲父視圖和setFillViewport(true)..應該工作! – AJit 2013-08-12 09:19:01

0

當動畫發生在viewgroup的大小上時,我遇到了類似的問題,它的onMeasure()被非常頻繁地調用。由於父視圖包含許多子視圖,頻繁級聯的onMeasure()調用導致動畫性能呃逆。我有另一個骯髒的解決方案,但比推出我自己的layoutManager更簡單。

long mLastOnMeasurTimestamp; 
... 
@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    ... 
    long currentTimestamp = System.currentTimeMillis(); 
    if(currentTimestamp - mLastOnMeasureTimestamp < SKIP_PERIOD_IN_MILL){ 
     return; 
    } 
    mLastOnMeasureTimestamp = currentTimestamp; 
    ... 
0

我也遇到了類似的問題,我的解決辦法是檢查尺寸發生改變:

int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 
int parentHeight = MeasureSpec.getSize(heightMeasureSpec); 
setMeasuredDimension(parentWidth, parentHeight); 
if (mClientWidth == parentWidth && mClientHeight == parentHeight) { 
    return; 
} 

mClientWidth = parentWidth; 
mClientHeight = parentHeight; 

所以,如果父母的尺寸沒有真正改變,它不會被級聯下到它的孩子。

+2

如果您在初始測量後向該父母添加了一個孩子,新孩子是否會接受測量?似乎不會在這種情況下...或者我忽略了什麼? – 2011-11-09 16:44:41