[Android]引导页

server/2024/9/22 16:43:08/

使用Kotlin + Jetpack Compose创建一个左右滑动的引导页, 效果如图.

1.添加依赖项

androidx.compose.ui最新版本查询:https://maven.google.com/web/index.html

com.google.accompanist:accompanist-pager最新版本查询:https://central.sonatype.com/

确保在 build.gradle (Module: app) 文件中添加:

dependencies {implementation("androidx.compose.ui:ui:1.7.0-alpha06")implementation("com.google.accompanist:accompanist-pager:0.35.0-alpha")
}

2.定义引导页

  • HorizontalPager 是一个实现水平滑动页面的组件,常用于实现引导页。它是通过Pager库提供的,支持滑动动画和状态保持。
  • rememberPagerState 是用于记忆并管理HorizontalPager的状态,例如当前页面和总页面数。
  • rememberCoroutineScope 用于创建一个协程作用域,允许在Compose函数外异步执行任务(例如页面滚动)。
package com.randomdt.www.main.guideimport androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
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.draw.alpha
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.randomdt.www.R
import com.randomdt.www.support.data.PrefKey
import com.randomdt.www.support.data.PrefsManager
import com.randomdt.www.ui.theme.customScheme
import kotlinx.coroutines.launch@OptIn(ExperimentalFoundationApi::class)
@Composable
fun GuideScreen(onGuideComplete: (Boolean) -> Unit) {val pages = listOf(GuidePage("Enhance your video recording with smooth script scrolling.", R.drawable.icon_guide1),GuidePage("Personalize settings to meet your recording needs.", R.drawable.icon_guide2),GuidePage("Intelligent scrolling for effortless recording control.", R.drawable.icon_guide3),GuidePage("Subscribe to the premium version and unlock additional features.", R.drawable.icon_guide4))val pagerState = rememberPagerState(pageCount = { pages.count() })val scope = rememberCoroutineScope()Box(modifier = Modifier.fillMaxSize().background(color = MaterialTheme.colorScheme.background)){HorizontalPager(state = pagerState,modifier = Modifier.matchParentSize()  // Use matchParentSize instead) { page ->GuidePageContent(page = pages[page], modifier = Modifier.fillMaxSize())}val isLast = pagerState.currentPage == pages.size - 1Column(modifier = Modifier.fillMaxSize().padding(horizontal = 16.dp),verticalArrangement = Arrangement.Bottom) {if (isLast) {Text("3 Days Trial, \$4.99/week, cancel anytime",fontSize = 14.sp,fontWeight = FontWeight.Normal,color = MaterialTheme.customScheme.text_aux99,textAlign = TextAlign.Center,modifier = Modifier.fillMaxWidth()  // 使宽度充满屏幕.padding(horizontal = 16.dp)  // 水平填充.padding(bottom = 16.dp)  // 与按钮之间的空隙)}// 渐变色定义val gradient = Brush.horizontalGradient(colors = listOf(MaterialTheme.customScheme.gradient_start_color,  // 渐变起始颜色MaterialTheme.customScheme.gradient_end_color  // 渐变结束颜色))// Next/Subscribe按钮Button(onClick = {if (pagerState.currentPage < pages.size - 1) {scope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) }} else {// Navigate to Home ScreengoHome(onGuideComplete)}},colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent), // 设置背景透明contentPadding = PaddingValues(0.dp),  // 移除内部填充border = BorderStroke(1.dp, Color.White), // 设置按钮的边框和背景shape = RoundedCornerShape(25.dp),  // 按钮圆角设置. Button 的 shape 只影响按钮本身的边界形状,而不会应用到渐变色背景上。modifier = Modifier.fillMaxWidth() // 使宽度充满屏幕.height(50.dp).background(gradient,shape = RoundedCornerShape(25.dp)), // 方式一: 添加渐变色背景, 已经为渐变背景导角) {Text(if (pagerState.currentPage == pages.size - 1) "Subscribe" else "Next",fontSize = 17.sp,fontWeight = FontWeight.Bold)/*// 方式二: 设置Button渐变色Box(modifier = Modifier.fillMaxSize().background(gradient, shape = RoundedCornerShape(25.dp))) {Text(if (pagerState.currentPage == pages.size - 1) "Subscribe" else "Next",modifier = Modifier.align(Alignment.Center))}*/}Box(modifier = Modifier.fillMaxWidth().height(100.dp).alpha(if (isLast) 1f else 0f)) {// Restore PurchasesButton(onClick = {},colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent), // 设置背景透明contentPadding = PaddingValues(0.dp),  // 移除内部填充modifier = Modifier.height(40.dp)) {Text("Restore Purchases",fontSize = 13.sp,fontWeight = FontWeight.Normal,style = TextStyle(textDecoration = TextDecoration.Underline) // 下划线)}// Privacy PolicyButton(onClick = {},colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent), // 设置背景透明contentPadding = PaddingValues(0.dp),  // 移除内部填充modifier = Modifier.align(Alignment.TopEnd).padding(end = 95.dp).height(40.dp)) {Text("Privacy Policy",fontSize = 13.sp,fontWeight = FontWeight.Normal,style = TextStyle(textDecoration = TextDecoration.Underline) // 下划线)}// Terms of UseButton(onClick = {},colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent), // 设置背景透明contentPadding = PaddingValues(0.dp),  // 移除内部填充modifier = Modifier.align(Alignment.TopEnd).height(40.dp)) {Text("Terms of Use",fontSize = 13.sp,fontWeight = FontWeight.Normal,style = TextStyle(textDecoration = TextDecoration.Underline) // 下划线)}//val scrollState = rememberScrollState()Box(modifier = Modifier.fillMaxWidth().padding(top = 40.dp)) {// 可滚动的详细文本视图Text(text = "This subscription automatically renews unless you cancel at least 24 hours before the end of the current subscription period. Your account will be charged for renewal within 24-hours prior to the end of the current subscription period. You can manage your subscription and auto-renewal in your Google Play account settings.",fontSize = 13.sp,color = MaterialTheme.customScheme.text_aux99,fontWeight = FontWeight.Normal,lineHeight = 20.sp,  // 设置行间距为20spmodifier = Modifier.fillMaxWidth().verticalScroll(scrollState)//.heightIn(max = 100.dp)  // 设置最大高度以限制视图高度.padding(bottom = 10.dp))}}}if (isLast) {// 跳过按钮Button(onClick = {// Navigate to Home ScreengoHome(onGuideComplete)},colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent), // 设置背景透明contentPadding = PaddingValues(0.dp),  // 移除内部填充modifier = Modifier.align(Alignment.TopStart).size(60.dp).padding(start = 8.dp, top = 8.dp)) {Image(painter = painterResource(R.drawable.icon_alert_close),contentDescription = "",)}}}}private fun goHome(onGuideComplete: (Boolean) -> Unit) {PrefsManager.set(PrefKey.IS_DID_GUIDE, true)onGuideComplete(true)
}@Composable
fun GuidePageContent(page: GuidePage, modifier: Modifier = Modifier) {Column(modifier = modifier) {Image(painter = painterResource(id = page.imageRes),contentDescription = null,modifier = Modifier.fillMaxWidth() // 填充最大宽度.aspectRatio(1167 / 1320f) // 设置宽高比例,例如 16:9 的比例为 1.77)Text(text = page.description,modifier = Modifier.padding(horizontal = 16.dp) // 设置水平间距.align(Alignment.CenterHorizontally), // 居中style = TextStyle(fontSize = 20.sp,textAlign = TextAlign.Center, // 让换行的文案也居中对齐)) // 高度根据内容自适应}
}// imageRes 是一个整数 (int),通常在 Android 开发中,这种整数类型用来代表资源文件(如图片)的 ID。
data class GuidePage(val description: String, val imageRes: Int)

3.定义PrefsManager

package com.randomdt.www.support.dataimport android.content.Context
import android.content.SharedPreferences
import android.util.Logobject PrefsManager {private lateinit var sharedPreferences: SharedPreferencesfun init(context: Context) {sharedPreferences = context.getSharedPreferences("AppPreferences", Context.MODE_PRIVATE)}fun <T> get(prefKey: PrefKey): T {val defaultValue: T = PrefDefaults.getDefaultValue(prefKey)return when (defaultValue) {is Boolean -> sharedPreferences.getBoolean(prefKey.key, defaultValue) as? T ?: defaultValueis Int -> sharedPreferences.getInt(prefKey.key, defaultValue) as? T ?: defaultValueis String -> sharedPreferences.getString(prefKey.key, defaultValue)  as? T ?: defaultValueelse -> {Log.w("SharedPreferences", "Unsupported type for SharedPreferences.get")defaultValue}}}fun <T> set(prefKey: PrefKey, value: T) {with(sharedPreferences.edit()) {when (value) {is Boolean -> putBoolean(prefKey.key, value)is Int -> putInt(prefKey.key, value)is String -> putString(prefKey.key, value)else -> Log.w("SharedPreferences", "Unsupported type for SharedPreferences.set")}apply()}}
}/// 让 PrefKey 枚举仅包含用户定义的键(key)
enum class PrefKey(val key: String) {IS_DID_GUIDE("isDidGuide"),USER_AGE("userAge"),USER_NAME("userName");
}/// 管理默认值和类型
object PrefDefaults {private val defaultValues = mapOf<PrefKey, Any>(PrefKey.IS_DID_GUIDE to false,PrefKey.USER_AGE to 18,PrefKey.USER_NAME to "John Doe")@Suppress("UNCHECKED_CAST")fun <T> getDefaultValue(prefKey: PrefKey): T = defaultValues[prefKey] as T
}/*
// 初始化(通常在应用启动时进行)
PrefsManager.init(context)// 存储数据
PrefsManager.set(PrefKey.IS_LOGGED_IN, true)
PrefsManager.set(PrefKey.USER_AGE, 30)
PrefsManager.set(PrefKey.USER_NAME, "Alice")// 读取数据
val isLoggedIn: Boolean = PrefsManager.get(PrefKey.IS_LOGGED_IN)
val userAge: Int = PrefsManager.get(PrefKey.USER_AGE)
val userName: String = PrefsManager.get(PrefKey.USER_NAME)
*/

4.引导页进入/离开

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)PrefsManager.init(this)setContent {RandomdtTheme {// A surface container using the 'background' color from the themeSurface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {MainContent()}}}}
}@Composable
fun MainContent() {val isDidGuideState = remember { mutableStateOf(PrefsManager.get<Boolean>(PrefKey.IS_DID_GUIDE)) }if (isDidGuideState.value) {Greeting("Android")} else {GuideScreen { isDidGuideCompleted ->isDidGuideState.value = isDidGuideCompleted}}
}

TO

HorizontalPager用法:https://juejin.cn/post/6978831090693701639


http://www.ppmy.cn/server/17176.html

相关文章

数据库服务类--Redis--未授权访问终端Getshell

免责声明:本文仅做技术交流与学习. 目录 前提条件: windows上开启redis服务: Linux上创建&开启redis服务: 操作: 1-连接靶机redis 2-写入webshell 3-访问后门 redis--->webshell Redis未授权访问漏洞复现与利用 - 知乎 (zhihu.com) 前提条件: 端口开放(6379) 目录…

Lock-It for Mac(应用程序加密工具)

OSXBytes Lock-It for Mac是一款功能强大的应用程序加密工具&#xff0c;专为Mac用户设计。该软件具有多种功能&#xff0c;旨在保护用户的隐私和数据安全。 Lock-It for Mac v1.3.0激活版下载 首先&#xff0c;Lock-It for Mac能够完全隐藏应用程序&#xff0c;使其不易被他人…

SQLite FTS5 扩展(三十)

返回&#xff1a;SQLite—系列文章目录 上一篇:SQLite的知名用户(二十九) 下一篇:SQLite 的命令行 Shell(三十一&#xff09; 1. FTS5概述 FTS5 是一个 SQLite 虚拟表模块&#xff0c;它为数据库应用程序提供全文搜索功能。在最基本的形式中&#xff0c; 全文搜索引擎允许…

nvm管理多个node版本,快速来回切换node版本

前言 文章基于 windows环境 使用nvm安装多版本nodejs。 最近公司有的项目比较老需要降低node版本才能运行&#xff0c;由于来回进行卸载不同版本的node比较麻烦&#xff1b;所以需要使用node工程多版本管理&#xff0c;后面自己就简单捯饬了一下nvm来管理node&#xff0c;顺便…

邦芒面试:巧妙应对“你对自己五年内的规划是什么?

面试中&#xff0c;面试官常常会抛出一个看似简单却暗藏玄机的问题&#xff1a;“你对自己五年内的规划是什么&#xff1f;”这个问题旨在考察应聘者的职业规划、目标设定以及对公司的兴趣和契合度。那么&#xff0c;如何巧妙回答这一棘手问题呢&#xff1f;以下是一些专家的建…

STM32读写备份寄存器BKP

今天学习的读写STM32的备份寄存器BKP的步骤&#xff0c;这节知识是比较简单的&#xff0c;一共也就两大部&#xff1a; 这个BKP寄存器的意思就是在芯片的VB引脚上接个电池&#xff0c;就能保存其寄存器中的数据掉电不丢失。先来看看电池的接法&#xff1a; 好&#xff0c;下面…

可使用的 ESRGAN 超分模型

Kaggle中使用 !pip install githttps://github.com/sberbank-ai/Real-ESRGAN.gitimport os from huggingface_hub import hf_hub_download import torch os.environ["HF_ENDPOINT"] "https://hf-mirror.com" model_path hf_hub_download(repo_id"S…

Android音视频开发-AudioTrack

Android音视频开发-AudioTrack 本篇文章我们主要介绍下AudioTrack. 1: 简介 AudioTrack是Android平台上的一个类&#xff0c;用于播放音频数据. 它允许PCM音频缓冲区流式传输到音频接收器进行播放. 创建AudioTrack对象&#xff1a;可以通过构造函数创建AudioTrack对象&…