Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What's new in Jetpack 2022

826c34bb94d4fe786230bd95c5bdc409?s=47 SOUP
June 11, 2022

What's new in Jetpack 2022

I/O 22 Extended in Korea Android 행사에서 발표한 자료입니다.
https://festa.io/events/2339

826c34bb94d4fe786230bd95c5bdc409?s=128

SOUP

June 11, 2022
Tweet

More Decks by SOUP

Other Decks in Technology

Transcript

  1. What’s new in Jetpack #androidjetpack

  2. android jetpack

  3. 79% 2021 using 2+ non-core Jetpack libraries 90% 2022 of

    top 1K apps
  4. Room Lifecycle Activity DataStore Browser JankStats Baseline Profiles Macrobenchmark Tracing

    WindowManager DragAndDrop AppCompat Compose Annotation Fragment Media Core Navigation WorkManager SlidingPaneLayout SavedState RecyclerView Preference LocalBroadcastManager Paging Emoji2 CustomView ConstraintLayout Camera Collection
  5. Unintentional dependency changes Android 12 Target

  6. None
  7. Link: https://developer.android.com/distribute/play-policies

  8. Link: https://developer.android.com/google/play/requirements/target-sdk

  9. Link: https://developer.android.com/guide/components/intents-filters#DeclareMutabilityPendingIntent

  10. // Application Codes PendingIntent.getBroadcast( context, REQUEST_CODE, intent, - flags +

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + flags or PendingIntent.FLAG_IMMUTABLE + } else { + flags + } )
  11. Adobe Stock#243026154 play-services- analytics 17.0.1 WorkManager 2.7 https://developers.google.com/android/guides/releases#august_19_2021 https://developer.android.com/jetpack/androidx/releases/work#version_27_2 Library

    Updates
  12. // androidx.work:work-runtime:2.7.0 // Unintentional dependency changes!!! androidx.annotation:annotation-experimental:1.0.0 -> 1.1.0 androidx.core:core:1.5.0

    -> 1.6.0 + androidx.startup:startup-runtime:1.0.0 + \--- androidx.tracing:tracing:1.0.0 androidx.room:room-runtime:2.1.0 -> 2.2.5 org.jetbrains.kotlin:kotlin-stdlib:1.4.32 -> 1.5.30 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32 -> 1.5.0 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3 -> 1.5.0
  13. // androidx.work:work-runtime:2.7.0 // Unintentional dependency changes!!! androidx.annotation:annotation-experimental:1.0.0 -> 1.1.0 androidx.core:core:1.5.0

    -> 1.6.0 + androidx.startup:startup-runtime:1.0.0 + \--- androidx.tracing:tracing:1.0.0 androidx.room:room-runtime:2.1.0 -> 2.2.5 org.jetbrains.kotlin:kotlin-stdlib:1.4.32 -> 1.5.30 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32 -> 1.5.0 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3 -> 1.5.0 How to prevent this? 🤔
  14. Link: https://github.com/dropbox/dependency-guard

  15. // Example dependencies { - implementation "androidx.activity:activity-ktx:1.3.1" + implementation "androidx.activity:activity-ktx:1.4.0"

    }
  16. // Example dependencies { - implementation "androidx.activity:activity-ktx:1.3.1" + implementation "androidx.activity:activity-ktx:1.4.0"

    } ----------------------------------------------------------------------- $ ./gradlew dependencyGuard > Task :app:dependencyGuard FAILED Dependencies Changed in :app for configuration releaseRuntimeClasspath - androidx.activity:activity-ktx:1.3.1 + androidx.activity:activity-ktx:1.4.0 - androidx.activity:activity:1.3.1 + androidx.activity:activity:1.4.0
  17. Rewritten from Java to Kotlin Kotlin

  18. 18 Support for Kotlin! Google I/O '17 Link: https://android-developers.googleblog.com/2017/05/android-announces-support-for-kotlin.html

  19. 2011 Android Support Library 2017 Architecture Components Kotlin Timeline ...

    Link: https://android-developers.googleblog.com/2017/11/announcing-architecture-components-10.html package helloWorld fun main(args: Array) { println("Hello World!") }
  20. 2011 Android Support Library 2017 Architecture Components Kotlin 2018 Android

    Jetpack KTX Timeline ... Link: https://android-developers.googleblog.com/2018/02/introducing-android-ktx-even-sweeter.html // Kotlin val uri = Uri.parse(myUriString) // Kotlin with Android KTX val uri = myUriString.toUri()
  21. 2011 Android Support Library 2017 Architecture Components Kotlin 2019 Kotlin

    first! Benchmark 2018 Android Jetpack KTX Timeline ... Link: https://android-developers.googleblog.com/2019/05/google-io-2019-empowering-developers.html Kotlin/Everywhere Many new Jetpack APIs and features will be offered first in Kotlin.
  22. 2011 Android Support Library 2017 Architecture Components Kotlin 2019 Kotlin

    first! Benchmark 2018 Android Jetpack KTX 2020 Compose DataStore Window Paging 3 Timeline ... Link: https://android-developers.googleblog.com/2020/07/getting-on-same-page-with-paging-3.html
  23. 2011 Android Support Library 2017 Architecture Components Kotlin 2019 Kotlin

    first! Benchmark 2018 Android Jetpack KTX 2020 Compose DataStore Window Paging 3 Timeline ... Link: https://developer.android.com/jetpack/androidx/releases/navigation#2.4.0 2022 Collection 1.3 Annotation 1.4 Navigation 2.4 Fragment 1.5 Activity 1.4 Partially rewritten.
  24. // Navigation.java public final class Navigation { @NonNull public static

    View.OnClickListener createNavigateOnClickListener( @IdRes final int resId) { return createNavigateOnClickListener(resId, null); } @NonNull public static View.OnClickListener createNavigateOnClickListener( @IdRes final int resId, @Nullable final Bundle args) { return new View.OnClickListener() { @Override public void onClick(View view) { findNavController(view).navigate(resId, args); } }; } }
  25. // Navigation.kt public object Navigation { @JvmStatic @JvmOverloads public fun

    createNavigateOnClickListener( @IdRes resId: Int, args: Bundle? = null ): View.OnClickListener { return View.OnClickListener { view -> findNavController(view).navigate(resId, args) } } }
  26. // api/current.txt public final class Navigation { - method public

    static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int); - method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int, android.os.Bundle?); + method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId); + method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId, optional android.os.Bundle? args); + field public static final androidx.navigation.Navigation INSTANCE; } Same APIs! -ktx is no longer required.
  27. 2011 Android Support Library 2017 Architecture Components Kotlin 2019 Kotlin

    first! Benchmark 2018 Android Jetpack KTX 2020 Compose DataStore Window Paging 3 Timeline 2022 Collection 1.3 Annotation 1.4 Navigation 2.4 Fragment 1.5 Activity 1.4 2023 (?) SavedState 1.2 Room 2.5 Work 2.8 ... ...
  28. // WorkManager // 2.7.1 val constraints = Constraints.Builder() .setRequiresCharging(true) .setRequiresBatteryNotLow(true)

    .setRequiresDeviceIdle(true) .build() // 2.8.0-alpha02 val constraints = Constraints( requiresCharging = true, requiresBatteryNotLow = true, requiresDeviceIdle = true, ) Enhanced APIs for Kotlin users!
  29. Room, Navigation, and more… Architecture Components

  30. Room Link: https://developer.android.com/jetpack/androidx/releases/room#2.4.0 Stable Auto Migrations Relational Query Methods Native

    support for Paging 3.0 (room-paging) Performance improvements with KSP androidx.room:room:2.4.2
  31. Link: https://developer.android.com/training/data-storage/room/migrating-db-versions#automated // Auto Migrations @Database( version = 3, entities

    = [User::class], autoMigrations = [ AutoMigration(from = 1, to = 2, spec = AppDatabase.MyAutoMigration::class), AutoMigration(from = 2, to = 3) ] ) abstract class AppDatabase : RoomDatabase() { @RenameTable(fromTableName = "User", toTableName = "AppUser") class MyAutoMigration : AutoMigrationSpec ... }
  32. Link: https://developer.android.com/training/data-storage/room/accessing-data#multimap // Supports multimap return types // like Map,

    SparseArray, LongSparseArray, etc. // 1:1 Relation Map @Query("SELECT * FROM Song JOIN Artist ON Song.artistId = Artist.artistId") fun getSongAndArtist(): Map<Song, Artist> // 1:N Relation Map @Query("SELECT * FROM Artist JOIN Album ON Artist.id = Album.artistId") fun getArtistAndAlbums(): Map<Artist, List<Album>>
  33. Paging Link: https://developer.android.com/jetpack/androidx/releases/paging#3.1.0 Stable Stable support for Rx and Guava

    integrations Improvements to handling of invalid or stale data More comprehensive callbacks New introductory codelab for Paging 3 androidx.paging:paging:3.1.1
  34. DataStore Link: https://developer.android.com/jetpack/androidx/releases/datastore#1.0.0 Stable MAD Skills: DataStore goo.gle/mad-skills-datastore How to

    migrate from SharedPreferences to Proto DataStore. d.android.com/codelabs/android-proto-datastore#7 androidx.datastore:datastore:1.0.0
  35. Lifecycle Deprecated @OnLifecycleEvent Integration between ViewModel and Jetpack Compose Restartable

    Lifecycle-aware coroutines androidx.lifecycle:lifecycle:2.4.1 androidx.lifecycle:lifecycle:2.5.0+ ViewModel CreationExtras Link: https://developer.android.com/jetpack/androidx/releases/lifecycle Stable / RC
  36. Link: https://developer.android.com/training/data-storage/room/accessing-data#multimap // Use DefaultLifecycleObserver or LifecycleEventObserver instead. - class

    MyObserver(...) : LifecycleObserver { + class MyObserver(...) : DefaultLifecycleObserver { - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { ... } - @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun onPause() { ... } } Stable
  37. Link: https://developer.android.com/topic/libraries/architecture/coroutines#restart // Restartable Lifecycle-aware coroutines viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {

    viewModel.someDataFlow.collect { // Process item } } } viewLifecycleOwner.lifecycleScope.launch { exampleProvider.exampleFlow() .flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED) .collect { // Process the value. } } Stable
  38. // current class MyActivity : AppCompatActivity() { private val viewModel:

    MyViewModel by viewModels() ... } class MyViewModel( private val savedStateHandle: SavedStateHandle, ) : ViewModel() { fun loadData(id: String) { ... } } RC
  39. // current class MyActivity : AppCompatActivity() { private val viewModel:

    MyViewModel by viewModels() override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory { return super.getDefaultViewModelProviderFactory() // SavedStateViewModelFactory } } class MyViewModel( private val savedStateHandle: SavedStateHandle, ) : ViewModel() { fun loadData(id: String) { ... } } RC
  40. class MyActivity : AppCompatActivity() { private val viewModel: MyViewModel by

    viewModels() } class MyViewModel( private val savedStateHandle: SavedStateHandle, + private val id: String, ) : ViewModel() { - fun loadData(id: String) { - ... - } } RC
  41. Link: https://pluu.github.io/blog/android/2022/03/12/creationextras/ class MyActivity : AppCompatActivity() { + private val

    key = object : CreationExtras.Key<String> {} private val factory = object : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create( modelClass: Class<T>, + extras: CreationExtras, ): T { val savedStateHandle = extras.createSavedStateHandle() + val id = extras[key].orEmpty() return MainViewModel( savedStateHandle = savedStateHandle, + id = id, ) as T } } override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory = factory ... RC
  42. Navigation Link: https://d.android.com/jetpack/androidx/releases/navigation#2.4.0 Stable Support for two-pane layouts Integration with

    Jetpack Compose Support for Multiple Back Stacks androidx.navigation:navigation:2.4.2
  43. Phone Tablet Link: https://github.com/CesarValiente/navigation-foldable

  44. Link: https://github.com/CesarValiente/navigation-foldable class NavigationActivity : AppCompatActivity(R.layout.activity_navigation) <!-- res/layout/activity_navigation.xml --> <androidx.fragment.app.FragmentContainerView

    android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" app:navGraph="@navigation/main_nav_graph" app:defaultNavHost="true" ... /> <!-- res/navigation/main_nav_graph.xml --> <navigation app:startDestination="@+id/two_pane"> <fragment android:id="@+id/two_pane" android:name="com.example.navigation.TwoPaneFragment" /> </navigation>
  45. Link: https://github.com/CesarValiente/navigation-foldable class TwoPaneFragment : AbstractListDetailFragment() { override fun onCreateListPaneView(

    inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { return ListPaneBinding.inflate(inflater, container, false) } override fun onListPaneViewCreated(view: View, savedInstanceState: Bundle?) { oneFragmentButton.setOnClickListener { openDetails(R.id.one_fragment) } } ... } <!-- res/layout/list_pane.xml --> <Button android:id="@+id/one_fragment_button" android:text="Fragment ONE" />
  46. Link: https://github.com/CesarValiente/navigation-foldable class TwoPaneFragment : AbstractListDetailFragment() { ... private fun

    openDetails(destinationId: Int) { val detailNavController = detailPaneNavHostFragment.navController detailNavController.navigate( resId = destinationId, args = null, navOptions = navOptions { popUpTo(detailNavController.graph.startDestinationId) { inclusive = true } } ) slidingPaneLayout.open() } override fun onCreateDetailPaneNavHostFragment(): NavHostFragment { return NavHostFragment.create(R.navigation.detail_pane_nav_graph) } }
  47. Link: https://github.com/CesarValiente/navigation-foldable <!-- res/navigation/detail_pane_nav_graph.xml --> <navigation app:startDestination="@+id/one_fragment"> <fragment android:id="@+id/one_fragment" android:name="com.example.navigation.OneFragment"

    /> <fragment android:id="@+id/two_fragment" android:name="com.example.navigation.TwoFragment" /> <fragment android:id="@+id/three_fragment" android:name="com.example.navigation.ThreeFragment" /> </navigation> class OneFragment : Fragment(R.layout.one_fragment) class TwoFragment : Fragment(R.layout.two_fragment) class ThreeFragment : Fragment(R.layout.three_fragment)
  48. Large Screen, and more... User Interface

  49. SlidingPaneLayout Link: https://d.android.com/jetpack/androidx/releases/slidingpanelayout#1.2.0 Stable Support for two-pane UX androidx.slidingpanelayout: slidingpanelayout:1.2.0

  50. <androidx.slidingpanelayout.widget.SlidingPaneLayout android:id="@+id/sliding_pane_layout"> <!-- List Pane (Left) --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/list_pane" android:layout_width="280dp"

    android:layout_height="match_parent" android:layout_gravity="start" /> <!-- Detail Pane (Right) --> <androidx.fragment.app.FragmentContainerView android:id="@+id/detail_container" android:layout_width="300dp" android:layout_weight="1" android:layout_height="match_parent" android:name="com.example.SelectAnItemFragment" /> </androidx.slidingpanelayout.widget.SlidingPaneLayout> Link: https://developer.android.com/guide/topics/ui/layout/twopane
  51. <androidx.slidingpanelayout.widget.SlidingPaneLayout android:id="@+id/sliding_pane_layout"> <!-- List Pane (Left) --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/list_pane" android:layout_width="280dp"

    android:layout_height="match_parent" android:layout_gravity="start" /> <!-- Detail Pane (Right) --> <androidx.fragment.app.FragmentContainerView android:id="@+id/detail_container" android:layout_width="300dp" android:layout_weight="1" android:layout_height="match_parent" android:name="com.example.SelectAnItemFragment" /> </androidx.slidingpanelayout.widget.SlidingPaneLayout> Link: https://developer.android.com/guide/topics/ui/layout/twopane
  52. Link: https://developer.android.com/guide/topics/ui/layout/twopane // A method on the Fragment that owns

    the SlidingPaneLayout, // called by the adapter when an item is selected. fun openDetails(itemId: Int) { childFragmentManager.commit { setReorderingAllowed(true) replace<ItemFragment>(R.id.detail_container, bundleOf("itemId" to itemId)) // If we're already open and the detail pane is visible, // crossfade between the fragments. if (binding.slidingPaneLayout.isOpen) { setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) } } binding.slidingPaneLayout.open() }
  53. WindowManager Link: https://developer.android.com/jetpack/androidx/releases/window#1.0.0 Stable Supports foldable phones to adjust how

    content is displayed Smart layout avoids placing content in occluded areas Already integrated into SlidingPaneLayout androidx.window:window:1.0.0
  54. Preference Link: https://d.android.com/jetpack/androidx/releases/preference#1.2.0 Stable Support for two-pane preference androidx.preference:preference:1.2.0

  55. Phone Tablet Link: https://github.com/CesarValiente/preferences-foldable

  56. Link: https://github.com/CesarValiente/preferences-foldable class PreferenceActivity : AppCompatActivity(R.layout.activity_preference) <!-- res/layout/activity_preference.xml --> <androidx.fragment.app.FragmentContainerView

    android:name="com.example.preferences.TwoPanePreference" ... /> class TwoPanePreference : PreferenceHeaderFragmentCompat() { override fun onCreatePreferenceHeader(): PreferenceFragmentCompat { return HeaderPreference() } } class HeaderPreference : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.preferences, rootKey) } }
  57. <!-- res/xml/preference.xml --> <PreferenceScreen> <Preference android:fragment="com.example.preferences.OneFragment" android:key="pref_one" ... /> <Preference

    android:fragment="com.example.preferences.TwoFragment" android:key="pref_two" ... /> <Preference android:fragment="com.example.preferences.ThreeFragment" android:key="pref_three" ... /> </PreferenceScreen> Link: https://github.com/CesarValiente/preferences-foldable
  58. DragAndDrop Link: https://developer.android.com/jetpack/androidx/releases/draganddrop#1.0.0 Stable Simplifies ingesting dropped data Drop target

    highlighting androidx.draganddrop:draganddrop:1.0.0
  59. Link: https://developer.android.com/guide/topics/ui/drag-drop

  60. Link: https://github.com/android/user-interface-samples/tree/main/DragAndDrop DragStartHelper(binding.textDragItem) { view, _ -> val text =

    (view as TextView).text // Create the ClipData to be shared val dragClipData = ClipData.newPlainText(/*label*/"Text", text) // Use the default drag shadow val dragShadowBuilder = View.DragShadowBuilder(view) // Initiate the drag. Note the DRAG_FLAG_GLOBAL, // which allows for drag events to be listened to by apps other than the source app. view.startDragAndDrop(dragClipData, dragShadowBuilder, null, DRAG_FLAG_GLOBAL) }.attach()
  61. Link: https://github.com/android/user-interface-samples/tree/main/DragAndDrop DropHelper.configureView( /* activity */ this, binding.textDropTarget, /* mimeTypes

    */ arrayOf( ClipDescription.MIMETYPE_TEXT_PLAIN, "application/x-arc-uri-list" // Support external items on Chrome OS Android 9 ), DropHelper.Options.Builder() .setHighlightColor(getColor(R.color.purple_300)) .build() ) { view, payload -> resetDropTarget() val item = payload.clip.getItemAt(0) val (_, remaining) = payload.partition { it == item } if (payload.clip.description.hasMimeType(MIMETYPE_TEXT_PLAIN) { binding.textDropTarget.text = item.text } // Allow the system to handle any remaining ClipData.Item objects if applicable remaining }
  62. Fragment Stable New state manager only Support multiple back stacks

    Support FragmentStrictMode androidx.fragment:fragment:1.4.1
  63. Link: https://medium.com/androiddevelopers/fragments-rebuilding-the-internals-61913f8bf48e Old New // Fragment 1.3 FragmentManager.enableNewStateManager(false) // Fragment

    1.4 FragmentManager.enableNewStateManager(false)
  64. Link: https://developer.android.com/guide/fragments/debugging FragmentStrictMode.defaultPolicy = FragmentStrictMode.Policy.Builder() .detectFragmentReuse() .detectFragmentTagUsage() .detectRetainInstanceUsage() .detectSetUserVisibleHint() .detectTargetFragmentUsage()

    .detectWrongFragmentContainer() .allowViolation(MyFragment::class.java, SetUserVisibleHintViolation::class.java) .apply { if (BuildConfig.DEBUG) { // Fail early on DEBUG builds penaltyDeath() } else { // Log to Crashlytics on RELEASE builds penaltyListener { FirebaseCrashlytics.getInstance().recordException(it) } } } .build()
  65. AppCompat Stable / Alpha Emoji2 integration androidx.appcompat:appcompat:1.4.2 androidx.appcompat:appcompat:1.6.0+ Support for

    custom app locales 🫠🧑🦰🦩 ☐☐☐ Link: https://developer.android.com/guide/topics/ui/look-and-feel/emoji2
  66. // Get app locales val locales: LocaleListCompat = AppCompatDelegate.getApplicationLocales() //

    Set app locales val locales: LocaleListCompat = LocaleListCompat.forLanguageTags("xx-YY") // or LocaleListCompat.create(Locale.KOREA) AppCompatDelegate.setApplicationLocales(locales) Link: https://developer.android.com/about/versions/13/features/app-languages#androidx-impl
  67. override fun attachBaseContext(newBase: Context) { if (SDK_INT < 33) {

    // Preferred locales are managed automatically on API level 33+, // but we need to load them manually on earlier platform versions. val locales = loadLocalesFromPreferences() AppCompatDelegate.setApplicationLocales(locales) } super.attachBaseContext(newBase) } Link: https://developer.android.com/about/versions/13/features/app-languages#androidx-impl
  68. override fun attachBaseContext(newBase: Context) { if (SDK_INT < 33) {

    // Preferred locales are managed automatically on API level 33+, // but we need to load them manually on earlier platform versions. val locales = loadLocalesFromPreferences() AppCompatDelegate.setApplicationLocales(locales) } super.attachBaseContext(newBase) } <service android:name="androidx.appcompat.app.AppLocalesMetadataHolderService" android:enabled="false" android:exported="false"> <meta-data android:name="autoStoreLocales" android:value="true" /> </service> Link: https://developer.android.com/about/versions/13/features/app-languages#androidx-impl
  69. ConstraintLayout Alpha Grid helper androidx.constraintlayout:constraintlayout:2.2.0+ Link: https://github.com/androidx/constraintlayout/wiki/What's-New-in-2.2

  70. <androidx.constraintlayout.widget.ConstraintLayout> <Button android:id="@+id/clear" android:text="C" /> ... <TextView android:id="@+id/calculatorText" android:background="@color/cardview_shadow_start_color" android:text="100"

    android:gravity="bottom|right" /> <Button android:id="@+id/btn0" android:text="0" /> ... </androidx.constraintlayout.widget.ConstraintLayout>
  71. <androidx.constraintlayout.widget.ConstraintLayout> <androidx.constraintlayout.helper.widget.Grid android:id="@+id/grid" android:layout_width="match_parent" android:layout_height="match_parent" app:constraint_referenced_ids=" calculatorText,btn0, clear,neg,percent,div,btn7,btn8,btn9,mult,btn4, btn5,btn6,sub,btn1,btn2,btn3,plus,btn0,dot,equal" app:grid_columns="4"

    app:grid_rows="7" app:grid_horizontalGaps="5dp" app:grid_verticalGaps="5dp" app:grid_orientation="horizontal" app:grid_spans="0:2x4,24:1x2" /> </androidx.constraintlayout.widget.ConstraintLayout>
  72. <androidx.constraintlayout.widget.ConstraintLayout> <androidx.constraintlayout.helper.widget.Grid android:id="@+id/grid" android:layout_width="match_parent" android:layout_height="match_parent" app:constraint_referenced_ids=" calculatorText,btn0, clear,neg,percent,div,btn7,btn8,btn9,mult,btn4, btn5,btn6,sub,btn1,btn2,btn3,plus,btn0,dot,equal" app:grid_columns="4"

    app:grid_rows="7" app:grid_horizontalGaps="5dp" app:grid_verticalGaps="5dp" app:grid_orientation="horizontal" app:grid_spans="0:2x4,24:1x2" /> </androidx.constraintlayout.widget.ConstraintLayout> 9 8 10 11 13 12 14 15 17 16 18 19 21 20 22 23 25 24 26 27 5 4 6 7 1 0 2 3
  73. <androidx.constraintlayout.widget.ConstraintLayout> <androidx.constraintlayout.helper.widget.Grid android:id="@+id/grid" android:layout_width="match_parent" android:layout_height="match_parent" app:constraint_referenced_ids=" calculatorText,btn0, clear,neg,percent,div,btn7,btn8,btn9,mult,btn4, btn5,btn6,sub,btn1,btn2,btn3,plus,btn0,dot,equal" app:grid_columns="4"

    app:grid_rows="7" app:grid_horizontalGaps="5dp" app:grid_verticalGaps="5dp" app:grid_orientation="horizontal" app:grid_spans="0:2x4,24:1x2" /> </androidx.constraintlayout.widget.ConstraintLayout> 5 4 6 7 1 0 2 3
  74. <androidx.constraintlayout.widget.ConstraintLayout> <androidx.constraintlayout.helper.widget.Grid android:id="@+id/grid" android:layout_width="match_parent" android:layout_height="match_parent" app:constraint_referenced_ids=" calculatorText,btn0, clear,neg,percent,div,btn7,btn8,btn9,mult,btn4, btn5,btn6,sub,btn1,btn2,btn3,plus,btn0,dot,equal" app:grid_columns="4"

    app:grid_rows="7" app:grid_horizontalGaps="5dp" app:grid_verticalGaps="5dp" app:grid_orientation="horizontal" app:grid_spans="0:2x4,24:1x2" /> </androidx.constraintlayout.widget.ConstraintLayout> 25 24
  75. Now in 1.2 beta! Compose

  76. Nested Scrolling Interop Link: https://d.android.com/jetpack/compose/interop/compose-in-existing-ui#nested-scrolling Compose 1.2 androidx.compose.ui:ui:1.2.0-alpha08 + Lazy

    Layouts Scrolling View Compose AppBar Layout
  77. Scrolling Compose in View Lazy Layouts AppBar Layout

  78. <androidx.coordinatorlayout.widget.CoordinatorLayout> <com.google.android.material.appbar.AppBarLayout> <com.google.android.material.appbar.CollapsingToolbarLayout> <androidx.appcompat.widget.AppCompatImageView /> <androidx.appcompat.widget.Toolbar /> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.compose.ui.platform.ComposeView

    android:id="@+id/compose_view" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> <com.google.android.material.floatingactionbutton.FloatingActionButton /> </androidx.coordinatorlayout.widget.CoordinatorLayout> Link: Nested Scrolling Interop Phase 1: Scrolling compose in cooperating view (I5d1ac)
  79. @ExperimentalComposeUiApi class ComposeInAndroidCoordinatorLayout : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?)

    { super.onCreate(savedInstanceState) setContentView(R.layout.compose_in_android_coordinator_layout) findViewById<ComposeView>(R.id.compose_view).apply { setContent { val nestedScrollInterop = rememberNestedScrollInteropConnection(this) LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop)) { items(20) { item -> Box(...) { Text(item.toString()) } } } } } } } Link: Nested Scrolling Interop Phase 1: Scrolling compose in cooperating view (I5d1ac)
  80. Scrolling View in Compose Scrolling View Compose

  81. <!-- res/layout/android_in_compose_nested_scroll_interop.xml —> <FrameLayout> <androidx.recyclerview.widget.RecyclerView android:id="@+id/main_list" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>

    <!-- res/layout/android_in_compose_nested_scroll_interop_list_item.xml —> <FrameLayout> <TextView android:id="@+id/list_item" /> </FrameLayout> Link: Nested Scrolling Interop Phase 2: A scrolling view child inside a compose parent (If7949)
  82. val ToolbarHeight = 48.dp @Composable private fun NestedScrollInteropComposeParentWithAndroidChild() { val

    toolbarHeightPx = with(LocalDensity.current) { ToolbarHeight.roundToPx().toFloat() } val toolbarOffsetHeightPx = remember { mutableStateOf(0f) } val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val delta = available.y val newOffset = toolbarOffsetHeightPx.value + delta toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f) return Offset.Zero } } } ... Link: Nested Scrolling Interop Phase 2: A scrolling view child inside a compose parent (If7949)
  83. @Composable private fun NestedScrollInteropComposeParentWithAndroidChild() { ... Box( Modifier .fillMaxSize() .nestedScroll(nestedScrollConnection)

    ) { TopAppBar( modifier = Modifier .height(ToolbarHeight) .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) }, title = { Text("toolbar offset is ${toolbarOffsetHeightPx.value}") } ) ... Link: Nested Scrolling Interop Phase 2: A scrolling view child inside a compose parent (If7949)
  84. @Composable private fun NestedScrollInteropComposeParentWithAndroidChild() { ... AndroidView( { context ->

    LayoutInflater.from(context) .inflate(R.layout.android_in_compose_nested_scroll_interop, null).apply { with(findViewById<RecyclerView>(R.id.main_list)) { layoutManager = LinearLayoutManager(context, VERTICAL, false) adapter = NestedScrollInteropAdapter() } }.also { ViewCompat.setNestedScrollingEnabled(it, true) } }, modifier = Modifier.padding(top = ToolbarHeight).fillMaxWidth(), ) } } Link: Nested Scrolling Interop Phase 2: A scrolling view child inside a compose parent (If7949)
  85. Compose in RecyclerView Link: https://d.android.com/jetpack/compose/interop/compose-in-existing-ui#compose-recyclerview Compose 1.2 androidx.compose.ui:ui:1.2.0-beta02 + androidx.recyclerview:

    recyclerview:1.3.0-alpha02 +
  86. // Compose 1.1 + RecyclerView 1.2 import androidx.compose.ui.platform.ComposeView class MyComposeAdapter

    : RecyclerView.Adapter<MyComposeViewHolder>() { override fun onCreateViewHolder(...): MyComposeViewHolder { return MyComposeViewHolder(ComposeView(parent.context)) } override fun onViewRecycled(holder: MyComposeViewHolder) { + holder.composeView.disposeComposition() } } class MyComposeViewHolder(val composeView: ComposeView) : RecyclerView.ViewHolder(composeView) { init { + composeView.setViewCompositionStrategy( + ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed + ) } } Link: https://developer.android.com/jetpack/compose/interop/compose-in-existing-ui#compose-recyclerview
  87. // Compose 1.2 + RecyclerView 1.3 import androidx.compose.ui.platform.ComposeView class MyComposeAdapter

    : RecyclerView.Adapter<MyComposeViewHolder>() { override fun onCreateViewHolder(...): MyComposeViewHolder { return MyComposeViewHolder(ComposeView(parent.context)) } // override fun onViewRecycled(holder: MyComposeViewHolder) { // holder.composeView.disposeComposition() // } } class MyComposeViewHolder(val composeView: ComposeView) : RecyclerView.ViewHolder(composeView) { // init { // composeView.setViewCompositionStrategy( // ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed // ) // } }
  88. CustomView-PoolingContainer Link: https://cs.android.com/.../androidx-customview-poolingcontainer-documentation.md

  89. abstract class AbstractComposeView(...) : ViewGroup(...) { private var disposeViewCompositionStrategy: (()

    -> Unit)? = - ViewCompositionStrategy.DisposeOnDetachedFromWindow.installFor(this) + ViewCompositionStrategy.Default.installFor(this) } class ComposeView(...) : AbstractComposeView(...) { ... } interface ViewCompositionStrategy { companion object { + val Default: ViewCompositionStrategy + get() = DisposeOnDetachedFromWindowIfNotInPoolingContainer } ... } Link: Integrate PoolingContainer into Compose UI(If7282)
  90. + object DisposeOnDetachedFromWindowIfNotInPoolingContainer : ViewCompositionStrategy { + override fun installFor(view:

    AbstractComposeView): () -> Unit { + val listener = object : View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) {} + override fun onViewDetachedFromWindow(v: View) { + if (!view.isWithinPoolingContainer) { + view.disposeComposition() + } + } + } + view.addOnAttachStateChangeListener(listener) + val poolingContainerListener = PoolingContainerListener { view.disposeComposition() } + view.addPoolingContainerListener(poolingContainerListener) + return { + view.removeOnAttachStateChangeListener(listener) + view.removePoolingContainerListener(poolingContainerListener) + } + } + } Link: Integrate PoolingContainer into Compose UI(If7282)
  91. + object DisposeOnDetachedFromWindowIfNotInPoolingContainer : ViewCompositionStrategy { + override fun installFor(view:

    AbstractComposeView): () -> Unit { + val listener = object : View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) {} + override fun onViewDetachedFromWindow(v: View) { + if (!view.isWithinPoolingContainer) { + view.disposeComposition() + } + } + } + view.addOnAttachStateChangeListener(listener) + val poolingContainerListener = PoolingContainerListener { view.disposeComposition() } + view.addPoolingContainerListener(poolingContainerListener) + return { + view.removeOnAttachStateChangeListener(listener) + view.removePoolingContainerListener(poolingContainerListener) + } + } + } Link: Integrate PoolingContainer into Compose UI(If7282)
  92. + object DisposeOnDetachedFromWindowIfNotInPoolingContainer : ViewCompositionStrategy { + override fun installFor(view:

    AbstractComposeView): () -> Unit { + val listener = object : View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) {} + override fun onViewDetachedFromWindow(v: View) { + if (!view.isWithinPoolingContainer) { + view.disposeComposition() + } + } + } + view.addOnAttachStateChangeListener(listener) + val poolingContainerListener = PoolingContainerListener { view.disposeComposition() } + view.addPoolingContainerListener(poolingContainerListener) + return { + view.removeOnAttachStateChangeListener(listener) + view.removePoolingContainerListener(poolingContainerListener) + } + } + } Link: Integrate PoolingContainer into Compose UI(If7282)
  93. public class RecyclerView extends ViewGroup implements ... { public RecyclerView(...)

    { ... + PoolingContainer.setPoolingContainer(this, true); } public final class Recycler { ... void recycleViewHolderInternal(ViewHolder holder) { ... mViewInfoStore.removeViewHolder(holder); if (!cached && !recycled && transientStatePreventsRecycling) { + PoolingContainer.callPoolingContainerOnRelease(holder.itemView); ... } } } } Link: Integrate PoolingContainer into RecyclerView(Ib89d2)
  94. Link: https://cs.android.com/.../androidx-main:customview/customview-poolingcontainer/.../PoolingContainer.kt See the code for details. 👀

  95. Lazy Layouts Link: https://developer.android.com/jetpack/compose/lists Compose 1.2 androidx.compose.foundation:foundation Lazy Layouts in

    Compose
  96. @OptIn(ExperimentalFoundationApi::class) LazyVerticalGrid( columns = GridCells.Fixed(2), verticalArrangement = Arragement.spaceBy(16.dp), horizontalArrangement =

    Arragement.spaceBy(16.dp), ) { items(data) { item -> Item(item) } }
  97. // New! LazyHorizontalGrid( rows = GridCells.Fixed(2), verticalArrangement = Arragement.spaceBy(16.dp), horizontalArrangement

    = Arragement.spaceBy(16.dp), ) { items(data) { item -> Item(item) } }
  98. instead of androidx.recyclerview.widget. StaggeredGridLayoutManager Not Available Staggered Grids Link: https://issuetracker.google.com/issues/182882362

  99. Adobe Stock#243026154 Downloadable Fonts Link: https://developer.android.com/jetpack/compose/text#downloadable-fonts Compose 1.2 androidx.compose.ui:ui-text-google-fonts

  100. val provider = GoogleFont.Provider( providerAuthority = "com.google.android.gms.fonts", providerPackage = "com.google.android.gms",

    certificates = R.array.com_google_android_gms_fonts_certs ) val MontserratFont = GoogleFont(name = "Montserrat") val MontserratFontFamily = FontFamily( Font(googleFont = MontserratFont, fontProvider = provider) ) Text( fontFamily = MontserratFontFamily, text = "Hello World!" ) Link: https://github.com/android/compose-samples/pull/787
  101. Text Magnifier Link: I30b38, b/139320979 Compose 1.2 androidx.compose.foundation:foundation

  102. Show Layout Bounds immediately Link: I843d5, b/225937688 Compose 1.2 androidx.compose.ui:ui:1.2.0-beta03

  103. Recommended! Performance best practices for Jetpack Compose

  104. Compose for Wear OS Jetpack Compose for Wear OS Now

    in beta! Link: g.co/wear/compose
  105. Navigation androidx.wear.compose.navigation instead of androidx.navigation:navigation-compose Compose for Wear OS val

    navController = rememeberSwipeDismissableNavController() SwipeDismissableNavHost( navController = navController, startDestination = Screen.MainScreen.route ) { composable(route = Screen.MainScreen.route) { MyListScreen(...) } ... }
  106. ScalingLazyColumn androidx.wear.compose.material instead of androidx.compose.material:material Compose for Wear OS val

    scalingLazyListState = rememberScalingLazyListState() ScalingLazyColumn( state = scalingLazyListState, autoCentering = AutoCenteringParams() ) { item { ListHeader { Text("Title") } } items(messages.size) { message -> Card(/*...*/) { /*...*/ } } }
  107. androidx.wear.compose.material Compose for Wear OS Picker Stepper InlineSlider

  108. androidx.wear.compose.material Compose for Wear OS Dialog Circular ProgressIndicator Horizontal PageIndicator

  109. Horologist Wear OS ѐߊ੗ܳ ࠁ৮ೞח Ѫਸ ݾ಴۽ ೞח ۄ੉࠳۞ܻ ޘ਺ੑפ׮.

    ѐߊ੗о ੌ߈੸ਵ۽ ೙ਃ۽ ೞ૑݅ 
 ই૒ ࢎਊೡ ࣻ হח ӝמਸ ઁҕ೤פ׮. • Media • Composables • Compose Layout • Audio and UI • Tiles Compose for Wear OS Link: https://github.com/google/horologist
  110. Health Services Link: https://developer.android.com/training/wearables/health-services Now in alpha! Developing Health and

    Fitness Apps on Android
  111. Jetpackਸ ѐࢶೞب۾, ੉गܳ ١۾೧઱ࣁਃ. 👆

  112. э਷ ੉गо ੉޷ ١۾غয ੓ਵݶ, Starܳ ׂ۞઱ࣁਃ. 👆

  113. Thank you! 
 Android Developer / NAVER WEBTOON Android GDE

    
 @fornewid d.android.com/jetpack speakerdeck.com/fornewid/ whats-new-in-jetpack-2022 Resources Sungyong An