Повторное использование объекта просмотра Android - предотвращает показ старого размера, когда View reappears

EDIT: Еще одна часть, возможно, релевантная информация: Вариант использования, в котором я вижу проблему, - это переключение табуляции. То есть, я создаю представление X на вкладке А, удаляю его, оставляя вкладку А, а затем перерабатываю ее в вкладку В. Это происходит при возникновении проблемы. Это также точно, когда мне нужно увеличение производительности.,.

Я работаю над производительностью своего Android-приложения. Я заметил, что могу ускорить процесс повторного использования объектов вида класса, которые мы будем называть MyLayout. (Это фактически собственный подкласс FrameLayout, но это, вероятно, не имеет значения. Кроме того, это не связанный с ListView.) То есть, когда я закончил просмотр, вместо того, чтобы позволить GC получить его, я положил его в бассейн. Когда тот же самый объект требует другого объекта MyLayout, я хватаю его из пула, если он доступен. Это действительно ускоряет работу приложения. Но мне нелегко очистить информацию о старом размере. В результате, когда я захватываю представление назад, все нормально, но в некоторых случаях новый вид кратко появляется до того, как он будет выложен с новой информацией о размере. Это происходит, даже если я установил новые LayoutParams незадолго до или после добавления представления обратно в иерархию (я пробовал в обоих направлениях, ни один из них не помогает). Таким образом, пользователь видит кратковременную (возможно, 100 мс) вспышку старого размера, прежде чем она вернется к правильному размеру.

Мне интересно, как и как я могу обойти это. Ниже, через С#/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 taken into account screen size and the like; the Model 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);

EDIT: по запросу некоторые из последовательностей, которые я пробовал. Все страдают от одной и той же проблемы. Основная проблема заключается в том, что, хотя 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: использование AddView, которое принимает аргумент layoutParams:

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

Во всех пяти случаях проблема заключается в том, что даже если layoutParams верны, представление становится видимым пользователю до того, как макет настраивается на новые layoutParams.

Ответ 1

Я столкнулся с одной и той же проблемой, кроме встроенного андроида, а не xamarin.

Имея мою собственную тестовую сцену, было намного легче отладить проблему. Кажется, я исправил его, установив справа и слева от определенного вида на 0, сразу после удаления его из родительского элемента и перед добавлением в другое:

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

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

otherLayout.addView(view);

Ответ 2

Как обычно, чтобы обновить положение/расположение любого вида, оно должно быть недействительным().

Не хочу давать вам хакерский способ пройти, но все же я хочу, чтобы вы взглянули на этот пост. Возможно, ваш requestLayout() не вызывает View invalidate.

Также попробуйте добавить android:hardwareAccelerated="true" в манифест.

Ответ 3

Попробуйте выполнить эти события (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); 
            }
        });

Я не уверен, что это работает в вашем случае. Если setLayoutParams или addView считается сообщением во внутренней реализации ОС, он помещает следующее событие в очередь, чтобы оно выполнялось после выполнения предыдущего события.

Ответ 4

Примените Gravity с представлением, с которым связаны эти параметры макета

ИЗМЕНИТЬ

Кроме того, в вашей попытке B вместо this.requestLayout(); вызовите, что на родительском View, который будет getView() для Fragment или Activity контента View... Расскажите ребенку о макете сам из него делает родительский dirty, поэтому он будет вызывать requestLayout() для себя для компоновки, вот что, по моему мнению, является причиной задержки, поскольку они работают последовательно, но если вы сделаете это прямым вызовом все это будет универсальная компоновка, которая устранит задержку

Надеюсь, что это поможет

Ответ 5

Я надеюсь, что это вам поможет:

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