当前位置: 首页 > news >正文

静海商城网站建设网页设计跳转链接怎么制作

静海商城网站建设,网页设计跳转链接怎么制作,网站申请名称,seo没什么作用了本文字数#xff1a;4680字 预计阅读时间#xff1a;30分钟 01 简介 之前我们探讨过KMM#xff0c;即Kotlin Multiplatform Mobile#xff0c;是Kotlin发布的移动端跨平台框架。当时的结论是KMM提倡将共有的逻辑部分抽出#xff0c;由KMM封装成Android(Kotlin/JVM)的aar和… 本文字数4680字 预计阅读时间30分钟 01 简介 之前我们探讨过KMM即Kotlin Multiplatform Mobile是Kotlin发布的移动端跨平台框架。当时的结论是KMM提倡将共有的逻辑部分抽出由KMM封装成Android(Kotlin/JVM)的aar和iOS(Kotlin/Native)的framework再提供给View层进行调用从而节约一部分的工作量。共享的是逻辑而不是UI。1 其实在这个时候我们就知道Kotlin在移动端的跨平台绝对不是想止于逻辑层的共享随着Compose的日渐成熟JetBrains推出了Compose-Multiplatform从UI层面上实现移动端Web端桌面端的跨平台。考虑到屏幕大小与交互方式的不同Android和iOS之间的共享会极大的促进开发效率。比如现在已经非常成熟的Flutter。令人兴奋的是Compose-Multiplatform目前已经发布了支持iOS系统的alpha版本虽然还在开发实验阶段但我们已经开始尝试用起来了。 02 Jetpack-Compose与 Compose-Multiplatform 作为Android开发Jetpack-Compose我们再熟悉不过了是Google针对Android推出的新一代声明式UI工具包完全基于Kotlin打造天然具备了跨平台的使用基础。JetBrains以Jetpack-Compose为基础相继发布了compose-desktopcompose-web和compose-iOS 使Compose可以运行在更多不同平台也就是我们今天要讲的Compose-Multiplatform。在通用的API上Compose-Multiplatform与Jetpack-Compose时刻保持一致不同的只是包名发生了变化。因此作为Android开发我们在使用Compose-Multiplatform时可以将Jetpack-Compose代码低成本地迁移到Compose-Multiplatform 03 使用 既然是UI框架那么我们就来实现一个简单的在移动端非常常规的业务需求 从服务器请求数据并以列表形式展现在UI上。 在此我们要说明的是Compose-Multiplatform是要与KMM配合使用的其中KMM负责把shared模块编译成Android的aar和iOS的frameworkCompose-Multiplatform负责UI层面的交互与绘制的实现。 首先我们先回顾一下KMM工程的组织架构 其中androidApp和iosApp分别为Android和iOS这两个平台的主工程模块shared为共享逻辑模块供androidApp和iosApp调用。shared模块中 commonMain为公共模块该模块的代码与平台无关是通过expected关键字对一些api的声明声明的实现在platform module中androidMain和iosMain分别Android和ios这两个平台通过actual关键字在平台模块进行具体的实现。 关于kmm工程的配置与使用方式运行方式编译过程原理还是请回顾一下之前的文章在此不做赘述。2 接下来我们看Compose-Multiplatform是怎么基于kmm工程进行的实现。  1、添加配置 在settings.gradle文件中声明compose插件 plugins{ //...val composeVersion  extra[compose.version] as Stringid(org.jetbrains.compose).version(composeVersion)} 其中compose.version在gradle.properties进行了声明。需要注意的是目前Compose-Multiplatform的版本有要求目前可以参考官方的具体配置。3 #Versions kotlin.version1.8.20 agp.version7.4.2 compose.version1.4.0 之后在shared模块的build.gradle文件中引用声明好的插件如下 plugins { //...id(org.jetbrains.compose) } 同时我们需要在build.gradle文件中配置compose静态资源文件的目录方式如下 Android: android { //...sourceSets[main].resources.srcDirs(src/commonMain/resources) } iOS: cocoapods { //...extraSpecAttributes[resources] [src/commonMain/resources/**, src/iosMain/resources/**]} 这意味着在寻找如图片等资源文件时将从src/commonMain/resources/这个目录下寻找如下图所示 由于目前compose-iOS还处于实验阶段我们需要在gradle.properties文件中添加如下代码开启UIKit org.jetbrains.compose.experimental.uikit.enabledtrue 最后我们需要在为commonMain添加compose依赖 val commonMain by getting {dependencies { //...implementation(compose.runtime)implementation(compose.foundation)implementation(compose.material) //                //implementation(compose.materialIconsExtended) // TODO not working on iOS for nowOptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)implementation(compose.components.resources)implementation(compose.ui)}} 好了到此为止我们的配置就完成了接下来开始写业务代码了。既然是从服务器获取数据我们肯定得封装一个网络模块下面我们将使用ktor封装一个简单的网络模块。  2、网络模块 先我们先在shared模块的build.gradle文件中添加依赖如下 val commonMain by getting {dependencies {implementation(io.ktor:ktor-client-core:$ktor_version)//coreimplementation(io.ktor:ktor-client-cio:$ktor_version)//CIOimplementation(io.ktor:ktor-client-logging:$ktor_version)//Loggingimplementation(io.ktor:ktor-client-content-negotiation:$ktor_version)implementation(io.ktor:ktor-serialization-kotlinx-json:$ktor_version)//Json格式化 //...}} 接下来我们封装一个最简单的HttpUtil包含post和get请求 package com.example.sharesampleimport io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.engine.cio.* import io.ktor.client.plugins.* import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.plugins.logging.* import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* import kotlinx.coroutines.* import kotlinx.serialization.json.Jsonclass HttpUtil{companion object{val client: HttpClient  HttpClient(CIO) {expectSuccess  trueengine {maxConnectionsCount  1000requestTimeout  30000endpoint {maxConnectionsPerRoute  100pipelineMaxSize  20keepAliveTime  30000connectTimeout  30000}}install(Logging) {logger  Logger.DEFAULTlevel  LogLevel.HEADERS}install(ContentNegotiation) {json(Json {ignoreUnknownKeys  trueisLenient  trueencodeDefaults  false})}}suspend inline fun reified T get(url: String,//请求地址): T?  {return try {val response: HttpResponse  client.get(url) {//GET请求contentType(ContentType.Application.Json)//content-type}val data: T  response.body()data} catch (e: ResponseException) {print(e.response)null} catch (e: Exception) {print(e.message)null}}suspend inline fun reified T post(url: String,): T?  {//coroutines 中的IO线程return try {val response: HttpResponse  client.post(url) {//POST请求contentType(ContentType.Application.Json)//content-type}val data: T  response.body()data} catch (e: ResponseException) {print(e.response)null} catch (e: Exception) {print(e.message)null}}} } 代码非常直观定义了HttpClient对象进行了基础的设置来实现网络请求。我们来定义一下接口请求返回的数据结构。 3、返回的数据结构 package com.example.sharesample.beankotlinx.serialization.Serializable class SearchResult {var count: Int?  nullvar resInfos: ListResInfoBean?  null } package com.example.sharesample.beankotlinx.serialization.Serializable class ResInfoBean {var name: String?  nullvar desc: String?  null } 接下来我们看看是怎么发送的请求。 4、发送请求 然后我们定义个SearchApi package com.example.sharesampleimport androidx.compose.material.Text import androidx.compose.runtime.* import com.example.sharesample.bean.SearchResult import io.ktor.client.plugins.logging.* import kotlinx.coroutines.*class SearchApi {suspend fun search(): SearchResult {Logger.SIMPLE.log(search2)var result: SearchResult? HttpUtil.get(url  http://h5-yapi.sns.sohuno.com/mock/229/api/v1/resInfo/search)if (result  null) {result  SearchResult()}return result} } 实现了search()方法。接着我们来看view层的实现与数据的绑定是如何实现的。 5、View层的实现 我们创建一个SearchCompose package com.example.sharesampleimport androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.example.sharesample.bean.SearchResult import io.ktor.client.plugins.logging.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.async import kotlinx.coroutines.job import kotlinx.coroutines.launch import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.resourceclass SearchCompose {private val searchApi  SearchApi()private var isInit  falseOptIn(ExperimentalResourceApi::class)Composablefun searchCompose() {var searchResult by remember { mutableStateOfSearchResult(SearchResult()) }if (!isInit) {scope().launch {val result  async {searchApi.search()}searchResult  result.await()}isInit  true}Column {Text(Total: ${searchResult.count ?: 0},style  TextStyle(fontSize  20.sp),modifier  Modifier.padding(start  20.dp, top  20.dp))val scrollState  rememberLazyListState()if (searchResult.resInfos ! null) {LazyColumn(state  scrollState,modifier  Modifier.padding(top  14.dp,bottom  50.dp,end  14.dp,start  14.dp)) {items(searchResult.resInfos!!) { item -Box(modifier  Modifier.padding(top  20.dp).fillMaxWidth().background(color  Color.LightGray, shape  RoundedCornerShape(10.dp)).padding(all  20.dp)) {Column {Row(verticalAlignment  Alignment.CenterVertically) {val picture  1.jpgvar imageBitmap: ImageBitmap? by remember(picture) {mutableStateOf(null)}LaunchedEffect(picture) {try {imageBitmap resource(picture).readBytes().toImageBitmap()} catch (e: Exception) {}}if (imageBitmap ! null) {Image(bitmap  imageBitmap!!, , modifier  Modifier.size(60.dp).clip(RoundedCornerShape(10.dp)))}Text(item.name ?: name,style  TextStyle(color  Color.Yellow),modifier  Modifier.padding(start  10.dp))}Text(item.desc ?: desc, style  TextStyle(color  Color.White))}}}}}}} }Composable fun scope(): CoroutineScope {var viewScope  rememberCoroutineScope()return remember {CoroutineScope(SupervisorJob(viewScope.coroutineContext.job)  ioDispatcher)} } 在searchCompose()里我们看到了在发送请求时开启了一个协程scope()方法指定了作用域除此之外我们还定义了ioDispatcher在不同平台下的实现具体的声明如下 expect val ioDispatcher: CoroutineDispatcher 在Android上的实现 actual val ioDispatcher  Dispatchers.IO 在ios上的实现 actual val ioDispatcher  Dispatchers.IO 需要注意的是Android平台Dispatchers.IO在jvmMain/Dispatchersios平台Dispatchers.IO在nativeMain/Dispatchers下。两者是不一样的。在获取了服务端数据后我们使用LazyColumn对列表进行实现。其中有图片和文本的展示。为了方便进行说明图片数据我们使用本地resources目录下的图片文本展示的是服务端返回的数据。下面我来说明一下图片的加载。 6、图片加载 具体的实现如下 val picture  1.jpg var imageBitmap: ImageBitmap? by remember(picture) {mutableStateOf(null) } LaunchedEffect(picture) {try {imageBitmap resource(picture).readBytes().toImageBitmap()} catch (e: Exception) {}} if (imageBitmap ! null) {Image(bitmap  imageBitmap!!, , modifier  Modifier.size(60.dp).clip(RoundedCornerShape(10.dp))) } 先创建了一个ImageBitmap的remember对象由于resource(picture).readBytes()是个挂起函数我们需要用LaunchedEffect来执行。这段代码的作用是从resources目录下读取资源到内存中然后我们在不同平台实现了toImageBitmap()将它转换成Bitmap。 toImageBitmap()的声明 expect fun ByteArray.toImageBitmap(): ImageBitmap Android端的实现 fun ByteArray.toAndroidBitmap(): Bitmap {return BitmapFactory.decodeByteArray(this, 0, size) } iOS端的实现 actual fun ByteArray.toImageBitmap(): ImageBitmap Image.makeFromEncoded(this).toComposeImageBitmap() 好了通过以上的方式我们就可以实现对本地图片的加载到此为止Compose的相应实现就完成了。那么它是怎么被Android和ios的view引用的呢Android端我们已经非常熟悉了和Jetpack-Compose的调用方式一样在MainActivity中直接调用即可 class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyApplicationTheme {Surface(modifier  Modifier.fillMaxSize(),color  MaterialTheme.colors.background) {SearchCompose().searchCompose()}}}} } ios端会稍微麻烦一点。我们先来看一下iosApp模块下iOSApp.swift的实现 import UIKit import sharedUIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {var window: UIWindow?func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) - Bool {window  UIWindow(frame: UIScreen.main.bounds)let mainViewController  Main_iosKt.MainViewController()window?.rootViewController  mainViewControllerwindow?.makeKeyAndVisible()return true} } 关键代码是这两行 let mainViewController  Main_iosKt.MainViewController()window?.rootViewController  mainViewController 创建了一个MainViewController对象然后赋给window的rootViewController。这个MainViewController是在哪儿怎么定义的呢我们回到shared模块定义一个main.ios的文件它会在framework编译成Main_iosKt文件。main.ios的实现如下 package com.example.sharesampleimport androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.ui.Modifier import androidx.compose.ui.window.ComposeUIViewController import platform.UIKit.UIViewControllerSuppress(FunctionName, unused) fun MainViewController(): UIViewController ComposeUIViewController {MaterialTheme {Surface(modifier  Modifier.fillMaxSize(),color  MaterialTheme.colors.background) {SearchCompose().searchCompose()}}} 我们看到在这儿会创建一个UIViewController对象MainViewController。这个便是ios端和Compose链接的桥梁。接下来我们来看看在Android和ios上的效果。 Android端 iOS端 好了到此为止我们看到了一个简单的列表业务逻辑是怎样实现的了。由于Compose-Multiplatform还未成熟在业务实现上势必有很多内容需要自己造轮子。  04 Android端的compose绘制原理 由于网上已经有很多Compose的相关绘制原理下一章我们只是进行简单的源码解析来说明它是如何生成UI树并进行自绘的。 1、Android端的compose绘制原理 Android端是在从onCreate()里实现setContent()开始的 setContent {MyApplicationTheme {Surface(modifier  Modifier.fillMaxSize(),color  MaterialTheme.colors.background) {SearchCompose().searchCompose()}}} setContent()的实现如下: public fun ComponentActivity.setContent(parent: CompositionContext?  null,content: Composable () - Unit ) {val existingComposeView  window.decorView.findViewByIdViewGroup(android.R.id.content).getChildAt(0) as? ComposeViewif (existingComposeView ! null) with(existingComposeView) {setParentCompositionContext(parent)setContent(content)} else ComposeView(this).apply {// Set content and parent **before** setContentView// to have ComposeView create the composition on attachsetParentCompositionContext(parent)setContent(content)// Set the view tree owners before setting the content view so that the inflation process// and attach listeners will see them already presentsetOwners()setContentView(this, DefaultActivityContentLayoutParams)} } 我们看到它主要是生成了ComposeView然后通过setContent(content)将compose的内容注册到ComposeView里其中ComposeView继承ViewGroup然后调用ComponentActivity的setContentView()方法将ComposeView添加到DecorView中相应的子View中。通过追踪ComposeView的setContent方法 private fun doSetContent(owner: AndroidComposeView,parent: CompositionContext,content: Composable () - Unit ): Composition {if (inspectionWanted(owner)) {owner.setTag(R.id.inspection_slot_table_set,Collections.newSetFromMap(WeakHashMapCompositionData, Boolean()))enableDebugInspectorInfo()}// 创建Composition对象传入UiApplierval original  Composition(UiApplier(owner.root), parent)val wrapped  owner.view.getTag(R.id.wrapped_composition_tag)as? WrappedComposition?: WrappedComposition(owner, original).also {owner.view.setTag(R.id.wrapped_composition_tag, it)}// 传入content函数wrapped.setContent(content)return wrapped } 我们发现主要做了两件事情 1.创建Composition对象传入UiApplier2.传入content函数 其中UiApplier的定义如下 internal class UiApplier(root: LayoutNode ) : AbstractApplierLayoutNode(root) 持有一个LayoutNode对象它的说明如下 An element in the layout hierarchy, built with compose UI 可以看到LayoutNode就是在Compose渲染的时候每一个组件就是一个LayoutNode最终组成一个LayoutNode树来描述UI界面。LayoutNode是怎么创建的呢 1LayoutNode 我们假设创建一个Image来看看Image的实现 fun Image(painter: Painter,contentDescription: String?,modifier: Modifier  Modifier,alignment: Alignment  Alignment.Center,contentScale: ContentScale  ContentScale.Fit,alpha: Float  DefaultAlpha,colorFilter: ColorFilter?  null ) { //...Layout({},modifier.then(semantics).clipToBounds().paint(painter,alignment  alignment,contentScale  contentScale,alpha  alpha,colorFilter  colorFilter)) { _, constraints -layout(constraints.minWidth, constraints.minHeight) {}} } 继续追踪Layout()的实现 Composable inline fun Layout(content: Composable UiComposable () - Unit,modifier: Modifier  Modifier,measurePolicy: MeasurePolicy ) {val density  LocalDensity.currentval layoutDirection  LocalLayoutDirection.currentval viewConfiguration  LocalViewConfiguration.currentReusableComposeNodeComposeUiNode, ApplierAny(factory  ComposeUiNode.Constructor,update  {set(measurePolicy, ComposeUiNode.SetMeasurePolicy)set(density, ComposeUiNode.SetDensity)set(layoutDirection, ComposeUiNode.SetLayoutDirection)set(viewConfiguration, ComposeUiNode.SetViewConfiguration)},skippableUpdate  materializerOf(modifier),content  content) }Composable ExplicitGroupsComposable inline fun T, reified E : Applier* ReusableComposeNode(noinline factory: () - T,update: DisallowComposableCalls UpdaterT.() - Unit,noinline skippableUpdate: Composable SkippableUpdaterT.() - Unit,content: Composable () - Unit ) {if (currentComposer.applier !is E) invalidApplier()currentComposer.startReusableNode()if (currentComposer.inserting) {currentComposer.createNode(factory)} else {currentComposer.useNode()}UpdaterT(currentComposer).update()SkippableUpdaterT(currentComposer).skippableUpdate()currentComposer.startReplaceableGroup(0x7ab4aae9)content()currentComposer.endReplaceableGroup()currentComposer.endNode() } 在这里创建了ComposeUiNode对象而LayoutNode就是ComposeUiNode的实现类。我们再来看看Composition。 2Composition 从命名来看Composition的作用就是将LayoutNode组合起来。其中WrappedComposition继承Composition private class WrappedComposition(val owner: AndroidComposeView,val original: Composition ) : Composition, LifecycleEventObserver 我们来追踪一下它的setContent()的实现 override fun setContent(content: Composable () - Unit) {owner.setOnViewTreeOwnersAvailable {if (!disposed) {val lifecycle  it.lifecycleOwner.lifecyclelastContent  contentif (addedToLifecycle  null) {addedToLifecycle  lifecycle// this will call ON_CREATE synchronously if we already createdlifecycle.addObserver(this)} else if (lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {original.setContent {Suppress(UNCHECKED_CAST)val inspectionTable owner.getTag(R.id.inspection_slot_table_set) as?MutableSetCompositionData?: (owner.parent as? View)?.getTag(R.id.inspection_slot_table_set)as? MutableSetCompositionDataif (inspectionTable ! null) {inspectionTable.add(currentComposer.compositionData)currentComposer.collectParameterInformation()}LaunchedEffect(owner) { owner.boundsUpdatesEventLoop() }CompositionLocalProvider(LocalInspectionTables provides inspectionTable) {ProvideAndroidCompositionLocals(owner, content)}}}}}} 在页面的生命周期是CREATED的状态下执行original.setContent() override fun setContent(content: Composable () - Unit) {check(!disposed) { The composition is disposed }this.composable  contentparent.composeInitial(this, composable)} 调用parent的composeInitial()方法这段代码我们就不再继续追踪下去了它最终的作用就是对布局进行组合创建父子依赖关系。  3Measure和Layout 在AndroidComposeView中的dispatchDraw()实现了measureAndLayout()方法 override fun measureAndLayout(sendPointerUpdate: Boolean) {trace(AndroidOwner:measureAndLayout) {val resend  if (sendPointerUpdate) resendMotionEventOnLayout else nullval rootNodeResized  measureAndLayoutDelegate.measureAndLayout(resend)if (rootNodeResized) {requestLayout()}measureAndLayoutDelegate.dispatchOnPositionedCallbacks()}}fun measureAndLayout(onLayout: (() - Unit)?  null): Boolean {var rootNodeResized  falseperformMeasureAndLayout {if (relayoutNodes.isNotEmpty()) {relayoutNodes.popEach { layoutNode -val sizeChanged  remeasureAndRelayoutIfNeeded(layoutNode)if (layoutNode  root  sizeChanged) {rootNodeResized  true}}onLayout?.invoke()}}callOnLayoutCompletedListeners()return rootNodeResized} 调用remeasureAndRelayoutIfNeeded遍历relayoutNodes为每一个LayoutNode去进行measure和layout。具体的实现不分析了。 4绘制 我们还是以Image举例 fun Image(bitmap: ImageBitmap,contentDescription: String?,modifier: Modifier  Modifier,alignment: Alignment  Alignment.Center,contentScale: ContentScale  ContentScale.Fit,alpha: Float  DefaultAlpha,colorFilter: ColorFilter?  null,filterQuality: FilterQuality  DefaultFilterQuality ) {val bitmapPainter  remember(bitmap) { BitmapPainter(bitmap, filterQuality  filterQuality) }Image(painter  bitmapPainter,contentDescription  contentDescription,modifier  modifier,alignment  alignment,contentScale  contentScale,alpha  alpha,colorFilter  colorFilter) } 主要的绘制工作是由BitmapPainter完成的它继承自Painter。 override fun DrawScope.onDraw() {drawImage(image,srcOffset,srcSize,dstSize  IntSize(thisonDraw.size.width.roundToInt(),thisonDraw.size.height.roundToInt()),alpha  alpha,colorFilter  colorFilter,filterQuality  filterQuality)} 在onDraw()方法里实现了drawImage() override fun drawImage(image: ImageBitmap,srcOffset: IntOffset,srcSize: IntSize,dstOffset: IntOffset,dstSize: IntSize,/*FloatRange(from  0.0, to  1.0)*/alpha: Float,style: DrawStyle,colorFilter: ColorFilter?,blendMode: BlendMode,filterQuality: FilterQuality)  drawParams.canvas.drawImageRect(image,srcOffset,srcSize,dstOffset,dstSize,configurePaint(null, style, alpha, colorFilter, blendMode, filterQuality)) 而最终也是在Canvas上进行了绘制。通过以上的分析我们了解到Compose并不是和原生控件一一映射的关系而是像Flutter一样有自己的UI组织方式并最终调用自绘引擎直接在Canvas上进行绘制的。在Android和iOS端使用的自绘引擎是skiko。这个skiko是什么呢它其实是Skia for Kotlin的缩写(Flutter在移动端也是用的Skia引擎进行的绘制)。事实上不止是在移动端我们可以通过以下的截图看到Compose的桌面端和Web端的绘制实际上也是用了skiko 关于skiko的更多信息还请查阅文末的官方链接。4 好了到此为止Compose的在Android端的绘制原理我们就讲完了。对其他端绘制感兴趣的同学可自行查看相应的源码细节有不同但理念都是一致的创建自己的Compose树并最终调用自绘引擎在Canvas上进行绘制。 05 Compose-Multiplatform与Flutter  为什么要单拿它俩出来说一下呢是因为在调研Compose-Multiplatform的过程中我们发现它跟Flutter的原理类似那未来可能就会有竞争有竞争就意味着开发同学若在自己的项目中使用跨平台框架需要选择。那么我们来对比一下这两个框架在之前KMM的文章中我们比较过KMM和Flutter结论是 KMM主要实现的是共享逻辑UI层的实现还是建议平台各自去处理Flutter是UI层的共享。 当时看来两者虽然都是跨平台但目标不同看上去并没有形成竞争。而在Compose-Multiplatform加入之后结合KMM成为了逻辑和UI都可以实现共享的结果。而且从绘制原理上来说Compose和Flutter都是创建自己的View树在通过自绘引擎进行渲染原理上差异不大。再加上Kotlin和Compose作为Android的官方推荐对于Android同学来说基本上是没有什么学习成本的。个人认为若Compose-Multiplatform更加成熟发布稳定版后与Flutter的竞争会非常大。  06 总结 Compose-Multiplatform目前虽然还不成熟但通过对其原理的分析我们可以预见的是结合KMM未来将成为跨平台的有力竞争者。特别对于Android开发同学来说可以把KMM先用起来结合Compose去实现一些低耦合的业务待未来Compose-iOS发布稳定版后可以愉快的进行双端开发节约开发成本。 参考 (1)https://www.jianshu.com/p/e1ae5eaa894e (2)https://www.jianshu.com/p/e1ae5eaa894e  (3)https://github.com/JetBrains/compose-multiplatform-ios-android-template (4)https://github.com/JetBrains/skiko
http://www.hyszgw.com/news/93356.html

相关文章:

  • 自己做网站 做什么好如何做网站的页面
  • 大学生对校园网站建设的需求是什么意思大连百度推广代理商
  • 什么是网站的功能模块高端做网站多少钱
  • 网站建设和信息更新的通知高端网站定制开发
  • 网站建设内容是经营项目吗十大手机必备软件
  • 青岛建站合作合优人才网合川
  • 外贸网站平台都有哪些平台优化方案英语2024版答案
  • 书店网站建设规划书海南新闻在线观看
  • 网络公司网站开发大连短视频代运营
  • 响应式网站不加载图片要加强县门户网站的建设管理办法
  • 基于网站开发小程序做个门户网站多少钱
  • 利于优化的网站要备案吗模版大全
  • 徐老师在那个网站做发视频h5页面制作报价
  • 自动化 东莞网站建设有没有交流做服装的网站
  • 站长素材官网免费宝安led行业网站建设
  • 自己怎样做公司广告视频网站秀米官网登录入口
  • 给公司做一个网站网站按照规模分为哪几类
  • 达州市住房和建设厅网站免费wap建站的网址是什么了
  • 网站开发人员属于什么东莞常平牙科
  • wordpress 添加gif百度小程序对网站seo
  • 网站建设如何接单模板建站系统
  • 长沙新能源建站补贴网页制做
  • 爬虫 网站开发实例个人网站设计公司
  • 韩国优秀网站设计欣赏有什么做美食的网站
  • 盗图来做网站网业拼音怎么写
  • 做北美市场用哪个网站网站空间上传工具
  • 游乐场网站开发备案接入阿里云后原网站还能访问吗
  • wordpress调用小工具海外广告优化师
  • 北海市网站建设培训学校管理系统
  • 做景观要知道哪些网站专门做包装的网站