{"id":437757,"date":"2024-11-07T21:21:52","date_gmt":"2024-11-07T21:21:52","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=437757"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=437757","title":{"rendered":"<span>\u041a\u0430\u043a \u044f \u043f\u0438\u0441\u0430\u043b \u0441\u0443\u043f\u0435\u0440\u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 Android-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 2024 \u0433\u043e\u0434\u0443<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><a href=\"https:\/\/habr.com\/en\/companies\/ruvds\/articles\/851256\/\"><\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/webt\/aw\/cs\/dk\/awcsdkn8nxulfgozpnwidutz3su.jpeg\" data-src=\"https:\/\/habrastorage.org\/webt\/aw\/cs\/dk\/awcsdkn8nxulfgozpnwidutz3su.jpeg\" data-blurred=\"true\"\/><\/div>\n<p><\/a><i><font color=\"#999999\">\u041a\u0430\u043a \u044f \u043f\u0438\u0441\u0430\u043b \u0441\u0443\u043f\u0435\u0440 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 Android \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 2024 \u0433\u043e\u0434\u0443<\/font><\/i><\/p>\n<p> \u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0433\u043e\u0434\u0430 \u0443 \u043c\u0435\u043d\u044f \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u043f\u0440\u0438\u043a\u043e\u043b\u044c\u043d\u0430\u044f \u0438\u0434\u0435\u044f: \u0441\u0434\u0435\u043b\u0430\u0442\u044c Android-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438. \u0427\u0442\u043e\u0431\u044b \u0432\u044b \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u043d\u044f\u043b\u0438, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0437 \u0441\u0435\u0431\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043d\u0430 <a href=\"https:\/\/github.com\/DmitryTsyvtsyn\/AlgoSortingAnimations\">GitHub<\/a> \u0435\u0441\u0442\u044c \u0441\u043a\u0440\u0438\u043d\u044b \u0438 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0435 \u0432\u0438\u0434\u0435\u043e. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e \u043a\u0443\u0441\u043e\u0447\u043a\u0430\u043c \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u043c\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442.<a name=\"habracut\"><\/a><\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u041c\u0438\u043d\u0438\u043c\u0443\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c \u0441\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439<\/font><\/h2>\n<p> \u0412 \u043c\u043e\u0451\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432\u0441\u0435\u0433\u043e \u0442\u0440\u0438 Gradle \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<ol>\n<li><a href=\"https:\/\/developer.android.com\/jetpack\/androidx\/releases\/core\">Androidx Core<\/a> \u2014 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043e\u0431\u043e\u0439\u0442\u0438\u0441\u044c \u0431\u0435\u0437 \u044d\u0442\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0431\u044b \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043c\u043e\u0440\u043e\u0447\u0438\u0442\u044c\u0441\u044f \u0441 edge-to-edge \u0438 \u043f\u0438\u0441\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0432 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u0447\u043d\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u0430\u0445 <a href=\"https:\/\/developer.android.com\/reference\/androidx\/core\/view\/WindowCompat\">WindowCompat<\/a> \u0438 <a href=\"https:\/\/developer.android.com\/reference\/androidx\/core\/view\/ViewCompat\">ViewCompat<\/a>.<\/li>\n<li><a href=\"https:\/\/developer.android.com\/kotlin\/coroutines\">Kotlin Coroutines<\/a> \u2014 \u043d\u0435 \u0441\u0442\u0430\u043b \u0437\u0430\u043c\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0441 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c\u0438 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438 \u0438 \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0442\u044c \u043a\u043e\u0434 callback&#8217;\u0430\u043c\u0438, \u043a \u0442\u043e\u043c\u0443 \u0436\u0435 \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u043a\u0438 \u043e\u0434\u043d\u0430 \u0438\u0437 \u043b\u044e\u0431\u0438\u043c\u044b\u0445 \u043c\u043e\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b \u043d\u0435 \u0437\u0430\u0442\u0430\u0449\u0438\u0442\u044c, \u0432\u0435\u0441\u0438\u0442 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e.<\/li>\n<li><a href=\"https:\/\/junit.org\/junit4\/\">Junit 4<\/a> \u2014 \u043a\u0430\u043a \u0436\u0435 \u0431\u0435\u0437 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/li>\n<\/ol>\n<p> \u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435, \u043d\u0435\u0442 \u043d\u0438\u043a\u0430\u043a\u043e\u0433\u043e <a href=\"https:\/\/developer.android.com\/compose\">Jetpack Compose<\/a> \u0438\u043b\u0438 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u043e\u0432, \u0432\u043e\u043e\u0431\u0449\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0435\u0439 \u0431\u044b\u043b\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0451 \u0441 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438 \u0432\u043f\u043b\u043e\u0442\u044c \u0434\u043e \u0441\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u044b\u0445 ViewModel \u0438 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u043d\u0430 View, \u043d\u0430 \u0437\u0430\u043a\u0443\u0441\u043e\u0447\u043a\u0443 \u0432\u043c\u0435\u0441\u0442\u043e xml \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0438 \u044f \u043f\u0438\u0441\u0430\u043b \u0432\u0451\u0440\u0441\u0442\u043a\u0443 \u043a\u043e\u0434\u043e\u043c.<\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u041d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f \u043d\u0430 View<\/font><\/h2>\n<p> \u0414\u043b\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u043d\u0430 View \u0431\u044b\u043b \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 <code>Navigator<\/code>:<\/p>\n<pre><code class=\"kotlin\">\/* parent \u2014 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0430\u044f View, \u043a\u0443\u0434\u0430 \u043a\u043b\u0430\u0434\u0443\u0442\u0441\u044f \u044d\u043a\u0440\u0430\u043d\u044b (\u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0435 View), \u0432 \u043c\u043e\u0451\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e FrameLayout  viewModelProvider \u2014 \u0448\u0442\u0443\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f ViewModel, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \/ \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 (\u043f\u0435\u0440\u0435\u0432\u043e\u0440\u043e\u0442 \u044d\u043a\u0440\u0430\u043d\u0430)  onBackPressedDispatcher \u2014 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043a\u043d\u043e\u043f\u043a\u0438 \"\u041d\u0430\u0437\u0430\u0434\", \u044f \u0442\u0443\u043f\u043e \u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043b \u043a\u043e\u0434 \u044d\u0442\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u0438\u0437 \u043d\u043e\u0432\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u0439 Android SDK *\/ class Navigator(     private val parent: ViewGroup,     private val viewModelProvider: ViewModelProvider,     private val onBackPressedDispatcher: OnBackPressedDispatcher ) {      \/\/ \u0441\u043f\u0438\u0441\u043e\u043a \u044d\u043a\u0440\u0430\u043d\u043e\u0432 \u0432 backstack'\u0435     private val stack = mutableListOf&lt;NavigationScreen&gt;()     \/\/ \u0441\u043f\u0438\u0441\u043e\u043a callback'\u043e\u0432 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043a\u043d\u043e\u043f\u043a\u0438 \"\u041d\u0430\u0437\u0430\u0434\"     private val callbacks = mutableListOf&lt;OnBackPressedCallback&gt;()     \/\/ ViewModelProvider'\u044b \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f ViewModel     private val providers = mutableMapOf&lt;String, ViewModelProvider&gt;()      private val stackId = R.id.navigation_stack      private val NavigationScreen.viewModelKey: String         get() = \"ViewModelProvider.SubProvider.Key.${this::class.java.canonicalName}\"      \/\/ \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0440\u0430\u043d (View) \u0438 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0434\u043b\u044f \u043d\u0435\u0433\u043e ViewModelProvider \u0441      \/\/ callback'\u043e\u043c \u0435\u0441\u043b\u0438 \u043e\u043d \u043a\u043b\u0430\u0434\u0451\u0442\u0441\u044f \u0432 backstack     fun navigateForward(screen: NavigationScreen, isAddToBackStack: Boolean = true) {         when {             isAddToBackStack -&gt; {                 val key = screen.viewModelKey                  \/*                 ViewModelProvider \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d \u043f\u043e \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0443 \u0434\u0435\u0440\u0435\u0432\u0430 (\u043f\u0430\u0442\u0442\u0435\u0440\u043d \u041a\u043e\u043c\u043f\u043e\u043d\u043e\u0432\u0449\u0438\u043a)                 \u043f\u0440\u0438 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u043d\u0430 \u044d\u043a\u0440\u0430\u043d \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 ViewModelProvider,                 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0445\u0440\u0430\u043d\u0438\u0442 ViewModel'\u0438 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430                 *\/                 val provider = parentViewModelProvider().createChildProvider(key)                 providers[key] = provider                                parent.addView(screen.view(BaseParams(parent.context, this, provider)))                 stack.add(screen)                  \/\/ \u043a\u043e\u0433\u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u044d\u043a\u0440\u0430\u043d \u0432 backstack                  \/\/ \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0451\u043c callback \u0434\u043b\u044f \u043a\u043d\u043e\u043f\u043a\u0438 \"\u041d\u0430\u0437\u0430\u0434\"                 val callback = createOnBackPressedCallback()                 callbacks.add(callback)                 onBackPressedDispatcher.addCallback(callback)             }             else -&gt; {                 parent.addView(screen.view(BaseParams(parent.context, this, viewModelProvider)))             }         }     }      \/*     \u0442\u0443\u0442 \u0432\u0441\u0451 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e, \u043b\u0438\u0431\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0435\u043c, \u0435\u0441\u043b\u0438 backstack \u043f\u0443\u0441\u0442\u043e\u0439,     \u043b\u0438\u0431\u043e \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0432\u0441\u0451, \u0447\u0442\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e: \u0441\u0430\u043c \u044d\u043a\u0440\u0430\u043d, ViewModelProvider \u0438      View \u0438\u0437 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f     *\/     fun navigateBack(): Boolean {         if (stack.isEmpty()) return false          val screen = stack.removeLast()         val key = screen.viewModelKey         viewModelProvider.removeChildProvider(key)         providers.remove(key)         parent.removeLast()         callbacks.removeLast().changeIsEnabled(false)          return true     }      \/\/ \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u044d\u043a\u0440\u0430\u043d\u043e\u0432 (backstack'\u0430) \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442      \/\/ \u0447\u0435\u0440\u0435\u0437 \u0447\u0442\u0435\u043d\u0438\u0435 Memory \u043a\u044d\u0448\u0430     fun onRestoreBackStack(cache: MemoryIDIdentityCache) {         val cachedStack = cache.read&lt;MutableList&lt;NavigationScreen&gt;&gt;(stackId) ?: return         cache.remove(stackId)          stack.clear()         callbacks.clear()          var index = 0         while (index &lt; cachedStack.size) {             navigateForward(cachedStack[index])              index++         }     }      \/\/ \u0441\u043f\u0438\u0441\u043e\u043a \u044d\u043a\u0440\u0430\u043d\u043e\u0432 \u0438\u043b\u0438 backstack \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 Memory \u043a\u044d\u0448     fun onSaveBackStack(cache: MemoryIDIdentityCache) {         callbacks.forEach { it.changeIsEnabled(false) }          cache.save(stackId, stack)     }      private fun createOnBackPressedCallback() =         object : OnBackPressedCallback(true) {             override fun handleOnBackPressed() {                 navigateBack()             }         }      \/*     \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043b\u0438\u0431\u043e \u0441\u0430\u043c\u044b\u0439 \u0432\u0435\u0440\u0445\u043d\u0435\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0439 ViewModelProvider,     \u043b\u0438\u0431\u043e \u0431\u0435\u0440\u0451\u0442 ViewModelProvidr \u043f\u043e \u043a\u043b\u044e\u0447\u0443 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430      \u0438\u0437 backstack'\u0430     *\/     private fun parentViewModelProvider(): ViewModelProvider {         if (stack.isEmpty()) return viewModelProvider          val key = stack.last().viewModelKey         return providers[key] ?: throw IllegalStateException(\"Not found such a ViewModelProvider with the key: $key\")     }  }<\/code><\/pre>\n<p> \u0414\u0443\u043c\u0430\u044e, \u0432\u044b \u0437\u0430\u043c\u0435\u0442\u0438\u043b\u0438, \u0447\u0442\u043e \u043c\u043e\u0439 \u043d\u0430\u0432\u0438\u0433\u0430\u0442\u043e\u0440 \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0435 \u043d\u0430 \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0440\u0430\u043d \u0442\u0443\u043f\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0433\u043e View, \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u044f \u0441 View \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430, \u043f\u0440\u0438 \u0442\u0430\u043a\u043e\u043c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0438 \u043c\u043e\u0436\u0435\u0442 \u0441\u043b\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u043f\u0435\u0440\u0435\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438, \u0435\u0441\u043b\u0438 \u0431\u0443\u0434\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0439 \u0441\u0442\u044d\u043a \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438, \u043d\u043e \u0434\u043b\u044f \u043c\u043e\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u044d\u0442\u043e \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u043e\u043f\u0440\u0430\u0432\u0434\u0430\u043d\u043e, \u0442\u0430\u043a \u043a\u0430\u043a \u0432 \u043d\u0451\u043c \u0432\u0441\u0435\u0433\u043e 2-5 \u044d\u043a\u0440\u0430\u043d\u043e\u0432.<\/p>\n<p> \u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c\u0441\u044f, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 <code>NavigationScreen<\/code>, \u044d\u0442\u043e \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439 View:<\/p>\n<pre><code class=\"kotlin\">fun interface NavigationScreen {     fun view(params: BaseParams): View }  \/\/ \u0445\u0440\u0430\u043d\u0438\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0448\u0442\u0443\u043a\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430 class BaseParams(     val context: Context,     val navigator: Navigator,     val viewModelProvider: ViewModelProvider )<\/code><\/pre>\n<p> \u0412 \u0438\u0442\u043e\u0433\u0435 \u043a\u0430\u0436\u0434\u044b\u0439 \u044d\u043a\u0440\u0430\u043d \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a:<\/p>\n<pre><code class=\"kotlin\">@SuppressLint(\"ViewConstructor\") class SortingAlgorithmMainFragment(params: BaseParams) : CoreLinearLayout(params.context) {      private val navigator = params.navigator     private val viewModel = params.viewModelProvider.provide(SortingAlgorithmViewModel::class.java) {         SortingAlgorithmViewModel()     }        init {         orientation = VERTICAL                  val toolbarView = ToolbarView(context)         toolbarView.changeMenuButtonDrawable(R.drawable.ic_settings)         toolbarView.changeMenuClickListener {             navigator.navigateForward(::SortingAlgorithmSelectionFragment)         }         addView(toolbarView)          ...     }      }<\/code><\/pre>\n<p> \u0410 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"kotlin\">\/\/ \u043a\u0440\u0430\u0442\u043a\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c navigator.navigateForward(     screen = ::SortingAlgorithmMainFragment,      isAddToBackStack = false )  \/\/ \u0431\u043e\u043b\u0435\u0435 \u0440\u0430\u0437\u0432\u0451\u0440\u043d\u0443\u0442\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c navigator.navigateForward(     screen = { params -&gt;         SortingAlgorithmMainFragment(params)     },     isAddToBackStack = false )<\/code><\/pre>\n<p> \u041f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u043e \u0438 \u0438\u0437\u044f\u0449\u043d\u043e, \u0447\u0442\u043e \u0435\u0449\u0451 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c.<\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f Memory \u043a\u044d\u0448\u0430<\/font><\/h2>\n<p> Memory \u043a\u044d\u0448 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e, \u0432\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u043e\u043d \u043b\u0435\u0436\u0438\u0442 \u0432 Application \u043a\u043b\u0430\u0441\u0441\u0435, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0443\u043c\u0435\u0440\u0435\u0442\u044c \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 (\u043f\u0435\u0440\u0435\u0432\u043e\u0440\u043e\u0442 \u044d\u043a\u0440\u0430\u043d\u0430) \u0438\u043b\u0438 \u043f\u0440\u0438 \u0441\u043c\u0435\u0440\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430:<\/p>\n<pre><code class=\"kotlin\">class App : Application() {      val cache = MemoryIDIdentityCache()  }<\/code><\/pre>\n<p> \u0412\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, \u043e\u043d \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 <a href=\"https:\/\/developer.android.com\/reference\/android\/util\/SparseArray\">SparseArray<\/a>, \u0433\u0434\u0435 \u043a\u043b\u044e\u0447\u0430\u043c\u0438 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0447\u0438\u0441\u043b\u0430 \u0442\u0438\u043f\u0430 Int, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e Android id:<\/p>\n<pre><code class=\"kotlin\">class MemoryIDIdentityCache {      private val data = SparseArray&lt;Any&gt;()      fun save(@IdRes key: Int, value: Any) {         data[key] = value     }      fun &lt;T&gt; read(@IdRes key: Int): T? {         return data[key] as? T     }      fun remove(@IdRes key: Int) {         data.remove(key)     }  }<\/code><\/pre>\n<p> \u0415\u0441\u043b\u0438 \u043d\u0435 \u0448\u0430\u0440\u0438\u0442\u0435 \u0437\u0430 SparseArray, \u0432 \u043c\u043e\u0451\u043c \u043a\u0430\u043d\u0430\u043b\u0435 \u0431\u044b\u043b \u043f\u043e\u0441\u0442 <a href=\"https:\/\/t.me\/android_under_the_hood\/26\">\u043d\u0430 \u044d\u0442\u0443 \u0442\u0435\u043c\u0443<\/a>.<\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f ViewModelProvider<\/font><\/h2>\n<p> \u042d\u0442\u043e \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c \u0447\u0430\u0441\u0442\u043d\u044b\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%9A%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%BE%D0%B2%D1%89%D0%B8%D0%BA_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)\">\u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0430 \u00ab\u041a\u043e\u043c\u043f\u043e\u043d\u043e\u0432\u0449\u0438\u043a\u00bb<\/a>:<\/p>\n<pre><code class=\"kotlin\">\/* ViewModelProvider \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a\u043e\u043c CoreViewModel, \u044d\u0442\u043e \u043a\u0430\u043a \u0440\u0430\u0437 \u043d\u0443\u0436\u043d\u043e \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0438 ViewModelProvider'\u043e\u0432 \u043d\u0430\u043f\u043e\u043c\u043d\u044e, \u0447\u0442\u043e \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u044d\u043a\u0440\u0430\u043d \u0432 backstack'\u0435 \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u0441\u0432\u043e\u0439 ViewModelProvider *\/ class ViewModelProvider : CoreViewModel {      \/\/ ViewModel \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 \u0445\u044d\u0448-\u0442\u0430\u0431\u043b\u0438\u0446\u0435     private val cache = hashMapOf&lt;String, CoreViewModel&gt;()      \/\/ \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0439 ViewModelProvider, \u0435\u0441\u043b\u0438 \u0442\u0430\u043a\u043e\u0439 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c, \u0442\u043e \u0432\u0435\u0440\u043d\u0451\u0442 \u0435\u0433\u043e     fun createChildProvider(key: String): ViewModelProvider {         val provider = cache[key]         if (provider != null &amp;&amp; provider is ViewModelProvider) return provider          val childProvider = ViewModelProvider()         val childProviderCache = childProvider.cache         \/\/ child ViewModelProviders must have access to parent ViewModels         cache.forEach { (key, value) -&gt;             if (value !is ViewModelProvider) {                 childProviderCache[key] = value             }         }          cache[key] = childProvider          return childProvider     }      \/\/ \u0443\u0434\u0430\u043b\u044f\u0435\u0442 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0439 ViewModelProvider     fun removeChildProvider(key: String) {         val provider = cache[key]         if (provider is ViewModelProvider) {             provider.cache.clear()             cache.remove(key)         }     }      \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 ViewModel \u0438\u043b\u0438 \u0431\u0440\u043e\u0441\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443, \u0435\u0441\u043b\u0438 \u0435\u0451 \u043d\u0435\u0442      \/\/ \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u043c ViewModelProvider'\u0435         fun &lt;T : CoreViewModel&gt; provide(viewModelClass: Class&lt;T&gt;): T {         val key = key(viewModelClass)         return cache[key] as? T ?: throw IllegalStateException(\"Not found such a ViewModel with the key: $key\")     }      \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 ViewModel \u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0435\u0451, \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044f      \/\/ \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u043c ViewModelProvider'\u0435     fun &lt;T : CoreViewModel&gt; provide(viewModelClass: Class&lt;T&gt;, factory: () -&gt; T): T {         val key = key(viewModelClass)         val cachedViewModel = cache[key]         if (cachedViewModel != null) return cachedViewModel as T          val viewModel = factory.invoke()         cache[key] = viewModel         return viewModel     }      private fun &lt;T&gt; key(viewModelClass: Class&lt;T&gt;): String =         \"ViewModelProvider.Key.${viewModelClass.canonicalName}\"  }<\/code><\/pre>\n<p> \u0410\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439:<\/p>\n<ol>\n<li>\u0421\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 ViewModelProvider \u0434\u043b\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430.<\/li>\n<li>\u0421\u0430\u043c\u044b\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 \u044d\u043a\u0440\u0430\u043d \u0441\u043e\u0437\u0434\u0430\u0451\u0442 ViewModel \u0438 \u043a\u043b\u0430\u0434\u0451\u0442 \u0435\u0451 \u0432 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 ViewModelProvider.<\/li>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u0442 \u043a\u043d\u043e\u043f\u043a\u0443 \u043d\u0430 \u043f\u0435\u0440\u0432\u043e\u043c \u044d\u043a\u0440\u0430\u043d\u0435 \u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0432\u0442\u043e\u0440\u043e\u0439.<\/li>\n<li>\u0421\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0439 ViewModelProvider \u0434\u043b\u044f \u0432\u0442\u043e\u0440\u043e\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430.<\/li>\n<li>\u0412\u0442\u043e\u0440\u043e\u0439 \u044d\u043a\u0440\u0430\u043d \u0441\u043e\u0437\u0434\u0430\u0451\u0442 ViewModel \u0438 \u043a\u043b\u0430\u0434\u0451\u0442 \u0435\u0451 \u0432 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0439 ViewModelProvider.<\/li>\n<li>\u041f\u0440\u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0435 \u043d\u0430 \u0442\u0440\u0435\u0442\u0438\u0439 \u044d\u043a\u0440\u0430\u043d \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0439 ViewModelProvider \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u0438 \u0442. \u0434.<\/li>\n<\/ol>\n<p> \u0412 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0434\u0435\u0440\u0435\u0432\u043e \u0438\u0437 ViewModelProvider&#8217;\u043e\u0432, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f ViewModel&#8217;\u0438 \u044d\u043a\u0440\u0430\u043d\u043e\u0432.<\/p>\n<p> \u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043d\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f ViewModel&#8217;\u0435\u0439 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 ViewModelProvider \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 <code>onRetainNonConfigurationInstance()<\/code> \u043c\u0435\u0442\u043e\u0434:<\/p>\n<pre><code class=\"kotlin\">class MainActivity : Activity() {      private var viewModelProvider by Delegates.notNull&lt;ViewModelProvider&gt;()      override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)          \/\/ \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 ViewModelProvider \u0438\u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0435\u0433\u043e         \/\/ \u0435\u0441\u043b\u0438 \u043e\u043d \u0440\u0430\u043d\u0435\u0435 \u0431\u044b\u043b \u0443\u0436\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d         viewModelProvider = lastNonConfigurationInstance as? ViewModelProvider ?: ViewModelProvider()     }      \/\/ \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 ViewModelProvider     override fun onRetainNonConfigurationInstance() = viewModelProvider  }<\/code><\/pre>\n<p> \u0422\u0430\u043a\u0436\u0435 \u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043b Junit \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u043a\u0438 ViewModelProvider&#8217;\u0430, \u0435\u0441\u043b\u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u043c\u043e\u0436\u0435\u0442\u0435 \u0433\u043b\u044f\u043d\u0443\u0442\u044c <a href=\"https:\/\/github.com\/DmitryTsyvtsyn\/AlgoSortingAnimations\">\u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0430\u0445<\/a> \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c.<\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0442\u0435\u043c\u044b<\/font><\/h2>\n<p> \u0412\u0441\u044f \u0432\u0451\u0440\u0441\u0442\u043a\u0430 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u043d\u0430 \u043a\u043e\u0434\u043e\u043c \u0431\u0435\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f xml \u0444\u0430\u0439\u043b\u043e\u0432 \u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u0442\u0430\u043a\u0438\u0445 \u043a\u0430\u043a <a href=\"https:\/\/developer.android.com\/compose\">Jetpack Compose<\/a>:<\/p>\n<pre><code class=\"kotlin\">class MainActivity : Activity() {      override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)          val fragmentContainerView = CoreFrameLayout(this)         fragmentContainerView.layoutParams(viewGroupLayoutParams().match())         setContentView(fragmentContainerView)          ...      }  }<\/code><\/pre>\n<p> \u0422\u0430\u043a \u043a\u0430\u043a xml \u0432\u0451\u0440\u0441\u0442\u043a\u0438 \u043d\u0435\u0442, \u0442\u043e \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0442\u0435\u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 \u0441\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u0430 \u0442\u043e\u0447\u043d\u0435\u0435 \u0447\u0435\u0440\u0435\u0437 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 View:<\/p>\n<pre><code class=\"kotlin\">open class CoreFrameLayout @JvmOverloads constructor(     ctx: Context,     \/\/ \u0446\u0432\u0435\u0442\u0430 \u0438 shape \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0442\u0435\u043c\u044b     private val backgroundColor: ColorAttributes = ColorAttributes.primaryBackgroundColor,     private val shape: ShapeAttribute = ShapeAttribute.medium,     private val shapeTreatmentStrategy: ShapeTreatmentStrategy = ShapeTreatmentStrategy.None() ): FrameLayout(ctx), ThemeManager.ThemeManagerListener {      \/*     \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0438\u0437 ThemeManager.ThemeManagerListener \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430,      \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434 \u0442\u0435\u043c\u0443 \u0438 WindowInsets (\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 edge-to-edge),      \u0435\u0441\u043b\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 \u0431\u044b\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u044b     *\/     override fun onThemeChanged(insets: ThemeManager.WindowInsets, theme: CoreTheme) {         val gradientDrawable = GradientDrawable()         \/*         shapeTreatmentStrategy \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 FloatArray \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0443\u0433\u043b\u0430,         \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0435 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043a\u0440\u0443\u0433\u043b\u0451\u043d\u043d\u044b\u0435 \u0432\u0435\u0440\u0445\u043d\u0438\u0435 \u0443\u0433\u043b\u044b \u0438\u043b\u0438         \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442, \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0438\u0436\u043d\u0438\u0435 \u0438 \u0442\u0434.          theme.shapeStyle[shape] \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 Float \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0440\u0430\u0434\u0438\u0443\u0441\u0430 \u0437\u0430\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u044f                  context.dp() \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u044d\u043a\u0432\u0438\u0432\u0430\u043b\u0435\u043d\u0442 \u0432 dp \u0435\u0434\u0438\u043d\u0438\u0446\u0430\u0445, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0432 \u043f\u0438\u043a\u0441\u0435\u043b\u044f\u0445         *\/         gradientDrawable.cornerRadii = shapeTreatmentStrategy.floatArrayOf(context.dp(theme.shapeStyle[shape]))         \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u0446\u0432\u0435\u0442 \u0434\u043b\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0442\u0435\u043c\u044b         gradientDrawable.setColor(theme.colors[backgroundColor])         background = gradientDrawable     }      \/\/ \u043a\u043e\u0433\u0434\u0430 View \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0430 \u043a Window, \u043e\u043d\u0430 \u043f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u043c\u044b     override fun onAttachedToWindow() {         super.onAttachedToWindow()         ThemeManager.addThemeListener(this)     }      \/\/ \u043a\u043e\u0433\u0434\u0430 View \u043e\u0442\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0430 \u043e\u0442 Window, \u043e\u043d\u0430 \u043e\u0442\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0442\u0435\u043c\u044b     override fun onDetachedFromWindow() {         super.onDetachedFromWindow()         ThemeManager.removeThemeListener(this)     }  }<\/code><\/pre>\n<p> \u0412\u0441\u0451 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e: \u043f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c\u0441\u044f \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0442\u0435\u043c\u044b, \u043a\u043e\u0433\u0434\u0430 View \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u0430 \u0438 \u043e\u0442\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u043d\u0435\u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 <code>ThemeManager<\/code>, \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c \u0445\u0440\u0430\u043d\u0438\u0442 callback&#8217;\u0438 \u0438 \u043e\u043f\u043e\u0432\u0435\u0449\u0430\u0435\u0442 \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u044e View, \u043a\u043e\u0433\u0434\u0430 \u0442\u0435\u043c\u0430 \u0438\u043b\u0438 <code>WindowInsets<\/code> \u043f\u043e\u043c\u0435\u043d\u044f\u043b\u0438\u0441\u044c (\u043f\u0430\u0442\u0442\u0435\u0440\u043d \u041d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u044c):<\/p>\n<pre><code class=\"kotlin\">\/* object \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0438\u043d\u0433\u043b\u0435\u0442\u043e\u043d\u043e\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443  \u043e\u0447\u0435\u043d\u044c \u0432\u0430\u0436\u043d\u043e \u043e\u0442\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f View \u043e\u0442 callback'\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f, \u043a\u043e\u0433\u0434\u0430 View \u0431\u044b\u043b\u0430 \u0443\u043d\u0438\u0447\u0442\u043e\u0436\u0435\u043d\u0430, \u0430  ThemeManager \u0445\u0440\u0430\u043d\u0438\u0442 \u043d\u0435\u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 callback *\/ object ThemeManager {      private var theme = CoreTheme.LIGHT     private var insets: WindowInsets = WindowInsets(0, 0, 0, 0)     \/\/ \u043d\u0430\u0431\u043e\u0440 callback'\u043e\u0432, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0431\u044b\u043b\u043e \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u0432     private val themeListeners = mutableSetOf&lt;ThemeManagerListener&gt;()      \/\/ \u043a\u043e\u0433\u0434\u0430 View \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u0430 (onAttachedToWindow) callback \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f     fun addThemeListener(listener: ThemeManagerListener) {         themeListeners.add(listener)         listener.notify()     }      \/\/ \u043a\u043e\u0433\u0434\u0430 View \u043d\u0435\u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u0430 (onDetachedFromWindow) callback \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f     fun removeThemeListener(listener: ThemeManagerListener) {         themeListeners.remove(listener)         listener.notify()     }      \/\/ \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0442\u0435\u043c\u044b \u0432\u0441\u0435 callback'\u0438 \u043e\u043f\u043e\u0432\u0435\u0449\u0430\u044e\u0442\u0441\u044f     fun changeTheme(newTheme: CoreTheme) {         if (theme == newTheme) return          theme = newTheme         themeListeners.notifyAll()     }      \/\/ \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 WindowInsets \u0432\u0441\u0435 callback'\u0438 \u043e\u043f\u043e\u0432\u0435\u0449\u0430\u044e\u0442\u0441\u044f     fun changeInsets(newInsets: WindowInsets) {         if (insets == newInsets) return                insets = newInsets         themeListeners.notifyAll()     }      private fun Set&lt;ThemeManagerListener&gt;.notifyAll() {         forEach { listener -&gt; listener.notify() }     }      \/\/ \u043f\u0440\u0438 \u043e\u043f\u043e\u0432\u0435\u0449\u0435\u043d\u0438\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f insets \u0438 theme     private fun ThemeManagerListener.notify() {         onThemeChanged(insets, theme)     }      fun interface ThemeManagerListener {         fun onThemeChanged(insets: WindowInsets, theme: CoreTheme)     }      data class WindowInsets(         val start: Int,         val top: Int,         val end: Int,         val bottom: Int     )  }<\/code><\/pre>\n<p> \u0412\u0441\u0451 \u043f\u0440\u0435\u0434\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e:<\/p>\n<ol>\n<li>\u0421\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f View \u0438 \u043f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f, <code>ThemeManager<\/code> \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0443 \u0441\u0435\u0431\u044f callback.<\/li>\n<li>\u041c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u0435\u043c\u0430 \u0438\u043b\u0438 WindowInsets, <code>ThemeManager<\/code> \u043f\u0440\u043e\u0431\u0435\u0433\u0430\u0435\u0442\u0441\u044f \u043f\u043e \u0432\u0441\u0435\u043c callback&#8217;\u0430\u043c \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u044f View \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u044d\u0442\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f.<\/li>\n<li>\u041f\u0440\u0438 \u0443\u043d\u0438\u0447\u0442\u043e\u0436\u0435\u043d\u0438\u0438 View \u043e\u0442\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u0430 <code>ThemeManager<\/code> \u0443\u0434\u0430\u043b\u044f\u0435\u0442 \u0443 \u0441\u0435\u0431\u044f callback.<\/li>\n<\/ol>\n<p> \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e \u0442\u0430\u043a\u043e\u0439 \u0448\u0442\u0443\u043a\u0435 \u043a\u0430\u043a \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u044b, \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435, \u043a\u0441\u0442\u0430\u0442\u0438, \u0432\u0437\u044f\u0442\u043e \u0438\u0437 Android xml \u0442\u0435\u043c, \u0432\u043e\u043e\u0431\u0449\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b \u0434\u0435\u043b\u0430\u043b\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u0442\u0435\u043c\u044b \u0432 Jetpack Compose, \u0442\u043e \u0437\u043d\u0430\u0435\u0442\u0435, \u0447\u0442\u043e \u043d\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u0438\u0434\u0443\u043c\u044b\u0432\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0445\u043e\u0436\u0435\u0435, \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439:<\/p>\n<pre><code class=\"kotlin\">Text(     color = CustomTheme.colors.primaryTextColor,     text = \"...\" )<\/code><\/pre>\n<p> \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 View \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u0442\u0430\u043a, \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 Jetpack Compose, \u0433\u0434\u0435 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0442\u0435\u043c\u044b \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u0435\u0440\u0435\u0440\u0438\u0441\u043e\u0432\u043a\u0430 \u0434\u0435\u0440\u0435\u0432\u0430 \u0441 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438, \u0432 \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0438 View \u0442\u0430\u043a\u043e\u0433\u043e \u043d\u0435\u0442 \u0438 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043b\u0438\u0431\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0432\u0441\u0435\u0433\u0434\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0443\u0435\u043c View:<\/p>\n<pre><code class=\"kotlin\">val textView1 = TextView(context) textView.setTextColor(theme.colors.primaryTextColor) textView.background = ... textView.typeface = ...  val textView2 = TextView(context) textView.setTextColor(theme.colors.primaryTextColor) textView.background = ... textView.typeface = ...  val textView3 = TextView(context) textView.setTextColor(theme.colors.primaryTextColor) textView.background = ... textView.typeface = ...<\/code><\/pre>\n<p> \u041b\u0438\u0431\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0438 \u043e\u0441\u043e\u0431\u043e \u043d\u0435 \u0434\u0443\u043c\u0430\u0435\u043c \u043e \u043a\u0430\u0436\u0434\u043e\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435:<\/p>\n<pre><code class=\"kotlin\">val textView1 = CoreTextView(     ctx = context,     textColor = ColorAttributes.primaryTextColor,     textStyle = TypefaceAttribute.Body1 )  \/\/ primaryTextColor is default val textView2 = CoreTextView(     ctx = context,     textStyle = TypefaceAttribute.Body2 )  \/\/ primaryTextColor is default val textView3 = CoreTextView(     ctx = context,     textStyle = TypefaceAttribute.Body3 )<\/code><\/pre>\n<p> \u0423 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430 \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u0438 \u043d\u044e\u0430\u043d\u0441\u044b, \u0432 \u043f\u0435\u0440\u0432\u043e\u043c \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0433\u0434\u0435-\u0442\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u043c\u044b \u0438 \u0431\u0440\u0430\u0442\u044c \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u0447\u0442\u043e \u043f\u043e\u0440\u043e\u0434\u0438\u0442 \u0435\u0449\u0451 \u0431\u043e\u043b\u044c\u0448\u0435 \u0448\u0430\u0431\u043b\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u043c \u0432\u0441\u0451 \u0438\u043d\u043a\u0430\u043f\u0441\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0432 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a\u0438 View, \u043d\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u044b.<\/p>\n<p> \u041f\u043e\u0447\u0435\u043c\u0443 \u043d\u0435\u043b\u044c\u0437\u044f \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432\u0441\u0451 \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 View \u0438 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u044b? \u0412\u0441\u0451 \u043f\u0440\u043e\u0441\u0442\u043e, \u0432\u044b \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435: <\/p>\n<pre><code class=\"kotlin\">open class CoreTextView @JvmOverloads constructor(     ctx: Context,     private val textColor: ColorAttributes = ColorAttributes.primaryTextColor,     ... ): TextView(ctx), ThemeManager.ThemeManagerListener {      override fun onThemeChanged(insets: ThemeManager.WindowInsets, theme: CoreTheme) {         ...         \/\/ \u043d\u0435\u043b\u044c\u0437\u044f \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0446\u0432\u0435\u0442 \u0442\u0435\u043a\u0441\u0442\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043d\u0430 primaryColor,         \/\/ \u0442\u0430\u043a \u043a\u0430\u043a \u0446\u0432\u0435\u0442 \u0442\u0435\u043a\u0441\u0442\u0430 \u0437\u0430\u0432\u044f\u0437\u0430\u043d \u043d\u0430 primaryTextColor         setTextColor(theme.colors.primaryTextColor)          \/\/ \u0441 \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u0430\u043c\u0438 \u043c\u043e\u0436\u043d\u043e, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u043e\u0439 \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442         setTextColor(theme.colors[textColor])     }      ...  }<\/code><\/pre>\n<p> \u0415\u0441\u0442\u044c, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u0433\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0440\u0435\u0448\u0438\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u2014 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a\u0438 \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440:<\/p>\n<pre><code class=\"kotlin\">class PrimaryTextColorTextView @JvmOverloads constructor(     ctx: Context ): TextView(ctx), ThemeManager.ThemeManagerListener {      override fun onThemeChanged(insets: ThemeManager.WindowInsets, theme: CoreTheme) {         setTextColor(theme.colors.primaryTextColor)     }  }  class PrimaryColorTextView @JvmOverloads constructor(     ctx: Context ): TextView(ctx), ThemeManager.ThemeManagerListener {      override fun onThemeChanged(insets: ThemeManager.WindowInsets, theme: CoreTheme) {         setTextColor(theme.colors.primaryColor)     }  }  class SecondaryColorTextView @JvmOverloads constructor(     ctx: Context ): TextView(ctx), ThemeManager.ThemeManagerListener {      override fun onThemeChanged(insets: ThemeManager.WindowInsets, theme: CoreTheme) {         setTextColor(theme.colors.secondaryColor)     }  }<\/code><\/pre>\n<p> \u041d\u043e \u0432 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u043f\u043b\u043e\u0434\u0438\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a\u043e\u0432 \u0438 \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0443\u0447\u0443 \u0448\u0430\u0431\u043b\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0437\u0434\u0435\u0441\u044c \u044f\u0432\u043d\u043e \u0432\u044b\u0438\u0433\u0440\u044b\u0432\u0430\u044e\u0442.<\/p>\n<p> \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0433\u043b\u044f\u043d\u0435\u043c, \u043a\u0430\u043a \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u044d\u0442\u0438 \u0441\u0430\u043c\u044b\u0435 \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u044b:<\/p>\n<pre><code class=\"kotlin\">\/\/ \u0435\u0441\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 enum class ColorAttributes {     primaryColor,     primaryDarkColor,     colorOnPrimary,     primaryBackgroundColor,     secondaryBackgroundColor,     disabledBackgroundColor,     selectableBackgroundColor,     primaryTextColor,     transparent }  \/\/ \u0435\u0441\u0442\u044c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u0446\u0432\u0435\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 \u043a\u0430\u0436\u0434\u043e\u0439 \u0442\u0435\u043c\u0435 class Colors(     private val primaryColor: Int = CoreColors.greenMedium,     private val primaryDarkColor: Int = CoreColors.greenDark,     private val colorOnPrimary: Int = CoreColors.white,     private val primaryBackgroundColor: Int,     private val secondaryBackgroundColor: Int,     private val disabledBackgroundColor: Int,     private val primaryTextColor: Int,     private val selectableBackgroundColor: Int ) {      \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0439 \u0446\u0432\u0435\u0442 \u0442\u0435\u043c\u044b \u043f\u043e \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u0443 (\u0443 \u043a\u0430\u0436\u0434\u043e\u0439 \u0442\u0435\u043c\u044b \u0441\u0432\u043e\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0446\u0432\u0435\u0442\u043e\u0432)     operator fun get(type: ColorAttributes): Int {         return when(type) {             ColorAttributes.primaryColor -&gt; primaryColor             ColorAttributes.primaryDarkColor -&gt; primaryDarkColor             ColorAttributes.colorOnPrimary -&gt; colorOnPrimary             ColorAttributes.primaryBackgroundColor -&gt; primaryBackgroundColor             ColorAttributes.secondaryBackgroundColor -&gt; secondaryBackgroundColor             ColorAttributes.disabledBackgroundColor -&gt; disabledBackgroundColor             ColorAttributes.primaryTextColor -&gt; primaryTextColor             ColorAttributes.selectableBackgroundColor -&gt; selectableBackgroundColor             ColorAttributes.transparent -&gt; CoreColors.transparent         }     }  }  open class CoreFrameLayout @JvmOverloads constructor(     ctx: Context,     private val backgroundColor: ColorAttributes = ColorAttributes.primaryBackgroundColor,     ... ): FrameLayout(ctx), ThemeManager.ThemeManagerListener {      override fun onThemeChanged(insets: ThemeManager.WindowInsets, theme: CoreTheme) {         val gradientDrawable = GradientDrawable()         \/*         \u0431\u0435\u0440\u0451\u043c \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0446\u0432\u0435\u0442 \u0434\u043b\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0442\u0435\u043c\u044b \u043f\u043e \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u0443,         \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0446\u0432\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0434\u0440\u0443\u0433\u043e\u0439 \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442         *\/         gradientDrawable.setColor(theme.colors[backgroundColor])         background = gradientDrawable     }      ...      } <\/code><\/pre>\n<p> \u0410\u0442\u0442\u0440\u0438\u0431\u0443\u0442 \u2014 \u044d\u0442\u043e \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0446\u0432\u0435\u0442, \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043c\u044b \u043d\u0435 \u0437\u043d\u0430\u0435\u043c, \u043a\u0430\u043a\u0430\u044f \u0441\u0435\u0439\u0447\u0430\u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u0430 \u0442\u0435\u043c\u0430, \u0442\u043e \u0435\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0438 \u043d\u0435 \u0434\u0443\u043c\u0430\u0435\u043c \u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u0445.<\/p>\n<p> \u041d\u0443 \u0438, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0445 \u0446\u0432\u0435\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0445 \u0442\u0435\u043c \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"kotlin\">enum class CoreTheme(     ...        val colors: Colors ) {      \/\/ \u0434\u043b\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u0442\u0435\u043c\u044b \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u0446\u0432\u0435\u0442\u0430     LIGHT(         colors = Colors(             primaryBackgroundColor = CoreColors.white,             secondaryBackgroundColor = CoreColors.white,             disabledBackgroundColor = CoreColors.grayMedium,             primaryTextColor = CoreColors.black,             selectableBackgroundColor = CoreColors.grayLight         )     ),      DARK(         colors = Colors(             primaryBackgroundColor = CoreColors.black,             secondaryBackgroundColor = CoreColors.grayBold,             disabledBackgroundColor = CoreColors.grayMedium,             primaryTextColor = CoreColors.white,             selectableBackgroundColor = CoreColors.grayLight         )     )  }<\/code><\/pre>\n<p> \u0412\u0440\u043e\u0434\u0435 \u0431\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u0441 \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u0430\u043c\u0438, \u0438\u0434\u0451\u043c \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<p> \u0421 \u0446\u0432\u0435\u0442\u0430\u043c\u0438 \u0438 \u0440\u0430\u0434\u0438\u0443\u0441\u043e\u043c \u0437\u0430\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u043e\u0441\u043e\u0431\u043e \u043d\u0435\u0442, \u044d\u0442\u043e \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c Int \u0438 Float \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u0430 \u0432\u043e\u0442 \u0441\u043e \u0448\u0440\u0438\u0444\u0442\u0430\u043c\u0438 \u043f\u043e\u0441\u043b\u043e\u0436\u043d\u0435\u0435, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d\u0438 \u0441\u0432\u044f\u0437\u0430\u043d\u044b \u0441 Android SDK: <\/p>\n<pre><code class=\"kotlin\">\/\/ \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0448\u0440\u0438\u0444\u0442 \u043d\u0443\u0436\u0435\u043d Context val robotoLight = Typeface.createFromAsset(context.assets, \"roboto_light.ttf\") val robotoRegular = Typeface.createFromAsset(context.assets, \"roboto_regular.ttf\") val robotoBold = Typeface.createFromAsset(context.assets, \"roboto_bold.ttf\")<\/code><\/pre>\n<p> \u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u043e \u0448\u0440\u0438\u0444\u0442\u0430\u043c\u0438 \u044f \u0440\u0435\u0448\u0438\u043b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441:<\/p>\n<pre><code class=\"kotlin\">object TypefaceManager {      \/\/ \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 MainActivity \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u0441\u043b\u0430\u0431\u0430\u044f \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 AssetManager     private var assetManagerReference: WeakReference&lt;AssetManager&gt;? = null     \/\/ \u0448\u0440\u0438\u0444\u0442\u044b \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437, \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043a\u044d\u0448\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0432 \u0445\u044d\u0448-\u0442\u0430\u0431\u043b\u0438\u0446\u0435     private val typefaces = hashMapOf&lt;String, Typeface&gt;()      fun setAssets(assetManager: AssetManager) {         assetManagerReference = WeakReference(assetManager)     }      \/\/ \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0448\u0440\u0438\u0444\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e AssetManager \u0438 \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u0442 \u0435\u0433\u043e     \/\/ \u0435\u0441\u043b\u0438 \u0448\u0440\u0438\u0444\u0442 \u0443\u0436\u0435 \u0431\u044b\u043b \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d, \u0442\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0435\u0433\u043e     fun typeface(weight: TypefaceWeight): Typeface {         val path = weight.assetPath         val savedTypeface = typefaces[path]         if (savedTypeface != null) {             return savedTypeface         }          val assetManager = assetManagerReference?.get()              ?: throw IllegalStateException(\"assetManager is null, first call setAssets\")         val typeface = Typeface.createFromAsset(assetManager, path)         typefaces[path] = typeface         typeface     }  }  \/\/ \u0448\u0440\u0438\u0444\u0442\u044b \u043b\u0435\u0436\u0430\u0442 \u0432 assets \u043f\u0430\u043f\u043a\u0435 Android \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f enum class TypefaceWeight(val assetPath: String) {     LIGHT(\"sf_pro_rounded_light.ttf\"),     REGULAR(\"sf_pro_rounded_regular.ttf\"),     MEDIUM(\"sf_pro_rounded_medium.ttf\"),     SEMI_BOLD(\"sf_pro_rounded_semibold.ttf\"),     BOLD(\"sf_pro_rounded_bold.ttf\") }<\/code><\/pre>\n<p> \u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u043e \u043a\u0440\u0430\u0448\u0430 \u0438\u0437-\u0437\u0430 \u043f\u0443\u0441\u0442\u043e\u0439 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 <code>AssetManager<\/code>, \u043d\u0430\u0434\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 \u0432 <code>MainActivity<\/code>:<\/p>\n<pre><code class=\"kotlin\">class MainActivity : Activity() {      override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)          TypefaceManager.setAssets(assets)          ...      }  }<\/code><\/pre>\n<p> \u0413\u043b\u044f\u043d\u0435\u043c, \u043a\u0430\u043a <code>TypefaceManager<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 TextView:<\/p>\n<pre><code class=\"kotlin\">open class CoreTextView @JvmOverloads constructor(     ctx: Context,     private val textColor: ColorAttributes = ColorAttributes.primaryTextColor,     private val textStyle: TypefaceAttribute = TypefaceAttribute.Body1 ): TextView(ctx), ThemeManager.ThemeManagerListener {      override fun onThemeChanged(insets: ThemeManager.WindowInsets, theme: CoreTheme) {         \/\/ fontFamily \u044d\u0442\u043e TypefaceWeight, \u0430 textSize \u2014 \u043e\u0431\u044b\u043a\u043d\u043e\u0432\u0435\u043d\u043d\u043e\u0435 Float \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435         val (fontFamily, textSize) = theme.textStyle[textStyle]         \/\/ \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0438\u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0448\u0440\u0438\u0444\u0442, \u0435\u0441\u043b\u0438 \u043e\u043d \u0431\u044b\u043b \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d         typeface = TypefaceManager.typeface(fontFamily)         \/\/ fontSize \u044d\u0442\u043e Kotlin Extension, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0440\u0430\u0437\u043c\u0435\u0440 \u0448\u0440\u0438\u0444\u0442\u0430 \u0432 sp \u0435\u0434\u0438\u043d\u0438\u0446\u0430\u0445         fontSize(textSize)         setTextColor(theme.colors[textColor])     }      ...  }<\/code><\/pre>\n<p> \u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435, \u0441\u0442\u0438\u043b\u044c \u0442\u0435\u043a\u0441\u0442\u0430 \u0445\u0440\u0430\u043d\u0438\u0442 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e <code>TypefaceWeight<\/code>, \u043d\u043e \u0438 \u0435\u0449\u0451 \u0440\u0430\u0437\u043c\u0435\u0440, \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"kotlin\">class TypefaceStyle(     private val title1: Pair&lt;TypefaceWeight, Float&gt;,     private val title2: Pair&lt;TypefaceWeight, Float&gt;,     private val body1: Pair&lt;TypefaceWeight, Float&gt;,     private val body2: Pair&lt;TypefaceWeight, Float&gt;,     private val caption1: Pair&lt;TypefaceWeight, Float&gt; ) {      \/\/ \u0442\u0430\u043a\u0436\u0435 \u043a\u0430\u043a \u0446\u0432\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u0442\u0438\u043b\u044c \u0442\u0435\u043a\u0441\u0442\u0430 \u043f\u043e \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u0443     operator fun get(attr: TypefaceAttribute): Pair&lt;TypefacePath, Float&gt; {         return when (attr) {             TypefaceAttribute.Title1 -&gt; title1             TypefaceAttribute.Title2 -&gt; title2             TypefaceAttribute.Body1 -&gt; body1             TypefaceAttribute.Body2 -&gt; body2             TypefaceAttribute.Caption1 -&gt; caption1         }     }  }  \/\/ \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0434\u043b\u044f \u0441\u0442\u0438\u043b\u0435\u0439 \u0442\u0435\u043a\u0441\u0442\u0430 enum class TypefaceAttribute {     Title1,     Title2,     Body1,     Body2,     Caption1 }<\/code><\/pre>\n<p> \u0417\u043d\u0430\u043a\u043e\u043c\u0430\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u0430, \u043d\u0435 \u043f\u0440\u0430\u0432\u0434\u0430 \u043b\u0438? (\u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0434\u043b\u044f \u0446\u0432\u0435\u0442\u043e\u0432 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u043f\u043e \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u0441\u0445\u0435\u043c\u0435)<\/p>\n<p> \u0422\u0430\u043a\u0436\u0435 \u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 <a href=\"https:\/\/developer.android.com\/reference\/android\/view\/ViewGroup.LayoutParams\">ViewGroup.LayoutParams<\/a> \u0438 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0448\u0442\u0443\u043a\u0430\u043c\u0438, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u0441 View, \u0447\u0442\u043e\u0431\u044b \u0432\u0435\u0440\u0441\u0442\u0430\u0442\u044c \u043a\u043e\u0434\u043e\u043c \u0431\u044b\u043b\u043e \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0449\u0435 \u0438 \u0443\u0434\u043e\u0431\u043d\u0435\u0435:<\/p>\n<pre><code class=\"kotlin\">val closeView = CoreImageButtonView(     ctx = context,     shape = ShapeAttribute.medium,     shapeTreatmentStrategy = ShapeTreatmentStrategy.StartBottomTopEndRounded() ) closeView.setOnClickListener { viewModel.navigateBack() } \/\/ Kotlin Extension \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0445 \u043e\u0442\u0441\u0442\u0443\u043f\u043e\u0432 closeView.padding(context.dp(12)) closeView.setImageResource(R.drawable.ic_close) \/\/ \u0443\u0434\u043e\u0431\u043d\u044b\u0439 builder \u0432 \u0441\u0442\u0438\u043b\u0435 Kotlin'\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f LayoutParams closeView.layoutParams(frameLayoutParams()     .width(context.dp(48))     .height(context.dp(48))     .gravity(Gravity.END)) titleContentView.addView(closeView)<\/code><\/pre>\n<p> \u041e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0438 \u043f\u0440\u0438\u043a\u043e\u043b\u044c\u043d\u044b\u0435 \u0444\u0438\u0448\u043a\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0430\u0445.<\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f MVI \u043d\u0430 \u0431\u0438\u0442\u043e\u0432\u044b\u0445 \u043c\u0430\u0441\u043a\u0430\u0445<\/font><\/h2>\n<p> \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0438\u0434\u0435\u044f MVI \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0435\u0434\u0438\u043d\u043e\u043c \u043d\u0435\u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c\u043e\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438, \u0434\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432 \u043f\u0435\u0440\u0435\u0440\u0438\u0441\u043e\u0432\u043a\u0438 UI \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0434\u043e \u043d\u0430\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043f\u043e \u043a\u0443\u0441\u043e\u0447\u043a\u0430\u043c.<\/p>\n<p> \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0432 \u043c\u043e\u0451\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435:<\/p>\n<pre><code class=\"kotlin\">\/\/ \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0434\u043b\u044f \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f var cachedState = SortingAlgorithmState() coroutineScope.launch {     viewModel.state.collect {          \/*         \u043c\u0435\u0442\u043e\u0434 difference() \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435          \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u043e\u0432\u043e\u0435 \u0441 \u0431\u0438\u0442\u043e\u0432\u043e\u0439 \u043c\u0430\u0441\u043a\u043e\u0439, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f          \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0431\u044b\u043b\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043e         *\/         val state = it.difference(cachedState); cachedState = it          \/\/ \u0435\u0441\u043b\u0438 \u0431\u044b\u043b \u0438\u0437\u043c\u0435\u043d\u0451\u043d \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438, \u0442\u043e         \/\/ \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0435 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043a\u0438         if (state.hasChanged(SortingAlgorithmState.selectedAlgorithmChanged)) {             with(state.selectedAlgorithm) {                 toolbarView.changeTitle(context.getString(title))                  worstTimeComplexityView.changeDescription(worstTimeComplexity)                 bestTimeComplexityView.changeDescription(bestTimeComplexity)                 averageTimeComplexityView.changeDescription(averageTimeComplexity)                 worstSpaceComplexityView.changeDescription(worstSpaceComplexity)             }         }          \/\/ \u0435\u0441\u043b\u0438 \u0431\u044b\u043b\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u043d\u043e\u043f\u043a\u0438 \"\u0421\u0442\u0430\u0440\u0442\", \u043c\u0435\u043d\u044f\u0435\u043c \u0438\u043a\u043e\u043d\u043a\u0443          \/\/ \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043d\u0430\u0436\u0430\u0442\u0438\u044f         if (state.hasChanged(SortingAlgorithmState.buttonStateChanged)) {             val (imageResource, clickListener) = when (state.buttonState) {                 SortingAnimationButtonState.NONE,                 SortingAnimationButtonState.PAUSED -&gt; R.drawable.ic_play to OnClickListener {                     sortingAlgorithmView.startAnimation()                     viewModel.toggleAnimation()                 }                 SortingAnimationButtonState.RUNNING -&gt; R.drawable.ic_pause to OnClickListener {                     sortingAlgorithmView.pauseAnimation()                     viewModel.toggleAnimation()                 }             }              playPauseButtonView.setImageResource(imageResource)             playPauseButtonView.setOnClickListener(clickListener)         }          \/\/ \u0435\u0441\u043b\u0438 \u0431\u044b\u043b \u0438\u0437\u043c\u0435\u043d\u0451\u043d \u043c\u0430\u0441\u0441\u0438\u0432 \u0434\u043b\u044f \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438, \u043c\u0435\u043d\u044f\u0435\u043c \u0435\u0433\u043e         if (state.hasChanged(SortingAlgorithmState.sortingArrayChanged)) {             sortingAlgorithmView.changeArray(state.sortingArray)         }          \/\/ \u0435\u0441\u043b\u0438 \u0431\u044b\u043b \u0438\u0437\u043c\u0435\u043d\u0451\u043d \u043c\u0430\u0441\u0441\u0438\u0432 \u0434\u043b\u044f \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438 \u0438\u043b\u0438 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438,          \/\/ \u043c\u0435\u043d\u044f\u0435\u043c \u0448\u0430\u0433\u0438 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438         if (state.hasChanged(SortingAlgorithmState.selectedAlgorithmChanged)             or state.hasChanged(SortingAlgorithmState.sortingArrayChanged)) {             sortingAlgorithmView.changeAnimationSteps(state.steps(resources))         }      } }<\/code><\/pre>\n<p> \u0410\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0439:<\/p>\n<ol>\n<li>\u0411\u0435\u0440\u0451\u043c \u043d\u043e\u0432\u043e\u0435 \u0438 \u0441\u0442\u0430\u0440\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f, \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u043c \u0438\u0445 c \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 <code>SortingAlgorithmState.difference()<\/code>.<\/li>\n<li>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u043e\u0432\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441 \u0431\u0438\u0442\u043e\u0432\u043e\u0439 \u043c\u0430\u0441\u043a\u043e\u0439, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0432\u0441\u0451, \u0447\u0442\u043e \u0431\u044b\u043b\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043e.<\/li>\n<li>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0431\u044b\u043b\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043e, \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c UI<\/li>\n<\/ol>\n<p> \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0435\u043d <code>SortingAlgorithmState<\/code>:<\/p>\n<pre><code class=\"kotlin\">class SortingAlgorithmState(     val selectedAlgorithm: SortingAlgorithm = EmptyAlgorithm(),     val buttonState: SortingAnimationButtonState = SortingAnimationButtonState.NONE,     val sortingArray: IntArray = intArrayOf(),     \/\/ \u0431\u0438\u0442\u043e\u0432\u0430\u044f \u043c\u0430\u0441\u043a\u0430, \u0435\u0441\u043b\u0438 \u0432\u0441\u0435 \u0431\u0438\u0442\u044b \u0440\u0430\u0432\u043d\u044b \u043d\u0443\u043b\u044e, \u0437\u043d\u0430\u0447\u0438\u0442, \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c      private val compared: Int = 7 \/\/ low byte: 00000111 ) {      \/\/ \u0435\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0431\u0438\u0442 (pieceState) \u0440\u0430\u0432\u0435\u043d \u0435\u0434\u0438\u043d\u0438\u0446\u0435, \u0437\u043d\u0430\u0447\u0438\u0442      \/\/ \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043e     fun hasChanged(pieceState: Int): Boolean =          (compared and pieceState) == pieceState      \/*     \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c (other)      \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u043e\u0432\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441 \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d\u043d\u043e\u0439 \u0431\u0438\u0442\u043e\u0432\u043e\u0439 \u043c\u0430\u0441\u043a\u043e\u0439,     \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0432\u0441\u0451, \u0447\u0442\u043e \u0431\u044b\u043b\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043e     *\/     fun difference(other: SortingAlgorithmState): SortingAlgorithmState {         var compared = 0          \/\/ \u0435\u0441\u043b\u0438 \u0431\u044b\u043b \u0438\u0437\u043c\u0435\u043d\u0451\u043d \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0431\u0438\u0442         if (selectedAlgorithm != other.selectedAlgorithm) {             compared = compared or selectedAlgorithmChanged         }          \/\/ \u0435\u0441\u043b\u0438 \u043f\u043e\u043c\u0435\u043d\u044f\u043b\u043e\u0441\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u043d\u043e\u043f\u043a\u0438, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0431\u0438\u0442         if (buttonState != other.buttonState) {             compared = compared or buttonStateChanged         }          \/\/ \u0435\u0441\u043b\u0438 \u043f\u043e\u043c\u0435\u043d\u044f\u043b\u0441\u044f \u043c\u0430\u0441\u0441\u0438\u0432 \u0434\u043b\u044f \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0431\u0438\u0442         if (!sortingArray.contentEquals(other.sortingArray)) {             compared = compared or sortingArrayChanged         }          return SortingAlgorithmState(selectedAlgorithm, buttonState, sortingArray, compared)     }      fun steps(resources: Resources): List&lt;SortingAlgorithmStep&gt; {         return selectedAlgorithm.sort(sortingArray.copyOf(), resources)     }      fun changedWith(selectedAlgorithm: SortingAlgorithm) =         SortingAlgorithmState(selectedAlgorithm, buttonState, sortingArray)      fun changedWith(buttonState: SortingAnimationButtonState) =         SortingAlgorithmState(selectedAlgorithm, buttonState, sortingArray)      fun changedWith(sortingArray: IntArray) =         SortingAlgorithmState(selectedAlgorithm, buttonState, sortingArray)      companion object {         \/\/ \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u0431\u0438\u0442\u0430         const val selectedAlgorithmChanged: Int = 1 \/\/ low byte: 00000001         const val buttonStateChanged: Int = 2 \/\/ low byte: 00000010         const val sortingArrayChanged: Int = 4 \/\/ low byte: 00000100     } }<\/code><\/pre>\n<p> \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c, \u043a\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442:<\/p>\n<ol>\n<li>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u044d\u043a\u0440\u0430\u043d \u0441 \u043a\u0443\u0447\u0435\u0439 \u043a\u043d\u043e\u043f\u043e\u043a, \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0438 \u0442\u0435\u043a\u0441\u0442\u0430, \u0441\u0435\u0439\u0447\u0430\u0441 \u043e\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 X.<\/li>\n<li>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043a\u043d\u043e\u043f\u043a\u0443, \u043d\u0435\u0432\u0430\u0436\u043d\u043e \u043a\u0430\u043a\u0443\u044e, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043a\u0430\u043a\u0430\u044f-\u0442\u043e \u043b\u043e\u0433\u0438\u043a\u0430 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u0442\u0435\u043f\u0435\u0440\u044c \u043e\u043d\u043e Y.<\/li>\n<li>\u0422\u0430\u043a \u043a\u0430\u043a \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043e, \u043d\u0430\u0434\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c UI, \u043d\u043e \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0441\u0451 \u0431\u0435\u0437 \u0440\u0430\u0437\u0434\u0443\u043c\u0438\u0439, \u044d\u0442\u043e \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0434\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0442\u043e, \u0447\u0442\u043e \u0431\u044b\u043b\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043e, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 X \u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 Y, \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0431\u0438\u0442\u044b \u0434\u043b\u044f \u0442\u0435\u0445 \u0448\u0442\u0443\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u044b\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u044b.<\/li>\n<li>\u041a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432 UI, \u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0447\u0442\u043e \u0431\u044b\u043b\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043e, \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u043e \u043a\u0443\u0441\u043e\u0447\u043a\u0430\u043c.<\/li>\n<\/ol>\n<p> \u0412\u043e\u0442 \u0438 \u0432\u0441\u0451, \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0431\u0438\u0442\u043e\u0432\u043e\u0439 \u0430\u0440\u0438\u0444\u043c\u0435\u0442\u0438\u043a\u0438 \u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e, \u043a\u0441\u0442\u0430\u0442\u0438, \u0432 Jetpack Compose \u0442\u043e\u0436\u0435 \u0435\u0441\u0442\u044c \u043f\u043e\u0445\u043e\u0436\u0438\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u043d \u0441\u043a\u0440\u044b\u0442 \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c.<\/p>\n<h2><font color=\"#3AC1EF\">\u258d \u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/font><\/h2>\n<p> \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u044f \u043d\u0435 \u0441\u043c\u043e\u0433 \u0434\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0434\u043e \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0439 View, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0440\u0438\u0441\u0443\u0435\u0442 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438, \u0432\u043e\u043e\u0431\u0449\u0435, \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0446\u0435\u043b\u044c\u044e \u0441\u0442\u0430\u0442\u044c\u0438 \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0431\u044b\u043b\u043e \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043a\u0430\u043a \u0443\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u044d\u0442\u0430 \u0441\u0430\u043c\u0430\u044f \u0432\u044c\u044e\u0448\u043a\u0430, \u043d\u043e \u0442\u0430\u043a \u0443\u0436 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u044f \u043d\u0430\u0447\u0430\u043b \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0438 \u0441\u0442\u0430\u0442\u044c\u044f \u0440\u0430\u0437\u0440\u043e\u0441\u043b\u0430\u0441\u044c \u043f\u0440\u0438\u043b\u0438\u0447\u043d\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443, \u0435\u0441\u043b\u0438 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0437\u0430\u0439\u0434\u0451\u0442, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u043d\u0430\u0447\u0435\u0440\u043a\u0430\u044e \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c\u2026<\/p>\n<p> \u041f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0441\u0441\u044b\u043b\u043a\u0438:<\/p>\n<ol>\n<li><a href=\"https:\/\/t.me\/android_under_the_hood\">\u041c\u043e\u0439 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u043a\u0430\u043d\u0430\u043b.<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/DmitryTsyvtsyn\/AlgoSortingAnimations\">Github \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a><\/li>\n<li><a href=\"https:\/\/habr.com\/en\/users\/DmitryTsyvtsyn\/publications\/articles\/\">\u0414\u0440\u0443\u0433\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438.<\/a><\/li>\n<\/ol>\n<p> \u041f\u0438\u0448\u0438\u0442\u0435 \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0445 \u0432\u0430\u0448\u0435 \u043c\u043d\u0435\u043d\u0438\u0435 \u0438 \u0432\u0441\u0435\u043c \u0445\u043e\u0440\u043e\u0448\u0435\u0433\u043e \u043a\u043e\u0434\u0430!<\/p>\n<p> \u00a9 2024 \u041e\u041e\u041e \u00ab\u041c\u0422 \u0424\u0418\u041d\u0410\u041d\u0421\u00bb<\/p>\n<blockquote><p><b><font color=\"#3AC1EF\"><a href=\"https:\/\/t.me\/ruvds_community\">Telegram-\u043a\u0430\u043d\u0430\u043b \u0441\u043e \u0441\u043a\u0438\u0434\u043a\u0430\u043c\u0438, \u0440\u043e\u0437\u044b\u0433\u0440\u044b\u0448\u0430\u043c\u0438 \u043f\u0440\u0438\u0437\u043e\u0432 \u0438 \u043d\u043e\u0432\u043e\u0441\u0442\u044f\u043c\u0438 IT \ud83d\udcbb<\/a><\/font><\/b><\/p><\/blockquote>\n<p><a href=\"http:\/\/ruvds.com\/ru-rub?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=DmitryTsyvtsyn&amp;utm_content=kak_ya_pisal_superkastomizirovannoe_android_prilozhenie_v_2024_godu\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/webt\/7u\/o0\/_9\/7uo0_9itb0bkl_y8_0_n1a57vuc.jpeg\" data-src=\"https:\/\/habrastorage.org\/webt\/7u\/o0\/_9\/7uo0_9itb0bkl_y8_0_n1a57vuc.jpeg\" data-blurred=\"true\"\/><\/a><\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/851256\/\"> https:\/\/habr.com\/ru\/articles\/851256\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><a href=\"https:\/\/habr.com\/en\/companies\/ruvds\/articles\/851256\/\"><\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/webt\/aw\/cs\/dk\/awcsdkn8nxulfgozpnwidutz3su.jpeg\" data-src=\"https:\/\/habrastorage.org\/webt\/aw\/cs\/dk\/awcsdkn8nxulfgozpnwidutz3su.jpeg\" data-blurred=\"true\"\/><\/div>\n<p><\/a><i><font color=\"#999999\">\u041a\u0430\u043a \u044f \u043f\u0438\u0441\u0430\u043b \u0441\u0443\u043f\u0435\u0440 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 Android \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 2024 \u0433\u043e\u0434\u0443<\/font><\/i><\/p>\n<p> \u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0433\u043e\u0434\u0430 \u0443 \u043c\u0435\u043d\u044f \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u043f\u0440\u0438\u043a\u043e\u043b\u044c\u043d\u0430\u044f \u0438\u0434\u0435\u044f: \u0441\u0434\u0435\u043b\u0430\u0442\u044c Android-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438. \u0427\u0442\u043e\u0431\u044b \u0432\u044b \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u043d\u044f\u043b\u0438, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0437 \u0441\u0435\u0431\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043d\u0430 <a href=\"https:\/\/github.com\/DmitryTsyvtsyn\/AlgoSortingAnimations\">GitHub<\/a> \u0435\u0441\u0442\u044c \u0441\u043a\u0440\u0438\u043d\u044b \u0438 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0435 \u0432\u0438\u0434\u0435\u043e. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e \u043a\u0443\u0441\u043e\u0447\u043a\u0430\u043c \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u043c\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-437757","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/437757","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=437757"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/437757\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=437757"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=437757"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=437757"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}