阅读本文需要一定compose基础,如果没有请移步Jetpack Compose入门详解(实时更新)
前言💎💎💎
(ps:因为是给初学者学习使用的,本文尽可能的写的比较繁琐,没有采用最佳的写法以便让代码的可读性达到最佳)
本篇文章没有资源文件,自己酌情替换
学习Jetpack Compose也有一段时间了,始终都是一些简单的小案例,或者和其他Jetpack组件结合使用,没有画过复杂的页面,这次就来实战下,先上效果图
一、xml实现
上图是我用xml实现的,点击按钮过后弹出二维码分享页面,下面是xml代码
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#99000000"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/bottom_layout">
<LinearLayout
android:id="@+id/ll_qr_code"
android:layout_width="@dimen/dp270"
android:layout_height="@dimen/dp370"
android:layout_centerInParent="true"
android:background="@mipmap/self_qr_bg"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_avatar_address"
android:layout_width="@dimen/dp40"
android:layout_height="@dimen/dp40"
android:layout_gravity="center"
android:layout_marginTop="@dimen/dp13"
android:src="@mipmap/ic_show_qccode_head" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/dp4"
android:text="邀请你加入工队"
android:textColor="@color/color_1C1C1C"
android:textSize="@dimen/sp13" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/dp3"
android:ellipsize="end"
android:singleLine="true"
android:textColor="@color/color_969696"
android:textSize="@dimen/sp12" />
<ImageView
android:id="@+id/iv_qr_code"
android:layout_width="@dimen/dp148"
android:layout_height="@dimen/dp148"
android:layout_gravity="center"
android:layout_marginTop="@dimen/dp45"
android:src="@mipmap/add" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/dp20"
android:text="扫码立即加入"
android:textColor="@color/color_666666"
android:textSize="@dimen/sp12" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:id="@+id/bottom_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/dp167"
android:layout_alignParentBottom="true"
android:background="@drawable/shape_top_round_white_14"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp25"
android:orientation="horizontal"
android:paddingStart="@dimen/dp8"
android:paddingEnd="@dimen/dp8">
<TextView
android:id="@+id/tv_dingding"
android:layout_width="@dimen/dp45"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableTop="@mipmap/icon_details_share_dingding"
android:drawablePadding="@dimen/dp8"
android:gravity="center_horizontal"
android:minWidth="@dimen/dp45"
android:text="钉钉"
android:textColor="@color/color_666666"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_qq"
android:layout_width="@dimen/dp45"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableTop="@mipmap/icon_details_share_qq"
android:drawablePadding="@dimen/dp8"
android:gravity="center_horizontal"
android:text="QQ"
android:textColor="@color/color_666666"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_wechat"
android:layout_width="@dimen/dp45"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableTop="@mipmap/icon_details_share_wechat"
android:drawablePadding="@dimen/dp8"
android:gravity="center_horizontal"
android:text="微信好友"
android:textColor="@color/color_666666"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_wechat_moments"
android:layout_width="@dimen/dp45"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableTop="@mipmap/icon_details_share_wechat_moments"
android:drawablePadding="@dimen/dp8"
android:gravity="center_horizontal"
android:text="朋友圈"
android:textColor="@color/color_666666"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_copy"
android:layout_width="@dimen/dp45"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableTop="@mipmap/icon_details_share_copy"
android:drawablePadding="@dimen/dp8"
android:gravity="center_horizontal"
android:text="复制链接"
android:textColor="@color/color_666666"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_save"
android:layout_width="@dimen/dp45"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableTop="@mipmap/icon_details_share_save"
android:drawablePadding="@dimen/dp8"
android:gravity="center_horizontal"
android:text="保存图片"
android:textColor="@color/color_666666"
android:textSize="12sp" />
</LinearLayout>
<TextView
android:id="@+id/tv_cancel"
android:layout_width="match_parent"
android:layout_height="@dimen/dp48"
android:layout_alignParentBottom="true"
android:background="@color/white"
android:gravity="center"
android:text="取消"
android:textColor="@color/color_666666"
android:textSize="16sp" />
</RelativeLayout>
</RelativeLayout>
因为是透明弹窗,所以需要一个透明弹窗的样式,这个compose也是一样的
<!--透明activity的style-->
<style name="transparent_activity">
<item name="android:windowBackground">@color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
</style>
二、Compose实现
通过上面的xml布局不难看出,我们想要实现的这个分享页面,结构如下
接下来我们将分享页面拆分为 二维码分享组件和底部分享组件来实现,底部分享组件在下方,二维码分享组件在中间,所以我采用了约束布局
//分享页面
@Composable
fun ShareQRCode() {
ConstraintLayout {
val (bottomLayout, llQrcode) = createRefs()
//二维码分享组件
LinearLayoutQrcode(
Modifier.constrainAs(llQrcode){
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
)
//底部分享组件
BottomLayout(
modifier = Modifier.constrainAs(bottomLayout) {
bottom.linkTo(parent.bottom)
}
)
}
}
1.底部分享组件
让我们来分析下,底部分享组件需要哪些元素
所以我们需要这样一个结构来实现
Column{//背景需要半圆角
Row{
//这里有六个按钮
}
//取消按钮需要在底部
Button{
}
}
a.背景半圆角
Column(
modifier = modifier
.height(167.dp)
.background(
colorResource(R.color.color_f5f5f5),
shape = RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp)
)
)
shape 属性设置圆角,上述代码中RoundedCornerShape分别设置左上,右上,左下,右下的圆角
b.自定义分享按钮
自定义分享按钮的样式是这样的
上面是一个图片,下面是文字,然后都有一个局居中,代码如下
@Composable
fun ImageButton(@DrawableRes id: Int, text: String, modifier: Modifier) {
Column(
modifier
.width(45.dp),
) {
Image(
painter = painterResource(id = id),
contentDescription = "",
Modifier
.fillMaxWidth()
.padding(bottom = 8.dp),
Alignment.Center
)
Text(
text = text,
fontSize = 11.sp,
color = colorResource(id = R.color.color_666666),
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
}
Alignment.Center是一个居中属性,TextAlign.Center代表文字居中,但是在实际使用中,我发现单独使用Alignment.Center并不生效,于是我搭配了Modifier.fillMaxWidth()使其生效,painterResource是使用图片资源函数
然后再在row中使用,从xml中可以看到这个地方使用了权重,在compose中我们也可以用 Modifier.weight()属性来代替
Row(
Modifier
.fillMaxWidth()
.padding(8.dp, 25.dp, 8.dp, 0.dp)
) {
ImageButton(
id = R.mipmap.icon_details_share_dingding,
text = "钉钉",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_qq,
text = "QQ",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_wechat,
text = "微信好友",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_wechat_moments,
text = "朋友圈",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_copy,
text = "复制链接",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_save,
text = "保存图片",
Modifier.weight(1f)
)
}
c.在底部的取消按钮
为了学习嘛,就尝试了另外一种将控件放于底部的方式
Column(
Modifier.fillMaxHeight(),
Arrangement.Bottom
){
Button(){
}
}
d.总体
这里为了显示点击的Toast,多传递了一个context: Activity,BottomLayout完整代码如下
@Composable
fun BottomLayout(
modifier: Modifier,
context: Activity
) {
Column(
modifier = modifier
.height(167.dp)
.background(
colorResource(R.color.color_f5f5f5),
shape = RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp)
)
) {
Row(
Modifier
.fillMaxWidth()
.padding(8.dp, 25.dp, 8.dp, 0.dp)
) {
ImageButton(
id = R.mipmap.icon_details_share_dingding,
text = "钉钉",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_qq,
text = "QQ",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_wechat,
text = "微信好友",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_wechat_moments,
text = "朋友圈",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_copy,
text = "复制链接",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_save,
text = "保存图片",
Modifier.weight(1f)
)
}
Column(
Modifier.fillMaxHeight(),
Arrangement.Bottom
) {
Button(
onClick = {
Toast.makeText(context, "取消分享", Toast.LENGTH_SHORT).show()
context.finish()
},
Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(
containerColor = Color.White
),
shape = RoundedCornerShape(0.dp)//圆角
) {
Text(
text = "取消",
fontSize = 16.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
)
}
}
}
}
@Composable
fun ImageButton(@DrawableRes id: Int, text: String, modifier: Modifier) {
Column(
modifier
.width(45.dp),
) {
Image(
painter = painterResource(id = id),
contentDescription = "",
Modifier
.fillMaxWidth()
.padding(bottom = 8.dp),
Alignment.Center
)
Text(
text = text,
fontSize = 11.sp,
color = colorResource(id = R.color.color_666666),
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
}
效果如下
2.二维码分享组件
一样的我们先分析一下
可以看出二维码分享组件还是比较简单,当然在绘制组件前还要定位
a.组件居中
首先我们让它显示出来
@Composable
fun LinearLayoutQrcode(modifier: Modifier){
Column(
modifier.paint(painterResource(id = R.mipmap.self_qr_bg))
.width(270.dp)
.height(370.dp)
) {
}
}
运行出来效果如下,显而易见,这和我们理想中的效果有点偏差
对比了下之前的xml布局,发现我们在compose中的组件是居于父布局居中的,xml布局是上半部分布局居中的,下面我们来修改一下,将ConstraintLayout中的居中取消,并在Column中居中
//引用
LinearLayoutQrcode(
Modifier.constrainAs(llQrcode){
// top.linkTo(parent.top)
// start.linkTo(parent.start)
// end.linkTo(parent.end)
// bottom.linkTo(parent.bottom)
}
)
@Composable
fun LinearLayoutQrcode(modifier: Modifier){
Column(
modifier.paint(painterResource(id = R.mipmap.self_qr_bg))
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
}
}
然而并没有变化,给我干蒙了,在仔细检查后,我觉得可能是因为LinearLayoutQrcode使用的是父布局的modifier,所以fillMaxSize属性也是父布局的,所以我用了另外一个办法,先让LinearLayoutQrcode组件设置一个和BottomLayout高度一样的外边距,然后再让它居中,就能实现我们想要的效果,可是compose没有外边距,所以我多包了一层Column,代码如下
//引用
LinearLayoutQrcode(
Modifier.constrainAs(llQrcode){
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
)
@Composable
fun LinearLayoutQrcode(modifier: Modifier){
Column(
modifier
.padding(bottom = 167.dp)
) {
Column(
Modifier.paint(painterResource(id = R.mipmap.self_qr_bg)),
horizontalAlignment = Alignment.CenterHorizontally
) {
}
}
}
效果
可以看到,非常理想啊,我们继续下一步
b.顶部头像
Image(
painter = painterResource(id = R.mipmap.ic_show_qccode_head),
contentDescription = "默认头像",
Modifier
.width(53.dp)
.height(53.dp)
.padding(top = 13.dp)
)
Text(
text = "邀请你加入工队",
color = colorResource(id = R.color.color_1C1C1C),
fontSize = 13.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
)
Text(
text = "水电费-试一试1-施工队", color = colorResource(id = R.color.color_969696),
fontSize = 12.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(top = 3.dp)
)
这就没啥好说的,比较简单,有点不习惯的就是没有了外边距,好难受好难受
c.二维码
最后加上二维码
Image(
painter = painterResource(id = R.mipmap.qrcode),
contentDescription = "默认头像",
Modifier
.width(193.dp)
.height(193.dp)
.padding(top = 45.dp),
)
Text(
text = "扫码立即加入",
color = colorResource(id = R.color.color_666666),
fontSize = 12.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp)
)
大功告成,让我们看看效果
![在这里插入图片描述](https://img-blog.csdnimg.cn/4b198d0eeded4f41a4c227e92f8c680d.jpeg
非常的完美啊,和xml分毫不差,大功告成,完整代码
package com.xf.mycomepose
import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.widget.ImageButton
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CornerBasedShape
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.paint
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import com.xf.mycomepose.ui.theme.MyComeposeTheme
class ShareQRCodeActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyComeposeTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = Color(99000000)
) {
ShareQRCode(this@ShareQRCodeActivity)
}
}
}
}
}
@Composable
fun ShareQRCode(context: Activity) {
ConstraintLayout {
val (bottomLayout, llQrcode) = createRefs()
LinearLayoutQrcode(
Modifier.constrainAs(llQrcode) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
)
BottomLayout(
modifier = Modifier.constrainAs(bottomLayout) {
bottom.linkTo(parent.bottom)
},
context = context
)
}
}
@Composable
fun LinearLayoutQrcode(modifier: Modifier) {
Column(
modifier
.padding(bottom = 167.dp)
) {
Column(
Modifier.paint(painterResource(id = R.mipmap.self_qr_bg)),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = R.mipmap.ic_show_qccode_head),
contentDescription = "默认头像",
Modifier
.width(53.dp)
.height(53.dp)
.padding(top = 13.dp)
)
Text(
text = "邀请你加入工队",
color = colorResource(id = R.color.color_1C1C1C),
fontSize = 13.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
)
Text(
text = "水电费-试一试1-施工队",
color = colorResource(id = R.color.color_969696),
fontSize = 12.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(top = 3.dp)
)
Image(
painter = painterResource(id = R.mipmap.qrcode),
contentDescription = "默认头像",
Modifier
.width(193.dp)
.height(193.dp)
.padding(top = 45.dp),
)
Text(
text = "扫码立即加入",
color = colorResource(id = R.color.color_666666),
fontSize = 12.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp)
)
}
}
}
@Composable
fun BottomLayout(
modifier: Modifier,
context: Activity
) {
Column(
modifier = modifier
.height(167.dp)
.background(
colorResource(R.color.color_f5f5f5),
shape = RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp)
)
) {
Row(
Modifier
.fillMaxWidth()
.padding(8.dp, 25.dp, 8.dp, 0.dp)
) {
ImageButton(
id = R.mipmap.icon_details_share_dingding,
text = "钉钉",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_qq,
text = "QQ",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_wechat,
text = "微信好友",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_wechat_moments,
text = "朋友圈",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_copy,
text = "复制链接",
Modifier.weight(1f)
)
ImageButton(
id = R.mipmap.icon_details_share_save,
text = "保存图片",
Modifier.weight(1f)
)
}
Column(
Modifier.fillMaxHeight(),
Arrangement.Bottom
) {
Button(
onClick = {
Toast.makeText(context, "取消分享", Toast.LENGTH_SHORT).show()
context.finish()
},
Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(
containerColor = Color.White
),
shape = RoundedCornerShape(0.dp)//圆角
) {
Text(
text = "取消",
fontSize = 16.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
)
}
}
}
}
@Composable
fun ImageButton(@DrawableRes id: Int, text: String, modifier: Modifier) {
Column(
modifier
.width(45.dp),
) {
Image(
painter = painterResource(id = id),
contentDescription = "",
Modifier
.fillMaxWidth()
.padding(bottom = 8.dp),
Alignment.Center
)
Text(
text = text,
fontSize = 11.sp,
color = colorResource(id = R.color.color_666666),
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
}
在MainActivity中跳转到这个页面
val intent = Intent()
intent.setClass(this, ShareQRCodeActivity::class.java)
Button(
onClick = {
startActivity(mainActivity, intent, null);
},
modifier = Modifier.constrainAs(button) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
) {
Text(text = "Hello $name!")
}
别忘了透明主题
总结
没有官方示例,还原一个稍微复杂一点的布局还是有很多难点,但是使用过程中还是可以看到写法比在xml中使用简单很多,如果熟练确实能提高开发效率