今天大概會聊到的範圍
- Android View
前兩天來回進出了公司樓下的 7-11 兩三次,每次都要掃一次實名制的 QR Code。我現在用的 App 又有點笨,每次掃完之後都還要手動選要送 SMS(也是啦,又不是專門為 1922 開發的 App)。不過,作為工程師的好處是,當有件事情無法滿足你的需求,你可以自己開發東西來改變它!於是,我想要自己寫一個 QR Code Scanner 而且能認得 1922 的格式自動送 SMS。
我想說,QR Code Scanner 之前有試著做過,沒有很複雜。用 CameraX 配上 MLKit 應該就可以達成了吧! 打開 Android Studio,熟悉的建立了一個 Compose App,然後插入 CameraPreview ....
等等!沒有 XML 我要怎麼放 CameraPreview?
在 Compose 中放入 Android view
Compose 中,有一個 Composable 叫做 AndroidView 。在 AndroidView 中,我們可以擺放繼承 View 的元件。
@Composable
fun Screen() {
AndroidView(
factory = { context ->
val view = // create view
view
}
)
}
舉個例,Compose 的 Text 並沒有直接顯示 html 的功能,但是 android.widget 的 TextView 可以吃轉成 html 的 Spanable。所以可以透過 AndroidView 來使用 TextView 。
@Composable
fun TextViewHtml() {
val html = """
<h3">Title</h3>
<hr>
<ul>
<li><p>First</p></li>
<li><p>Second</p></li>
</ul>
""".trimIndent()
AndroidView(
factory = { context ->
val textView = TextView(context)
textView.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
HtmlCompat.fromHtml(html, 0)
} else {
Html.fromHtml(html)
}
textView
},
modifier = Modifier.background(color = Color.White)
)
}
除了熟悉的 modifier 參數外,AndroidView 需要一個負責產生 view 的 factory lambda,這個 Lambda 中可以取得 context ,透過這個 context 可以產生出 Android 的 View。
除了 factory 外,還可以提供另一個 lambda update,因為 composable 會在 recomposition 時重新繪製,但是每次重新繪製都產生一個新的 android view 太浪費了。所以 factory 其實不會在 recomposition 被觸發。要改變 AndroidView 中的內容的話,需要透過 update 參數可以告訴 AndroidView 我們要使用的 view 如何變動。
@Composable
fun <T : View> AndroidView(
factory: (Context) -> T,
modifier: Modifier = Modifier,
update: (T) -> Unit = NoOpUpdate
) { ... }
@Composable
fun TextViewHtml() {
var data by remember { mutableStateOf(0) }
AndroidView(
factory = { context ->
val html = """
<h3">Title</h3>
<br/>
<p>$data</p>
""".trimIndent()
val textView = TextView(context)
textView.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
HtmlCompat.fromHtml(html, 0)
} else {
Html.fromHtml(html)
}
textView
},
update = { textView: TextView -> // <-- update 中可以拿到 factory 建立的 view
val html = """
<h3">Title</h3>
<br/>
<p>$data</p>
""".trimIndent()
textView.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
HtmlCompat.fromHtml(html, 0)
} else {
Html.fromHtml(html)
}
},
modifier = Modifier.fillMaxWidth().background(color = Color.White).clickable { data += 1 }
)
}
今天的題目比較小,但卻是非常實用的東西。有了 AndroidView 我就可以將 CameraX 放進 Compose 專案了。
但實際上還一些問題沒解,例如如何在取得 camera permission 之後觸發 recomposition 。這些,就等後續分享吧!