当前位置:   article > 正文

Android Compose UI (一) 登录页面的编写_compose登录界面

compose登录界面

1.概述

Jetpack Compose 是用于构建原生 Android 界面的新工具包。它使用更少的代码、强大的工具和直观的 Kotlin API,可以帮助您简化并加快 Android 界面开发.
但是需要熟悉一下新的Compose的相关控件,虽然也是文本,图片,水平,垂直,但不是LinearLayout,ImageView这些了,不过也类似,记住就行了.

2.相关组件(最好是每个都知道是做什么的)

Text:用于呈现文本的控件。
TextField:可以让用户输入文本的控件。
Button:用于执行操作的控件。
Image:用于显示图像的控件。
Row: 横排布局
Column: 是竖排布局
Card:用于呈现卡片式布局的控件。
Scaffold:可以将应用程序的基本UI元素包装在一起的控件。
FloatingActionButton: 实现浮动操作按钮
Slider:实现滑动条
ProgressBar:实现进度条
Tab:用于在选项卡之间切换的控件。
BottomNavigation:实现底部导航栏
Dialog:用于显示对话框的控件。
Column:用于将子控件垂直排列
Row:用于将子控件水平排列
Box:类似于 FrameLayout,可以用于布局或者装饰
Scaffold:实现 Material Design 风格的屏幕布局
Surface:实现 Material Design 风格的表面,具有形状和阴影
Divider:用于绘制分割线,可以在Column和Row中使用。
TextButton:实现文本按钮
OutlinedButton:实现带边框的按钮
Checkbox:实现多选框
RadioGroup:实现单选框
TabRow:实现选项卡,可用于导航
ModalBottomSheet:实现底部弹出框
AlertDialog:实现弹出框
BottomSheet: 底部弹出式窗口。
Menu: 弹出式菜单。
Tooltip: 文本提示框。
RadioButton: 单选框。
Switch: 开关按钮。
LinearProgressIndicator: 线性进度指示器
CircularProgressIndicator:圆形进度指示器,圆形进度条。
Spacer:用于占据空白区域,并支持自定义大小
AppBar: 应用栏。
Drawer: 抽屉式布局。用于显示侧边栏的控件。
Box:用于在自由布局中控制位置、大小和绘制顺序等。
Snackbar:用于在屏幕底部显示消息的控件。
Navigation:用于管理应用程序的导航,提供了一种可以让用户从一个屏幕到另一个屏幕的方式。
ViewPager2:用于创建可左右滑动的页面。
SwipeRefreshLayout:可用于实现下拉刷新操作的控件。
ProgressIndicator:用于显示进度的控件,提供了多种样式,如环形进度条、线性进度条等。
WebView:用于在应用中加载网页的控件。
SurfaceView:用于在应用中显示视频的控件,支持播放本地视频和网络视频。
LinearProgressIndicator:线性进度条
DropdownMenu:实现下拉菜单
PopupMenu:弹出菜单
LazyColumn:垂直滚动列表
LazyRow:水平滚动列表
LazyVerticalGrid:垂直滚动网格
LazyHorizontalGrid:水平滚动网格
Pager:分页控件
Surface:用于创建表面,可以用来绘制自定义的UI元素。
SwipeRefresh:用于创建下拉刷新的控件。
Accompanist:提供了许多有用的Compose控件,例如各种加载占位符、图片缩放控件、滑动刷新控件等等。
Compose Charts:提供了各种绘图控件,包括折线图、柱状图、饼状图等等。
Compose Navigator:提供了一种新的导航方式,通过声明式路由和导航来管理不同屏幕之间的转换。
Compose DataTable:提供了数据表格控件,用于展示数据的表格。
Compose Countdown Timer:提供了倒计时控件。
Compose Material Dialogs:提供了Material Design风格的对话框控件。
Compose Timeline:提供了时间线控件。
Compose Dropdown Menu:提供了下拉菜单控件。
BottomAppBar:用于底部应用程序栏。
DatePicker:用于选择日期。
BottomAppBar: 底部应用栏
BottomDrawer: 底部抽屉
TopAppBar: 顶部应用栏
ViewPager: 用于滑动切换多个页面的控件

3.基本用法

  • 所有的组件函数都是由 方法参数 + 结尾block块来组合的
    • 这里有个统一的概念,方法参数中可以决定当前控件的大小,颜色,样式等,也就是之前在xml中写的属性样式
    • 大部分操作可以通过Modifier来完成,这个类会有构建各种,包含但不限于(宽度,高度,描边,圆角)等等
    • 特殊的像设置图片资源之类的才有专门的形参指令,例如Image的 painter需要指向一个资源描述符

代码示例如下

		Image(
                painter = painterResource(id = R.drawable.ic_launcher_background),//通过painterResource函数指定资源图
                contentDescription = "头像",//指定描述文案
                modifier = Modifier //通过Modifier指定控件大小,裁切类型,描边类型
                    .size(40.dp)
                    .clip(CircleShape)
                    .border(1.dp, Color.Blue, CircleShape)
            )
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.通过Compose UI简单的实现一个登录界面

//Activity onCreate中调用setContent方法,这里的initView是一个Compose函数
 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]
            initView()
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

通过@Composable注解函数会被认定为compose相关视图,对应的内部才能调用compose的函数,以及预览等,这一块是有编码校验的

  @OptIn(ExperimentalUnitApi::class)
    @Preview
    @Composable
    fun initView() {
        Column(
            modifier = Modifier
                .background(color = colorResource(id = R.color.bg_white))
                .fillMaxWidth()
                .fillMaxHeight(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally
        ) {
            LogIcon()
            VSpacer(30)
            Text(text = "Welcome to example", fontWeight = FontWeight.Bold, fontSize = TextUnit(23f, TextUnitType.Sp))
            VSpacer(30)
            InputEdit(hint = "please input your userName")
            VSpacer(5)
            InputEdit(hint = "please input your password", true)
            VSpacer(30)
            Button(onClick = { Toast.makeText(this@LoginActivity, "测试", Toast.LENGTH_SHORT).show() }) {
                Text(text = "Login")
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

以上定义的initView函数就是添加@Composable注解,还有@Preview,添加了@Preview注解之后,会在编码框右边看到当前代码的预览图像.如下图所示这样,左边是代码,右边就是预览图,整体还是比较精准的.不过这里没办法选择设备类型,也就是预览图像没有发现哪里可以选择预览的分辨率.能够看个大概
在这里插入图片描述

    @Composable
    fun LogIcon() {
        Image(
            painter = painterResource(id = R.drawable.ic_launcher_background),
            contentDescription = "图标",
            modifier = Modifier
                .width(50.dp)
                .height(50.dp)
                .clip(CircleShape)
                .border(2.dp, colorResource(id = android.R.color.black), CircleShape)
        )
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

LogIcon就是顶部的图标,指定了图标资源,描边,圆形裁切.宽度和高度.

 @Composable
    fun VSpacer(height: Int) {
        Spacer(
            modifier = Modifier
                .fillMaxWidth()
                .height(height.dp)
        )
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

因为这里的分割线都是横向的,所以单独抽了一个函数,通过传入的高度决定是多高.不在外面重复编写.
这里就能看出对比xml的优势了.如果是xml中想要复用的话,是很难做到这种程度的,得挨着复制.

Text(text = "Welcome to example", fontWeight = FontWeight.Bold, fontSize = TextUnit(23f, TextUnitType.Sp))
  • 1

顶部的欢迎文案 所有的控件都通过Column来管理,顶部有介绍这个类似于垂直布局的LinearLayout

    @Composable
    fun InputEdit(hint: String, isPassword: Boolean = false) {
        val textState = remember {
            mutableStateOf("")
        }
        if (!isPassword) {
            loginViewModel.saveName.observeForever {  }
            loginViewModel.saveName.observe(this) {
                textState.value = it
            }
        }
        TextField(visualTransformation = if (isPassword) PasswordVisualTransformation() else VisualTransformation.None,
            value = textState.value,
            onValueChange = {
                textState.value = it
                if (!isPassword) {
                    loginViewModel.saveName.value = it
                }
            },
            modifier = Modifier
                .width(260.dp)
                .height(56.dp),
            label = { Text(if (isPassword) "Password" else "UserName") },
            keyboardOptions = KeyboardOptions.Default.copy(
                imeAction = ImeAction.Done, keyboardType = KeyboardType.Password
            ),
            placeholder = { Text(text = hint, Modifier.alpha(0.5f)) })
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

以上是对应的用户账号和用户密码的输入框,定义了两个形参hint和isPassword 用来动态的显示提示文案和是否是密码输入框
因为这个控件是复用的.对比xml如果要这样做的话,要不就是需要抽取style公共主题,要不就是通过include的方式,然后代码中动态设置.
但是声明式就是这样,关注你的展示结果,而不关注实现过程.可以一个函数搞定这些配置化.这里东西稍微多一些.分开描述一下

  • TextField输入框,不过源码有打上ExperimentalMaterial3Api注解,后期可能会做出调整
    • 形参 visualTransformation 对应的是输入框内容的显示模式,因为密码的话应该要显示***这种样式需要指定为PasswordVisualTransformation()
    • 形参value 对应输入框内容,这里比如要有接受参数,否则输入框会无法键入内容.所以上面创建了一个mutableStateOf对象来接受数据,这个mutableStateOf是会动态更新的,也就是修改state的值,会自动刷新到ui上面去.
    • 形参onValueChange 内容变更的时候就会触发,这里会简单演示了一下和ViewModle配合使用的场景,把这个数据设置到livedata中去,这样切换白天黑夜就依然会保存值,但是需要在上面obser监听.
    • 然后就是通用的Modier指定宽高.
    • 形参label是指定顶部有一个类似于常驻的提示文本.这个不同于hint,是不会因为输入内容而消失的.
    • 形参keyboardOptions 指定键盘的输入类型
    • 形参placeholder 指定在没有内容的时候显示什么,这里就是一个Text显示hint的文案.并且透明度为0.5f
    • 因为没有直接指定hit的参数…
 Button(onClick = { Toast.makeText(this@LoginActivity, "测试", Toast.LENGTH_SHORT).show() }) {
                Text(text = "Login")
            }
  • 1
  • 2
  • 3

最后剩下一个登录按钮,通过Button就可以了,传入onClick blcok参数.后面的block体重,传入Text用于显示文本

class LoginViewModel : ViewModel() {
    val saveName = MutableLiveData<String>()
}
  • 1
  • 2
  • 3

最后贴一下ViewModel的代码
Activity完整代码如下

@OptIn(ExperimentalMaterial3Api::class)
class LoginActivity : ComponentActivity() {

    private lateinit var loginViewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)


        setContent {
            loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]
            initView()
        }
    }


    @OptIn(ExperimentalUnitApi::class)
    @Preview
    @Composable
    fun initView() {
        Column(
            modifier = Modifier
                .background(color = colorResource(id = R.color.bg_white))
                .fillMaxWidth()
                .fillMaxHeight(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally
        ) {
            LogIcon()
            VSpacer(30)
            Text(text = "Welcome to example", fontWeight = FontWeight.Bold, fontSize = TextUnit(23f, TextUnitType.Sp))
            VSpacer(30)
            InputEdit(hint = "please input your userName")
            VSpacer(5)
            InputEdit(hint = "please input your password", true)
            VSpacer(30)
            Button(onClick = { Toast.makeText(this@LoginActivity, "测试", Toast.LENGTH_SHORT).show() }) {
                Text(text = "Login")
            }
        }
    }

    @Composable
    fun LogIcon() {
        Image(
            painter = painterResource(id = R.drawable.ic_launcher_background),
            contentDescription = "图标",
            modifier = Modifier
                .width(50.dp)
                .height(50.dp)
                .clip(CircleShape)
                .border(2.dp, colorResource(id = android.R.color.black), CircleShape)
        )
    }

    @Composable
    fun VSpacer(height: Int) {
        Spacer(
            modifier = Modifier
                .fillMaxWidth()
                .height(height.dp)
        )
    }

    @Composable
    fun InputEdit(hint: String, isPassword: Boolean = false) {
        val textState = remember {
            mutableStateOf("")
        }
        if (!isPassword) {
            loginViewModel.saveName.observeForever {  }
            loginViewModel.saveName.observe(this) {
                textState.value = it
            }
        }
        TextField(visualTransformation = if (isPassword) PasswordVisualTransformation() else VisualTransformation.None,
            value = textState.value,
            onValueChange = {
                textState.value = it
                if (!isPassword) {
                    loginViewModel.saveName.value = it
                }
            },
            modifier = Modifier
                .width(260.dp)
                .height(56.dp),
            label = { Text(if (isPassword) "Password" else "UserName") },
            keyboardOptions = KeyboardOptions.Default.copy(
                imeAction = ImeAction.Done, keyboardType = KeyboardType.Password
            ),
            placeholder = { Text(text = hint, Modifier.alpha(0.5f)) })
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/305623
推荐阅读
相关标签
  

闽ICP备14008679号