Android视图对象重用 - 在View重新显示时阻止显示旧尺寸

时间:2015-05-30 14:44:45

标签: c# android android-layout layout object-pooling

编辑:还有一条可能相关的信息:我看到问题的用例是制表切换。也就是说,我在选项卡A上创建视图X,在离开选项卡A时将其删除,然后将其回收到选项卡B.当问题发生时。这也正是我需要性能提升的时候。 。

我正在研究Android应用的性能。我注意到我可以通过重用我们称之为MyLayout的类的View对象来加快速度。 (它实际上是一个自定义的FrameLayout子类,但这可能并不重要。而且,这与ListView无关。)也就是说,当我完成View时,而不是让GC获取看哪,我把它放进了游泳池。当同一活动想要另一个MyLayout对象时,我从池中抓取一个,如果可用的话。这确实加快了应用程序的速度。但我很难清除旧尺寸信息。结果是当我抓回View时,事情通常很好,但在某些情况下,新视图会在使用新的大小信息布局之前短暂出现。即使我在将View添加回层次结构之前或之后不久设置了新的LayoutParams,也会发生这种情况(我已经尝试了两种方式;两者都没有帮助)。因此,在尺寸达到正确尺寸之前,用户会看到旧尺寸的短暂(可能是100毫秒)闪光灯。

我想知道是否/如何解决这个问题。下面,通过C#/ Xamarin,我尝试了一些东西,但没有一个帮助:

回收时:

//Get myLayoutParams, then:
myLayoutParams.Width = 0;
myLayoutParams.Height = 0;
this.SetMeasuredDimension(0, 0);
this.RequestLayout();

在恢复之前或之后 - 在将布局添加到其新父级的相同事件循环中:

// a model object has already computed the desired x, y, width, and height
// It's taken into account screen size and the like; the Model's sizes
// are definitely what I want.
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams (model.width, model.height);
layoutParams.LeftMargin = model.x;
layoutParams.TopMargin = model.y; 
this.LayoutParameters = layoutParams;

我也试过像以下那样把它带回来,但问题仍然存在:

FrameLayout.LayoutParams layoutParams = . . .  // the same LayoutParams as above
parent.AddView(viewThatIsBeingRecycled, layoutParams);
编辑:根据请求,我尝试了一些序列。所有人都遇到同样的问题。基本问题是即使LayoutParams是正确的,布局本身也不正确,因为实际的布局尚未发生。

回收时间:

尝试A:

this.RemoveFromHierarchy();
// problem is that the width and height are retained

尝试B:

//Get myLayoutParams, then:
myLayoutParams.Width = 0;
myLayoutParams.Height = 0;
this.SetMeasuredDimension(0, 0);
this.RequestLayout();
this.RemoveFromHierarchy();
//problem is that even though layout has been requested, it does not actually happen.  
//Android seems to decide that since the view is no longer in the hierarchy,
//it doesn't need to do the actual layout.  So the width and height
//remain, just as they do in attempt A above.

添加视图时:

所有尝试都会调用以下子例程之一将LayoutParams同步到模型:

public static void SyncExistingLayoutParamsToModel(FrameLayout.LayoutParams layoutParams, Model model) {
  layoutParams.TopMargin = model.X;
  layoutParams.LeftMargin = model.Y;
  layoutParams.Width = model.Width;
  layoutParams.Height = model.Height;
}

public static FrameLayout.LayoutParams CreateLayoutParamsFromModel(Model model) {
  FrameLayout.LayoutParams r = new FrameLayout.LayoutParams(model.Width, model.Height);
  r.LeftMargin = x;
  r.TopMargin = y;
  return r;
}

尝试A:

newParent.AddView(viewThatIsBeingRecycled);
// get layoutParams of the view, then:
SyncExistingLayoutParamsToModel(myLayoutParams, model);

尝试B:与A相同,但顺序相反:

// get layoutParams of the view, then:
SyncExistingLayoutParamsToModel(myLayoutParams, model);
newParent.AddView(viewThatIsBeingRecycled);

尝试C:与A相同,但有新的layoutParams:

newParent.AddView(viewThatIsBeingRecycled);
FrameLayout.LayoutParams layoutParams = CreateLayoutParamsFromModel(model);
viewThatIsBeingRecycled.LayoutParams = layoutParams;

尝试D:与B相同,但使用新的layoutParams:

FrameLayout.LayoutParams layoutParams = CreateLayoutParamsFromModel(model);
viewThatIsBeingRecycled.LayoutParams = layoutParams;
newParent.AddView(viewThatIsBeingRecycled);

尝试E:使用带有layoutParams参数的AddView:

FrameLayout.LayoutParams layoutParams = CreateLayoutParamsFromModel(model);
newParent.AddView(viewThatIsBeingRecycled, layoutParams);

在所有五种情况下,问题是即使layoutParams是正确的,在布局将自身调整为新的layoutParams之前,视图也会对用户可见。

5 个答案:

答案 0 :(得分:2)

我遇到了完全相同的问题,除了原生的android,而不是xamarin。

拥有自己的测试场景,使调试问题变得更容易。我似乎通过将特定视图的右侧和左侧设置为0来修复它,只是在将其从父级中删除之后再添加到另一个之前:

((ViewGroup)view.getParent()).removeView(view);

view.setRight(0);
view.setLeft(0);

otherLayout.addView(view);

答案 1 :(得分:2)

尝试在消息队列中执行这些事件(Types.directSupertypes()setLayoutParams)。

解决方案1)

addView

解决方案2)

FrameLayout.LayoutParams layoutParams = CreateLayoutParamsFromModel(model);
viewThatIsBeingRecycled.setLayoutParams(layoutParams);
viewThatIsBeingRecycled.post(new Runnable() {
            @Override
            public void run() {
                newParent.AddView(viewThatIsBeingRecycled);
            }
        });

我不确定这是否适用于您的情况。如果FrameLayout.LayoutParams layoutParams = CreateLayoutParamsFromModel(model); viewThatIsBeingRecycled.setLayoutParams(layoutParams); viewThatIsBeingRecycled.setVisibility(View.INVISIBLE); newParent.AddView(viewThatIsBeingRecycled); newParent.post(new Runnable() { @Override public void run() { viewThatIsBeingRecycled.setVisibility(View.VISIBLE); } }); setLayoutParams被认为是内部OS实现中的消息,那么它会将下一个事件放入队列中,以便在执行上一个事件后执行该事件。

答案 2 :(得分:2)

Gravity 应用于与这些布局参数相关联的视图

修改

同样在您的尝试B中,而不是this.requestLayout();在父View上调用getView()Fragment内容Activity { {1}} ...告诉孩子将自己布局为父View,因此它会调用dirty为自己布局,这就是我认为延迟的原因 - 因为它们以连续的方式运行,但如果你直接调用所有它将是通用布局,这将消除延迟

希望有所帮助

答案 3 :(得分:2)

我希望它会对你有所帮助:

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
   @Override
   public void onGlobalLayout() {
       //set layout params here
   }
 });

答案 4 :(得分:1)

像往常一样,要更新任何视图的位置/排列,它必须是无效的()。

不想让你以破解方式通过,但我还是希望你看一下this帖子。 您的requestLayout()可能导致视图无效。

还可以尝试在清单中添加android:hardwareAccelerated="true"