package com.tiger.jetpackcomposelayouts
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
val LocalContentAlpha = compositionLocalOf { error("No ViewModel found!") }
@Composable
fun PhotographerCard(modifier: Modifier = Modifier) {//用反射的方式去创建
Row(
modifier = modifier
.clip(RoundedCornerShape(4.dp))//切圆角
.background(color = MaterialTheme.colorScheme.surface)//白色 有黑就有白
.clickable(onClick = {})
.padding(16.dp)//这里如果 先padding 后clickable 就会在内填充的地方没有水泵
) {
Surface(
modifier = Modifier.size(50.dp),//大小50
shape = CircleShape, //圆形
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.2f) //一个 颜色 黑色 alpha = 0.2f透明度,不让他那么透明
) {
Image(
painter = painterResource(id = R.drawable.touxiang),
contentDescription = null
)
}
Column(
modifier = Modifier
.padding(start = 8.dp)//左边距8 dp
.align(Alignment.CenterVertically)//垂直居中
) {
Text(text = "Alfred Sisley", fontWeight = FontWeight.Bold)
CompositionLocalProvider(LocalContentAlpha provides 0.3f) {
val ap = LocalContentAlpha.current
//这个好像新版本没有
Text(
text = "3 minutes ago", modifier = Modifier.alpha(ap) //透明度
, style = MaterialTheme.typography.bodySmall// 字体样式
)
}
}
}
}
package com.jmj.myapp
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.jmj.myapp.ui.theme.MyAppTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyAppTheme {
PhotographerCard()
}
}
}
}
package com.jmj.myapp
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material3.AlertDialogDefaults.containerColor
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LayoutStudy(){
//插槽
Scaffold(
//解决头部
topBar= { //顶部
TopAppBar( //顶部组件
title = { //标题
Text(text = "LayoutStudy")
},
actions = {//按钮,行为
IconButton(onClick = { /*TODO*/ }) { //图标按钮
Icon(imageVector = Icons.Filled.Favorite //矢量图标
,contentDescription = null) //残障设计
}
}, colors = TopAppBarDefaults.smallTopAppBarColors(containerColor= Color(0xFF753BDD))
)
}) { innerPadding -> //默认插槽下面是有自己的填充的 将填充的值传入进来
//内容部分
BodyContent(Modifier.padding(innerPadding))
}
}
@Composable
fun BodyContent(modifier: Modifier =Modifier){
//不管他传进来多少, 我在默认填充 8.dp
Column(modifier = modifier.padding(8.dp)) {
Text(text = "Hi there!")
Text(text = "Thanks for going through the LayoutStudy !")
}
}
第一种实现方式
package com.tiger.jetpackcomposelayouts
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.unit.dp
import coil.compose.SubcomposeAsyncImage
import coil.compose.rememberAsyncImagePainter
import coil.compose.rememberImagePainter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Composable
fun SimpleColumn() {
Column {
repeat(100) { //repeat函数 重复执行100次
Text(text = "Item #$it", style = MaterialTheme.typography.bodyMedium)
}
}
}
/**
* 上面的组件不支持滚动 由此来支持
*/
@Composable
fun SimpleList() {
//给他一个状态
val scrollState = rememberScrollState()
//垂直方向上的滚动
Column(Modifier.verticalScroll(state = scrollState)) {
repeat(100) { //repeat函数 重复执行100次
Text(text = "Item #$it", style = MaterialTheme.typography.bodyMedium)
}
}
}
/**
* 这个是一个缓冲域,用于不知道有多少条数量的列表进行加载
*/
@Composable
fun LazyList() {
//给他一个状态
val scrollState = rememberLazyListState()
LazyColumn(state = scrollState) {
items(100) {
Text(text = "Item #$it", style = MaterialTheme.typography.bodyMedium)
}
}
}
suspend fun fetch() {}
/**
* 自动跳到首行和末尾
*/
@Composable
fun ScrollingList() {
val scrollState = rememberLazyListState()
val scope = rememberCoroutineScope()
val map = remember {
mutableStateOf(mutableMapOf("a" to "a"))
}
data class Student(
val name: String,
val age: Int,
)
val student = Student(name = "asd", age = 10).apply {
this.copy(name = "修改")
}
LaunchedEffect(Unit, block = {
scope.launch {
fetch()
}
})
Column {
Row {
Button(
modifier = Modifier.weight(1f),
onClick = {
scope.launch {
scrollState.animateScrollToItem(0)
}
}
) {
Text(text = "Scroll to the top")
}
Button(
modifier = Modifier.weight(1f),
onClick = {
scope.launch {
scrollState.animateScrollToItem(99)
}
}
) {
Text(text = "Scroll to the end")
}
}
LazyColumn(state = scrollState) {
item {
Column {
map.value.entries.forEach {
Row {
Text(text = "${it.key}----------${it.value}")
}
}
Button(onClick = {
var m = map.value
val hashMap = HashMap(m)
// val update = map.value.toMutableMap()
hashMap["b"] = "b"
map.value = hashMap
}) {
Text(text = "Add")
}
}
}
items(100) {
Row {
ImageListItem(it)
Text(text = "Item #$it", style = MaterialTheme.typography.bodyMedium)
}
}
}
}
}
@Composable
fun ImageListItem(index: Int) {
Row(verticalAlignment = Alignment.CenterVertically) {
SubcomposeAsyncImage(
model = "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
loading = {
CircularProgressIndicator()
},
contentDescription = "avatar",
contentScale = ContentScale.Crop,
modifier = Modifier.size(48.dp)
)
// Image(
// painter = rememberAsyncImagePainter("https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"),
// contentDescription = "image"
// )
}
}
第二种实现方式
package com.jmj.myapp
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import coil.compose.rememberAsyncImagePainter
import kotlinx.coroutines.launch
@Composable
fun SimpleColumn() {
Column {
repeat(100) {
Text(text = "Item #$it", style = MaterialTheme.typography.bodyMedium)
}
}
}
/**
* 上面是不能滚动的
*/
@Composable
fun SimpleList() {
val scrollState = rememberScrollState()
Column(Modifier.verticalScroll(scrollState)) {
repeat(100) {
Text(text = "Item #$it", style = MaterialTheme.typography.bodyMedium)
}
}
}
/**
* 缓冲集合,不知道集合的数量的时候可以用这个
*/
@Composable
fun LazyList() {
val scrollState = rememberLazyListState()
LazyColumn(state = scrollState) {//朝那个地方划他才会去重新绘制,这样节省了空间
items(100) {
Text(text = "Item #$it", style = MaterialTheme.typography.bodyMedium)
}
}
}
/**
* 小案例 点击按钮改变状态
*/
@Composable
fun ScrollingList() {
val listSize = 100
val scrollState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()//拿到一个协程作用域
Column {
Row {
Button(modifier = Modifier.weight(1f), onClick = {
//通过状态 去滑动
coroutineScope.launch {
scrollState.animateScrollToItem(0)
}
}) {
Text(text = "Scroll to the top")
}
Button(modifier = Modifier.weight(1f), onClick = {
coroutineScope.launch {
scrollState.animateScrollToItem(listSize-1)
}
}) {
Text(text = "Scroll to the bottom")
}
}
LazyColumn(state = scrollState) {//朝那个地方划他才会去重新绘制,这样节省了空间
items(listSize) {
ImageListItem(index = it)
}
}
}
}
/**
* 在线图片组件
*/
@Composable
fun ImageListItem(index: Int) {
Row(verticalAlignment = Alignment.CenterVertically) {//垂直居中
Image(
//给上图片路径即可
painter = rememberAsyncImagePainter(model = "https://i01piccdn.sogoucdn.com/d0515abf9470c2d4"),
contentDescription = "",
modifier = Modifier.size(50.dp)//图片大小
)
Spacer(modifier = Modifier.size(10.dp)) //中间间隔距离
Text(text = "Item #$index", style = MaterialTheme.typography.bodyMedium)
}
}
需要开启网络权限
package com.jmj.myapp
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.magnifier
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.FirstBaseline
import androidx.compose.ui.layout.layout
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.jmj.myapp.ui.theme.MyAppTheme
fun Modifier.firstBaseLineToTop(//写一个扩展函数
firstBaseLineToTop:Dp //参数为Dp 类型
)=this.then(
layout{measurable, constraints -> //修改元素的位置
//测量元素
val placeable = measurable.measure(constraints)
//测量之后,获取元素的基线值
val firstBaseline = placeable[FirstBaseline] //文字底部到第一个父级元素的距离
val placeableY =firstBaseLineToTop.roundToPx() - firstBaseline //用像素相减
val height = placeable.height+placeableY
layout(placeable.width,height){ //设置组件的总高度
placeable.placeRelative(0,placeableY) //设置元素坐标
}
}
)
@Composable
fun TextWithPaddingToBaseLine(){
MyAppTheme {
Column() {
Box(
modifier = Modifier
.then(Modifier.border(1.dp, Color.Black))
) {
Text(
text = "Hi there!", Modifier
.firstBaseLineToTop(24.dp)
.background(Color.Red)
)
}
}
}
}
package com.tiger.jetpackcomposelayouts
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.dp
import com.tiger.jetpackcomposelayouts.ui.theme.JetpackComposeLayoutsTheme
@Composable
fun MyOwnColumn(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
//这个modifier 修饰的是 content的父容器也就是MyOwnColumn
//肯定会有Layout是由Layout去布局的 ,所以参数被使用的是 Layout这个父容器
Layout(
modifier = modifier,
content = content
) { measurables, constrains ->
// 测量里面的元素
val placeables = measurables.map { measurable ->
//一一测量
//stream流类似 映射成 List
measurable.measure(constrains)
}
var yPosition = 0
//当前布局的大小
//父容器决定最大值
layout(constrains.maxWidth, constrains.maxHeight) {
placeables.forEach{placeable->
placeable.placeRelative(x = 0,y=yPosition)
yPosition+=placeable.height
}
}
}
}
@Composable
fun MyOwnColumnSample() {
JetpackComposeLayoutsTheme {
MyOwnColumn(modifier = Modifier.padding(8.dp)) {
Text(text = "MyOwnColumn")
Text(text = "places items")
Text(text = "vertically")
Text(text = "we've done it by hand!")
}
}
}
package com.tiger.jetpackcomposelayouts
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
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.unit.Dp
import androidx.compose.ui.unit.dp
val topics = listOf(
"Arts & Crafts",
"Maths",
"Chinese",
"Books",
"Beauty",
"Fashion",
"Deign",
"History",
"Film",
"Business",
)
@Composable
fun StaggeredGrid(
modifier: Modifier = Modifier
) {
Chip(modifier,"Arts & Crafts")
}
@Composable
fun Chip(
modifier: Modifier = Modifier,
text: String
) {
//一个卡片,圆角,里面包含一个Row ,第一列是Box ,第二列是文本
Card(
modifier = modifier,
border = BorderStroke(color = Color.Black, width = Dp.Hairline),//边框 黑色 1像素
shape = RoundedCornerShape(8.dp)//圆角边框弧度
) {
Row(
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), //水平方向 和垂直方向 填充
verticalAlignment = Alignment.CenterVertically //垂直居中
) {
//Row里的内容
Box(
modifier = Modifier
.size(16.dp, 16.dp)
.background(color = MaterialTheme.colorScheme.secondary)
)//一个正方形图片
Spacer(modifier = Modifier.width(4.dp))
Text(text = text)
}
}
}
package com.tiger.jetpackcomposelayouts
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
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.layout.Layout
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlin.math.max
val topics = listOf(
"Arts & Crafts",
"Maths",
"Chinese",
"Books",
"Beauty",
"Fashion",
"Deign",
"History",
"Film",
"Business",
)
@Composable
fun StaggeredGrid(
modifier: Modifier = Modifier,
rows: Int = 3, //自定义传进来多少行
content: @Composable () -> Unit
) {
Layout(
modifier = modifier,
content = content
) { measurables, constraints ->
//计算每一行的宽度和高度
val rowWidths = IntArray(rows) { 0 }//用来存放每行的宽度值 = 每行的元素宽度之和
val rowHeight = IntArray(rows) { 0 }//用来存放每行的高度值
val placeables: List = measurables.mapIndexed { index, measurable ->
//测量所有元素
val placeable = measurable.measure(constraints)
//计算每一行的宽度与高度
//元素下标 ,假设总共11个元素
//index :0,1,2,3,4,5,6,7,8,9,10
//行数 ,假设3行
//rows:3
// 保存行宽高数组的下标值:
//row 0,1,2
val row = index % rows
//一行的宽度等于这一行所有元素之和
rowWidths[row] += placeable.width;
//一行的高度等于这一行所有元素高度最高的
// val i = rowHeight[row]
// val height = placeable.height
// if (i
val row = index % rows //读出下表
val x = rowX[row]
val y = rowY[row]
placeable.placeRelative(x, y)
rowX[row] += placeable.width
}
}
}
}
@Composable
fun Chip(
modifier: Modifier = Modifier,
text: String
) {
//一个卡片,圆角,里面包含一个Row ,第一列是Box ,第二列是文本
Card(
modifier = modifier,
border = BorderStroke(color = Color.Black, width = Dp.Hairline),//边框 黑色 1像素
shape = RoundedCornerShape(8.dp)//圆角边框弧度
) {
Row(
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), //水平方向 和垂直方向 填充
verticalAlignment = Alignment.CenterVertically //垂直居中
) {
//Row里的内容
Box(
modifier = Modifier
.size(16.dp, 16.dp)
.background(color = MaterialTheme.colorScheme.secondary)
)//一个正方形图片
Spacer(modifier = Modifier.width(4.dp))
Text(text = text)
}
}
}
@Composable
fun StaggeredGridBodyContent(modifier: Modifier = Modifier) {
Row(
modifier = modifier
.background(color = Color.LightGray)
.padding(16.dp)
.horizontalScroll(rememberScrollState()), //水平滚动
content = {
StaggeredGrid(modifier = Modifier, rows = 2) {
for (topic in topics) { //从上面的主题集合 放入
Chip(modifier = Modifier.padding(8.dp), text = topic)
}
}
}
)
}
implementation("androidx.constraintlayout:constraintlayout-compose:1.0.0-beta01")
@Composable
fun ConstraintLayoutContent(){
ConstraintLayout {
//通过createRefs创建引用,ConstrainLayout 中的每个元素都需要关联一个引用
val (button,text) = this.createRefs() //创建两个对象 的引用 button,text
Button(
onClick = {},
// 使用Modifier.constrainAs来提供约束,引用作为它的第一个参数
//在lambda表达式中指定约束规则
modifier = Modifier.constrainAs(button){//关联引用
//使用linkTo 指定约束,parent 是 ConstraintLayout 的引用
this.top.linkTo(this.parent.top, margin = 16.dp)
}
) {
Text(text = "Button")
}
Text(text = "Text", modifier = Modifier.constrainAs(text){
this.top.linkTo(button.bottom, margin = 16.dp)
//在ConstraintLayout 中水平居中
this.centerHorizontallyTo(parent)
})
}
}
@Composable
fun ConstraintLayoutContent2(){
ConstraintLayout {
val (button1,button2,text) = createRefs()
Button(
onClick = {},
modifier = Modifier.constrainAs(button1){
top.linkTo(parent.top, margin = 16.dp)
}
) {
Text(text = "Button 1")
}
Text(text = "Text", modifier = Modifier.constrainAs(text){
this.top.linkTo(button1.bottom, margin = 16.dp)
centerAround(button1.end)//它的中间 在button1按钮的结尾
})
//将button1 和 text组合起来,建立一个屏障(barrier)
val barrier = createEndBarrier(button1,text)
Button(
onClick = {},
modifier = Modifier.constrainAs(button2){
top.linkTo(parent.top, margin = 16.dp)
start.linkTo(barrier) //起始是在构建的组合的末尾
}
) {
Text(text = "Button 1")
}
}
}
第三种:文字换行约束
@Composable
fun LargeLayoutContent(){
ConstraintLayout {
val text = createRef()
val guidenline = createGuidelineFromStart(fraction =0.5f)//指导线将会位于布局的水平方向上中央的位置
Text(text = "this is a very very very very very very very very very very very very very very very very very very very very very very very long Text"
, modifier = Modifier.constrainAs(text){
linkTo(start = guidenline ,end = parent.end)
width= Dimension.preferredWrapContent//开启可以自动换行
})
}
@Composable
fun DecoupledConstraintLayout2() {
BoxWithConstraints {
val constraints = if (maxWidth
package com.tiger.jetpackcomposelayouts
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material3.Divider
import androidx.compose.material3.Text
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.unit.dp
@Composable
fun TwoTexts(modifier:Modifier=Modifier){
Row (modifier = modifier.height(IntrinsicSize.Min)){
Text(
text = "Hi",
modifier = Modifier
.weight(1f)
.padding(start = 4.dp)
.wrapContentWidth(Alignment.Start)
)
Divider(
color = Color.Black,
modifier = Modifier
.fillMaxHeight()
.width(1.dp)
)
Text(
text = "There",
modifier = Modifier
.weight(1f)
.padding(start = 4.dp)
.wrapContentWidth(Alignment.End)
)
}
}