12

编辑:还有一条可能相关的信息:我看到问题的用例是选项卡切换。也就是说,我在选项卡 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 之前视图对用户可见。

4

5 回答 5

2

我遇到了完全相同的问题,除了在本机 android 上,而不是在 xamarin 上。

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

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

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

otherLayout.addView(view);
于 2015-06-02T13:13:05.650 回答
2

尝试在消息队列中执行这些事件(setLayoutParams和)。addView

解决方案1)

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

解决方案2)

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); 
            }
        });

我不确定这是否适用于您的情况。如果setLayoutParamsaddView被认为是内部操作系统实现中的消息,则它将下一个事件放入队列中,以便在执行前一个事件后执行。

于 2015-06-03T10:09:43.127 回答
2

与这些布局参数关联的视图一起应用Gravity

编辑

同样在您的尝试B中,而不是在将属于或内容this.requestLayout();的父级上调用它...告诉孩子将自己布局出来使父级,因此它会要求自己布局出来,这就是我的想法是延迟的原因-因为它们以连续的方式运行,但是如果您直接调用它,它将是通用布局,这将消除延迟ViewgetView()FragmentActivityViewdirtyrequestLayout()

希望能帮助到你

于 2015-06-04T01:15:03.613 回答
2

我希望它会帮助你:

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
   @Override
   public void onGlobalLayout() {
       //set layout params here
   }
 });
于 2015-06-09T08:18:22.843 回答
1

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

不想给你黑客的方式来通过,但我仍然希望你看看这篇文章。您可能requestLayout()不会导致 View 无效。

也尝试添加android:hardwareAccelerated="true"清单。

于 2015-06-03T09:17:07.400 回答