前言
通過前面的一番折騰,項目是已經(jīng)搭好構(gòu)建完成了,接下來就應(yīng)該進(jìn)入compose的編碼的環(huán)節(jié)了,首先應(yīng)該明白,compose是用來替換原生的xml方案去實現(xiàn)界面布局顯示的,在此之前先對原生的xml進(jìn)行一個簡單的回顧
傳統(tǒng)布局方式
xml布局會被反射加載為具體的一個個View對象:
有一定的Android開發(fā)基礎(chǔ)的朋友都知道,Android的原生布局,是通過在Activity的onCreate()方法中,通過setContentView()傳入xml的布局資源id,然后再PhoneWindow類中,初始化了decor和基礎(chǔ)布局后,調(diào)用LayoutInflater.inflate()方法,調(diào)用XmlResourceParser對我們寫入的xml進(jìn)行解析,把解析后的控件全路徑取出來,調(diào)用createViewFromTag(),再通過Factory2里面的工廠方法對一個一個的控件類進(jìn)行反射實例化,最終在通過addView添加到視圖體系里面。
在傳統(tǒng)模式下,我們看的的視圖都是一個一個的android.view.View的對象,View對象通過自己的onMeasure()和onDraw()方法實現(xiàn)自己的測量規(guī)則和繪制流程,然后在ViewGroup的協(xié)調(diào)下,通過ViewGroup的onLayout()方法調(diào)整擺放位置,并且ViewGroup也是繼承自View的,所以ViewGroup有自己的測量和繪制,這也會影響到它內(nèi)部的View的繪制,對于系統(tǒng)沒有提供的控件可以直接繼承自View或者ViewGroup通過重寫onMeasure,onDraw()和onLayout()方法去實現(xiàn)自己的界面邏輯,這一套通過ViewGroup對View進(jìn)行嵌套的流程我們已經(jīng)相當(dāng)熟悉。
順帶提一句,在傳統(tǒng)模式下的布局嵌套不適合太深,因為在ViewGroup的測量階段會對自己內(nèi)部的子控件也進(jìn)行測量和布局,嵌套太多會導(dǎo)致更多的性能開銷,在ConstraintLayout沒有出現(xiàn)之前,為了實現(xiàn)布局的各種左右居中規(guī)則,不得不使用LinearLayout嵌套RelativeLayout進(jìn)行嵌套,所以在ConstraintLayout出來后,我經(jīng)歷過對布局的全面重構(gòu)。
反過來看Compose,這是一套完全不一樣的布局流程,首先不用再編寫xml布局文件,通過一個一個的函數(shù)嵌套對界面進(jìn)行搭建,傳統(tǒng)模式下對于樣式的個性化調(diào)整是通過屬性的控制,在compose模式下就是對于函數(shù)參數(shù)的選擇傳遞,所以要了解一個compose控件的使用就得從它的函數(shù)參數(shù)出發(fā),從控件開始入門。
詳解示例代碼
還是先從示例代碼出發(fā)進(jìn)行解析
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//提供了整個compose運行的最基礎(chǔ)的環(huán)境,compose代碼需要寫在setContent之中
//setContent是一個沒有傳遞參數(shù)的高階函數(shù),直接寫成lambda的形式
setContent {
ComposeLearnTheme {
Surface(
//Surface傳遞了參數(shù),調(diào)整了尺寸的大小和背景顏色
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
//Greeting在下面可以看到聲明,是一個帶有@Composable的函數(shù)
Greeting("Android")
}
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
//@Preview注解是compose提供的預(yù)覽功能,可以直接在IDE中顯示效果
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
ComposeLearnTheme {
Greeting("Android")
}
}
在前面的一篇文章中已經(jīng)提到了這個默認(rèn)的示例代碼,整個compose代碼都運行在setContent()方法中,有的函數(shù)省略了默認(rèn)參數(shù),根據(jù)lambda的特性,當(dāng)最后一個函數(shù)參數(shù)是函數(shù)類型的時候,可以直接省略參數(shù)的”()“,直接寫函數(shù)體,這樣可以讓compose看起來更加的簡潔
函數(shù)的優(yōu)點
相較于傳入的xml方式,xml由于需要在運行時進(jìn)行反射實例化,所以xml更多的是一個靜態(tài)的布局聲明,沒有在xml中承載太多的運算和邏輯處理,compose采用函數(shù)式的寫法,則讓在使用compose寫布局文件的時候提高了靈活性,在Compose函數(shù)的前后都可以加入更靈活的運算邏輯
@Composable
compose函數(shù)的特點是需要增加一個@Composable注解,標(biāo)記有這個注解的函數(shù)就是一個compose組件函數(shù),再其內(nèi)部就可以調(diào)用其他的compose組件,如果沒有這個注解,就是一個普通函數(shù),沒有compose函數(shù)的運行環(huán)境,編譯就會報錯

把示例代碼的Greeting()的注解注釋后,由于內(nèi)部的Text()是一個帶有@Composable注解的函數(shù),所以不能在普通函數(shù)中調(diào)用,示例代碼中的調(diào)用方法ComposeLearnTheme和Surface應(yīng)該都是帶有了@Composable的注解
@Composable
fun ComposeLearnTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {}
@Composable
fun Surface(
modifier: Modifier = Modifier,
shape: Shape = RectangleShape,
color: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(color),
border: BorderStroke? = null,
elevation: Dp = 0.dp,
content: @Composable () -> Unit
) {}
從這兩個函數(shù)可以看到最后一個參數(shù),都是一個帶@Composable注解的 “()->Unit” 類型的函數(shù)類型參數(shù),并且前面的參數(shù)都提供了默認(rèn)值,所以在調(diào)用的時候可以直接省略參數(shù)直接寫成lambda的形式
順帶提一下,這一點類似kotlin的協(xié)程,協(xié)程有有一個掛起函數(shù)的概念,需要通過@suspend注解進(jìn)行標(biāo)記
@Preview
在示例代碼中,還有一個通過@Preview注解的函數(shù),這個函數(shù)在setContent中沒有被調(diào)用,解釋一下這個的作用是compose在在編譯器中集成的預(yù)覽的功能,可以不用編譯運行到機器上,就可以在IDE中看到界面的顯示效果,方便對于界面的調(diào)試,加速界面的開發(fā)
需要注意的是,這個預(yù)覽注解不能使用在帶有參數(shù)的compose函數(shù)中,畢竟是需要即時顯示效果,弄個帶參數(shù)的預(yù)覽,這個參數(shù)從哪里傳遞,具體是什么都無從知曉,如果需要預(yù)覽一個帶參數(shù)的compose函數(shù),則需要嵌套一個沒有參數(shù)的compose函數(shù),然后調(diào)用的時候?qū)懮蠀?shù)預(yù)覽不能有參數(shù).png
預(yù)覽功能還是支持熱更新的,當(dāng)更改代碼的時候,會有實時顯示的效果,如果沒有實時顯示,點擊一下刷新按鈕

默認(rèn)代碼里面的預(yù)覽功能,內(nèi)部的顯示其實就是一個Text()組件,可見其長寬跟文字的內(nèi)容適配
如果將上面完整的帶有Surface的代碼進(jìn)行預(yù)覽對比可見,Surface中配置了大小和顏色,如下

可以看到界面撐滿了整個屏幕,并且文字組件在默認(rèn)的坐標(biāo)系的起始位置,可以知道在compose中就是通過函數(shù)的嵌套實現(xiàn)了類型ViewGroup和View的嵌套邏輯
Text()
聊完了示例代碼,接下來以示例代碼中的Text()函數(shù)進(jìn)行分析
package androidx.compose.material
@Composable
fun Text(
text: String, //文字內(nèi)容
modifier: Modifier = Modifier, //用來控制通用尺寸,點擊事件等的對象
color: Color = Color.Unspecified, //顯示文字的顏色
fontSize: TextUnit = TextUnit.Unspecified, //文字大小
fontStyle: FontStyle? = null, //文字的樣式,是否是斜體
fontWeight: FontWeight? = null,//文字的比重,加粗
fontFamily: FontFamily? = null, //字體,Monospace,SansSerif等
letterSpacing: TextUnit = TextUnit.Unspecified,//字符間距
textDecoration: TextDecoration? = null,//字體裝飾,比如下劃線
textAlign: TextAlign? = null,//文字對齊的方式
lineHeight: TextUnit = TextUnit.Unspecified,//文字一行的高度
overflow: TextOverflow = TextOverflow.Clip, //文字超過寬度的顯示方式,比如...或者直接裁剪
softWrap: Boolean = true,//是否超過寬度后換行
maxLines: Int = Int.MAX_VALUE,//最大的行數(shù)
onTextLayout: (TextLayoutResult) -> Unit = {},//布局后的一個回調(diào)
style: TextStyle = LocalTextStyle.current//設(shè)置封裝好的統(tǒng)一樣式
) {
val textColor = color.takeOrElse {
style.color.takeOrElse {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
}
}
val mergedStyle = style.merge(
TextStyle(
color = textColor,
fontSize = fontSize,
fontWeight = fontWeight,
textAlign = textAlign,
lineHeight = lineHeight,
fontFamily = fontFamily,
textDecoration = textDecoration,
fontStyle = fontStyle,
letterSpacing = letterSpacing
)
)
BasicText(
text,
modifier,
mergedStyle,
onTextLayout,
overflow,
softWrap,
maxLines,
)
}
可以看到Text()函數(shù)內(nèi)部有很多可以設(shè)置的參數(shù),并且這些參數(shù)都給了默認(rèn)值,方便調(diào)用的時候更簡潔的調(diào)用,除了text需要顯示的文字內(nèi)容必填,其他的都可以省略,省略后的樣式就是默認(rèn)字體樣式顯示在坐標(biāo)系的起點位置

沒有額外配置參數(shù)的樣式,就跟著各種默認(rèn)的樣式顯示


以fontStyle為參考,系統(tǒng)提供了正常和斜體的樣式可以配置,其他的參數(shù)同理,具體的就不多展示了,通過這些默認(rèn)參數(shù)基本實現(xiàn)了傳統(tǒng)樣式在xml的基本配置
<TextView
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/white"
android:textSize="20sp"
android:textStyle="bold"
android:lineHeight="20dp"
android:gravity="center"
android:fontFamily="serif"
android:letterSpacing="5dp"
/>
compose相對于原生的xml的TextView,提供了有更加方便的下劃線的樣式風(fēng)格,那么對于在xml中配置的文字寬度,背景色的設(shè)置則統(tǒng)一交給了Modifier參數(shù)去配置,畢竟除了文字需要設(shè)置寬度間距,其他的界面組件也需要這些通用的配置,Modifier是一塊很大的內(nèi)容,compose相關(guān)的一些測量布局,點擊事件,滑動監(jiān)聽這些都基本上依靠modifier去實現(xiàn),在后面的文章會具體的分析,這一篇介紹就簡單的展示一下配置的能力

小結(jié)
通過對Text()函數(shù)參數(shù)的分析,了解了在使用compose函數(shù)對界面?zhèn)€性化配置參數(shù)的方式,除了Text()函數(shù),具體的這些函數(shù)有哪些可以配置的參數(shù)和功能,點擊對應(yīng)函數(shù)的源碼基本上可以對其有一個了解,這一篇文章主要對默認(rèn)示例代碼做了一個結(jié)構(gòu)的拆分,了解@Preview預(yù)覽功能的方便好用,在后面的文章,我會對在開發(fā)中常用的組件進(jìn)行一個簡單的介紹,組件還是很多的,這些內(nèi)容在網(wǎng)上一搜有很多成熟的文章寫的比我都好,如果在需要使用具體控件的功能時,在網(wǎng)上搜索會更加簡單直接。
