Повторное использование объекта представления Android - предотвращение появления старого размера при повторном появлении представления
РЕДАКТИРОВАТЬ: Еще одна часть, возможно, актуальной информации: вариант использования, в котором я вижу проблему, это переключение вкладок. То есть я создаю представление X на вкладке A, удаляю его, когда покидаю вкладку A, затем перерабатываю его на вкладку B. Именно тогда возникает проблема. Это также точно, когда мне нужно увеличение производительности.,,
Я работаю над производительностью моего приложения для Android. Я заметил, что могу ускорить процесс, повторно используя объекты View класса, который мы будем называть MyLayout. (На самом деле это собственный подкласс FrameLayout, но это, вероятно, не имеет значения. Кроме того, это НЕ связано с ListView.) То есть, когда я закончу с View, вместо того, чтобы позволить GC получить его, я помещаю его в бассейн. Когда та же активность хочет другой объект MyLayout, я беру его из пула, если он доступен. Это действительно ускоряет приложение. Но мне трудно очистить информацию о старом размере. В результате, когда я возвращаю вид, все в порядке, но в некоторых случаях новый вид появляется на короткое время, прежде чем он будет представлен новой информацией о размере. Это происходит, даже если я установил новые 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 и являются правильными, сам макет не является корректным, поскольку фактический макет еще не произошел.
Время переработки:
попытка А:
this.RemoveFromHierarchy();
// problem is that the width and height are retained
попытка Б:
//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;
}
Попытка А:
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, но со свежим макетом Params:
newParent.AddView(viewThatIsBeingRecycled);
FrameLayout.LayoutParams layoutParams = CreateLayoutParamsFromModel(model);
viewThatIsBeingRecycled.LayoutParams = layoutParams;
Попытка D: такая же, как B, но со свежим макетом. Параметры:
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.
5 ответов
Применять Gravity
с видом, с которым связаны эти параметры макета
РЕДАКТИРОВАТЬ
Также в вашей попытке B, а не this.requestLayout();
позвони на Родителя View
это будет getView()
из Fragment
или Activity
содержание View
... Говоря ребенку, чтобы он сам делал макет, он становится родителем dirty
следовательно, это будет называть requestLayout()
для себя, чтобы планировать, это - то, что я думаю, является причиной задержки - так как они работают последовательно, но если вы сделаете это прямым вызовом все, это будет универсальное расположение, которое устранит задержку
Надеюсь, поможет
Как обычно, чтобы обновить позицию / расположение любого представления, оно должно быть недействительным ().
Не хочу давать вам хакерский способ пройти, но все же я хочу, чтобы вы взглянули на этот пост. Вполне возможно, что ваш requestLayout()
не вызывает вид недействительным.
Также попробуйте с добавлением android:hardwareAccelerated="true"
в манифесте.
Я столкнулся с точно такой же проблемой, кроме как на родном андроиде, а не на xamarin.
Наличие моей собственной тестовой сцены значительно облегчило решение проблемы. Я, кажется, исправил это, установив правый и левый от определенного представления к 0, только после удаления его от его родителя и перед добавлением к другому:
((ViewGroup)view.getParent()).removeView(view);
view.setRight(0);
view.setLeft(0);
otherLayout.addView(view);
Я надеюсь, что это поможет вам:
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//set layout params here
}
});
Попробуйте выполнить эти события (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
во внутренней реализации ОС считается сообщением, а затем помещает следующее событие в очередь, так что оно будет выполнено после выполнения предыдущего события.