Jeptpack Compose 官網(wǎng)教程學習筆記(七)View 遷移至 Compose

View 遷移至 Compose

主要學習內(nèi)容

  • 如何逐步將應用遷移到 Compose
  • 如何將 Compose 添加到使用 Android View 構建的現(xiàn)有界面
  • 如何在 Compose 中使用 Android View
  • 如何在 Compose 中使用 View 系統(tǒng)中的主題

原理

我們使用 Compose 都是在ComponentActivity中調(diào)用setContent方法硅则,那么與Activity的setContextView有什么區(qū)別呢迹卢?

查看setContent源碼:

public fun ComponentActivity.setContent(
    parent: CompositionContext? = null,
    content: @Composable () -> Unit
) {
    //獲取decorView下的第一個子VIew
    //顯然第一次啟動時 getChildAt(0) 返回null
    val existingComposeView = window.decorView
        .findViewById<ViewGroup>(android.R.id.content)
        .getChildAt(0) as? ComposeView

    if (existingComposeView != null) with(existingComposeView) {
        setParentCompositionContext(parent)
        setContent(content)
    } else {
        //會進入該分支
        //創(chuàng)建ComposeView實例
        ComposeView(this).apply {
            // 在 setContentView 之前設置內(nèi)容和父項
            // 讓 ComposeView 在 attach 時創(chuàng)建 composition
            setParentCompositionContext(parent)
            setContent(content)
            // 在設置內(nèi)容視圖之前設置視圖樹Owner
            // 以便 inflation process 和 attach listeners 能感知到它們存在
            setOwners()
            setContentView(this, DefaultActivityContentLayoutParams)
        }
    }
}

可以看到在setContent方法中會去獲取ComposeView對象晦闰,最后會去調(diào)用setContentView筷屡,而這個setContentView就是Activity的setContentView方法

androidx.activity.ComponentActivity

@Override
public void addContentView(@SuppressLint({"UnknownNullness", "MissingNullability"}) View view,
        @SuppressLint({"UnknownNullness", "MissingNullability"})
                ViewGroup.LayoutParams params) {
    initViewTreeOwners();
    super.addContentView(view, params);
}

那么ComposeView又是什么呢凰浮?

class ComposeView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AbstractComposeView(context, attrs, defStyleAttr){ ... }

abstract class AbstractComposeView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr){ ... }

可以看到ComposeView其實本質(zhì)上就是個ViewGroup立叛,也就是說 Compose 中設置的界面最終會在ComposeView上進行展示造壮,是 View和 Compose 的交界點

并不是說 Compose 可組合項最終都會變?yōu)閂iew吼过,而是可組合項形成的界面會在ComposeView上進行繪制顯示

在 View 中要使用 Compose 泼诱,只需要通過創(chuàng)建ComposeView坛掠,在ComposeView中通過setContent調(diào)用可組合項就可以了

而在 Compose 中調(diào)用 View就會稍微麻煩一點,會在之后進行介紹

遷移規(guī)劃

要將 Jetpack Compose 集成到現(xiàn)有 Android 應用中治筒,有多種不同的方法屉栓。常用的兩種遷移策略為:

  • 完全使用 Compose 開發(fā)一個新界面

    在重構應用代碼以適應新技術時,一種常用的方法是只在應用構建的新功能中采用新技術耸袜,比較適合在創(chuàng)建新的界面使用該方法友多,原本的應用部分繼續(xù)使用 View 體系

  • 選取一個現(xiàn)有界面,然后逐步遷移其中的各個組件

    • View作為外部布局

      將部分界面遷移到 Compose堤框,讓其他部分保留在 View 系統(tǒng)中域滥,例如:遷移 RecyclerView纵柿,同時將界面的其余部分保留在 View 系統(tǒng)中

    • Compose 作為外部布局

      使用 Compose 作為外部布局,并使用 Compose 中可能沒有的一些現(xiàn)有 View启绰,比如 MapViewAdView

準備工作

官網(wǎng)示例下載

因為之后的代碼都是基于其中的項目進行的昂儒,而且遷移的學習是基于一個較完善的項目中進行,存在多個界面之間的切換

所以建議下載示例委可,并通過Import Project方式導入其中的MigrationCodelab項目

在解壓文件中的MigrationCodelab 目錄中存放本次學習的案例代碼

在此學習中荆忍,我們將逐步把 Sunflower 的植物詳情界面遷移到 Compose,并 Compose 和 View 結合起來

添加 Compose 依賴

android {
    ...
    kotlinOptions {
        jvmTarget = '1.8'
        useIR = true
    }
    buildFeatures {
        ...
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion rootProject.composeVersion
    }
}

dependencies {
    ...
    // Compose
    implementation "androidx.compose.runtime:runtime:$rootProject.composeVersion"
    implementation "androidx.compose.ui:ui:$rootProject.composeVersion"
    implementation "androidx.compose.foundation:foundation:$rootProject.composeVersion"
    implementation "androidx.compose.foundation:foundation-layout:$rootProject.composeVersion"
    implementation "androidx.compose.material:material:$rootProject.composeVersion"
    implementation "androidx.compose.runtime:runtime-livedata:$rootProject.composeVersion"
    implementation "androidx.compose.ui:ui-tooling:$rootProject.composeVersion"
    implementation "com.google.android.material:compose-theme-adapter:$rootProject.composeVersion"
    ...
}

初步遷移 Compose

在植物詳情界面中撤缴,我們需要將對植物的說明遷移到 Compose,同時讓界面的總體結構保持完好

Compose 需要有 activity 或 fragment 才能呈現(xiàn)界面叽唱。在 Sunflower 中屈呕,所有界面都使用 fragment,因此我們需要使用 ComposeView

ComposeView 可以使用 setContent 方法托管 Compose 界面內(nèi)容

這里我們選取植物詳情界面棺亭,然后逐步遷移其中的各個組件

移除XML代碼

打開 fragment_plant_detail.xml 并執(zhí)行以下操作:

  1. 切換到code視圖
  2. 移除 NestedScrollView 中的 ConstraintLayout 代碼和嵌套的 TextView(建議使用代碼注釋的方式虎眨,方便進行比較和遷移)
  3. 添加一個 ComposeView,它會改為托管 Compose 代碼镶摘,并以 compose_view 作為 id

fragment_plant_detail.xml

<androidx.core.widget.NestedScrollView
    android:id="@+id/plant_detail_scrollview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    android:paddingBottom="@dimen/fab_bottom_padding"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <!-- 其余部分全部注釋掉 -->
    <!-- 這里就不展示注釋部分 -->

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.core.widget.NestedScrollView>

添加Compose代碼

在該項目中嗽桩,我們可以將 Compose 代碼添加到 plantdetail 文件夾下的 PlantDetailDescription.kt 文件中

plantdetail/PlantDetailDescription.kt

//文件中原本存在的可組合項函數(shù)
@Composable
fun PlantDetailDescription() {
    Surface {
        Text("Hello Compose")
    }
}

我們要在ComposeView中調(diào)用PlantDetailDescription可組合項

plantdetail/PlantDetailFragment.kt中凄敢,訪問 composeView 并調(diào)用 setContent,以便在界面上顯示 Compose 代碼

Sunflower 項目中使用 DataBinding 的方式滩援,我們可以直接訪問composeView

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(
        inflater, R.layout.fragment_plant_detail, container, false
    ).apply {
        ...
        composeView.setContent { 
            MaterialTheme {
                PlantDetailDescription()
            }
        }
    }
    setHasOptionsMenu(true)

    return binding.root
}

運行該應用,界面上會顯示“Hello Compose!

應用界面

從XML映射到可組合項

我們首先遷移植物的名稱憎蛤。更確切地說碾盟,是在 fragment_plant_detail.xml 中 ID 為 @+id/plant_detail_nameTextView

<TextView
    android:id="@+id/plant_detail_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="@dimen/margin_small"
    android:layout_marginEnd="@dimen/margin_small"
    android:gravity="center_horizontal"
    android:text="@{viewModel.plant.name}"
    android:textAppearance="?attr/textAppearanceHeadline5"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    tools:text="Apple" />

我們根據(jù)XML中的樣式創(chuàng)建的新 PlantName 可組合項與之對應

@Composable
private fun PlantName(name: String) {
    Text(
        text = name,
        style = MaterialTheme.typography.h5,
        modifier = Modifier
            .fillMaxWidth()
            .padding(
                start = dimensionResource(id = R.dimen.margin_small),
                end = dimensionResource(id = R.dimen.margin_small)
            )
            .wrapContentWidth(Alignment.CenterHorizontally)
    )
}

映射關系:

  • Text 的樣式為 MaterialTheme.typography.h5熙尉,從 XML 代碼映射到 textAppearanceHeadline5锨推。
  • 修飾符會修飾 Text,以將其調(diào)整為類似于 XML 版本:
    • fillMaxWidth 修飾符對應于 XML 代碼中的 android:layout_width="match_parent"
    • margin_small 的水平 padding,其值是使用 dimensionResource 輔助函數(shù)從 View 系統(tǒng)獲取的
    • wrapContentWidth 水平對齊 Text

如何觀察 LiveData 將在稍后介紹磁椒,因此先假設我們有可用的名稱

通過預覽查看效果

@Preview(showBackground = true, backgroundColor = 0XFFFFFF)
@Composable
private fun PlantNamePreview() {
    MaterialTheme {
        PlantName("Apple")
    }
}
預覽圖

ViewModel和LiveData

現(xiàn)在医增,我們將PlantName顯示到界面。如需執(zhí)行此操作夺欲,我們需要使用 PlantDetailViewModel 加載數(shù)據(jù)

ViewModel

由于 fragment 中使用了 PlantDetailViewModel 的實例市埋,因此我們可以將其作為參數(shù)傳遞給 PlantDetailDescription

@Composable
fun PlantDetailDescription(plantDetailViewModel: PlantDetailViewModel) {
    ...
}

在 fragment 調(diào)用此可組合項時傳遞 ViewModel 實例:

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(
        inflater, R.layout.fragment_plant_detail, container, false
    ).apply {
        ...
        composeView.setContent { 
            MaterialTheme {
                PlantDetailDescription(plantDetailViewModel)
            }
        }
    }
    ...
}

可組合項沒有自己的 ViewModel 實例,相應的實例將在可組合項和托管 Compose 代碼的生命周期所有者(activity 或 fragment)之間共享

如果您遇到了 ViewModel 無法使用的情況镣陕,或者您不希望將該依賴項傳遞給可組合項,則可以在可組合項中使用 viewModel 函數(shù),以獲取 ViewModel 的實例

如需使用 viewModel() 函數(shù)耀销,請將 androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1 依賴項添加到 build.gradle 文件中

class ExampleViewModel : ViewModel() { /*...*/ }

@Composable
fun MyExample(
    viewModel: ExampleViewModel = viewModel()
) {
    ...
}

viewModel() 會返回一個現(xiàn)有的 ViewModel,或在給定作用域內(nèi)創(chuàng)建一個新的 ViewModel肮蛹。只要該作用域處于有效狀態(tài)勺择,就會保留 ViewModel

例如,如果在某個 Activity 中使用了可組合項蔗崎,則在該 Activity 完成或進程終止之前酵幕,viewModel() 會返回同一實例

如果 ViewModel 具有依賴項,則 viewModel() 會將可選的 ViewModelProvider.Factory 作為參數(shù)

如需詳細了解 Compose 中的 ViewModel 以及實例如何與 Compose Navigation 庫或 activity 和 fragment 一起使用缓苛,請參閱互操作性文檔

LiveData

PlantDetailDescription可以通過PlantDetailViewModelLiveData<Plant> 字段芳撒,以獲取植物的名稱

如需從可組合項觀察 LiveData,請使用 LiveData.observeAsState() 函數(shù)

LiveData.observeAsState() 開始觀察 LiveData未桥,并通過 State 對象表示它的值笔刹。每次向 LiveData 發(fā)布一個新值時,返回的 State 都會更新冬耿,這會導致所有 State.value 用法重組

由于 LiveData 發(fā)出的值可以為 null舌菜,因此我們需要將其用法封裝在 null 檢查中。有鑒于此亦镶,以及為了實現(xiàn)可重用性日月,最好將 LiveData 的使用和監(jiān)聽拆分到不同的可組合項中

因此,我們創(chuàng)建 PlantDetailDescription 的新可組合項缤骨,用于顯示 Plant 信息

@Composable
fun PlantDetailDescription(plantDetailViewModel: PlantDetailViewModel) {
    val plant by plantDetailViewModel.plant.observeAsState()

    plant?.let {
        PlantDetailContent(it)
    }
}

@Composable
fun PlantDetailContent(plant: Plant) {
    PlantName(plant.name)
}

遷移更多XML代碼

現(xiàn)在我們繼續(xù)遷移ConstraintLayout中的 View:澆水信息和植物說明

fragment_plant_detail.xml中澆水信息 XML 代碼由兩個 ID 為 plant_watering_headerplant_watering 的 TextView 組成

<TextView
    android:id="@+id/plant_watering_header"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="@dimen/margin_small"
    android:layout_marginTop="@dimen/margin_normal"
    android:layout_marginEnd="@dimen/margin_small"
    android:gravity="center_horizontal"
    android:text="@string/watering_needs_prefix"
    android:textColor="?attr/colorAccent"
    android:textStyle="bold"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/plant_detail_name" />

<TextView
    android:id="@+id/plant_watering"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="@dimen/margin_small"
    android:layout_marginEnd="@dimen/margin_small"
    android:gravity="center_horizontal"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/plant_watering_header"
    app:wateringText="@{viewModel.plant.wateringInterval}"
    tools:text="every 7 days" />

和之前的操作類似爱咬,創(chuàng)建 PlantWatering 新可組合項,以在界面上顯示澆水信息:

@Composable
fun PlantWatering(wateringInterval: Int) {
    Column(Modifier.fillMaxWidth()) {
        val centerWithPaddingModifier = Modifier
            .padding(horizontal = dimensionResource(R.dimen.margin_small))
            .align(Alignment.CenterHorizontally)

        val normalPadding = dimensionResource(R.dimen.margin_normal)

        Text(
            text = stringResource(R.string.watering_needs_prefix),
            color = MaterialTheme.colors.primaryVariant,
            fontWeight = FontWeight.Bold,
            modifier = centerWithPaddingModifier.padding(top = normalPadding)
        )

        val wateringIntervalText = LocalContext.current.resources.getQuantityString(
            R.plurals.watering_needs_suffix, wateringInterval, wateringInterval
        )
        Text(
            text = wateringIntervalText,
            modifier = centerWithPaddingModifier.padding(bottom = normalPadding)
        )
    }
}

由于 Text 可組合項會共享水平內(nèi)邊距和對齊修飾绊起,因此您可以將修飾符分配給局部變量(即 centerWithPaddingModifier)精拟,以重復使用修飾符。修飾符是標準的 Kotlin 對象虱歪,因此可以重復使用

Compose 的 MaterialThemeplant_watering_header 中使用的 colorAccent 不完全匹配》湟铮現(xiàn)在,我們可以使用將在主題設置部分中加以改進的 MaterialTheme.colors.primaryVariant

我們將各個部分組合在一起笋鄙,然后同樣在 PlantDetailContent 中調(diào)用 PlantWatering师枣,因為ConstraintLayout還有 margin 值,我們還需要將值添加到 Compose 代碼中

為了確保背景顏色和所用的文本顏色均合適萧落,我們需要添加 Surface 用于處理這種設置

@Composable
fun PlantDetailContent(plant: Plant) {
    Surface {
        Column(Modifier.padding(dimensionResource(R.dimen.margin_normal))) {
            PlantName(plant.name)
            PlantWatering(plant.wateringInterval)
        }
    }
}

@Preview(showBackground = true, backgroundColor = 0XFFFFFF)
@Composable
private fun PlantNamePreview() {
    val plant = Plant("id", "Apple", "description", 3, 30, "")
    MaterialTheme {
        PlantDetailContent(plant)
    }
}

刷新預覽

預覽圖

Compose 中調(diào)用View

接下來坛吁,我們來遷移植物說明

<TextView
    android:id="@+id/plant_description"
    style="?android:attr/textAppearanceMedium"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="@dimen/margin_small"
    android:layout_marginTop="@dimen/margin_small"
    android:layout_marginEnd="@dimen/margin_small"
    android:minHeight="@dimen/plant_description_min_height"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/plant_watering"
    app:renderHtml="@{viewModel.plant.description}"
    tools:text="Details about the plant" />

TextView中包含了app:renderHtml="@{viewModel.plant.description}"renderHtml 是一個綁定適配器铐尚,可在 PlantDetailBindingAdapters.kt 文件中找到拨脉。該實現(xiàn)使用 HtmlCompat.fromHtmlTextView 上設置文本

@BindingAdapter("wateringText")
fun bindWateringText(textView: TextView, wateringInterval: Int) {
    val resources = textView.context.resources
    val quantityString = resources.getQuantityString(
        R.plurals.watering_needs_suffix,
        wateringInterval, wateringInterval
    )

    textView.text = quantityString
}

但是,Compose 目前不支持 Spanned 類宣增,也不支持顯示 HTML 格式的文本玫膀。因此,我們需要在 Compose 代碼中使用 View 系統(tǒng)中的 TextView 來繞過此限制

由于 Compose 目前還無法呈現(xiàn) HTML 代碼爹脾,因此您需要使用 AndroidView API 程序化地創(chuàng)建一個 TextView帖旨,從而實現(xiàn)此目的

AndroidView接受程序化地創(chuàng)建的 View箕昭。如果您想嵌入 XML 文件,可以結合使用視圖綁定與 androidx.compose.ui:ui-viewbinding 庫中的 AndroidViewBinding API

創(chuàng)建 PlantDescription 可組合項解阅。此可組合項中使用 AndroidView 創(chuàng)建TextView落竹。在 factory 回調(diào)中,請初始化使用給定 Context 來回應 HTML 交互的 TextView货抄。在 update 回調(diào)中述召,用已保存的 HTML 格式的說明設置文本

@Composable
private fun PlantDescription(description: String) {
    // Remembers the HTML formatted description. Re-executes on a new description
    val htmlDescription = remember(description) {
        //使用 HtmlCompat 解析 html
        //HtmlCompat 內(nèi)部做了版本適配
        HtmlCompat.fromHtml(description, HtmlCompat.FROM_HTML_MODE_COMPACT)
    }

    // 在屏幕上顯示 TextView 并在 inflate 時使用 HTML 描述進行更新
    // 對 htmlDescription 的更新將使 AndroidView 重新組合并更新文本
    AndroidView(
        factory = { context ->
            TextView(context).apply {
                //對于鏈接點擊的處理,若不設置movementMethod蟹地,則鏈接無效
                movementMethod = LinkMovementMethod.getInstance()
            }
        },
        update = {
            it.text = htmlDescription
        }
    )
}

remember中將description作為 key 积暖,如果 description 參數(shù)發(fā)生變化,系統(tǒng)會再次執(zhí)行 remember 中的 htmlDescription 代碼

同樣怪与,如果 htmlDescription 發(fā)生變化夺刑,AndroidView 更新回調(diào)會重組。在回調(diào)中讀取的任何狀態(tài)都會導致重組

我們將 PlantDescription 添加到 PlantDetailContent 可組合項

@Composable
fun PlantDetailContent(plant: Plant) {
    Column(Modifier.padding(dimensionResource(id = R.dimen.margin_normal))) {
        PlantName(plant.name)
        PlantWatering(plant.wateringInterval)
        PlantDescription(plant.description)
    }
}

現(xiàn)在分别,我們就將原始 ConstraintLayout 中的所有內(nèi)容遷移到 Compose了

效果圖

ViewCompositionStrategy

默認情況下遍愿,只要 ComposeView 與窗口分離,Compose 就會處理組合耘斩。Compose 界面 View 類型(例如 ComposeViewAbstractComposeView)使用定義此行為的 ViewCompositionStrategy

默認情況下沼填,Compose 使用 DisposeOnDetachedFromWindow 策略。但是煌往,在 Compose 界面 View 類型用于以下各項的部分情況下,默認策略可能不太合適:

  • Fragment轧邪。Compose 界面 View 類型應該遵循 fragment 的視圖生命周期去保存狀態(tài)
  • Transitions 動畫刽脖。當 Transitions 過程中使用 Compose 界面 View 類型,系統(tǒng)會在轉(zhuǎn)換開始(而不是轉(zhuǎn)換結束)時將其與窗口分離忌愚,從而導致可組合項在它仍然在屏幕上時就開始 detach
  • RecyclerView或者帶有生命周期管理的自定義控件

在上述某些情況下曲管,除非您手動調(diào)用 AbstractComposeView.disposeComposition,否則應用可能會因為組合實例泄漏內(nèi)存

如需在不再需要組合時自動處理組合硕糊,請通過調(diào)用 setViewCompositionStrategy 方法設置其他策略或創(chuàng)建自己的策略

例如院水,DisposeOnLifecycleDestroyed 策略會在 lifecycle 被銷毀時處理組合

此策略適用于與已知的 LifecycleOwner 具有一對一關系的 Compose 界面 View 類型

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(
        inflater, R.layout.fragment_plant_detail, container, false
    ).apply {
        ...
        composeView.apply {
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnLifecycleDestroyed(this@PlantDetailFragment.lifecycle))
            setContent {
                PlantDetailDescription(plantDetailViewModel)
            }
        }

    }
    ...
}

LifecycleOwner 未知時,可以使用 DisposeOnViewTreeLifecycleDestroyed

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(
        inflater, R.layout.fragment_plant_detail, container, false
    ).apply {
        ...
        composeView.apply {
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                PlantDetailDescription(plantDetailViewModel)
            }
        }

    }
    ...
}

如需了解如何使用此 API简十,請參閱“Fragment 中的 ComposeView”部分

互操作性主題設置

我們已將植物詳情的文本內(nèi)容遷移到 Compose檬某。不過,Compose 使用的主題顏色有誤螟蝙。當植物名稱應該使用綠色時恢恼,它使用的是紫色

顯然 View 體系使用的主題和MaterialTheme并沒有進行關聯(lián),我們需要 Compose 繼承 View 系統(tǒng)中可用的主題胰默,而不是從頭開始在 Compose 中重新編寫您自己的 Material 主題

<style name="Base.Theme.Sunflower" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    <item name="colorPrimary">@color/sunflower_green_500</item>
    <item name="colorPrimaryVariant">@color/sunflower_green_700</item>
    <item name="colorOnPrimary">@color/sunflower_black</item>
    <item name="colorPrimaryDark">@color/sunflower_green_700</item>
    <item name="colorAccent">?attr/colorPrimaryVariant</item>
    <item name="colorSecondary">@color/sunflower_yellow_500</item>
    <item name="colorOnSecondary">@color/sunflower_black</item>
    <item name="android:colorBackground">@color/sunflower_green_500</item>

    <item name="android:windowTranslucentStatus">true</item>
    <item name="android:navigationBarColor">?colorOnSurface</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>

如需在 Compose 中重復使用 View 系統(tǒng)的 Material Design 組件 (MDC) 主題场斑,您可以使用 compose-theme-adapter漓踢。MdcTheme 函數(shù)將自動讀取主機上下文的 MDC 主題,并代表您將它們傳遞給 MaterialTheme漏隐,以用于淺色和深色主題

dependencies {
    ...
    implementation "com.google.android.material:compose-theme-adapter:$rootProject.composeVersion"
    ...
}

如需使用此庫喧半,請將 MaterialTheme 的用法替換為 MdcTheme

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(
        inflater, R.layout.fragment_plant_detail, container, false
    ).apply {
        ...
        composeView.apply {
            ...
            setContent {
                MdcTheme {
                    PlantDetailDescription(plantDetailViewModel)
                }
            }
        }
    }
    ...
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市青责,隨后出現(xiàn)的幾起案子挺据,更是在濱河造成了極大的恐慌,老刑警劉巖爽柒,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吴菠,死亡現(xiàn)場離奇詭異,居然都是意外死亡浩村,警方通過查閱死者的電腦和手機做葵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來心墅,“玉大人酿矢,你說我怎么就攤上這事≡踉铮” “怎么了瘫筐?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铐姚。 經(jīng)常有香客問我策肝,道長,這世上最難降的妖魔是什么隐绵? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任之众,我火速辦了婚禮,結果婚禮上依许,老公的妹妹穿的比我還像新娘棺禾。我一直安慰自己,他們只是感情好峭跳,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布膘婶。 她就那樣靜靜地躺著,像睡著了一般蛀醉。 火紅的嫁衣襯著肌膚如雪悬襟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天拯刁,我揣著相機與錄音古胆,去河邊找鬼。 笑死,一個胖子當著我的面吹牛逸绎,可吹牛的內(nèi)容都是我干的惹恃。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼棺牧,長吁一口氣:“原來是場噩夢啊……” “哼巫糙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起颊乘,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤参淹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后乏悄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浙值,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年檩小,在試婚紗的時候發(fā)現(xiàn)自己被綠了开呐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡规求,死狀恐怖筐付,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情阻肿,我是刑警寧澤裳涛,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布郑趁,位于F島的核電站侮叮,受9級特大地震影響嚎花,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赴邻,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一印衔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乍楚,春花似錦当编、人聲如沸届慈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽金顿。三九已至臊泌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間揍拆,已是汗流浹背渠概。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人播揪。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓贮喧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親猪狈。 傳聞我的和親對象是個殘疾皇子箱沦,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容