Jetpack Compose入门详解(实时更新)
Posted 我怀里的猫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpack Compose入门详解(实时更新)相关的知识,希望对你有一定的参考价值。
Jetpack Compose入门详解
- 前排提醒
- 前言(Compose是什么)
- 一、优势与缺点
- 二、前四课
- 三、标准布局组件
- 四、xml和compose混合使用 + livedata数据绑定
- 五.compose结合navigation使用
- 六.Compose 中的 ConstraintLayout
- 七.Compose 手写一个分享二维码弹窗
- 八.Compose 设置颜色的三种方式
- 九.Compose事件与状态简略介绍
- 十.Compose中的预览@Preview与@PreviewParameter的使用
- 十一.Compose中的获取Context
- 十二.Compose 动画api之我的电子木鱼青春版
- 十三.Compose布局之Image初步使用到了解
- 总结
前排提醒
我知道点进来的人都是想学习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)
)
)
BottomNavi以上是关于Jetpack Compose入门详解(实时更新)的主要内容,如果未能解决你的问题,请参考以下文章