GVKun编程网logo

Android,将文本切换器设为中心吗?(切换成文本)

5

本文的目的是介绍Android,将文本切换器设为中心吗?的详细情况,特别关注切换成文本的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解Android,将文本切换器设为

本文的目的是介绍Android,将文本切换器设为中心吗?的详细情况,特别关注切换成文本的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解Android,将文本切换器设为中心吗?的机会,同时也不会遗漏关于Android compose wanandroid app 之导航规整以及登录页个人中心页实现、Android Studio – 使用依赖于另一个Android库的Android库构建Android项目、Android TextSwitcher文本切换器和ViewFlipper使用详解、Android TextView将文本显示为链接的知识。

本文目录一览:

Android,将文本切换器设为中心吗?(切换成文本)

Android,将文本切换器设为中心吗?(切换成文本)

如何集中我的文本切换器?我已经尝试过设置重力,但是它似乎没有用.

ts.setFactory(new ViewFactory() {
        public View makeView() {
        TextView t = new TextView(this);
        t.setTypeface(tf);
        t.setTextSize(20);
        t.setTextColor(Color.WHITE);
        t.setText("my text switcher");
        t.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
        return t;
    }
    });

解决方法:

该代码正常,您需要将父textSwitcher宽度设置为fill_parent

用XML

<TextSwitcher android:layout_width="fill_parent" ...

或用代码

import android.widget.LinearLayout.LayoutParams;
//...
textSwitcher=...
textSwitcher.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

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

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

OSC 请你来轰趴啦!1028 苏州源创会,一起寻宝 AI 时代

转:

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

导航规整并实现登录页个人中心页

  • 前言
      • 导航规整
      • 个人中心的实现
        • MineViewmodel 获取数据
        • MinePage
        • 请求头添加 cookie
      • 登录页面的实现
        • OutlinedTextField 属性解析
        • 封装输入框
        • 输入框的使用
        • 登录按钮实现
        • 创建按钮状态枚举
        • 定义 transition
        • 设置按钮颜色、大小以及 shape
        • 使用 Button 并配置样式
        • 按钮全部代码
        • LoginViewModel
      • 源码地址

前言

在前面开发时只是注重了页面绘制,已经 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 以及用户积分,更多的东西等到实现收藏等操作之后在添加,简单看一下效果图。
Android compose wanandroid app之导航规整以及登录页个人中心页实现
布局元素比较简单,这里就不贴布局文件了。

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)

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

登录页面的实现

首先来看效果图。
Android compose wanandroid app之导航规整以及登录页个人中心页实现
简单的绘制了一个登录页面,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,
    leadingIcon: @Composable (() -> Unit)? = null,
    trailingIcon: @Composable (() -> Unit)? = null,
    isError: Boolean = false,
    visualTransformation: VisualTransformation = VisualTransformation.None,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions.Default,
    singleLine: Boolean = false,
    maxLines: Int = Int.MAX_VALUE,
    interactionSource: MutableInteractionSource = remember {
     
      MutableInteractionSource() },
    shape: Shape = MaterialTheme.shapes.small,
    colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
) 
  • 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 输入框提示的内容
  • leadingIcon: @Composable (() -> Unit)? = null 输入框左侧的图标
  • trailingIcon: @Composable (() -> Unit)? = null 输入框右侧的图标
  • isError: Boolean = false 是否处于错误状态
  • visualTransformation: VisualTransformation = VisualTransformation.None, 转换输入值的视觉表示
  • keyboardOptions: KeyboardOptions = KeyboardOptions.Default 输入框输入类型
  • singleLine: Boolean = false, 是否单行显示
  • maxLines: Int = Int.MAX_VALUE 最大行数
  • colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors () 颜色集合,设置获取焦点,失去焦点以及光标等颜色

大致就是这些属性了,知道使用之后就可以封装输入框了。

封装输入框

定义状态枚举 PwdShowState,通过状态设置密码是否可见。

通过 value:String 设置输入框显示的值。

通过 placeholder:String 设置输入框提示的值。

通过 color:TextFieldColors 设置对应状态下的颜色,获得焦点、失去焦点、以及光标时候的颜色。

通过 leadingIcon:ImageVector 设置左侧图标。

通过 trailingIcon:ImageVector 设置右侧图标,通过 trailingtintIcon:Color 设置图标颜色。

通过 keyboardOptions: KeyboardOptions 设置输入框输入类型。

通过 visualTransformation: VisualTransformation = VisualTransformation.None 改变设置密码是否可见。

通过 onValueChange:(String) -> Unit 获取输入框发生改变时值的回调。

//输入框
enum class PwdShowState{
     
     
    Show,Hide
}
@Composable
fun MyTextField(value:String,
                label:String,
                placeholder:String,
                color:TextFieldColors,
                leadingIcon:ImageVector,
                trailingIcon:ImageVector,
                trailingtintIcon:Color,
                modifier: Modifier,
                modifierTrailing: Modifier,
                keyboardOptions: KeyboardOptions,
                visualTransformation: VisualTransformation = VisualTransformation.None,
                onValueChange:(String) -> Unit){
     
     

    val showState = remember {
     
     
        mutableStateOf(PwdShowState.Hide)
    }

    val icon =  if (showState.value === PwdShowState.Hide){
     
     
        painterResource(id = R.drawable.pwd_look)
    }else{
     
     
        painterResource(id = R.drawable.pwd_hide)
    }

    OutlinedTextField(value = value,
        colors = color,
        label = {
     
     
            Text(text = label)
        },
        placeholder = {
     
     
            Text(text = placeholder)
        },
        modifier = modifier,
        keyboardOptions = keyboardOptions,
        leadingIcon = {
     
     
            Icon(leadingIcon,"左边图标",modifierTrailing,trailingtintIcon)
        },
        trailingIcon = {
     
     
            if (label.equals("密码")){
     
     
                IconButton(onClick = {
     
     
                    if (showState.value === PwdShowState.Hide){
     
     
                        showState.value = PwdShowState.Show
                    }else{
     
     
                        showState.value = PwdShowState.Hide
                    }
                }) {
     
     
                    if (showState.value === PwdShowState.Hide){
     
     
                        Icon(icon, contentDescription = "点击密码可见",modifier = Modifier.size(30.dp))
                    }else{
     
     
                        Icon(icon, contentDescription = "点击密码隐藏",modifier = Modifier.size(30.dp))
                    }
                }
            }
        },
        visualTransformation = if (label.equals("密码")){
     
     
            if (showState.value === PwdShowState.Hide){
     
      PasswordVisualTransformation()} else visualTransformation
        }else{
     
     
            visualTransformation
        },
        singleLine = true,
        onValueChange = onValueChange)
}

输入框的使用

根据不同的使用场景,设置不同的参数。

账号:

val userName = remember {
     
     
         mutableStateOf("")
}
val colors = TextFieldDefaults.outlinedTextFieldColors(
           focusedBorderColor = Color(68,84,246),
           unfocusedBorderColor = Color.Gray,
           cursorColor = Color(68,84,246)
)
MyTextField(
           value = userName.value,
           label = "账号",
           placeholder = "请输入账号",
           color = colors,
           leadingIcon = Icons.Default.Phone,
           trailingIcon = Icons.Default.Phone,
           trailingtintIcon = Color(68,84,246),
           modifier = Modifier
                 .padding(12.dp, 0.dp, 12.dp, 0.dp)
                 .fillMaxWidth(),
           modifierTrailing = Modifier,
           keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Text
           ),
           onValueChange = {
     
     
                 userName.value = it
          }
)

密码:

val password = remember {
     
     
        mutableStateOf("")
}
val colors = TextFieldDefaults.outlinedTextFieldColors(
           focusedBorderColor = Color(68,84,246),
           unfocusedBorderColor = Color.Gray,
           cursorColor = Color(68,84,246)
)
MyTextField(
       value = password.value,
       label = "密码",
       placeholder = "请输入密码",
       color = colors,
       leadingIcon = Icons.Default.Lock,
       trailingIcon = Icons.Default.Lock,
       trailingtintIcon = Color(68,84,246),
       modifier = Modifier
               .padding(12.dp, 0.dp, 12.dp, 0.dp)
               .fillMaxWidth(),
       modifierTrailing = Modifier,
       keyboardOptions = KeyboardOptions(
               keyboardType = KeyboardType.Password
       ),
       onValueChange = {
     
     
         password.value = it
       }
)

输入框实现完成~

登录按钮实现

在点击登录按钮的时候,登录接口请求过程中加载一个简单的动画,在登录成功或者失败之后结束动画。

创建按钮状态枚举

Normal 正常情况下的状态

Pressed 按下时的状态

remember 记录状态的值

//按钮添加动画
enum class ButtonState{
     
     
    Normal,Pressed
}
//记录状态值
val buttonState = remember {
     
     
        mutableStateOf(ButtonState.Normal)
}

定义 transition

定义一个 transition,以及后面通过该元素设置颜色、大小等参数。

val transition = updateTransition(targetState = buttonState, label = "ButtonTransition")

设置按钮颜色、大小以及 shape

    val buttonBackgroundColor: Color by transition.animateColor(
        transitionSpec = {
     
      tween(duration)}
    ) {
     
      buttonState ->
        when(buttonState.value){
     
     
            ButtonState.Normal -> Color(68,84,246)
            ButtonState.Pressed -> Color(68,84,246)
        }
    }

    val buttonWidth: Dp by transition.animateDp(transitionSpec = {
     
     
        tween(duration)}
    ) {
     
     buttonState ->
        when(buttonState.value){
     
     
            ButtonState.Normal -> 300.dp
            ButtonState.Pressed -> 60.dp
        }
    }

    val buttonShape: Dp by transition.animateDp(transitionSpec = {
     
     
        tween(duration)}
    ) {
     
     buttonState ->
        when(buttonState.value){
     
     
            ButtonState.Normal -> 4.dp
            ButtonState.Pressed -> 100.dp
        }
    }

使用 Button 并配置样式

属性列表:

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun Button(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember {
     
      MutableInteractionSource() },
    elevation: ButtonElevation? = ButtonDefaults.elevation(),
    shape: Shape = MaterialTheme.shapes.small,
    border: BorderStroke? = null,
    colors: ButtonColors = ButtonDefaults.buttonColors(),
    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
    content: @Composable RowScope.() -> Unit
)
  • onClick: () -> Unit 点击事件回调
  • enabled: Boolean = true 是否可用,是否可以点击,这里可以加上判断,当用户名和密码都不为空的时候可以使用 enabled = !userName.isNullOrBlank () && !password.isNullOrBlank ()
  • colors: ButtonColors = ButtonDefaults.buttonColors () 设置背景颜色以及点击时候的背景颜色等
  • content: @Composable RowScope.() -> Unit compose 函数,实现逻辑
Button(modifier = Modifier
        .width(buttonWidth)
        .height(50.dp),shape = RoundedCornerShape(buttonShape),
        onClick = {
     
     
        //todo
	    },colors = ButtonDefaults.buttonColors(
	        backgroundColor = buttonBackgroundColor,
	        disabledBackgroundColor = Color(68,84,246).copy(0.5f)
	    ),enabled = !userName.isNullOrBlank() && !password.isNullOrBlank()) {
     
     
	        if (buttonState.value == ButtonState.Normal){
     
     
	            Text(text = "登录")
	        }else{
     
     
	            CircularProgressIndicator(
	                color = Color.White,
	                strokeWidth = 2.dp,
	                modifier = Modifier.size(24.dp)
	            )
	        }
	    }

点击逻辑,将按钮状态设置成 Pressed

buttonState.value = ButtonState.Pressed

并请求登录接口

loginViewModel.toLogin(userName,password,{
     
     
	//回调  状态重置
      buttonState.value = ButtonState.Normal
      navHostController.navigateUp()
})

通过 CircularProgressIndicator 实现动画

if (buttonState.value == ButtonState.Normal){
     
     
            Text(text = "登录")
}else{
     
     
            CircularProgressIndicator(
                color = Color.White,
                strokeWidth = 2.dp,
                modifier = Modifier.size(24.dp)
            )
}

按钮全部代码

按钮封装的代码:

//按钮添加动画
enum class ButtonState{
     
     
    Normal,Pressed
}

@Composable
fun MyButton(userName:String,password:String,loginViewModel:LoginViewModel,navHostController: NavHostController){
     
     
    val buttonState = remember {
     
     
        mutableStateOf(ButtonState.Normal)
    }

    val transition = updateTransition(targetState = buttonState, label = "ButtonTransition")

    val duration = 600

    val buttonBackgroundColor: Color by transition.animateColor(
        transitionSpec = {
     
      tween(duration)}
    ) {
     
      buttonState ->
        when(buttonState.value){
     
     
            ButtonState.Normal -> Color(68,84,246)
            ButtonState.Pressed -> Color(68,84,246)
        }
    }

    val buttonWidth: Dp by transition.animateDp(transitionSpec = {
     
     
        tween(duration)}
    ) {
     
     buttonState ->
        when(buttonState.value){
     
     
            ButtonState.Normal -> 300.dp
            ButtonState.Pressed -> 60.dp
        }
    }

    val buttonShape: Dp by transition.animateDp(transitionSpec = {
     
     
        tween(duration)}
    ) {
     
     buttonState ->
        when(buttonState.value){
     
     
            ButtonState.Normal -> 4.dp
            ButtonState.Pressed -> 100.dp
        }
    }

    Button(modifier = Modifier
        .width(buttonWidth)
        .height(50.dp),shape = RoundedCornerShape(buttonShape),onClick = {
     
     
        buttonState.value = ButtonState.Pressed
        loginViewModel.toLogin(userName,password,{
     
     
            buttonState.value = ButtonState.Normal
            navHostController.navigateUp()
        })
    },colors = ButtonDefaults.buttonColors(
        backgroundColor = buttonBackgroundColor,
        disabledBackgroundColor = Color(68,84,246).copy(0.5f)
    ),enabled = !userName.isNullOrBlank() && !password.isNullOrBlank()) {
     
     
        if (buttonState.value == ButtonState.Normal){
     
     
            Text(text = "登录")
        }else{
     
     
            CircularProgressIndicator(
                color = Color.White,
                strokeWidth = 2.dp,
                modifier = Modifier.size(24.dp)
            )
        }
    }
}

调用:

MyButton(userName.value,password.value,loginViewModel,navHostController)

到这里呢整个登录页面所有的元素都构建好了,剩下的就是 viewmodel 实现登录请求以及结果回调了。

LoginViewModel

class LoginViewModel : ViewModel() {
     
     

    private val _loginInfo = MutableLiveData<Any>()
    val loginInfo = _loginInfo

    fun toLogin(username:String,password:String,callback:()->Unit){
     
     
        NetWork.service.login(username,password).enqueue(object : Callback<BaseResult<Any>>{
     
     
            override fun onResponse(call: Call<BaseResult<Any>>,response: Response<BaseResult<Any>>) {
     
     
                response.body()?.let {
     
     
                    _loginInfo.value = it
                }
                callback.invoke()
            }

            override fun onFailure(call: Call<BaseResult<Any>>, t: Throwable) {
     
     
                callback.invoke()
            }
        })
    }

}

登录页面的绘制以及实现就完成了,因为不能放置视频,登录按钮点击时的动画也没有弄成 GIF,这里就不放效果图了,代码很简单,效果跑起来就能看到。

源码地址

gitee 源码地址:戳我~

转:

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


--Posted from Rpc

Android Studio – 使用依赖于另一个Android库的Android库构建Android项目

Android Studio – 使用依赖于另一个Android库的Android库构建Android项目

我需要使用Gradle构建一个依赖于Android库项目A的Android项目,该项目取决于另一个Android库项目B.

到目前为止,我有以下内容:

Android项目:

的build.gradle:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.5.+'
    }
}

apply plugin: 'android'

dependencies {
    compile project(':LibA')
}

android {
    compileSdkVersion 7
    buildToolsversion "17.0.0"
}

manifest.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.cyborg.template"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="7"
        android:targetSdkVersion="7" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name="com.cyborg.template.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Android库A:

的build.gradle:

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:0.5.+'
    }
}

apply plugin: 'android-library'

dependencies {
    compile project(':LibB')
}


android {
    compileSdkVersion 7
    buildToolsversion "17.0.0"
} 

manifest.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.lib.project.a"
          android:versionCode="1"
          android:versionName="1.0">

    <uses-sdk
            android:minSdkVersion="7"
            android:targetSdkVersion="7"/>

</manifest>

Android库B:

的build.gradle:

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:0.5.+'
    }
}

apply plugin: 'android-library'

android {
    compileSdkVersion 7
    buildToolsversion "17.0.0"
} 

manifest.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.lib.project.b"
          android:versionCode="1"
          android:versionName="1.0">

    <uses-sdk
            android:minSdkVersion="7"
            android:targetSdkVersion="7"/>

</manifest>

在尝试构建Android项目时,Android studio报告了以下错误:

Gradle: Execution Failed for task ':LibA:processDebugManifest'.
  > Manifest merging Failed. See console for more info.

那个控制台在哪里我想搜索有关错误的更多信息?

我发现了一些关于这个错误的问题,但它似乎与我的情况不一样.

启示?

谢谢,
亚当.

解决方法:

该库的清单文件当前必须具有< application />节点,即使它是空的.

这是我们将在某些时候删除的限制,但现在,只需添加它.

Android TextSwitcher文本切换器和ViewFlipper使用详解

Android TextSwitcher文本切换器和ViewFlipper使用详解

本文为大家分享了Android TextSwitcher文本切换器的使用,供大家参考,具体内容如下

1.TextSwitcher

 使用:

应用分为三步:

1.得到 TextSwitcher 实例对象
  TextSwitcher switcher = (TextSwitcher) findViewById(R.id.textSwitcher);
2.为switcher指定ViewSwitcher.ViewFactory工厂,该工厂会产生出转换时需要的View
  switcher.setFactory(this);
3.为switcher设定显示的内容,该方法执行,就会切换到下个View
  switcher.setText(String.valueOf(new Random().nextInt()));

2.ViewFlipper

实现:

①创建主布局文件,包含ViewFlipper控件(从源码来看,ViewFlipper控件是集成FrameLayout,也是相当于一个帧布局,只是包含了一些特殊的属性)

②创建ViewFlipper的子View,就是两个LinearLayout(里面包含两个TextView)

③创建ViewFlipper中子view的进入和退出动画anim_in和anim_out两个动画文件

④在Activity中将两个子View添加到ViewFlipper中去,调动的是ViewFlipper的addView方法

具体代码:

①创建主布局文件

 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:fitsSystemWindows="true"
  tools:context="com.qianmo.rollads.MainActivity">
 
  <android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/AppTheme.AppBarOverlay">
 
    <android.support.v7.widget.Toolbar
      android:id="@+id/toolbar"
      android:layout_width="match_parent"
      android:layout_height="?attr/actionBarSize"
      android:background="?attr/colorPrimary"
      app:popupTheme="@style/AppTheme.PopupOverlay"/>
 
  </android.support.design.widget.AppBarLayout>
 
  <RelativeLayout
    android:id="@+id/content_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.qianmo.rollads.MainActivity"
    tools:showIn="@layout/activity_main">
 
    <ViewFlipper
      android:id="@+id/viewFlipper"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:autoStart="true"
      android:background="#fff"
      android:flipInterval="3000"
      android:inAnimation="@anim/anim_in"
      android:outAnimation="@anim/anim_out"
      android:paddingLeft="30dp"
      >
    </ViewFlipper>
  </RelativeLayout>
  <android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|end"
    android:layout_margin="@dimen/fab_margin"
    app:srcCompat="@android:drawable/ic_dialog_email"/>
 
</android.support.design.widget.CoordinatorLayout>

②创建ViewFlipper的子View,这里有两个,我就只给出一个来了,另一个是一样的

one_ads.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical"
       android:padding="8dp"
  >
 
  <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    >
 
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="热议"
      android:textColor="#F14C00"
      android:textSize="12sp"
      android:background="@drawable/bg"
      />
 
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:ellipsize="end"
      android:padding="3dp"
      android:singleLine="true"
      android:text="小米8问世,雷胖子现在笑的开心了啦!"
      android:textColor="#333"
      android:textSize="16sp"
      />
 
  </LinearLayout>
 
  <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    >
 
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="热议"
      android:textColor="#F14C00"
      android:textSize="12sp"
      android:background="@drawable/bg"
      />
 
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:ellipsize="end"
      android:padding="3dp"
      android:singleLine="true"
      android:text="取了个漂亮的媳妇,整的是好开心!"
      android:textColor="#333"
      android:textSize="16sp"
      />
 
  </LinearLayout>
 
</LinearLayout>

③创建ViewFlipper中子view的进入和退出动画anim_in和anim_out两个动画文件,这里也只给出anim_in的代码,anim_out代码是类似的

anim_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
  <translate
    android:duration="1500"
    android:fromYDelta="100%p"
    android:toYDelta="0"
    />
</set>

④在Activity中将两个子View添加到ViewFlipper中去,调动的是ViewFlipper的addView方法

  MainActivity.java

package com.qianmo.rollads;
 
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ViewFlipper;
 
public class MainActivity extends AppCompatActivity {
 
  private ViewFlipper viewFlipper;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    viewFlipper = (ViewFlipper) findViewById(R.id.viewFlipper);
    setSupportActionBar(toolbar);
 
    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setonClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
        Snackbar.make(view,"Replace with your own action",Snackbar.LENGTH_LONG)
            .setAction("Action",null).show();
      }
    });
 
    viewFlipper.addView(View.inflate(this,R.layout.one_ads,null));
    viewFlipper.addView(View.inflate(this,R.layout.two_ads,null));
  }
 
} 

ok,来看一下我们的效果

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

Android TextView将文本显示为链接

Android TextView将文本显示为链接

我在我的应用程序中有TextView,我想将文本显示为链接.

我有“查看地图”字符串,我想将其显示为超链接(蓝色和下划线).

我正在尝试这个:

tvSeeMap.setText(getResources().getString(R.string.see_map));
    Linkify.addLinks(tvSeeMap,Linkify.ALL);

但它不会起作用.

解决方法

我找到了解决方法

String tempString = new String(getResources().getString(R.string.see_map));
 SpannableString content = new SpannableString(tempString);
 content.setSpan(new Underlinespan(),tempString.length(),0);
 tvSeeMap.setText(content);
 tvSeeMap.setTextColor(getResources().getColor(R.color.blue));

就那么简单.

今天的关于Android,将文本切换器设为中心吗?切换成文本的分享已经结束,谢谢您的关注,如果想了解更多关于Android compose wanandroid app 之导航规整以及登录页个人中心页实现、Android Studio – 使用依赖于另一个Android库的Android库构建Android项目、Android TextSwitcher文本切换器和ViewFlipper使用详解、Android TextView将文本显示为链接的相关知识,请在本站进行查询。

本文标签: