Використання forceLayout (), requestLayout () та визнання недійсним ()


185

Я трохи розгублений щодо ролей forceLayout(),requestLayout() і invalidate()методів Viewкласу.

Коли вони будуть називатися?

Відповіді:


357

Щоб краще зрозуміти відповіді , надані Франсуа BOURLIEUX і Dalvik я пропоную вам поглянути на цей дивовижний вид діаграми життєвого циклу по Arpit Матура : введіть тут опис зображення


28
Я часто бачу, що виклик layoutLayout відбувається безпосередньо після виклику недійсного, я навіть бачу, що відбувається у вихідному коді Android для таких речей, як TextView, але відповідно до цієї схеми це робити зайвим, правда? То чи є якась мета для цього?
tcox

5
Ну, це цікаве питання, і якщо чесно, я не знаю, чому вони називають обидва способи, наприклад TextView. Я подумав, що, можливо, вони хочуть зробити Viewостанній раз перед тим, як змінити параметри, пов’язані з компонуванням, але це насправді не має сенсу, якщо ми подумаємо про те, що виклики викликаються в іншому порядку (і вони дзвонять invalidate()відразу після requestLayout()в TextViewтакож). Може бути, це заслуговує на інше питання щодо StackOverflow :)?
Бартек Ліпінський

14
(1/2) : Я не думаю, що ви розумієте ці два методи ( invalidate()і requestLayout()) належним чином. Мета цих методів полягає в тому, щоб розповісти, Viewяка інвалідність (як ви її назвали) сталася. Це не так, як Viewвирішувати, який шлях слід пройти після виклику одного з цих методів. Логіка вибору шляху View-lifecycle - вибір правильного методу для виклику себе. Якщо є щось, що стосується зміни розміру - requestLayout()слід викликати, якщо є лише візуальна зміна без зміни розміру - вам слід зателефонувати invalidate().
Bartek Lipinski

11
(2/2): Якщо ви Viewпевним чином зміните розмір свого , наприклад, ви отримуєте струмLayoutParams a Viewі змінюєте їх, але НЕ дзвоните requestLayoutабо setLayoutParams( або дзвінки requestLayoutвнутрішньо), ви можете дзвонити invalidate()скільки завгодно, і Viewне буде проходити через процес вимірювання верстки, тому не буде змінювати його розмір. Якщо ви не говорите ваш , Viewщо його розмір змінився (з requestLayoutвикликом методу), то Viewбудемо вважати , що не зробив, а onMeasureй onLayoutне називатиме.
Bartek Lipinski

2
@tcox більше тут -> stackoverflow.com/questions/35279374 / ...
Sotti

125

invalidate()

Виклик invalidate()проводиться, коли ви хочете запланувати перемальовування перегляду. Це призведе до виклику в onDrawкінцевому підсумку (незабаром, але не відразу). Прикладом того, коли користувацький перегляд називав би це, коли властивість тексту або кольору фону змінилася.

Вид буде перероблений, але розмір не зміниться.

requestLayout()

Якщо щось у вашому перегляді зміниться, що вплине на розмір, то вам слід зателефонувати requestLayout(). Це викличе onMeasureі onLayoutне тільки для цієї точки зору , але все шляху вгору по лінії поглядів батьківських.

Виклик requestLayout()буде не гарантовано результат вonDraw (попри те , що передбачає діаграма в загальноприйнятому відповіді), так що, як правило , в поєднанні з invalidate().

invalidate();
requestLayout();

Прикладом цього є те, коли власна мітка змінила властивість тексту. Етикетка змінила б розмір і, таким чином, потрібно буде переосмислити та переробити.

forceLayout()

Коли requestLayout()в батьківській групі перегляду є те, що викликається, йому не потрібно переоцінювати та ретрансляцію своїх дочірніх поглядів. Однак, якщо дитину слід включити до переоцінки та ретрансляції, то можна зателефонувати forceLayout()на дитину. forceLayout()працює на дитину, лише якщо це відбувається спільно з requestLayout()прямим батьком. Виклик forceLayout()сам по собі не матиме ефекту, оскільки він не запускає requestLayout()дерево перегляду.

Прочитайте цю запитання для більш детального опису forceLayout().

Подальше навчання


1
але чи не було б тоді більше сенсу спочатку виклик requestLayout (), а потім недійсний ()?
scholt

2
@scholt, Наскільки я знаю, замовлення не має значення, тому ви можете зателефонувати requestLayout()раніше, invalidate()якщо захочете. Ні один не робить макет або малювати негайно. Швидше, вони встановлюють прапори, що в підсумку призведе до ретрансляції та перемальовки.
Сурагч

27

Тут ви можете знайти відповідь: http://developer.android.com/guide/topics/ui/how-android-draws.html

Для мене дзвінок invalidate()лише оновлює подання, а виклик - для requestLayout()оновлення подання та обчислення розміру перегляду на екрані.


3
як щодо forceLayout () тоді?
sdabet

@fiddler, цей метод встановлює два прапори: PFLAG_FORCE_LAYOUT та PFLAG_INVALIDATED
suitianshi

7
@suitianshi Які наслідки встановлення прапорців тоді?
Сергій

1
@Sergey Наслідком цього є те, що цей прапор замінює кеш-вимірювання (представлення використовують кеш для швидшого вимірювання в майбутньому для того ж MeasureSpec); дивіться рядок 18783 з View.java тут: github.com/android/platform_frameworks_base/blob/master/core/…
RhetoricalRuvim

3

ви використовуєте Invalidate () для подання, яке ви хочете перемалювати, воно зробить його onDraw (Canvas c) для виклику, а requestLayout () призведе до повторного запуску всього макета (фаза вимірювання та фаза позиціонування). Ви повинні використовувати його, якщо ви змінюєте розмір дитячого подання під час виконання, але лише в окремих випадках, наприклад, обмеження батьківського перегляду (маючи на увазі, що висота або ширина батьків є WRAP_CONTENT, і тому відповідайте вимірюванню дітей, перш ніж вони зможуть їх завернути)


3

Ця відповідь не є правильноюforceLayout() .

Як ви бачите в кодіforceLayout() він просто позначає представлення як "потребує ретрансляції", але він не ані графік, ані запуск цього ретрансляції. Передача не відбудеться до тих пір, поки в якийсь момент батьківський погляд не буде викладений з якоїсь іншої причини.

Існує також набагато більша проблема при використанні forceLayout()та requestLayout():

Скажімо, ви зателефонували forceLayout()на перегляд. Тепер, коли закликає requestLayout()нащадка цього погляду, Android буде рекурсивно закликати requestLayout()предків цього нащадка. Проблема полягає в тому, що вона зупинить рекурсію при погляді, на який ви дзвонили forceLayout(). Таким чином, requestLayout()виклик ніколи не дійде до кореня перегляду і, таким чином, ніколи не планує пропуск макета. Ціле піддерево ієрархії перегляду чекає макета, і виклик requestLayout()будь-якого виду цього піддерева не спричинить макет. Лише заклик requestLayout()до будь-якого виду поза цим піддеревом порушить заклинання.

Я б розглядав реалізацію forceLayout()(і як це впливає requestLayout()на порушення функції, і ви ніколи не повинні використовувати цю функцію у своєму коді.


1
Привіт! Як ви придумали це заклинання ? Це десь задокументовано?
azizbekian

1
На жаль, це не зафіксовано документально і, ймовірно, навіть не передбачається. Я зрозумів це, досліджуючи код Viewі запускаючи проблему сам і налагоджуючи її.
флюїдсон

Тоді мені цікаво мета forceLayout()API: він насправді не примушує пропускати макет, він просто змінює прапор, який спостерігаєтьсяonMeasure() , але onMeasure()не буде викликаний, якщо не буде викликано requestLayout()явний або явний View#measure(). Це означає, що з цим forceLayout()слід поєднуватися requestLayout(). З іншого боку, навіщо тоді виконувати, forceLayout()якщо мені все одно потрібно виступати requestLayout()?
azizbekian

@azizbekian ні, не потрібно. requestLayout()робить все, що forceLayout()також робить.
рідинний звук

1
@Suragch пропустив це, дякую! Дуже цікавий аналіз. Передбачуване використання forceLayoutмає сенс. Отже, врешті-решт, це дуже погано названо та задокументовано.
рідинний звук

0

invalidate()---> onDraw()з потоку інтерфейсу користувача

postInvalidate()---> onDraw()з фонової нитки

requestLayout()---> onMeasure()і onLayout()І НЕ обов'язково onDraw()

  • ВАЖЛИВО : Виклик цього методу не впливає на дитину, що викликається.

forceLayout()---> onMeasure()і onLayout() ТІЛЬКИ ЯКЩО прямий батько називається requestLayout().

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.