Pull to refresh
15
0
Андрей Куликов @andkulikov

Jetpack Compose at Google

Send message
Точно так же как в ImageView нельзя передать url точно так же это не задача Image компонента в Compose. Но библиотеки типа условного Glide смогут точно так же очень легко создать условный GlideImage(url: String), который сначала отобразит плейсхолдер, потом пойдет загружать картинку.
7) Modifier достаточно объемное решение. мы внутри команды тоже все еще обсуждаем все и взвешиваем. в итоге точно не останется оба варианта, только один будет рекомендованный.
10) Для этой задачи у нас есть специальный вид листа. Он создается через метод modelListOf(). все изменения в нем точно так же вызывают обновление
11) Есть сотни архитектур и подходов. В идеале Compose UI это просто юай слой и он должен работать с любым подходом. На данный момент мы сконцентрированы на этой части. Интеграция с конкретными архитектурами может решаться по разному. Например, как вы заметили, можно прямо модель презентера пометить @Model. Но не все захотят добавлять аннотации к моделям слоя данных и хранить их мутабельными. В этом случае нужны будут какие то потоки данных на которых Compose будет подписываться
Привет! Я работаю над Jetpack Compose и хотелось прокоментировать несколько замечаний из статьи.
Во первых, спасибо! Очень подробный и интересный разбор. Приятно что в проекте вам оказалось достаточно легко сориентироваться даже при отсутствии официальной документации. И рад что наши сэмплы помогают.
1) Могу подтвердить то, что ответил sergeyfitis. Версия dev01 это не официальный релиз, а откатка процесса релиза для такого большого проекта. Jetpack Compose все еще требует специальную версию студии, AGP и котлина.
2) Минимальная поддерживаемая версия такой и останется — 21. Мы завязываемся на некоторые апи которые появились в этой версии. Плюс проект еще довольно далек от стейбл версии, надеюсь к тому моменту все больше приложений перейдут на данный minSdkVersion.
3) Автоматическое подхватывание темы из андроид стилей еще не существует. Мы еще рассматриваем как это лучше всего решить. В том числе планируется поддержка dark mode из коробки (тоже еще не до конца реализовано)
4) Чтобы вручную не проставлять +themeColor { onBackground } на каждый Text можно обернуть весь «экран» в Surface {… }, тогда весь текст автоматически перекрасится в onSurface из темы. или же можно использовать Surface(color = +themeColor { background }) {… }, тогда текст перекрасится в onBackground. Думаю логика понятна. Еще до конца не решили будем ли автоматически закрашивать фон в background с применением текста onBackground
5) Тестирование на основе View и их айди перестанет работать, да. Работаем над новым апи для тестирования Compose Ui. Можно посмотреть примеры здесь: android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/ui/ui-material/src/androidTest/java/androidx/ui/material/CheckboxUiTest.kt
6) Все функции вызывающие другие @Composable функции будет необходимо тоже помечать @Composable. Аналогично с тем как работают suspend функции. В дальнейшем такой код не помеченный аннотацией будет ошибкой компиляции.
7) «свойства для виджетов превратились в отдельные виджеты (Center вместо android:gravity, Padding вместо android:margin, …)» — это еще не финальное решение. На данный момент мы изучаем возможность вместо них использовать идею layout modifier. Тут есть примеры: android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/PaddingSample.kt
8) «У кнопки параметр onClick сделан не последним». На данный момент это сделано умышлено чтобы можно было отличить несколько вариантов Button. У этой функции есть несколько перегрузок в разными наборами параметров. Например вместо
Button(“Text", { openAppInPlayStore() })
Можно написать
Button(onClick ={ openAppInPlayStore() }) {
Text(text = «Text», style = ...)
}
Что дает более гибкую настройку текста или даже вызов любого другого Composable вместо текста как контент кнопки
9) Пример про DataTable на самом деле рабочий, как вы видите мы его же используем в нашем семпл приложении: android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/DataTableActivity.kt
Как я понимаю разница в том, что у вас получился белый текст на белом фоне. В нашем примере мы еще оборачиваем пример в Surface {}. Как это меняет цвет текста я уже рассказывал выше
10) Аннотация @Model требуется только для классов с мутабельными филдами. Таким образом компайлер сгенерирует дополнительный код в геттеры и сеттеры и каждое изменение поля будет автоматически отслеживаться и вызывать рекомпозицию участка кода, в котором это поле было использовано. Именно таким образом работает +state { DialogVisibleModel(true) }. DialogVisibleModel оборачивается в специальный дата класс с одним var полем и каждый раз когда вы вызываете openDialog.value = DialogVisibleModel(false) все, кто раньше читали значение из openDialog.value автоматически перевызваны чтобы применить изменение
11) «Контекст можно получить, присвоив значение функции setContent{} в onCreateView, но как его использовать, например в Presenter или другом классе, отличном от Fragment или Activity, для изменения состояния – пока непонятно.». Официальная рекомендация на этот вопрос еще не выработана, обязательно скажем как мы советуем это решать после. Например, ваш композабл сможет принимать LiveData или Observable и будет автоматически перерисовываться каждый раз когда новое значение будет доступно из потока
позволю себе предложить еще пару улучшений, кроме уже озвученного переопределить onMeasure():

1) canvas.drawBitmap(finalBitmap, rect, rect, imgPaint); rect здесь не имеет никакого смысла. потому что суть метода в том, чтобы эти src и dst прямоугольники были разные, чтобы при отрисовки применился скейл и сдвиг. можно было написать чуть проще canvas.drawBitmap(finalBitmap, 0, 0, imgPaint), где 0 это верхняя и левая позиция для отрисовки. или же можно было использовать этот rect для скейла исходной картинки и обойтись без шага Bitmap finalBitmap = bitmap.createScaledBitmap(bitmap, radius, radius, false); и необходимости создавать промежуточный Bitmap (кстати, у вас параметр метода называется radius, хотя используете вы его как диаметр):
    private Bitmap getRoundedCroppedBitmap(Bitmap bitmap, int size) {
        Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        Rect rect = new Rect(0, 0, size, size);

        Canvas canvas = new Canvas(output);
        canvas.drawCircle(
                size  / 2,
                size / 2,
                size / 2,
                imgPaint);

        imgPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(finalBitmap, null, rect, imgPaint);

        return output;
    }

2) в onTouchEvent следует еще реагировать на MotionEvent.ACTION_CANCEL — он вызовется когда юзер нажмет пальцем в области вашей View, но потом уведет палец куда то в сторону, то есть фактически ACTION_UP будет выполнен потом уже не в области вашей View. На этот event не стоит реагировать как на tap, то есть отмечать картинку как выделенную, но стоит выполнить обратную scale анимацию, иначе вьюшка так и останется уменьшенной.
Странно, но по нажатию на кнопку «Отправить» на special.habrahabr.ru/google/md ничего не происходит( Пробую подать в раздел «Уже обновили ваше приложение». И в хроме и в сафари
тоже имею опыт разработки циферблатов. причем не простых, а с акселерометром)
play.google.com/store/apps/details?id=com.weareal.live
а эмбиент модов в системе вообще три типа подразумевается.
тоже начинал с использования clockwise от ustwo, правда пришлось под себя напильником переделывать многое.
я уже года три-четыре хожу с изменным денсити. мне как раз, наоборот в дефолтном варианте все кажется слишком крупным. риска на себе никакого не почувствовал)
дело то в том, что, естественно, приложение это не только текст. как андроид разработчик скажу, что когда вы меняете в настройках системы размер шрифта(!) это применяется только к стандартным элементам приложения, отображающим текст и то если разработчик не указал размер в так называемых scale-independent pixels, а не density-independent pixels, что зачастую специально не делают, чтобы как раз это изменение не портило верстку экрана. в общем, технически все размеры, все таки, не должны зависеть от выбранного размера шрифта.
другое дело что это производитель обязан подбирать для каждого устройства такой денсити, с которым все должно выглядеть примерно как и всех остальных в плане физических размеров, и если он сделал это не так, то это недочет конкретного производителя устройства, а не системы андроид в целом.
так что все таки советую попробовать поэкспериментировать с денсити. и могу с большой вероятностью утверждать, что андроид никогда не исправит описанную вами проблему именно таким путем, каким бы вам хотелось.
с помощью рута можно легко подобрать нужную цифру dpi, с которой будет комфортный для вас «масштаб».
wccftech.com/article/increase-screen-dpi
здесь говорят, что можно и без рута, но я не пробовал:
android.wonderhowto.com/how-to/change-your-androids-screen-resolution-without-root-access-0160439
хотя бы потому что это лишь одна из задач, которые можно решить этим подходом, вариантов на большой проект кучи) и либа сама по себе очень легкая и компактная по объему кода. еще можно использовать для тех же целей LocalBroadcastReceiver из support библиотеки, но он не такой удобный.
а можно еще и от интерфейсов и приведения к ним отказаться и перейти на логику ивентов с помощью либы
github.com/greenrobot/EventBus
или подобной. активити регистрируется как слушатель ивента смены фрагмента. а фрагмент посылает эти ивенты, в которые вкладывает нужную информацию с айдишником и текстом для экшнбара. так еще гибче получается
Все бы хорошо, да размеры слишком огромные!
Имел возможность протестировать другой «комбайн»: autoax.ru/catalog/videoregistratory-street-storm/cvr-g2750-st — по внешнему виду гораздо больше понравился, но кронштейн такой же ужасный.
чтобы анимация была и после вызова простого notifyDataSetChange(), а не только после явных сообщений типа notifyItemRemoved(index) можете попробовать написать так:
добавить в RecyclerViewAdapter, например, в конструкторе

setHasStableIds(true);

и в нем же переопределить метод

public long getItemId(int position)

в котором возвращать некий уникальный айди типа long для элемента в этой позиции.
тогда он сам поймет соответствие элементов до и после вызова notifyDataSetChange, какой удалился, добавился или переместился
у вас небольшая проблема тут есть:
не нужно вызвать invalidate() внутри onDraw.
invalidate это и есть просьба вьюшки заново вызвать onDraw — перерисоваться. и так у вас onDraw может вызываться бесконечно, потому что при каждой отрисовке в конце мы просим все перерисовать опять
а я жду когда выйдут колонки, совместимые с анонсированным Google Cast for audio. тут подробнее:
www.google.com/cast/audio/
в версии 1.3.0 сделал бекпорт suppressLayout для всех версий андроида. тем самым исправил один баг
вот ещё аналог того же самого с гитхаба. может кому пригодится как дополнительный пример
github.com/kanytu/android-parallax-recyclerview
ещё когда задаешь кастомный лэйаут для уведомления надо самому возиться с устновкой правильного цвета текста для TextView. лучше всего сделать это через стили. например, чтобы если мы хотим эмулировать стандартное уведомление, которое состоит из двух строк — Title и Text, то первому TextView ставим стиль
style="@style/NotificationTitle"
второму
style="@style/NotificationText"

и теперь описываем стиль. если ещё поддерживаем 2.2, то для него примерно так:

    <style name="NotificationTitle">
        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
        <item name="android:textStyle">bold</item>
    </style>

    <style name="NotificationText">
        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
    </style>


для 2.3 и выше появился стандартный стиль для этих текстов
    <style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" />
    
    <style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />


а на android 5.0 сейчас заметил, что с этим стилем цвет опять стал не соответствовать стандартному цвету заголовка уведомлений, а фон уведомлений изменился с черного на белый и нужно и цвет текста поменять. поэтому пока для values-v21 написал так

    <style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent">
        <item name="android:textColor">@android:color/black</item>
    </style>
Я не собирался ни на чем ловить)
LinearLayout тоже сначала меряет (measure) своих детей, чтобы узнать сколько они бы «хотели» занять, а потом с уже примененной логикой весов. Именно из-за этого в приведенной статье Relative и выиграл, потому что был один источник сложных вычислений RelativeLayout вместо двух LinearLayout.

Ну и с помощью RelativeLayout можно избавиться от проблемы излишних вложенностей (nested views) из-за которой на андроидах 2.x можно было наткнутся и на StackOverflow при отрисовке на довольно сложном экране
в официальной документации как раз есть разобранный контраргумент на ваш совет
developer.android.com/training/improving-layouts/optimizing-layout.html

тут же главное ко всему подходить с умом и понимать, что потом будет внутри происходить. и тогда можно написать RelativeLayout оптимальнее, чем получилось бы с другими стандартными ViewGroup.

для каждой конкретной ситуации можно написать CustomView, который будет работать оптимальнее, чем тот же вариант на RelativeLayout. если посмотреть на элементы списков в приложении Gmail в режиме отображения границ лэйаутов, то можно увидеть, что это как раз один CustomView. но этот вариант не всегда выиграет по соотношению производительность/затраченное время программиста.
1

Information

Rating
Does not participate
Location
London, England - London, Великобритания
Registered
Activity