Jetpack Compose-1.3.0 alpha03 基础布局组件

最近尝试了Jetpack Compose ,参考Compose 编程思想,我在测试中使用的版本是“androidx.activity:activity-compose:1.3.0-alpha03”
本文中所写的案例95%都经过测试,可以完成一个页面的基本布局,跟kotlin的anko相比,这个更好用,是推荐的。但在生产环境完成页面来说还有距离,对复杂页面我感觉不适合,查代码可太复杂了。
本篇只讲基础操作,动画和手势放在了单独的篇章讲解

1、运行工程遇到的问题

1)下载了示例工程代码,使用as4.0打开始终找不到app,报错:

Extension with name ‘android’ does not exist
咋办呢?下载官方编译器:
Canary build
Get the leading-edge features in a lightly tested build.
DOWNLOAD ARCTIC FOX (2020.3.1) CANARY 9

2)Please remove usages of jcenter() Maven repository from your build scripts and migrate your build to other Maven repositories.

移除repositories中的`jcenter()

3)Failed to find Build Tools revision 30.0.2

我的问题是根据提示升级了com.android.tools.build:gradle:7.0.0-alpha08’
到09,运行报错,然后发现
android {
compileSdkVersion 30
buildToolsVersion “30.0.2”//这一句消失了,可以把它指定为as中当前有的版本。
之后发现是因为我没有30.0.2这个版本才报错,我就下了一个
然后就运行起来了Jetpack Compose-1.3.0 alpha03 基础布局组件_第1张图片
然后就运行起来了,当然页面相当丑
关于实时预览,我的电脑是17配的windows中端机,i76700CPU(3.4、3.41GHZ),8G内存,每次预览要延迟个40s-1分钟,大部分保存能触发刷新,但有时候得手动刷新,刷新结果还不一定准确

4)setContent{}

setContent从androidx.compose.ui.platform移到了androidx.activity.compose,需引入implementation “androidx.activity:activity-compose:1.3.0-alpha02”

2、Modifier

这是一个用来声明View属性的对象,所有的View都有这个属性

modifier = Modifier
//尺寸
	.fillMaxHeight() / .fillMaxWidth() / .fillMaxSize() //相当于match
	.wrapContentHeight(align = Alignment.Top)//Top|Bottom|CenterVertically 
    .wrapContentWidth(align = Alignment.End)//CenterHorizontally|Start|End
    .wrapContentSize(align = Alignment.Center)//Center|CenterStart|CenterEnd|TopCenter|TopStart|TopEnd|BottomCenter|BottomStart|BottomEnd                
	.height(80.dp).width(70.dp) //设置组件给定的宽高
//间隔
	.padding(10.dp,10.dp,0.dp,10.dp)//设置组件padding,注意padding结合background的作用相当于margin;同时padding作用于Size内部,而非外部,因此需要注意背景尺寸变化
	.background(  )//背景
		background(     color = Color.LightGray) //默认填充颜色
		background(     color = Color.LightGray,      shape = RoundedCornerShape(20.dp))//带颜色的圆角Shap,需要注意圆角外的空间一开始默认填充为黑色,后来就完全填充看不出形状了,解决办法是再设置shape=RoundedCornerShape(20.dp))
	.border()//边框,注意边框在背景内部,因此边框较宽时需要增大组件尺寸
		.border(width = 3.dp,color = Color.Black)// 宽度和颜色
	.alpha() //透明度,对背景不起作用

总体来说JC的组件分成三类:基础布局、基础组件、嵌套组件

3、基础布局

Jetpack Compose-1.3.0 alpha03 基础布局组件_第2张图片

1)Column 纵向布局 &Row 横向布局:LinearLayout

Column 默认设置会直接将所有子项逐个堆叠起来,中间不留间距,子View默认靠左。

Column(modifier = Modifier
        .padding(16.dp)
        .fillMaxWidth()
        .background(Color.LightGray)
        ,verticalArrangement = Arrangement.Bottom
        //verticalArrangement: Arrangement.Vertical = Arrangement.Top	布局子级的 竖直 放置方式,默认从布局顶部往布局底部方向放置
        verticalArrangement = Arrangement.spacedBy(4.dp),//官方提供的用法,内部组件的间隔
        ,horizontalAlignment = Alignment.Start        
        //horizontalAlignment: Alignment.Horizontal = Alignment.Start	布局子级的 水平直 对其方式,默认从布局开始对齐
    ) {内容}

2)Box :FrameLayout

Box(modifier = Modifier            .height(150.dp)            .width(150.dp)
                ,contentAlignment = Alignment.Center//内容对齐方式
        ){
           //子View
        }

3)LazyRow/LazyColumn :RecyclerView & ScrollView

alpha11废弃了滚动视图系列 ScrollableRow、ScrollableColumn,因为超长列表的情况,滚动视图没有缓存机制,列表过长的时候,容易造成内存溢出。
替代实现滚动的组件是LazyRow/LazyColumn,对标RecyclerView,无需Adapter。但下拉刷新,上拉加载等效果待研究;
当然如果内容不多可以用Row(Modifier.horizontalScroll(rememberScrollState()) / Column
()来代替实现滚动效果。不过Row/Column实现的滚动效果都不会处理视图的缓存复用。
需要注意的是,Lazy的布局内部必须是item{},其内部才能增加布局,否则会报错。而且只能实现单行或者单列布局

val listState = rememberLazyListState()//用于监听滚动
LazyRow(
    content = {
         item {
             Text(text = "Head")
         }
         val listData = (0..5).toList()
         items(listData.size) {//循环生成的布局
             Text(
                 text = "Text:${listData.get(it)}",
                 Modifier.fillMaxHeight().width(60.dp)
                     .border(width = 1.dp, color = Color.Cyan)
             )
         }
         item { //单独成型的布局
             Text(
                 text = "Footer", modifier = Modifier
                     .background(color = Color.Green)
                     .fillMaxHeight(), textAlign = TextAlign.Center
             )
         }
     }, modifier = Modifier.fillMaxWidth().height(80.dp)
         .background( Color.LightGray )
         ,state = listState
 )

需要提的是,根据官网发布的消息,LazyColumn 具有固定头stickyHeader ,但其为ExperimentalFoundationApi。而且目前不支持动画

4)LazyVerticalGrid:GirdView

网格布局 1.0.0-beta02版本出现,这个在用的时候一直报错,原因是因为它是个实验性的组件,因此需要添加额外的注释@ExperimentalFoundationApi,并且需要给调用该组件的所有父函数添加。
比如我在函数testApp()中调用了该组件,那么调用testApp()的函数alpha()和调用了alpha()的beta()都需要添加@ExperimentalFoundationApi。

LazyVerticalGrid(
      cells = GridCells.Fixed(2)
  ) {
      items(listData) {
          Box(
              modifier = Modifier
                  .fillMaxWidth()
                  .height(24.dp)
                  .background(
                      color = when {
                          it % 2 == 0 -> {
                              Color.Yellow
                          }
                          else -> {
                              Color.Red
                          }
                      }
                  ),
              contentAlignment = Alignment.Center,
          ) {
              Text(
                  text = "列表项:$it"
              )
          }
      }
  }

5)ConstraintLayout

这个我在引用的时候只有android下面需要传context的版本,因此我猜测需要额外的引包,或者需要带包名的类全程
ConstraintLayout
约束布局迁移到了新包中,需引入implementation “androidx.constraintlayout:constraintlayout-compose:1.0.0-alpha02”

 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)
            }
        ) {
            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)
        })
    }

上述为官方案例

5)Scaffold :Material顶层布局

JC提供了一个Material 顶层布局Scaffold,官方介绍的用途是“让您实现具有基本 Material Design 布局结构的界面,使用 Scaffold,很容易确保 TopAppBar、BottomAppBar、FloatingActionButton 和 Drawer这些组件得到适当放置且正确地协同工作”

 Scaffold (
        drawerContent = { /*...*/ },
        topBar = { /*...*/ },
        bodyContent = { /*...*/ }
    )
    //一个完整案例(未测试)
     val scaffoldState = rememberScaffoldState()
    Scaffold(
        scaffoldState = scaffoldState,
        drawerContent = {

            Box(
                modifier = Modifier.fillMaxSize(),
                contentAlignment = Alignment.Center
            ) {
                Text(text = "抽屉组件中内容")
            }

        },

        //标题栏区域
        topBar = {
            TopAppBar(
                title = { Text(text = "脚手架示例") },
                navigationIcon = {
                    IconButton(
                        onClick = {
                            scaffoldState.drawerState.open()
                        }
                    ) {
                        Icon(
                            imageVector = Icons.Filled.Menu,
                            contentDescription = null
                        )
                    }
                }
            )
        },

        //悬浮按钮
        floatingActionButton = {
            ExtendedFloatingActionButton(
                text = { Text("悬浮按钮") },
                onClick = { }
            )
        },
        floatingActionButtonPosition = FabPosition.End,

        //屏幕内容区域
        content= {
            Box(
                modifier = Modifier.fillMaxSize(),
                contentAlignment = Alignment.Center
            ) {
                Text(text = "屏幕内容区域")
            }
        },
    )`

4、基础组件

内部不能嵌套任何东西的原组件,最小分子

1)Text / ClickableText:TextView

二者区别在于能否使用onClick ,Button只能设置背景和点击事件不能显示文字

Text(
	text = "YAMA",//要显示的文字		
	color = Color.White,// 文字颜色,modifuer中alpha的作用对象
	fontWeight = FontWeight.ExtraBold,//样式
	fontStyle = FontStyle.Italic,//字体
	fontFamily = FontFamily.Cursive,//字体
	fontWeight
	textAlign = TextAlign.Center,//文本在段落行内的对齐方式,测试时发现他调整的是横向的文字位置,不涉及到纵向,因此height巨大时不能View的居中显示。其中Justify、left和start表现是一样的;right和end表现一样。设置他不能做纵向的居中
	textDecoration = TextDecoration.LineThrough 给文字加线,三种样式:文字中间、文字底部、没有
	style = androidx.compose.ui.text.TextStyle.Default,//样式,推荐使用MaterialTheme.typography.XXX
	//尺寸相关
	fontSize lineHeight letterSpacing 三个属性都要求 TextUnit参数,默认值为TextUnit.Unspecified。值为XX.sp,注意此处不能使用dp
	//其他属性
	maxLines = 2 //最大显示行数
	overflow = TextOverflow.Ellipsis //超出限制后显示……
	softWrap = true//自动折行
)

关于文字位置的设置遇到一些问题:
1)当Text处于Surface中,且Surface尺寸固定时,居中仅设置modifier = Modifier.wrapContentSize()就可以实现;不设置宽高或者设置为fill默认在左上角;
2)当Text没有外容器时,设置fillMaxHeight,背景能填充的范围,但文字通过textAlign 不能设置居中
3)使用第三方字体
需要将字体放入res/font下面,之后需要声明

//声明
val firaSansFamily = FontFamily(
    Font(R.font.firasans_light, FontWeight.Light),
    Font(R.font.firasans_regular, FontWeight.Normal),
    Font(R.font.firasans_italic, FontWeight.Normal, FontStyle.Italic),
    Font(R.font.firasans_medium, FontWeight.Medium),
    Font(R.font.firasans_bold, FontWeight.Bold)
)
//使用
 Text( fontFamily = firaSansFamily, fontWeight = FontWeight.Normal,fontStyle = FontStyle.Italic)

4)文字重复并限制显示
Text("hello ".repeat(50), maxLines = 2)
在这里插入图片描述
5)点击事件:给整个Text设置点击事件,需要在Modifier中的clickable()方法设置

2)Image / Icon:ImageView

这个地方有个坑,教程和网上都说用imageResource来获取图片,然而我这版本没有这个函数,最后看的源码发现了内置函数1中的图片获取.而且我是真没想到,为了区分bitmap和vector专门做了两个组件

Image(bitmap = image, contentDescription = "image test",modifier = Modifier
            .width(80.dp)
            .height(80.dp))
Icon(imageVector =ImageVector.Companion.vectorResource(id=R.drawable.ic_baseline_ac_unit_24)  , contentDescription ="icon deta"
        tint = Color.Blue )

Image和Icon一样 接受 imageVector: ImageVector, bitmap: ImageBitmap, painter: Painter, 三种图片资源,其中可以转换:

ImageBitmap转Painter remember(bitmap) { BitmapPainter(bitmap) }
imageVector转Painter rememberVectorPainter(imageVector)

注意VectorDrawable和BitmapDrawable严格区分,混用会报错

Icon的tint有两点要说:

1)使纯色图标改色使用 tint = 颜色
2)使彩色图标显色使用 tint = Color.Unspecified(不应用任何色调) 不设置会是黑乎乎的一团,受 AmbientContentColor.current 默认着色影响

3)Switch / CheckBox

整个按钮我就想说他们都需要val checkedState = remember { mutableStateOf(true) }来记录状态,否则选中状态是不会更改的

 val switchCheck = remember { mutableStateOf(true) }
            val checkCheck = remember { mutableStateOf(true) }
			Switch(checked = switchCheck.value, onCheckedChange = {
                Log.i("switch", "$it")
                switchCheck.value = it
            })
            Checkbox(checked = checkCheck.value, onCheckedChange = {
                Log.i("checkbox", "$it")
                checkCheck.value = it
//                check(it)//baocuo
            })

绝大部分属性和button很像,这俩目前不知道如何更换图片,使用IconButton倒是可以

4)RadioButton

这个组件一般用来多选,单选建议使用checkbox或者iconButton

//RadioButton数量
val tags = arrayListOf("Java", "Kotlin", "XML", "Compose", "JetPack")
//选中的值
val radioCheck = remember { mutableStateOf("Null") }
        Row() {
            tags.forEach {
                Row {
                    RadioButton(
                        selected = it == radioCheck.value,
                        onClick = {
                            radioCheck.value = it
                        }
                    )
                    Text(text = it)
                }

                Spacer(modifier = Modifier.width(20.dp))
            }
        }

5) CircularProgressIndicator /LinearProgressIndicator :ProgressBar

圆形进度条 CircularProgressIndicator

CircularProgressIndicator不声明progress参数的时候是无限转动的,声明之后则需要触发,可以通过rememberProgress.value的增加和减少来改变进度

CircularProgressIndicator() //loading
val rememberProgress = remember { mutableStateOf(0.1f) }//正经进度条
CircularProgressIndicator(
     progress = rememberProgress.value
 )
TextButton(onClick = {//点击触发
     if (rememberProgress.value < 1f) rememberProgress.value += 0.1f }) {
     Text(text = "增加进度")
 }        
长条进度条 LinearProgressIndicator
 LinearProgressIndicator( )//loading
 val rememberProgress = remember { mutableStateOf(0.1f) }//正经进度条
 LinearProgressIndicator( progress = rememberProgress.value)
  TextButton(onClick = {//点击触发
	 if (rememberProgress.value < 1f) rememberProgress.value += 0.1f }) {
     Text(text = "增加进度")
  }

进度条是可以添加动画的,要点是progress = animatedProgress;关于animatedProgress的写法我所用的版本有争议还在探索

6)Slider :SeekBar可拖动进度条

var sliderValue = remember{ mutableStateOf(0f)}
Slider(value = sliderValue.value
        , onValueChange = {
       		sliderValue.value = it
            Log.i("slider","$it")  }
        ,enabled = true
        ,valueRange = 0f..1f //取值范围
        ,colors = SliderDefaults.colors(thumbColor = Color.Yellow)
        ,steps = 3//步骤 分段,划到两段中间会自动靠边
        ,interactionSource = remember { MutableInteractionSource() }
 )

7)Spacer:Space & View

Spacer(modifier = Modifier.height(20.dp).width(100.dp)
	.background(Color.Red,shape = CutCornerShape(10.dp))

测试了一下可以当作Space或者仅设置背景色的View用,jc当中没有margin,我看范例中都是用Spacer

5、嵌套组件

1)Surface

这个布局可以看作是一个FrameLayout,内部组件全部在左上角,可以做 Image和Text的组合嵌套

Surface(  modifier = Modifier.height(60.dp).width(60.dp),
                shape = RoundedCornerShape(20.dp), //形状的颜色由下面的color控制,而非modifier 中的background
                color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
            ) {      //这里是布局内容        }

注意经过测试发现给Surface设置modifier .background不生效,默认为白色填充;不设置background时默认为透明;border生效可以正常显示

2)Button

没想到吧,我当初用这个的时候一头雾水薪水这个Button怎么不能设置Text呢,结果他是内部嵌套View。Button默认是个横向布局

Button(
	onClick = {//点击事件
            Log.d("ClickableText", " -th character is clicked.") }
            ,modifier = Modifier.background(Color.Yellow).padding(10.dp)
            //button背景
            ,border = BorderStroke(1.dp, Color.White)
            ,colors =  ButtonDefaults.buttonColors(contentColor = Color.Yellow)
            //阴影
            ,elevation = ButtonDefaults.elevation(defaultElevation = 3.dp)
            ,shape = MaterialTheme.shapes.small.copy(
                topStart = ZeroCornerSize, topEnd = CornerSize(40.dp)
                ,bottomEnd = CornerSize(40.dp),bottomStart = ZeroCornerSize
            )
            //可用性
            ,enabled = true
            //容器和子View之间的距离值
            ,contentPadding = PaddingValues(40.dp)
            ) {//Button内部的文字和图标
	            Text(text = buildAnnotatedString {
	                append("Button:")
	                withStyle(style = SpanStyle(color = Color.Blue,
	                    fontWeight = FontWeight.Bold )){
	                    append("style \n");
	                }
	            })
	            Text(text = stringResource(id = R.string.app_name))
        }

Button的着色顺序是:modifier -> Button属性 -> 子View
在这里插入图片描述
上图中 黄色部分是modifier .background,紫色部分是Button的color+shape作用
需要注意的是colors 和elevation 获取 与12版本的教程不一样,最后翻源码翻出来的。
还有OutlinedButton和TextButton在参数上没有什么区别,更多是概念上的,Button用于主要操作;Outlined 主要用于重要但不主要的操作;TextButton用于不明显的动作,如cards and dialogs中的按钮
IconButton是可单击的图标,用于表示操作。 IconButton的整体最小触摸目标大小为48 x 48dp,以满足辅助功能准则。内容位于IconButton内部。
注意Button系列中的interactionState在新版本中用,interactionSource = MutableInteractionSource()替代了

3)IconToggleButton

该组件必须放置一个icon,否则显示空白

val checkedState = remember { mutableStateOf(true) }//更换状态的关键字
          IconToggleButton(checked =  checkedState.value
               ,onCheckedChange = {
                   Log.e("IconToggleButton", "点击了:$it")
                   checkedState.value = it
               } ) {
               Icon( imageVector = Icons.Filled.Favorite,
                   contentDescription ="icon"
,                    tint = if (checkedState.value) {  Color.Red    }
						 else {  Color.Gray   }
               )
           }
       }

4)文字选中 DisableSelection / SelectionContainer

这是一种用于text的容器控件,控件内的文字处于“可以被选中”和“不可以被选中”的状态,我顺带测试了一下,具有文字的组件都不能被选中

	SelectionContainer {//可选的
	       Text("This text is selectable")
	}
	DisableSelection {//不可选的
	       Text("This text is not selectable")
	}

5)CardJetpack Compose-1.3.0 alpha03 基础布局组件_第3张图片

card是典型的嵌套组件,官方案例测试得到

var expanded = remember { mutableStateOf(false) }
var title = "aaaaaaaa"
var body="aaaaa\naaaaaa\naaaaa\naaa\n"
// Describe the card for the current state of expanded.
Card {
    Column {
        Text(text = title)
        // Content of the card depends on the current value of expanded.
        if (expanded.value) {
            Text(text = body)
            // Change expanded in response to click events.
            IconButton(onClick = { expanded.value = false }) {
                Icon(Icons.Default.ExpandLess, contentDescription = "Collapse")
            }
        } else {
            // Change expanded in response to click events.
            IconButton(onClick = { expanded.value = true }) {
                Icon(Icons.Default.ExpandMore, contentDescription = "Expand")
            }
        }
    }
}

6、其他组件

1) TextField :TextInputLayout

从使用上说这是一个更优化的TextInputLayout。普通的Text是不能输入并更改更新的。该组件分为TextField(效果与EditText默认效果一致)和OutlinedTextField(效果为带有一个边框用于自定义)
示例代码我运行不起来,测试后发现是源代码中的var text by remember { mutableStateOf(“Hello”) }这句有问题,TextField要求我们自行处理内容更新的问题,然而我找到的资料在我用的版本上用不了,痛哭
var password by rememberSaveable { mutableStateOf("") }//官方提供,我所在的JC版本运行不起来
var text by savedInstanceState { “” } //网友提供,也不好用
最后发现和checkbox的选择一样,使用:val textValue = remember{mutableStateOf(“null”)} 时,可以通过textValue.value 来获取的,万能的mutableStateOf
所以我觉得,犹豫不决的时候都可以用remember{mutableStateOf(Object)}

 val textValue = remember{mutableStateOf("null")}//文字缓存
    TextField(
        //值
        value= textValue.value //,包含了输入框编辑状态的信息,这个功能很强大,可以用来更新文本,光标等,然后还可以从其他位置直接观察到这些值的变化。也就是相当于双向绑定的意思
        ,onValueChange = {Log.i("textChange","内容:$it")
                         textValue.value = it}//输入服务更新TextFieldValue中的值时触发的回调,更新后的TextFieldValue值作为回调参数
        //显示在文本字段内的可选标签 Title
        ,label = { Text("Enter password") }//显示在文本字段内的可选标签,类似于占位符,但是是输入框没有获得焦点时候的占位符
        //hint
        ,placeholder = {
            Text("请输入内容")
        }
        //输入框的样式,默认样式为AmbientTextStyle.current
        ,textStyle = MaterialTheme.typography.button// 应用于输入框的样式,目前还没成功
        ,shape = CutCornerShape(20.dp)
        //文字颜色控制
        ,colors = TextFieldDefaults.textFieldColors(textColor = Color.Yellow)
        ,interactionSource = MutableInteractionSource()
        //图标
        ,leadingIcon = {//左侧图标
            Icon(imageVector = Icons.Filled.Lock, contentDescription = "icon")
        }
        ,trailingIcon = {//右侧图标,一般用来显示delete
            Icon(
                imageVector = Icons.Filled.Delete,
                contentDescription = "icon"
                ,modifier = Modifier.clickable(  onClick = { textValue.value=""  } )  ) }
        //输入限制
        ,visualTransformation = androidx.compose.ui.text.input.PasswordVisualTransformation(),//注意这个要写带包名的全称,我用的类名一直报错
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
        //capitalization  //autoCorrect  //keyboardType //imeAction
        ,keyboardActions = KeyboardActions(onDone = {  })
        ,maxLines = 2 //Int.MAX_VALUE,显示行数
        ,enabled = true
        ,readOnly = true //只读模式不可修改值
        ,singleLine = true //单行显示
        ,isError = true //是否处于报错状态,有报错样式
                )

组件具有左侧的icon,右侧的删除按钮,背景设置和错误提示,算是比较全面的了

2)TabRow / ScrollableTabRow:TabBar

TabRow、ScrollableTabRow的区别是,前者让选项平分充满屏幕宽度;后者让选项完全展开。虽然有了TabBar但目前还缺少ViewPager

 var state = remember { mutableStateOf(0) }//存tab号
 val titles = listOf("标签1", "标签2", "这是很长的标签3","6789uij")//标签
 Column {
     //超出范围后可以滚动的TabBar
    ScrollableTabRow(
         selectedTabIndex = state.value,
         modifier = Modifier.wrapContentWidth(),
         edgePadding = 16.dp //独有属性,前后padding
     ) {
         titles.forEachIndexed { index, title ->
             Tab(
                 text = { Text(title) },
                 selected = state.value == index,
                 onClick = { state.value = index }
             )    }
     }
     //完全填充Tabbar
     TabRow(selectedTabIndex = state.value//当前选中项
         //标签文字颜色
        ,backgroundColor = Color.Yellow
    	,contentColor = Color.Cyan
         //标签点击的动画效果
         ,indicator ={tabPositions ->
             TabRowDefaults.Indicator( Modifier.tabIndicatorOffset(tabPositions[state.value]) ) }
         //位于tab组件上指定位置的一条线,默认是在底部和活动条一个位置,如果高度高于活动条,则活动条会露白
     	,divider = { TabRowDefaults.Divider(color = Color.Yellow,modifier = Modifier.height(3.dp)) }
     ) {
         titles.forEachIndexed { index, title ->
             Tab(
                 text = { Text(title) },
                 selected = state.value == index,
                 onClick = { state.value = index }
             )
         }
     }
     Spacer(modifier = Modifier.height(20.dp))
     Text(
         modifier = Modifier.align(Alignment.CenterHorizontally),
         text = "第${state.value + 1}个标签被选中了",
         style = MaterialTheme.typography.body1
     )
 }

Jetpack Compose-1.3.0 alpha03 基础布局组件_第4张图片
上图为divider 示例

3)TopAppBar、BottomAppBar:Toolbar

TopAppBar包含标题,导航图标和其他的更多操作等。

//底部
BottomAppBar(
     modifier = Modifier.fillMaxWidth().height(80.dp),
         backgroundColor = Color.Green,//背景色
         contentColor = Color.Cyan,//文字颜色
         cutoutShape = CutCornerShape(20.dp),//看不出来
         elevation = 1.dp
     ) {
     //BottomAppBar的内容,实际上是高度自定义的没有任何限制,所以我感觉可以用来替代tabBar
		}

我测试了一下,TopAppBar和BottomAppBar所处的位置由布局决定,不会因为用了TopAppBar就自动置顶,BottomAppBar同理。这俩的区别直观来说:
TopAppBar的内容不会居中,呈现一种贴top的样式;而BottomAppBar的内容会垂直居中
最后:两个组件都需要使用 Spacer(Modifier.weight(1f, true))来撑开空隙
遇到的问题:TopAppBar有两个构造函数,使用带参数title的这个函数时会报错,判断指向,指向的是另一个

4)DropdownMenu:popupwindows菜单

这个我在网上看的博客测评的是alpha09版本,和我用的1.3.0-alpha03差别巨大,主要区别是对menu的触发icon的处理,从组件中声明,变成了下面这种全靠mutableStateOf开关的方式,后者我感觉更解耦
它内部使用PopUp实现

var expandedDropdownMenu = remember { mutableStateOf(false) }
   IconButton(onClick = { expandedDropdownMenu.value = true }) {
       Icon(imageVector = Icons.Default.MoreVert
           ,contentDescription = "")
   }

   DropdownMenu(
       expanded = expandedDropdownMenu.value,
       onDismissRequest = { expandedDropdownMenu.value = false }
   ,modifier = Modifier.wrapContentSize()
   ,offset = DpOffset(0.dp,10.dp)
//                ,properties = PopupProperties()
   ) {
       DropdownMenuItem(onClick = { }) {
           Text("分享")
       }
       DropdownMenuItem(onClick = { }) {
           Text("举报")
       }
       DropdownMenuItem(onClick = { }) {
           Text("关注")
       }
   }

5)AlertDialog

就是很普通的alertDialog样式

var expandedDropdownMenu = remember { mutableStateOf(false) }
if (expandedDropdownMenu.value) {
      AlertDialog(
          onDismissRequest = {
              expandedDropdownMenu.value = false
          },
          title = {
              Text(text = "这是对话框标题")
          },
          text = {
              Text(
                  "这是一段描述对话框提示内容的文本, " +
                          "这个文本有点长,还有点俏皮!"
              )
          },
          confirmButton = {
              TextButton(
                  onClick = {
                      expandedDropdownMenu.value = false
                  }
              ) {
                  Text("确认")
              }
          },
          dismissButton = {
              TextButton(
                  onClick = {
                      expandedDropdownMenu.value = false
                  }
              ) {
                  Text("取消")
              }
          }
      )
  }
}

7、内置函数

0)主题

方便一键换色,官方指导

MaterialTheme(
    colors = …,
    typography = …,
    shapes = …
) {
    // app content
}

这个我感觉尤其像anko

1)Resources系列

位于在ui:ui:1.0.0-beta01@aar下统一package为androidx.compose.ui.res

//资源类
//颜色
colorResource(id: kotlin.Int):androidx.compose.ui.graphics.Color
ResourcesCompat.getColor(@NonNull Resources res, @ColorRes int id, @Nullable Theme theme)
ColorStateList getColorStateList(@NonNull Resources res, @ColorRes int id, @Nullable Theme theme)
//尺寸
dimensionResource(id: kotlin.Int): androidx.compose.ui.unit.Dp { /* compiled code */ }
animatedVectorResource(id: kotlin.Int): androidx.compose.ui.graphics.vector.AnimatedImageVector 
fontResource(fontFamily = ):androidx.compose.ui.text.font.Typeface
//数值类
stringResource(id: kotlin.Int): kotlin.String
    text = stringResource(R.string.congratulate, "New Year", 2021) //Happy %1$s %2$d
booleanResource(id: kotlin.Int): kotlin.Boolean { /* compiled code */ }
integerArrayResource(id: kotlin.Int): kotlin.IntArray { /* compiled code */ }
stringArrayResource(id: kotlin.Int): kotlin.Array 
// 
//    %1$d minute
//    %1$d minutes
// 
resources.getQuantityString(  R.plurals.runtime_format, quantity, quantity )
integerResource(id: kotlin.Int):kotlin.Int
//图片类
ImageVector:
	ImageVector.Companion.vectorResource(id: kotlin.Int) 
	使用 androidx.compose.material.icons 包下的Icons中提供的资源,例如Icons.Outlined.Face
ImageBitmap 
	ImageBitmap.Companion.imageResource(id = )
androidx.compose.ui.graphics.painter.Painter
painter = painterResource(id = R.drawable.ic_logo)

我用的这个版本一致找不到网上和官网资料上的imageResource(id = R.drawable.xxx),而这让我比较头疼是因为 Image和icon组件可以用imageBitmap和imageVector,稍有不慎就写错了

2)Shape

CutCornerShape(20.dp) 折角矩形
AbsoluteCutCornerShape(20.dp) 折角矩形
AbsoluteRoundedCornerShape() 圆角矩形
CircleShape 如果尺寸为方则为圆形,长方则为圆角矩形
RectangleShape 直角矩形
MaterialTheme.shapes.small | medium 小圆角矩形
MaterialTheme.shapes.large 直角矩形
//小修改(复制系统的修改圆角)
shape = MaterialTheme.shapes.small.copy(
        topStart = ZeroCornerSize, topEnd = CornerSize(40.dp)
            ,bottomEnd = CornerSize(40.dp),bottomStart = ZeroCornerSize)

3)保存值

remember{ mutableStateOf(0f)} 我目前只用这一个,已经能满足需求,可存入 String、bool、int、float完全没压力
remember { MutableInteractionSource() }这个目前还没探索,但是很多组件都有这个interactionSource属性

大量值的保存

官方有个案例值得借鉴,通过HelloViewModel 做数据传递

class HelloViewModel : ViewModel() {
    // LiveData holds state which is observed by the UI.
    // (state flows down from ViewModel)
    private val _name = MutableLiveData("")
    val name: LiveData = _name

    // onNameChanged is an event we're defining that the UI can invoke.
    // (events flow up from UI)
    fun onNameChanged(newName: String) {
        _name.value = newName
    }
}

@Composable
fun HelloScreen(helloViewModel: HelloViewModel = viewModel()) {
    // By default, viewModel() follows the Lifecycle as the Activity or Fragment
    // that calls HelloScreen().

    // name is the _current_ value of [helloViewModel.name]
    // with an initial value of "".
    // observeAsState returns a State that will trigger a recomposition
    // of the composables that read the state whenever it changes.
    val name: String by helloViewModel.name.observeAsState("")

    Column {
        Text(text = "Hello, $name")
        TextField(
            value = name,
            onValueChange = { helloViewModel.onNameChanged(it) },
            label = { Text("Name") }
        )
    }
}

4)函数

repeat

这个函数不仅可以让string重复,还能重复组件

 repeat(10) {
    Text("Item $it", modifier = Modifier.padding(2.dp))
 }

5)测试

官方发布

8、其他

1)绘图

绘图这个简直吹爆JC,非常简洁,参考博客
官方文档

Canvas(modifier = Modifier.fillMaxSize()){
    val canvasWidth = size.width
    val canvasHeight = size.height
    drawLine(
        start = Offset(x=canvasWidth, y = 0f),
        end = Offset(x = 0f, y = canvasHeight),
        color = Color.Blue
    )
}

2)动画

Jetpack Compose-1.3.0 alpha03 基础布局组件_第5张图片

手势

9、遇到的问题

1)预览延迟

我是有延迟,在真机上运行后,编译器的预览颜色才发生改变
或者使用快捷键Build(ctrl+Shift+F5)

2)预览

首先说一个函数内的布局能被预览,函数需要被声明为可组合函数,使用@Composable注释
创建可组合函数,只需将 @Composable 注释添加到函数名称中即可。
首先说一下@Preview这个标签不能给class用,使用是有限制的在这里插入图片描述
@Preview
@Preview(“Light Theme”, widthDp = 720, heightDp = 1080) //第一个是名字

你可能感兴趣的:(android前端笔记,android,Jetpack,Compose)