- Русские Блоги
- [Android] getWidth () и getMeasuredWidth ()
- Ссылка ссылка:
- Предисловие:
- 1. В чем разница между getWidth () и getMeasuredWidth ()?
- 1. Оригинальное введение на официальном сайте
- 2. Многие люди в Интернете пришли к выводу:(На самом деле, это неточно или неправильно)
- 3. Как доказать, что приведенная выше точка зрения неверна?
- Во-вторых, анализ исходного кода:
- 1. Исходный код GetMeasuredWidth ():
- 2. Исходный код getWidth ():
- 3. Резюме:
- В-третьих, вручную получить ширину и высоту вида
- 1. Увеличьте общий макет мониторинга
- 2. Добавить мониторинг перед отрисовкой компонента
- 3. Не указывая ширину и высоту родительского макета, вручную вызовите measure ()
- 4. Задержка, получить ширину и высоту, когда представление добавляется в представление
- 4. Другое
- анализ:
- Анимируем RecyclerView легко без перехода на ViewPager2
Русские Блоги
[Android] getWidth () и getMeasuredWidth ()
Ссылка ссылка:
Предисловие:
Когда можно получить getWidth () и getMeasuredWidth ()? Почему я не могу получить ширину часто? Ширина равна 0?
1. В чем разница между getWidth () и getMeasuredWidth ()?
1. Оригинальное введение на официальном сайте
2. Многие люди в Интернете пришли к выводу:(На самом деле, это неточно или неправильно)
Когда экран может отображать полные элементы управления, то есть когда содержимое может быть перенесено, значения, полученные с помощью getMeasuredWidth () и getWidth (), равны.
Когда представление превышает экран, вы можете увидеть разницу:
- getMeasuredWidth() Метод получает фактическую высоту просмотра независимо от экрана.
- getWidth() Полученная ширина зависит от экрана. Если элемент управления превышает экран, метод getWidth () может получить только ширину элемента управления, отображаемого на экране. Когда элемент управления превышает ширину экрана, ширина, полученная элементом управления с помощью метода getMeasuredWidth () = ширина, полученная с помощью getWidth () + ширина элемента управления за пределами экрана
3. Как доказать, что приведенная выше точка зрения неверна?
Идея: поскольку при вызове метода onWindowFocusChanged () элемент управления был измерен, и можно получить ширину и высоту элемента управления, чтобы непрерывно изменять ширину и высоту элемента управления до тех пор, пока он не превысит ширину экрана, и непрерывно печатать getMeasuredWidth () и getWidth () стоимость. код шоу, как показано ниже:
Код файла макета:
В это время в журнале журнала выводится следующая информация: видно, что значения getWidth () и getMeasuredWidth () совпадают
Затем установите значение android: layout_height в указанном выше файле макета равным 1000dp, за пределами экрана результат печати будет следующим:
Во-вторых, анализ исходного кода:
Сначала пришли к выводу:
- getWidth():Должен быть onLayout() После того, как метод выполнен, ширина может быть получена;
- getMeasuredWidth(): Получить измеренную ширину, покаonMeasure() После того, как метод выполнен, вы можете использовать его, чтобы получить ширину, используйте этот метод больше в пользовательском элементе управления, используйтеview.measure(0,0) Метод может активно информировать систему измерения, а затем его можно использовать непосредственно для получения ширины и высоты.
- getMeasureWidth(): Значение в методе устанавливается методом setMeasuredDimension () (то есть измеренная ширина и высота представления, которое является фактической высотой элемента управления, не имеет отношения к экрану), а значение в методе getWidth () определяется как Координаты правой стороны вида вычисляются путем вычитания координат левой стороны (то есть конечная ширина и высота вида, то есть высота отображения, относятся к отображению вида на экране).
затемgetMeasuredWidth() с участием getWidth() Какая разница?
1. Исходный код GetMeasuredWidth ():
Смотрите здесь его возвращаемое значениеmMeasuredWidth,Вот этот mMeasuredWidth Откуда это? это setMeasuredDimension() Параметры, передаваемые методом, посмотритеsetMeasuredDimension() Исходный код метода:
Как видите, в звонилкеsetMeasuredDimension() Метод, наборmeasuredWidth Прямо здесьmMeasuredWidth, Который нашgetMeasuredWidth() Возвращаемое значение метода, здесь мы будем пониматьmeasure() После окончания методаgetMeasuredWidth() Метод будет иметь ценностную причину.
2. Исходный код getWidth ():
Найдено, что его возвращаемое значениеmRight — mLeft,Вот mRight с участием mLeft Да layout() Два из четырех параметров, переданных процессом:
Позвонил вsetFrame() Метод, его исходный код выглядит следующим образом:
Здесь мы нашлиgetWidth() Два параметра возвращаемого значения метода: ①mRight ②mLeft, так getWidth() Метод находится вlayout() Значение только после завершения метода.
3. Резюме:
Так что при настройке элементов управления,onLayout() Обычно используется в методеgetMeasuredWidth() Чтобы получить ширину элемента управления, потому чтоgetMeasuredWidth() Вmeasure() После значения иgetWidth() Вlayout() Только тогда имеет значение. Хотя в дополнениеonLayout() Используется в методеgetMeasuredWidth() В дополнение к методу, обычно используется в других местахgetWidth() Метод, чтобы получить ширину элемента управления.
В общем, значения getMeasuredWidth () и getWidth () совпадают. Рассмотрим это под другим углом, потому что в layout (), как правило, это левый от дочернего View + child View.getMeasuredWidth () для определения правой границы вправо, а getWidth () — правого левого, так что, вообще говоря, дочерний вид getWidth () = = getMeasuredWidth ()
В-третьих, вручную получить ширину и высоту вида
В жизненном цикле действия onCreate (), onStart (), onResume (), чтобы попытаться получить ширину и высоту, он часто возвращает значение 0, только чтобы получить ширину и высоту в onWindowFocusChanged (), потому что представление было нарисовано.
Поэтому, если мы хотим получить большую высоту в функции жизненного цикла, нам нужно выполнить некоторые дополнительные операции. Некоторые общие идеи теперь организованы следующим образом:
1. Увеличьте общий макет мониторинга
2. Добавить мониторинг перед отрисовкой компонента
3. Не указывая ширину и высоту родительского макета, вручную вызовите measure ()
4. Задержка, получить ширину и высоту, когда представление добавляется в представление
4. Другое
Перепишите onMeasure () в пользовательском элементе управления, а затем напрямую вызовите super.onMeasure (widthMeasureSpec, widthMeasureSpec), чтобы измерить процесс в соответствии с процессом по умолчанию. Если вы вызовете getMeasuredWidth () в методе onWindowFocusChanged () таким образом, вы обнаружите, что значение равно 0? Зачем?
Поскольку вызывается метод super.onMeasure (widthMeasureSpec, widthMeasureSpec), в методе onMeasure () вызывается метод setMeasuredDimension (). Если mMinWidth не задано, значением по умолчанию для параметра width является 0, поэтому возвращаемым методом метода getMeasuredDimension () является 0, поэтому мы знаем, что если вы хотите получить значение через getMeasuredWidth (), вы должны измерить его, то есть вы можете вызвать метод setMeasuredDimension () через путь, в противном случае значение getMeasuredWidth () в общем случае равно 0.
анализ:
Процесс measure () в представлении завершается методом measure (). Метод measure () является конечным типом, и подклассы не могут быть переопределены. Метод measure () в представлении вызывает метод onMeasure () и анализирует метод onMeasure (). Исходный код выглядит следующим образом:
Приведенный выше метод очень прост: он устанавливает значение измерения View для измерения высоты и ширины, и это значение измерения получается методом getDefaultSize ():
При условии UNSPECIFIED передается значение по умолчанию для измеренной высоты и ширины вида.getSuggestedMinimumWidth() с участием getSuggestedMinimumHeight() Приобретение функции, эти два метода имеют один и тот же принцип, посмотритеgetSuggestedMinimumHeight() Исходный код:
Приведенный выше код можно увидеть,Если у вида нет фона, его высота равна mMinHeight. Этот mMinHeight управляется свойством android: minHeight, которое может быть 0. Если фон есть, он возвращает максимальное значение между mMinHeight и минимальной высотой фона.
Источник
Анимируем RecyclerView легко без перехода на ViewPager2
Когда мы работаем с коллекциями и их отображением, перед многими из нас часто
встает выбор между ViewPager (теперь ещё и ViewPager2 ) и RecyclerView . Эти
компоненты похожи друг на друга по области применения, но серьезно отличаются
интерфейсом и реализацией. Начиная с support library 24.2.0 границы между
данными компонентами стали ещё более размытыми, т.к. появился вспомогательный
класс SnapHelper для автоматического доведения сhildView до
определенного положения на экране, и без устаревшего ViewPager стало проще
обходиться. С недавним релизом ViewPager2 , казалось бы, о старом ViewPager и о
практиках его имитации вообще можно забыть ( ViewPager2 — это по сути
RecyclerView с дополнительными вспомогательными классами, он позволяет
практически идентично повторить поведение ViewPager и сохраняет совместимость со
старым api).
Так ли это на самом деле? Лично для меня всё оказалось не так просто. Во-первых,
в классическом RecyclerView отсутствует интерфейс PageTransformer для
анимирования сhildView в зависимости от позиции (далее по тексту используется
понятие «позиционная анимация»). Во-вторых, неприятными сюрпризами долгожданного
ViewPager2 оказались модификатор класса final , который ставит крест на
переопределении метода onInterceptTouchEvent (компонент мало пригоден для
вложения горизонтальных списков в вертикальные), и приватность поля
recyclerView .
Итак, столкнувшись в очередной раз с трудностями позиционной анимации при
отображении коллекций с помощью RecyclerView и поковырявшись в ViewPager2 и
MotionLayout , я подумал, что позаимствовать принцип работы
ViewPager.PageTransformer для классической реализации RecyclerView а-ля
ViewPager2 не самая плохая идея.
Задача, в контексте которой это затевалось, была достаточно необычной:
- сделать компонент для отображения коллекции в горизонтальном и вертикальном
представлении - горизонтальный список должен повторять поведение ViewPager и пролистываться
со sticky-эффектом - при движении нижней “шторки” ( BottomSheetBehavior ) должна происходить
анимированная трансформация ориентации списка — выпадение элементов лесенкой - должна быть возможность выбора элемента из вертикального списка с
анимированным сдвигом остальных элементов влево и последующим превращением
вертикального списка в горизонтальный.
Запутались? Вот вам пример такого горизонтально-вертикального списка в
интернет-банкинг приложении «Мой кредит» Банка Хоум Кредит:
1. Делаем компонент для анимированного списка
Сам компонент было решено спроектировать как наследника ConstraintLayout с двумя
recyclerView внутри. Прогресс анимации берется из BottomSheetCallback нашего
BottomSheetBehavior :
У компонента есть метод transformation , принимающий прогресс “превращения” как
аргумент.
2. Решаем проблему позиционного анимирования
Реализацию анимации я начал с интерфейса для позиционной анимации — аналога
ViewPager.PageTransformer.
Идея была такой — есть абстрактный класс ItemViewTransformer с абстрактным
методом transformItemView , повторяющим сигнатуру метода transformPage интерфейса
ViewPager.PageTransformer :
В attachToRecycler происходит инициализация свойства recyclerView и слушателей
RecyclerView.AdapterDataObserver и RecyclerView.OnScrollListener :
После вызова updatePosition происходят вызовы метода transformItemView для
каждого видимого childView с передачей в аргументы текущей позиции относительно
левого края (такой расчет позиции справедлив только для childViews одинаковой
ширины/высоты с учетом отступов):
Начиная с этого момента, вы можете взять, например, свой старый добрый
pageTransformer , на который вы когда-то потратили так много времени, и
переиспользовать его код для recyclerView .
3. Sticky-эффект горизонтального списка
В примере выше sticky-эффект реализован как раз с помощью этого абстрактного
класса. В имплементации метода размер и положение описываем простейшими
кусочными функциями:
Аттачим наш BouncingTransformer сразу после инициализации адаптера и
recyclerView:
После компиляции мы получим пролистывание списка со sticky-эффектом:
В общем-то, ничто не мешает анимировать список, например, как колоду карт, и не
придется прибегать даже к помощи ItemDecoration .
4. Делаем универсальный прогресс-аниматор
Следующим этапом был реализован аналогичный интерфейс для выполнения
value-анимации с абстрактным методом onUpdate :
Здесь attachToRecycler инициализирует лишь свойство recyclerView :
Методом update задается прогресс анимации для видимой позиции или позиции
адаптера:
5. Трансформация списка
Теперь мы можем описать трансформацию “лесенкой” вертикального recyclerView в
имплементации метода onUpdate :
Аттачим точно так же, как предыдущий класс, прогресс берем из
BottomSheetCallback . После компиляции увидим следующее:
6. Эффект разбегания для горизонтального списка
Этой трансформации должен предшествовать эффект “разбегания” вьюшек в
горизонтальном списке:
Получаем желаемое поведение:
7. Анимация выбора элемента в вертикальном списке
При выборе элемента в вертикальном списке должна произойти анимированная
трансформация в горизонтальный список с пресетом в нем выбранного ранее
childView . Для достижения нужного эффекта удаляем остальные элементы из
вертикального адаптера. Анимацию удаления элементов реализуем на свой вкус, либо
нашим ItemViewAnimatorUpdater :
Либо классически с помощью ItemAnimator :
Получаем похожее поведение:
Завершаем трансформацию списка выездом правого видимого элемента горизонтального
списка:
После компиляции видим:
8. Результаты
По большому счету на этом реализация анимации завершена. Что в итоге мы
получили:
- отказались от использования ViewPager, что, как минимум, обещает лучшую
консистентность адаптеров, а значит и более удобный data building - получили преимущества ViewPager.PageTransformer — удобное позиционное
анимирование - сохранили преимущества RecyclerView — более экономное потребление ресурсов,
гибкую систему нотификации изменений адаптера, ItemAtimator, ItemDecoration - не добавляли новые зависимости в проект
- реализовали достаточно сложную анимационную последовательность на основе
всего двух новых абстрактных классов.
Надеюсь, эта статья окажется для вас полезной. Удачи!
Источник