Android compose wanandroid app之导航规整以及登录页个人中心页实现

Posted theyangchoi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android compose wanandroid app之导航规整以及登录页个人中心页实现相关的知识,希望对你有一定的参考价值。

前言

在前面开发时只是注重了页面绘制,已经compose各种组件的使用,没有规整导航,所以页面跳转的操作很难实现;今天先规整一下页面导航,在页面跳转操作完成之后在绘制登录页以及个人中心页。

导航规整

在前面绘制页面的时候说到,compose打开页面的时候会在当前页面直击打开,所以就需要把要打开的页面都放在主页中进行打开,那么就要区分页面是首页,还是其他页面。

首先定义一个页面枚举,main代表首页,其他则是其他页面:

/**
 * 页面类
 * */
enum class RouteKey(val route:String)
    Main("main"),
    Login("login"),
    WebView("webview")

使用navhost进行导航,主页所有页面都用navigation进行包裹:

@ExperimentalMaterialApi
@ExperimentalCoilApi
@ExperimentalPagerApi
@Composable
fun RouteNavigation(navHostController: NavHostController,
           onFinish: () -> Unit
)
    val context = LocalContext.current

    NavHost(navController = navHostController, startDestination = RouteKey.Main.route)
		//主页面
        navigation(
            route = RouteKey.Main.route,
            startDestination = Nav.BottomNavScreen.HomeScreen.route
        )

            composable(Nav.BottomNavScreen.HomeScreen.route)
                HomePage(navHostController = navHostController)
            
            ....//其他要展示在主页面的paer
        
        //要打开的新页面
        //登录页
        composable(RouteKey.Login.route) 
            LoginPage(navHostController = navHostController)
        
    

首页导航页面把四个页面都封装起来:

object Nav 
    sealed class BottomNavScreen(val route: String, @StringRes val resourceId: Int, @DrawableRes val id: Int) 
        object HomeScreen: BottomNavScreen("home", R.string.nav_home, R.drawable.home_unselected)
        object ProjectScreen: BottomNavScreen("project",R.string.nav_project,R.drawable.project_unselected)
        object ClassicScreen: BottomNavScreen("classic",R.string.nav_classic,R.drawable.classic_unselected)
        object MineScreen: BottomNavScreen("mine", R.string.nav_mine, R.drawable.mine_unselected)
    
    //主页点击两次返回桌面
    var onMainBackPressed = false
    val bottomNavRoute = mutableStateOf<BottomNavScreen>(BottomNavScreen.HomeScreen)

将不同页面的展示封装到一个page,所有页面都在这个page打开,并加载到MainActivity里面去,但是要区分是主页,还是其他页面。

先判断是否是主页:

fun isMainScreen(route:String):Boolean = when(route)
    Nav.BottomNavScreen.HomeScreen.route,
        Nav.BottomNavScreen.ProjectScreen.route,
        Nav.BottomNavScreen.ClassicScreen.route,
        Nav.BottomNavScreen.MineScreen.route -> true

    else -> false

然后根据得到的结果加载页面:

@ExperimentalPagerApi
@ExperimentalMaterialApi
@Composable
fun MainPage(
    navHostController: NavHostController = rememberNavController(),
    onFinish:() -> Unit
)
    //返回back堆栈的顶部条目
    val navBackStackEntry by navHostController.currentBackStackEntryAsState()
    //返回当前route
    val currentRoute = navBackStackEntry?.destination?.route ?: Nav.BottomNavScreen.HomeScreen.route
    //加载主页内容
    if (isMainScreen(currentRoute))
        Scaffold(
            contentColor = MaterialTheme.colors.background,
            //标题栏
            topBar = 
                Column 
                    Spacer(
                        modifier = Modifier
                            .background(MaterialTheme.colors.primary)
                            .statusBarsHeight()
                            .fillMaxWidth()
                    )
                
            ,
            //底部导航栏
            bottomBar = 
                Column 
                    BottomNavBar(Nav.bottomNavRoute.value, navHostController)
                    Spacer(
                        modifier = Modifier
                            .background(MaterialTheme.colors.primary)
                            .navigationBarsHeight()
                            .fillMaxWidth()
                    )
                
            ,
            //内容
            content =  paddingValues: PaddingValues ->
                //内容嵌套在Scaffold中
                RouteNavigation(navHostController, paddingValues, onFinish)

                OnBackClick(navHostController)
            )
    else
        //加载独立页面
        RouteNavigation(navHostController, onFinish = onFinish)
    

到这里就完成了导航的规整,页面打开也没有问题,接下来就是个人中心页面以及登录页面的绘制和实现了。

个人中心的实现

目前个人中心比较简单,就展示了一个头像,昵称,用户id以及用户积分,更多的东西等到实现收藏等操作之后在添加,简单看一下效果图。

布局元素比较简单,这里就不贴布局文件了。

MineViewmodel获取数据

登录成功之后保存cookie,通过cookie调用用户信息接口,获取用户信息。

class MineViewModel : ViewModel() 
    //默认头像
    val defaultHead = "https://jusha-info.oss-cn-shenzhen.aliyuncs.com/obt/mall/upload/image/store/2021/08/06/1628250153533.png"

    private val _userInfo = MutableLiveData<UserConfigModule>()
    val userInfo = _userInfo
    fun getUserInfo()
        Log.e("intoTAG","get user info")
        NetWork.service.getUserInfo().enqueue(object :  Callback<BaseResult<UserConfigModule>>
            override fun onResponse(
                call: Call<BaseResult<UserConfigModule>>,response: Response<BaseResult<UserConfigModule>>) 
                Log.e("intoTAG","response")
                response.body()?.let 
                    _userInfo.value = it.data
                
            

            override fun onFailure(call: Call<BaseResult<UserConfigModule>>, t: Throwable) 
                Log.e("intoTAG","onFailure$t.message")
            
        )
    

MinePage

获取信息并展示。

@Composable
fun MinePage(navHostController: NavHostController)
    val mineViewModel:MineViewModel = viewModel()
    val userInfo by mineViewModel.userInfo.observeAsState()
    mineViewModel.getUserInfo()
    Column(
        Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())) 
        com.yangchoi.composeuidemo.ui.bar.TopAppBar(title = "我的")
        Box(
            Modifier
                .background(Color.White)
                .fillMaxSize()
        ) 
            Column(Modifier.fillMaxSize()) 
                if (userInfo !== null)
                    //头像昵称
                    ConstraintLayout 
                        val (headImg,userName,userId)  = createRefs()

                        Image(painter = rememberImagePainter(mineViewModel.defaultHead),
                            contentDescription = "用户头像",
                            modifier = Modifier
                                .size(80.dp)
                                .padding(16.dp, 20.dp, 0.dp, 0.dp)
                                .clip(shape = RoundedCornerShape(50))
                                .constrainAs(headImg) )

                        Text(text = "$userInfo!!.userInfo.nickname",fontSize = 14.sp,color = Color.Black,modifier = Modifier
                            .padding(10.dp, 20.dp, 0.dp, 0.dp)
                            .constrainAs(userName) 
                                start.linkTo(headImg.end)
                                top.linkTo(headImg.top)
                            )

                        Text(text = "$userInfo!!.userInfo.id",fontSize = 12.sp,color = Color.Gray,modifier = Modifier
                            .padding(10.dp, 20.dp, 0.dp, 0.dp)
                            .constrainAs(userId) 
                                start.linkTo(headImg.end)
                                bottom.linkTo(headImg.bottom)
                            )
                    

                    ConstraintLayout(modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 40.dp)
                        .height(50.dp)) 
                        val (icons,title,integral,btmLine) = createRefs()

                        Row(Modifier
                            .constrainAs(icons) 
                            .fillMaxHeight()
                            .padding(16.dp, 0.dp, 0.dp, 0.dp),
                            verticalAlignment = Alignment.CenterVertically) 
                            Image(painter = painterResource(id =R.drawable.icon_integral),
                                contentDescription = "积分", modifier = Modifier
                                    .height(20.dp)
                                    .width(20.dp))
                        

                        Row(modifier = Modifier
                            .constrainAs(title) 
                                start.linkTo(icons.end)
                            
                            .fillMaxHeight()
                            .padding(horizontal = 10.dp),
                            verticalAlignment = Alignment.CenterVertically) 
                            Text(text = "积分",fontSize = 12.sp,color = Color.Black,textAlign = TextAlign.Center)
                        

                        Row(modifier = Modifier
                            .fillMaxHeight()
                            .constrainAs(integral) 
                                end.linkTo(parent.end)
                            
                            .padding(horizontal = 16.dp),
                            verticalAlignment = Alignment.CenterVertically) 
                            Text(text = "$userInfo!!.coinInfo.coinCount",fontSize = 12.sp,color = Color.Gray,textAlign = TextAlign.Center,)
                        

                        Divider(
                            modifier = Modifier
                                .padding(0.dp, 0.dp, 16.dp, 0.dp,)
                                .constrainAs(btmLine) 
                                    bottom.linkTo(parent.bottom)
                                ,
                            color = Color(229,224,227),
                            thickness = 1.dp,
                            startIndent = 16.dp)
                    
                else
                    Row(modifier = Modifier
                        .padding(horizontal = 16.dp, vertical = 200.dp)
                        .fillMaxWidth()
                        .height(50.dp)
                        .border(
                            1.dp,
                            color = Color(114, 160, 240),
                            shape = RoundedCornerShape(20.dp)
                        ),verticalAlignment = Alignment.CenterVertically) 

                        Text(text = "登  录",
                            fontSize = 16.sp,
                            modifier = Modifier
                                .fillMaxWidth()
                                .clickable 
                                    navHostController.navigate("$RouteKey.Login.route")
                                ,
                            color = Color(114, 160, 240),
                            textAlign = TextAlign.Center)

                    
                


            
        
    

请求头添加cookie

登录成功之后会返回一个cookie在请求头里面,只需要将cookie拦截并保存下来,就可以通过cookie去获取用户信息。

//创建OKhttp
private val client: OkHttpClient.Builder = OkHttpClient.Builder()
            .addInterceptor(LogInterceptor())
            .addInterceptor 
                val request = it.request()
                val response = it.proceed(request)
                val requestUrl = request.url.toString()
                val domain = request.url.host
                //cookie可能有多个,都保存下来
                if ((requestUrl.contains(SAVE_USER_LOGIN_KEY) || requestUrl.contains(SAVE_USER_REGISTER_KEY))) 
                    val cookies = response.headers(SET_COOKIE_KEY)
                    val cookie = encodeCookie(cookies)
                    saveCookie(requestUrl, domain, cookie)
                
                response
            
            //请求时设置cookie
            .addInterceptor 
                val request = it.request()
                val builder = request.newBuilder()
                val domain = request.url.host
                //获取domain内的cookie
                if (domain.isNotEmpty()) 
                    val sqDomain: String = DataStoreUtil.readStringData(domain, "")
                    val cookie: String = if (sqDomain.isNotEmpty()) sqDomain else ""
                    if (cookie.isNotEmpty()) 
                        builder.addHeader(COOKIE_NAME, cookie)
                    
                
                it.proceed(builder.build())
            
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
            .retryOnConnectionFailure(false)

在网络请求的位置添加以上两个拦截器就行了。

登录页面的实现

首先来看效果图。

简单的绘制了一个登录页面,UI就不要纠结了,丑是真的丑~

可以看到在输入框左边有一个图标,然后是提示内容,以及密码框右边的显示和隐藏密码的图标;选中的时候颜色发生改变,并且在左上角显示提示用户输入的内容。

OutlinedTextField 属性解析

在实现以上效果前,先要了解OutlinedTextField的属性,才能加以运用 ;先看一下属性列表。

@Composable
fun OutlinedTextField(
    value: String,
    onValueChange: (String) -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    readOnly: Boolean = false,
    textStyle: TextStyle = LocalTextStyle.current,
    label: @Composable (() -> Unit)? = null,
    placeholder: @Composable (() -> Unit)? = null,<

以上是关于Android compose wanandroid app之导航规整以及登录页个人中心页实现的主要内容,如果未能解决你的问题,请参考以下文章

Android compose crane

Android compose crane

Android笔记--Compose基础

Android Kotlin Jetpack Compose 使用

Android Jetpack Compose学习—— Jetpack compose基础布局

Android Jetpack Compose学习—— Jetpack compose基础布局