zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Jetpack Compose入门详解(实时更新)

2023-04-18 14:29:28 时间


前排提醒

我知道点进来的人都是想学习JC的,所以可能都不知道环境怎么弄,事实上如果只是学习的话,安装了最新版的Android studio后,创建项目时就可以构建一个Jetpack Compose,用于学习是再好不过了

前言(Compose是什么)

提示:需要对原生xml布局有一定了解,另外它最好是配合Kotlin 使用更佳

借用官方的解释:Jetpack Compose 是用于构建原生 Android 界面的新工具包。它使用更少的代码、强大的工具和直观的 Kotlin API,可以帮助您简化并加快 Android 界面开发。

1.实战准备

因为是新东西,所以配置上和平常有点不一样,可以对照着加下依赖 和配置,有些不用的可以酌情添加,例如:navigation

app的build.gradle

  plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdk 31

    defaultConfig {
        applicationId "com.zyf.myjetpack"
        minSdk 22
        targetSdk 30
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    buildFeatures {
        viewBinding true
        // Enables Jetpack Compose for this module
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion '1.1.1'
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
    // Integration with activities
    implementation 'androidx.activity:activity-compose:1.4.0'
    // Compose Material Design
    implementation 'androidx.compose.material:material:1.1.1'
    // Animations
    implementation 'androidx.compose.animation:animation:1.1.1'
    // Tooling support (Previews, etc.)
    implementation 'androidx.compose.ui:ui-tooling:1.1.1'
    // Integration with ViewModels
    implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1'
    // UI Tests
    androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.1.1'
    // When using a AppCompat theme
    implementation "com.google.accompanist:accompanist-appcompat-theme:0.16.0"
    // Lifecycles only (without ViewModel or LiveData)
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1"

    implementation  "androidx.compose.runtime:runtime-livedata:1.1.1"

    implementation   "androidx.compose.ui:ui:1.1.1"

    implementation   "androidx.navigation:navigation-compose:2.4.1"

    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

}

project的build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        google()
        jcenter()
        maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
    }
    dependencies {
        classpath "com.android.tools.build:gradle:7.0.3"
        //1.6.10版本
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

一、优势与缺点

它和安卓传统xml布局相比,又拥有以下几点优势

  • 更少的代码

    编写更少的代码会影响到所有开发阶段:作为代码撰写者,需要测试和调试的代码会更少,出现 bug 的可能性也更小,您就可以专注于解决手头的问题;作为审核人员或维护人员,您需要阅读、理解、审核和维护的代码就更少。
    与使用 Android View 系统(按钮、列表或动画)相比,Compose 可让您使用更少的代码实现更多的功能。无论您需要构建什么内容,现在需要编写的代码都更少了。

  • 直观
    Compose 使用声明性 API,这意味着您只需描述界面,Compose 会负责完成其余工作。这类 API 十分直观 - 易于探索和使用:“我们的主题层更加直观,也更加清晰。我们能够在单个 Kotlin 文件中完成之前需要在多个 XML 文件中完成的任务,这些 XML 文件负责通过多个分层主题叠加层定义和分配属性。”(Twitter)

  • 加速开发
    Compose 与您所有的现有代码兼容:您可以从 View 调用 Compose 代码,也可以从 Compose 调用 View。大多数常用库(如 Navigation、ViewModel 和 Kotlin 协程)都适用于 Compose,因此您可以随时随地开始采用。“我们集成 Compose 的初衷是实现互操作性,我们发现这件事情已经‘水到渠成’。我们不必考虑浅色模式和深色模式等问题,整个体验无比顺畅。”

  • 功能强大
    利用 Compose,您可以凭借对 Android 平台 API 的直接访问和对于 Material Design、深色主题、动画等的内置支持,创建精美的应用:“Compose 不仅解决了声明性界面的问题,还改进了无障碍功能 API、布局等各种内容。将设想变为现实所需的步骤更少了”;您可以轻松快速地通过动画让应用变得生动有趣:“在 Compose 中添加动画效果非常简单,没有理由不去为颜色/大小/高度变化添加动画效果”(Monzo),“不需要任何特殊的工具就能制作动画,这与显示静态屏幕没有什么不同”(Square)。

上面都是官方文档的官话,下面是我自己的归纳,上面提到的优点我就不赘述

优点

他是一套全新的声明式UI,完全不同于传统所有组件继承于臃肿庞大的view,而是基于更底层的canvas,简单来说,就是它的性能要比安卓原生的xml布局要好,比如xml的多重布局嵌套导致的一些问题,相信安卓开发对复杂页面嵌套优化都头疼过,只要你使用Compose,就不会遇到这样的问题

缺点

目前还是一个新的东西,大部分公司都还没有将Compose 纳入到项目当中,一些将Compose 融入到项目中的细节还没有敲定,例如:如何优雅的将viewmodel与Compose 绑定用于显示UI;一些技术点还待开发人员熟悉

说的好听支持java,但是大家也就图一乐,现在安卓官方主推的是什么语言大家心里都有数

二、前四课

安卓官方文档推出了Jetpack Compose的四课内容,带我们从开始到构建一个简单的聊天屏幕,如果你拥有安卓xml布局和Kotlin 的基础,那么这将非常的简单
安卓官方Jetpack Compose 教程

成果类似下图:
安卓官方文档插图

该屏幕显示包含图片和文字的可展开的动画消息列表,使用 Material Design 原则设计,添加了深色主题,具有预览功能,所有内容只需不到 100 行代码!

以下是您目前为止所学的内容:

  • 定义可组合函数
  • 在可组合项中添加不同的元素
  • 使用布局可组合项构建界面组件
  • 使用修饰符扩展可组合项
  • 创建高效列表
  • 跟踪状态以及修改状态
  • 在可组合项上添加用户互动
  • 在展开消息时显示动画效果

三、标准布局组件

在许多情况下,我们只需使用 Compose 的标准布局元素即可。

1.Column

使用 Column 可将多个项垂直地放置在屏幕上。

@Composable
fun ArtistCard() {
    Column {
        Text("Alfred Sisley")
        Text("3 minutes ago")
    }
}

我们会得到如下布局
在这里插入图片描述

2.Row

同样,使用 Row 可将多个项水平地放置在屏幕上。Column 和 Row 都支持配置它们所含元素的对齐方式。

@Composable
fun ArtistCard(artist: Artist) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Image(/*这里是你的图片*/)
        Column {
            Text(artist.name)
            Text(artist.lastSeenOnline)
        }
    }
}

得到如下布局

在这里插入图片描述

3.Box

使用 Box 可将元素放在其他元素上。Box 还支持为其包含的元素配置特定的对齐方式。

@Composable
fun ArtistAvatar(artist: Artist) {
    Box {
        Image(/*这里是头像*/)
        Icon(/*这里是角标*/)
    }
}

得到如下布局
在这里插入图片描述
通常,您只需要这些构建块。您可以自行编写可组合函数,将这些布局组合成更精美的布局,让其适合您的应用。

在这里插入图片描述

四、xml和compose混合使用 + livedata数据绑定

1.xml和compose混合使用

a.xml中使用compose

在xml中嵌入composeView,通过id: compose_home 绑定布局 ,这个时候就可以和原生xml布局混合使用

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.home.HomeFragment">

    <TextView
        android:id="@+id/xml_home"
        android:text="我是原生xml"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:textAlignment="center"
        android:textSize="20sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_home"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

kt代码(这里博主用的Fragment示例)

class HomeFragment : Fragment() {


    private var _binding: FragmentHomeBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!




    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        
        _binding = FragmentHomeBinding.inflate(inflater, container, false)
        val root: View = binding.root
        //这里绑定了ComposeView
        val view = binding.composeHome
        view.setContent {
            AppCompatTheme{
                HomePge()
            }
        }
        
        

        return root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }


    @Preview
    @Composable
    private fun HomePge(){
        Column() {
            Text(text = "我是 Jetpack Compose")
        }

    }



}

效果如下
在这里插入图片描述

b.compose中使用view

作为之前的补充,所以阅读起来可能会感到断层

通过跟评论区的博友沟通,我留意了在compose中使用view的相关方面,在补充的今天发现了AndroidView这一compose控件,它设计的初衷在于在compose中嵌套使用一些compose暂未支持的view控件(例如WebView, SurfaceView, AdView),当然也可以嵌入xml布局

我们先来看看源码
在这里插入图片描述

  • factory 是我们需要嵌套的view
  • modifier 修饰符
  • update 在布局膨胀后调用的回调,每当在该回调中读取的 State 发生变化时,AndroidView 都会重组。

使用代码如下

@Composable
fun XmlView(){
    var selectedItem by remember { mutableStateOf(0) }

        AndroidView(factory = { context ->
            android.widget.Button(context).apply{
                setOnClickListener{
                    selectedItem += 1
                }
            }
        },
        modifier = Modifier.fillMaxSize(),
        update = {view ->
            view.text = selectedItem.toString()
        })
}

效果如下

在这里插入图片描述

(安卓官方提醒)最好在 AndroidView factory lambda 中构建一个 View,而不是使用 remember 在 AndroidView 之外保存对 View 的直接引用。

如需嵌入 XML 布局,请使用 androidx.compose.ui:ui-viewbinding 库提供的 AndroidViewBinding API。为此,您的项目必须启用视图绑定。
这边直接引入官方代码

@Composable
fun AndroidViewBindingExample() {
    AndroidViewBinding(ExampleLayoutBinding::inflate) {
        exampleView.setBackgroundColor(Color.GRAY)
    }
}

可以看到代码中的ExampleLayoutBinding,这里的布局应该就是 exampl_layout,而exampleView则是xml布局中一个view的id

2.livedata数据绑定

创建 一个ViewModel如下:

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class HomeViewModel : ViewModel() {


    private val _count = MutableLiveData(0)
    val count: LiveData<Int> = _count

    fun add() {
        _count.value = _count.value?.plus(1)
    }

}

在HomePge()页面中稍作改动

    @Preview
    @Composable
    /*    括号里的HomeViewModel 是初始化viewModel,实际并不需要在传参中带进来
    viewModel()为androidx.lifecycle.viewmodel.compose中方法,等同于
    ViewModelProvider(this).get(HomeViewModel::class.java)*/
    private fun HomePge(viewModel: HomeViewModel = viewModel()){
        Column() {
            Text(text = "我是 Jetpack Compose")
            //引用包import androidx.compose.runtime.livedata.observeAsState
            //.observeAsState()绑定 viewModel中的count值,当count发生改变Text组件将会重绘
            val count = viewModel.count.observeAsState()
            Text(
                text =count.value.toString(),
                style = MaterialTheme.typography.h5,
                fontSize = TextUnit.Unspecified,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = dimensionResource(R.dimen.dp20))
                    .wrapContentWidth(Alignment.CenterHorizontally)
                    .clickable {
                        //调用  viewModel的add()方法让其值+1
                        viewModel.add()
                        Toast.makeText(context,count.value.toString(), Toast.LENGTH_SHORT).show()
                    }
            )
        }

    }

效果图
在这里插入图片描述

五.compose结合navigation使用

1.集成导航

先创建三个页面,分别为HomePage,DashboardPage和NotificationPage

@Composable
    fun HomePage(){
        Text(
            "This is HomePage",
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = dimensionResource(R.dimen.dp20))
                .wrapContentWidth(Alignment.CenterHorizontally)
        )
    }

@Composable
    fun DashboardPage(){
        Text(
            "This is DashboardPage",
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = dimensionResource(R.dimen.dp20))
                .wrapContentWidth(Alignment.CenterHorizontally)
        )
    }


    @Composable
    fun NotificationPage(){
        Text(
            "This is NotificationPage",
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = dimensionResource(R.dimen.dp20))
                .wrapContentWidth(Alignment.CenterHorizontally)
        )
    }

为了让代码看起来稍微规范些,我们创建了一个RouteConfig

object  RouteConfig {
    /**
     * homePage路由
     */
    const val ROUTE_HomePage = "Home"

    /**
     * dashboardPage路由
     */
    const val ROUTE_DashboardPage = "Dashboard"

    /**
     * dashboardPage路由
     */
    const val ROUTE_NotificationPage = "Notifications"
}

然后创建一个NavHost对象

@Composable
    fun MainNavHost(){
        NavHost(navController = ,startDestination = ,
            ){
           
        }
    }

NavHost对象需要两个必传参数,一个是NavController,一个是起始路由地址,NavController 对象是 Navigation 组件的中心 API,我们可以通过 rememberNavController创建,代码如下所示:

import androidx.navigation.compose.rememberNavController

val navController = rememberNavController()

我们接着往下写,我们先将navController作为参数传递进来,然后startDestination 设置HomePage为我们的启始路由,每一个composable中为页面添加路由(route)

    @Composable
    fun MainNavHost(navController:NavHostController){
        NavHost(navController = navController,
            startDestination = RouteConfig.ROUTE_HomePage,
            ){
            composable(
                route = RouteConfig.ROUTE_HomePage,
                ){
                HomePage()
            }
            composable(
                route = RouteConfig.ROUTE_DashboardPage,
            ){
                DashboardPage()
            }
            composable(
                route = RouteConfig.ROUTE_NotificationPage,
            ){
                NotificationPage()
            }
        }
    }
  • RouteConfig.ROUTE_HomePage 对应 HomePage()页面
  • RouteConfig.ROUTE_DashboardPage 对应 DashboardPage()页面
  • RouteConfig.ROUTE_NotificationPage 对应 NotificationPage()

学习过navigation的小伙伴们应该知道BottomNavigationView这个组件,compose中也有相对应的组件名为BottomNavigation,我们也这里使用到了,并通过navController.navigate()方法实现了页面之间的跳转,值得一提的是,navController依旧是传递进来的

 @Composable
    fun MyBottomNavigation(navController:NavHostController){
        BottomNavigation(
            Modifier
                .fillMaxWidth()
                .height(64.dp)

        ) {
            BottomNavigationItem(
                true,
                onClick = {
                    navController.navigate(RouteConfig.ROUTE_HomePage)
                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_home_black_24dp),
                        contentDescription = RouteConfig.ROUTE_HomePage,
                    )
                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_HomePage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                        )
                }

            )
            BottomNavigationItem(
                false,
                onClick = {
                    navController.navigate(RouteConfig.ROUTE_DashboardPage)
                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_dashboard_black_24dp),
                        contentDescription = RouteConfig.ROUTE_DashboardPage,
                    )

                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_DashboardPage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                    )
                }
            )

            BottomNavigationItem(
                false,
                onClick = {
                    navController.navigate(RouteConfig.ROUTE_NotificationPage)
                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_notifications_black_24dp),
                        contentDescription = RouteConfig.ROUTE_NotificationPage,
                    )

                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_NotificationPage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                    )
                }
            )

        }
    }

最后我们在oncreate处将MainNavHost和MyBottomNavigation放在activity视图中,这里我使用了Scaffold脚手架,它的bottomBar方法可以将MyBottomNavigation置于底部

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
        //这里进行了navController的初始化
            val navController = rememberNavController()
            Scaffold(
                //设置底部导航栏
                bottomBar = {
                    MyBottomNavigation(navController)
                }
            ) {
                MainNavHost(navController)
            }

        }
    }

我们可以ctrl+右键看看Scaffold的源码,除了bottomBar 还有很多其他的参数可配置,感觉是比较方便的一个脚手架呢
在这里插入图片描述

完整代码,稍作改良

import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.zyf.myjetpack.R


/**
 * @ProjectName : My jetpack
 * @Author : yifeng_zeng
 * @Time : 2022/6/28 19:47
 * @Description : Navigation+Compose
 */
class NavigationActivity : AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            //这里进行了navController的初始化
            val navController = rememberNavController()
            Scaffold(
                //设置底部导航栏
                bottomBar = {
                    MyBottomNavigation(navController)
                }
            ) {
                MainNavHost(navController)
            }

        }
    }

    @Composable
    fun MyBottomNavigation(navController:NavHostController){
        BottomNavigation(
            Modifier
                .fillMaxWidth()
                .height(64.dp)

        ) {
            BottomNavigationItem(
                true,
                onClick = {
                    navController.navigate(RouteConfig.ROUTE_HomePage)
                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_home_black_24dp),
                        contentDescription = RouteConfig.ROUTE_HomePage,
                    )
                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_HomePage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                        )
                }

            )
            BottomNavigationItem(
                false,
                onClick = {
                    navController.navigate(RouteConfig.ROUTE_DashboardPage)
                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_dashboard_black_24dp),
                        contentDescription = RouteConfig.ROUTE_DashboardPage,
                    )

                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_DashboardPage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                    )
                }
            )

            BottomNavigationItem(
                false,
                onClick = {
                    navController.navigate(RouteConfig.ROUTE_NotificationPage)
                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_notifications_black_24dp),
                        contentDescription = RouteConfig.ROUTE_NotificationPage,
                    )

                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_NotificationPage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                    )
                }
            )

        }
    }


    @Composable
    fun MainNavHost(navController:NavHostController){
        NavHost(navController = navController,
            startDestination = RouteConfig.ROUTE_HomePage,
            ){
            composable(
                route = RouteConfig.ROUTE_HomePage,
                ){
                HomePage()
            }
            composable(
                route = RouteConfig.ROUTE_DashboardPage,
            ){
                DashboardPage()
            }
            composable(
                route = RouteConfig.ROUTE_NotificationPage,
            ){
                NotificationPage()
            }
        }
    }



    @Composable
    fun HomePage(){
        Text(
            "This is HomePage",
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = dimensionResource(R.dimen.dp20))
                .wrapContentWidth(Alignment.CenterHorizontally)
        )
    }


    @Composable
    fun DashboardPage(){
        Text(
            "This is DashboardPage",
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = dimensionResource(R.dimen.dp20))
                .wrapContentWidth(Alignment.CenterHorizontally)
        )
    }


    @Composable
    fun NotificationPage(){
        Text(
            "This is NotificationPage",
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = dimensionResource(R.dimen.dp20))
                .wrapContentWidth(Alignment.CenterHorizontally)
        )
    }
    
    

}

效果图
在这里插入图片描述

2.传递参数

在集成导航的时候,我们只能进行简单的页面跳转,下面就在页面跳转时带上我们需要的参数,传递参数是要在路由上添加的,为了便于理解,我们新增一个参数的配置


/**
 * @ProjectName : My jetpack
 * @Author : yifeng_zeng
 * @Time : 2022/6/30 9:01
 * @Description : Navigation参数配置
 */
object ParamsConfig {
    /**
     * 参数-name
     */
    const val PARAMS_COME = "come"

    /**
     * 参数-age
     */
    const val PARAMS_FROM = "from"
}

本来我们的home页面的路由如下

      NavHost(navController = navController,
            startDestination = RouteConfig.ROUTE_HomePage,
            ){
            composable(
                route = RouteConfig.ROUTE_HomePage,
                ){
                HomePage()
            }
            composable(
                route = RouteConfig.ROUTE_DashboardPage,
            ){
                DashboardPage()
            }
            composable(
                route = RouteConfig.ROUTE_NotificationPage,
            ){
                NotificationPage()
            }
        }

现在我们做一点点修改,为路由加上可选参数和不可选参数

  
  /*
        * startDestination : 起始路由
        * */
        NavHost(navController = navController,
            startDestination = RouteConfig.ROUTE_HomePage +
                    "?${ParamsConfig.PARAMS_COME}={${ParamsConfig.PARAMS_COME}}"+
                    "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}",
            ){
            //ParamsConfig.PARAMS_COME与ParamsConfig.PARAMS_FROM都为可选参数
            composable(
                route = RouteConfig.ROUTE_HomePage +
                        "?${ParamsConfig.PARAMS_COME}={${ParamsConfig.PARAMS_COME}}"+
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}",
                arguments = listOf(
                    navArgument(ParamsConfig.PARAMS_COME) {
                        //如果不传的默认值
                        defaultValue = "FIRST HOME"
                    },
                    navArgument(ParamsConfig.PARAMS_FROM) {
                        defaultValue = 0
                        //参数类型,不写默认为String
                        type = NavType.IntType }
                )
                ){
                val argument = requireNotNull(it.arguments)
                val come= argument.getString(ParamsConfig.PARAMS_COME)
                val from = argument.getInt(ParamsConfig.PARAMS_FROM)
                HomePage(come,from)
            }
            composable(
                //ParamsConfig.PARAMS_COME为必填参数,ParamsConfig.PARAMS_FROM为可选参数
                route = "${RouteConfig.ROUTE_DashboardPage}/{${ParamsConfig.PARAMS_COME}}" +
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}",
                arguments = listOf(
                    navArgument(ParamsConfig.PARAMS_COME) {},
                    navArgument(ParamsConfig.PARAMS_FROM) {
                        defaultValue = 999999
                        type = NavType.IntType }
                )
            ){
                val argument = requireNotNull(it.arguments)
                val come= argument.getString(ParamsConfig.PARAMS_COME)
                val from = argument.getInt(ParamsConfig.PARAMS_FROM)
                DashboardPage(come,from)
            }
            composable(
                //ParamsConfig.PARAMS_COME为必填参数,ParamsConfig.PARAMS_FROM为可选参数
                route = "${RouteConfig.ROUTE_NotificationPage}/{${ParamsConfig.PARAMS_COME}}" +
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}",
                arguments = listOf(
                    navArgument(ParamsConfig.PARAMS_COME) {},
                    navArgument(ParamsConfig.PARAMS_FROM) {
                        defaultValue = 3
                        type = NavType.IntType
                    }
                )
            ){
                val argument = requireNotNull(it.arguments)
                val come= argument.getString(ParamsConfig.PARAMS_COME)
                val from = argument.getInt(ParamsConfig.PARAMS_FROM)
                NotificationPage(come,from)
            }
        }
           

此时home页面的路由由 /Home 变更为 /Home?come={come}?from = {from},所以启始路由startDestination 要与home页面路由保持一致,也 变更为 /Home?come={come}?from = {from}。这里的come和from都是可选参数,可以不传,如果不传就会取navArgument中的defaultValue ,navArgument还可以定义参数的类型,默认为String,而必传参数的写法示例为Dashboard页面的路由 由 /Dashboard 变更为 /Dashboard/come?from = {from} 这里come为必传参数,不传就会报错,from为可选参数,可传可不传,通过requireNotNull(it.arguments)方法拿到参数传递给页面,在页面中我们用占位符展示拿到的参数

@Composable
    fun HomePage(come: String?, from: Int) {
        Column() {
            Text(
                "This is HomePage",
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = dimensionResource(R.dimen.dp20))
                    .wrapContentWidth(Alignment.CenterHorizontally)
            )
            Spacer(modifier = Modifier.height(20.dp))
            Text(text = "我是$come 页面,我来自第$from 个页面")
        }
    }

接下来我们通过BottomNavigationItem的点击来传递参数,我么将原来的navController.navigate(RouteConfig.ROUTE_HomePage)修改为如下

navController.navigate("${RouteConfig.ROUTE_HomePage}?come=HOME?from=1")

这里我们的路由如果为可选参数,那么传递时写法为 ?参数 = 值 与路由保持一致,如果是必选参数,那么写法为 /参数 ,示例如下:

//传递可选参数
navController.navigate("${RouteConfig.ROUTE_DashboardPage}/Dashboard?from=2")

/Dashboard为必填参数,错误的写法

navController.navigate("${RouteConfig.ROUTE_DashboardPage}/Dashboard/2")

因为在NavHost中配置的可选参数,但传递参数时使用了必传参数的写法,这是程序就会抛出找不到路由的错误

值得一提的是,我在官文中发现可以将NavHost的选中状态与BottomNavigationItem进行绑定

//这里通过currentBackStackEntryAsState方法拿到navController的状态
            val navBackStackEntry by navController.currentBackStackEntryAsState()
            val currentDestination = navBackStackEntry?.destination

然后在BottomNavigationItem中设置他的选中状态

 BottomNavigationItem(
                /*
                这里将navController的状态与BottomNavigationItem的选中进行绑定,这里的it.route与
                NavHost中第一个composable的route比较,如果为true则为选中
                */
                selected =currentDestination?.hierarchy?.any { it.route ==
                        RouteConfig.ROUTE_HomePage +
                        "?${ParamsConfig.PARAMS_COME}={${ParamsConfig.PARAMS_COME}}"+
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}"
                } == true,
                onClick = {
                    navController.navigate("${RouteConfig.ROUTE_HomePage}?come=HOME?from=1")
                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_home_black_24dp),
                        contentDescription = RouteConfig.ROUTE_HomePage,
                    )
                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_HomePage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                        )
                }

            )

效果图
在这里插入图片描述
完整代码


import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.zyf.myjetpack.R


/**
 * @ProjectName : My jetpack
 * @Author : yifeng_zeng
 * @Time : 2022/6/28 19:47
 * @Description : Navigation+Compose
 */
class NavigationActivity : AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            //这里进行了navController的初始化
            val navController = rememberNavController()
            Scaffold(
                //设置底部导航栏
                bottomBar = {
                    MyBottomNavigation(navController)
                }
            ) {
                MainNavHost(navController)
            }

        }
    }

    @Composable
    fun MyBottomNavigation(navController:NavHostController){
        BottomNavigation(
            Modifier
                .fillMaxWidth()
                .height(64.dp)

        ) {
            //这里通过currentBackStackEntryAsState方法拿到navController的状态
            val navBackStackEntry by navController.currentBackStackEntryAsState()
            val currentDestination = navBackStackEntry?.destination

            BottomNavigationItem(
                /*
                这里将navController的状态与BottomNavigationItem的选中进行绑定,这里的it.route与
                NavHost中第一个composable的route比较,如果为true则为选中
                */
                selected =currentDestination?.hierarchy?.any { it.route ==
                        RouteConfig.ROUTE_HomePage +
                        "?${ParamsConfig.PARAMS_COME}={${ParamsConfig.PARAMS_COME}}"+
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}"
                } == true,
                onClick = {
                    navController.navigate("${RouteConfig.ROUTE_HomePage}?come=HOME?from=1")
                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_home_black_24dp),
                        contentDescription = RouteConfig.ROUTE_HomePage,
                    )
                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_HomePage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                        )
                }

            )
            BottomNavigationItem(
                selected =currentDestination?.hierarchy?.any { it.route ==
                        "${RouteConfig.ROUTE_DashboardPage}/{${ParamsConfig.PARAMS_COME}}" +
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}"
                } == true,
                onClick = {
                    //传递可选参数
                    navController.navigate("${RouteConfig.ROUTE_DashboardPage}/Dashboard?from=2")
                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_dashboard_black_24dp),
                        contentDescription = RouteConfig.ROUTE_DashboardPage,
                    )

                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_DashboardPage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                    )
                }
            )

            BottomNavigationItem(
                selected = currentDestination?.hierarchy?.any { it.route ==
                        "${RouteConfig.ROUTE_NotificationPage}/{${ParamsConfig.PARAMS_COME}}" +
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}"
                } == true,
                onClick = {
                    //不传递可选参数
                    navController.navigate("${RouteConfig.ROUTE_NotificationPage}/Notifications")
                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_notifications_black_24dp),
                        contentDescription = RouteConfig.ROUTE_NotificationPage,
                    )

                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_NotificationPage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                    )
                }
            )

        }
    }


    @Composable
    fun MainNavHost(navController:NavHostController){
        /*
        * startDestination : 起始路由
        * */
        NavHost(navController = navController,
            startDestination = RouteConfig.ROUTE_HomePage +
                    "?${ParamsConfig.PARAMS_COME}={${ParamsConfig.PARAMS_COME}}"+
                    "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}",
            ){
            //ParamsConfig.PARAMS_COME与ParamsConfig.PARAMS_FROM都为可选参数
            composable(
                route = RouteConfig.ROUTE_HomePage +
                        "?${ParamsConfig.PARAMS_COME}={${ParamsConfig.PARAMS_COME}}"+
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}",
                arguments = listOf(
                    navArgument(ParamsConfig.PARAMS_COME) {
                        //如果不传的默认值
                        defaultValue = "FIRST HOME"
                    },
                    navArgument(ParamsConfig.PARAMS_FROM) {
                        defaultValue = 0
                        //参数类型,不写默认为String
                        type = NavType.IntType }
                )
                ){
                val argument = requireNotNull(it.arguments)
                val come= argument.getString(ParamsConfig.PARAMS_COME)
                val from = argument.getInt(ParamsConfig.PARAMS_FROM)
                HomePage(come,from)
            }
            composable(
                //ParamsConfig.PARAMS_COME为必填参数,ParamsConfig.PARAMS_FROM为可选参数
                route = "${RouteConfig.ROUTE_DashboardPage}/{${ParamsConfig.PARAMS_COME}}" +
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}",
                arguments = listOf(
                    navArgument(ParamsConfig.PARAMS_COME) {},
                    navArgument(ParamsConfig.PARAMS_FROM) {
                        defaultValue = 999999
                        type = NavType.IntType }
                )
            ){
                val argument = requireNotNull(it.arguments)
                val come= argument.getString(ParamsConfig.PARAMS_COME)
                val from = argument.getInt(ParamsConfig.PARAMS_FROM)
                DashboardPage(come,from)
            }
            composable(
                //ParamsConfig.PARAMS_COME为必填参数,ParamsConfig.PARAMS_FROM为可选参数
                route = "${RouteConfig.ROUTE_NotificationPage}/{${ParamsConfig.PARAMS_COME}}" +
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}",
                arguments = listOf(
                    navArgument(ParamsConfig.PARAMS_COME) {},
                    navArgument(ParamsConfig.PARAMS_FROM) {
                        defaultValue = 3
                        type = NavType.IntType
                    }
                )
            ){
                val argument = requireNotNull(it.arguments)
                val come= argument.getString(ParamsConfig.PARAMS_COME)
                val from = argument.getInt(ParamsConfig.PARAMS_FROM)
                NotificationPage(come,from)
            }
        }
    }



    @Composable
    fun HomePage(come: String?, from: Int) {
        Column() {
            Text(
                "This is HomePage",
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = dimensionResource(R.dimen.dp20))
                    .wrapContentWidth(Alignment.CenterHorizontally)
            )
            Spacer(modifier = Modifier.height(20.dp))
            Text(text = "我是$come 页面,我来自第$from 个页面")
        }
    }


    @Composable
    fun DashboardPage(come: String?, from: Int){
        Column() {
            Text(
                "This is DashboardPage",
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = dimensionResource(R.dimen.dp20))
                    .wrapContentWidth(Alignment.CenterHorizontally)
            )
            Spacer(modifier = Modifier.height(20.dp))
            Text(text = "我是$come 页面,我来自第$from 个页面")
        }
    }


    @Composable
    fun NotificationPage(come: String?, from: Int){
        Column() {
            Text(
                "This is NotificationPage",
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = dimensionResource(R.dimen.dp20))
                    .wrapContentWidth(Alignment.CenterHorizontally)
            )
            Spacer(modifier = Modifier.height(20.dp))
            Text(text = "我是$come 页面,我来自第$from 个页面")
        }
    }
}

3.深层链接

我想在导航中跳转到网页(我的博客)
在清单文件中进行配置

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zyf.myjetpack">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyJetpack">
        <activity
            android:name=".navigation.NavigationActivity"
            android:exported="true"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
                <data android:scheme="https" android:host="blog.csdn.net" />//这里配置网址

            </intent-filter>
        </activity>
    </application>

</manifest>

在NavHost中添加deepLinks(深层链接,可添加多个)

 composable(
                route = "shop_and_sleep",//我的博客域名
                //深层链接格式可以存在多个
                deepLinks = listOf(navDeepLink {
                    uriPattern = "https://blog.csdn.net/shop_and_sleep"
                })
            ){

            }

然后响应它

 BottomNavigationItem(
                selected = currentDestination?.hierarchy?.any { it.route ==
                        "shop_and_sleep"
                } == true,
                onClick = {
                        //不生效
                    //navController.navigate("https://blog.csdn.net/shop_and_sleep".toUri())
                    val deepLinkIntent = Intent()
                    deepLinkIntent.data="https://blog.csdn.net/shop_and_sleep".toUri()
                    deepLinkIntent.flags= Intent.FLAG_ACTIVITY_NEW_TASK
                    navController.handleDeepLink(deepLinkIntent)

                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_notifications_black_24dp),
                        contentDescription = RouteConfig.ROUTE_NotificationPage,
                    )

                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_NotificationPage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                    )
                }
            )

点击时的效果图在这里插入图片描述
完整代码


import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import androidx.navigation.navDeepLink
import com.zyf.myjetpack.R


/**
 * @ProjectName : My jetpack
 * @Author : yifeng_zeng
 * @Time : 2022/6/28 19:47
 * @Description : Navigation+Compose
 */
class NavigationActivity : AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            //这里进行了navController的初始化
            val navController = rememberNavController()
            Scaffold(
                //设置底部导航栏
                bottomBar = {
                    MyBottomNavigation(navController)
                }
            ) {
                MainNavHost(navController)
            }

        }
    }

    @Composable
    fun MyBottomNavigation(navController:NavHostController){
        BottomNavigation(
            Modifier
                .fillMaxWidth()
                .height(64.dp)

        ) {
            //这里通过currentBackStackEntryAsState方法拿到navController的状态
            val navBackStackEntry by navController.currentBackStackEntryAsState()
            val currentDestination = navBackStackEntry?.destination

            BottomNavigationItem(
                /*
                这里将navController的状态与BottomNavigationItem的选中进行绑定,这里的it.route与
                NavHost中第一个composable的route比较,如果为true则为选中
                */
                selected =currentDestination?.hierarchy?.any { it.route ==
                        RouteConfig.ROUTE_HomePage +
                        "?${ParamsConfig.PARAMS_COME}={${ParamsConfig.PARAMS_COME}}"+
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}"
                } == true,
                onClick = {
                    navController.navigate("${RouteConfig.ROUTE_HomePage}?come=HOME?from=1")
                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_home_black_24dp),
                        contentDescription = RouteConfig.ROUTE_HomePage,
                    )
                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_HomePage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                        )
                }

            )
            BottomNavigationItem(
                selected =currentDestination?.hierarchy?.any { it.route ==
                        "${RouteConfig.ROUTE_DashboardPage}/{${ParamsConfig.PARAMS_COME}}" +
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}"
                } == true,
                onClick = {
                    //传递可选参数
                    navController.navigate("${RouteConfig.ROUTE_DashboardPage}/Dashboard?from=2")
                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_dashboard_black_24dp),
                        contentDescription = RouteConfig.ROUTE_DashboardPage,
                    )

                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_DashboardPage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                    )
                }
            )

            BottomNavigationItem(
                selected = currentDestination?.hierarchy?.any { it.route ==
                        "shop_and_sleep"
                } == true,
                onClick = {
                        //不生效
                    //navController.navigate("https://blog.csdn.net/shop_and_sleep".toUri())
                    val deepLinkIntent = Intent()
                    deepLinkIntent.data="https://blog.csdn.net/shop_and_sleep".toUri()
                    deepLinkIntent.flags= Intent.FLAG_ACTIVITY_NEW_TASK
                    navController.handleDeepLink(deepLinkIntent)

                },
                modifier = Modifier.padding(5.dp),
                icon = {
                    Image(
                        painter = painterResource(R.drawable.ic_notifications_black_24dp),
                        contentDescription = RouteConfig.ROUTE_NotificationPage,
                    )

                },
                label = {
                    Text(
                        text = RouteConfig.ROUTE_NotificationPage,
                        color = Color.Black,
                        modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
                    )
                }
            )

        }
    }


    @Composable
    fun MainNavHost(navController:NavHostController){
        /*
        * startDestination : 起始路由
        * */
        NavHost(navController = navController,
            startDestination = RouteConfig.ROUTE_HomePage +
                    "?${ParamsConfig.PARAMS_COME}={${ParamsConfig.PARAMS_COME}}"+
                    "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}",
            ){
            //ParamsConfig.PARAMS_COME与ParamsConfig.PARAMS_FROM都为可选参数
            composable(
                route = RouteConfig.ROUTE_HomePage +
                        "?${ParamsConfig.PARAMS_COME}={${ParamsConfig.PARAMS_COME}}"+
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}",
                arguments = listOf(
                    navArgument(ParamsConfig.PARAMS_COME) {
                        //如果不传的默认值
                        defaultValue = "FIRST HOME"
                    },
                    navArgument(ParamsConfig.PARAMS_FROM) {
                        defaultValue = 0
                        //参数类型,不写默认为String
                        type = NavType.IntType }
                )
                ){
                val argument = requireNotNull(it.arguments)
                val come= argument.getString(ParamsConfig.PARAMS_COME)
                val from = argument.getInt(ParamsConfig.PARAMS_FROM)
                HomePage(come,from)
            }
            composable(
                //ParamsConfig.PARAMS_COME为必填参数,ParamsConfig.PARAMS_FROM为可选参数
                route = "${RouteConfig.ROUTE_DashboardPage}/{${ParamsConfig.PARAMS_COME}}" +
                        "?${ParamsConfig.PARAMS_FROM}={${ParamsConfig.PARAMS_FROM}}",
                arguments = listOf(
                    navArgument(ParamsConfig.PARAMS_COME) {},
                    navArgument(ParamsConfig.PARAMS_FROM) {
                        defaultValue = 999999
                        type = NavType.IntType }
                )
            ){
                val argument = requireNotNull(it.arguments)
                val come= argument.getString(ParamsConfig.PARAMS_COME)
                val from = argument.getInt(ParamsConfig.PARAMS_FROM)
                DashboardPage(come,from)
            }
            composable(
                route = "shop_and_sleep",//我的博客域名
                //深层链接格式可以存在多个
                deepLinks = listOf(navDeepLink {
                    uriPattern = "https://blog.csdn.net/shop_and_sleep"
                })
            ){

            }
        }
    }



    @Composable
    fun HomePage(come: String?, from: Int) {
        Column() {
            Text(
                "This is HomePage",
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = dimensionResource(R.dimen.dp20))
                    .wrapContentWidth(Alignment.CenterHorizontally)
            )
            Spacer(modifier = Modifier.height(20.dp))
            Text(text = "我是$come 页面,我来自第$from 个页面")
        }
    }


    @Composable
    fun DashboardPage(come: String?, from: Int){
        Column() {
            Text(
                "This is DashboardPage",
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = dimensionResource(R.dimen.dp20))
                    .wrapContentWidth(Alignment.CenterHorizontally)
            )
            Spacer(modifier = Modifier.height(20.dp))
            Text(text = "我是$come 页面,我来自第$from 个页面")
        }
    }


    @Composable
    fun NotificationPage(come: String?, from: Int){
        Column() {
            Text(
                "This is NotificationPage",
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = dimensionResource(R.dimen.dp20))
                    .wrapContentWidth(Alignment.CenterHorizontally)
            )
            Spacer(modifier = Modifier.height(20.dp))
            Text(text = "我是$come 页面,我来自第$from 个页面")
        }
    }
}

六.Compose 中的 ConstraintLayout

依赖

   implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"

官网上说是否将 ConstraintLayout 用于 Compose 中的特定界面取决于开发者的偏好,这是什么意思呢?个人理解就是使用ConstraintLayout 能绘制出的布局是能通过其他组件绘制出同样的布局得,并且没有性能差别,这是与之前不同的,再通俗易懂一点呢,就是爱用不用,反正我有…

  • 引用是使用 createRefs()createRefFor() 创建的,ConstraintLayout 中的每个可组合项都需要有与之关联的引用。
  • 约束条件是使用 constrainAs() 修饰符提供的,该修饰符将引用作为参数,可让您在主体 lambda 中指定其约束条件。
  • 约束条件是使用 linkTo() 或其他有用的方法指定的。
  • parent 是一个现有的引用,可用于指定对 ConstraintLayout 可组合项本身的约束条件。

官网的话总是拗口,先直接把官方示例copy过来瞧瞧

@Preview(showBackground = true)
@Composable
fun ConstraintLayoutContent() {
    ConstraintLayout {
        // Create references for the composables to constrain
        val (button, text) = createRefs()

        Button(
            onClick = { /* Do something */ },
            // Assign reference "button" to the Button composable
            // and constrain it to the top of the ConstraintLayout
            modifier = Modifier.constrainAs(button) {
                top.linkTo(parent.top, margin = 16.dp,goneMargin = 8.dp)
            }
        ) {
            Text("Button")
        }

        // Assign reference "text" to the Text composable
        // and constrain it to the bottom of the Button composable
        Text("Text", Modifier.constrainAs(text) {
            top.linkTo(button.bottom, margin = 16.dp)
        })
    }
}

Preview过后是这个样子
在这里插入图片描述
这里我们以Text来做讲解

	//这里的 text 就是通过 createRefs() 来创建的
	val (button, text) = createRefs()

	//然后通过constrainAs()方法将Text控件与text进行绑定
   Text("Text", Modifier.constrainAs(text) {
   			//这里的top就是Text控件的上方,button.bottom就是Button控件的下方,通过linkTo连接起来
            top.linkTo(button.bottom, margin = 16.dp)
        })
    }

翻译翻译什么TMD叫…串台了,翻译过来就是Text控件的上方在Button控件的下方,外边距为16dp,我们完全可以用其他的控件把这个效果实现,并且没有性能优劣之分,其实这个翻译过来吧有点相对布局那味,算了不管了(狗头保命)

@Preview(showBackground = true)
@Composable
fun NotConstraintLayoutContent() {
    Column {
        Button(
            onClick = { /* Do something */ },
            Modifier.padding(0.dp,16.dp,0.dp,0.dp)
        ) {
            Text("Button")
        }
        Text("Text",Modifier.padding(0.dp,16.dp,0.dp,0.dp))
    }
}

你看,他两是不是一模一样
在这里插入图片描述
当然ConstraintLayout 不止top和bottom,他还包括这些

  • top 和end就是上下
  • start 就是布局的开始,如果你的布局是从左边开始start 就是左边,从右边开始start 就是右边,end也是一样的
  • absoluteLeft 和 absoluteRight 不管布局方向,就是左右
  • baseline 就是在一排
 /**
     * The start anchor of this layout. Represents left in LTR layout direction, or right in RTL.
     */
    @Stable
    val start = ConstraintLayoutBaseScope.VerticalAnchor(id, -2)

    /**
     * The left anchor of this layout.
     */
    @Stable
    val absoluteLeft = ConstraintLayoutBaseScope.VerticalAnchor(id, 0)

    /**
     * The top anchor of this layout.
     */
    @Stable
    val top = ConstraintLayoutBaseScope.HorizontalAnchor(id, 0)

    /**
     * The end anchor of this layout. Represents right in LTR layout direction, or left in RTL.
     */
    @Stable
    val end = ConstraintLayoutBaseScope.VerticalAnchor(id, -1)

    /**
     * The right anchor of this layout.
     */
    @Stable
    val absoluteRight = ConstraintLayoutBaseScope.VerticalAnchor(id, 1)

    /**
     * The bottom anchor of this layout.
     */
    @Stable
    val bottom = ConstraintLayoutBaseScope.HorizontalAnchor(id, 1)

    /**
     * The baseline anchor of this layout.
     */
    @Stable
    val baseline = ConstraintLayoutBaseScope.BaselineAnchor(id)

这里值得一提的是当开发者这样写时会报错
在这里插入图片描述
这个报错信息告诉我们在start中需要的是一个横着的属性,但是我们给了一个竖着的属性bottom

Type mismatch.
Required:
ConstraintLayoutBaseScope.VerticalAnchor
Found:
ConstraintLayoutBaseScope.HorizontalAnchor

所以linkTo方法链接的两端是有标准的

VerticalAnchor是一组可以一起使用的,HorizontalAnchor是一组可以一起使用的,BaselineAnchor是单独使用的,只有baseline 能用

   Text("Text", Modifier.constrainAs(text) {
            start.linkTo(button.end)
            baseline.linkTo(button.baseline)
        })

在这里插入图片描述
那我们如何让一个控件在约束布局中居中呢?

				top.linkTo(parent.top)
                start.linkTo(parent.start)
                end.linkTo(parent.end)
                bottom.linkTo(parent.bottom)

上面的代码可以实现
在这里插入图片描述

七.Compose 手写一个分享二维码弹窗

越写到后面文章就显得有点臃肿了,后续复杂更新会开单章
Jetpack Compose之手写分享页面

你可以学习到

  • 上一节Compose中约束布局使用实战
  • Compose中权重的使用
  • Compose中圆角的设置
  • Compose中padding的使用

八.Compose 设置颜色的三种方式

Compose 设置颜色的三种方式

九.Compose事件与状态简略介绍

Compose事件与状态简略介绍

十.Compose中的预览@Preview与@PreviewParameter的使用

Compose中的预览@Preview与@PreviewParameter的使用

十一.Compose中的获取Context

import androidx.compose.ui.platform.LocalContext

 Toast.makeText(LocalContext.current, "哈哈哈哈", Toast.LENGTH_SHORT).show()

出自Compose中的获取Context评论区

十二.Compose 动画api之我的电子木鱼青春版

空闲时间码的电子木鱼
阅读本文你可以学习到

Compose沉浸式样式
Compose一些动画API例如 animateSizeAsState, infiniteTransition,AnimatedVisibility
Compose沉底样式的Dialog
Compose LazyRow 中的ListItem
Compose的手势监听

Compose 动画api之我的电子木鱼青春版

十三.Compose布局之Image初步使用到了解

Compose布局之Image初步使用到了解


总结

本篇文章只介绍了Compose最基础的使用,这只是它的冰山一角,后续博主自己学习后会更新Compose的其他相关文章。有错误的地方欢迎指正,Compose真正的太新啦,好多组件连官方自己都没有示例,只能看源码肝爆,大家多多支持一下

文章中的源码及自己刷力扣的一些杂七杂八的东西
ssh clone
git@gitcode.net:shop_and_sleep/mycompose.git
https clone
https://gitcode.net/shop_and_sleep/mycompose.git

参考资料
安卓Compose官方文档
JetPack Compose 底部导航栏实现
Jetpack Compose之 在Compose中使用Navigation导航