[Android]引导页

这篇具有很好参考价值的文章主要介绍了[Android]引导页。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

[Android]引导页,Android,kotlin[Android]引导页,Android,kotlin

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.guide

import 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 - 1
        Column(
            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 Screen
                        goHome(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 Purchases
                Button(
                    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 Policy
                Button(
                    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 Use
                Button(
                    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,  // 设置行间距为20sp
                        modifier = Modifier
                            .fillMaxWidth()
                            .verticalScroll(scrollState)
                            //.heightIn(max = 100.dp)  // 设置最大高度以限制视图高度
                            .padding(bottom = 10.dp)
                    )
                }
            }
        }

        if (isLast) {
            // 跳过按钮
            Button(
                onClick = {
                    // Navigate to Home Screen
                    goHome(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.data

import android.content.Context
import android.content.SharedPreferences
import android.util.Log

object PrefsManager {
    private lateinit var sharedPreferences: SharedPreferences

    fun 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 ?: defaultValue
            is Int -> sharedPreferences.getInt(prefKey.key, defaultValue) as? T ?: defaultValue
            is String -> sharedPreferences.getString(prefKey.key, defaultValue)  as? T ?: defaultValue
            else -> {
                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 theme
                Surface(
                    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文章来源地址https://www.toymoban.com/news/detail-856394.html

到了这里,关于[Android]引导页的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android AlertDialog setView,kotlin

    Android AlertDialog setView,kotlin             Android DialogFragment(1)_zhangphil的博客-CSDN博客 Android DialogFragment(1)和过去的AlertDialog类似,Android引入的DialogFragment旨在为开发者提供一个“富”dialog,而不必受到过去Android AlertDialog的局限。首先,DialogFragment是一个Fragment,它有Fragm

    2024年02月13日
    浏览(58)
  • android 自定义车牌键盘(kotlin)

    平时停车缴费都要填车牌号码,就想着自己能不能也做个车牌键盘demo。 自定义车牌键盘能满足(普通车牌,新能源,警车,军车,领事馆车,教练车以及特种车辆等车牌) 1、车牌前两位默认是:粤A 2、第一个控件,默认是省份键盘 3、剩下控件,默认是abc键盘 4、当前输入

    2024年02月16日
    浏览(44)
  • Kotlin 轻量级Android开发

    Kotlin 是一门运行在 JVM 之上的语言。 它由 Jetbrains 创建,而 Jetbrains 则是诸多强大的工具(如知名的 Java IDE IntelliJ IDEA )背后的公司。 Kotlin 是一门非常简单的语言,其主要目标之一就是提供强大语言的同时又保持简单且精简的语法。 其主要特性如下所示: 轻量级:这一点对

    2024年02月07日
    浏览(132)
  • Android初学之android studio运行java/kotlin程序

    第一步骤: File — New — New Module ,然后弹出一个框,(左边)选择 Java or Kotlin Library ,(右边)编辑自己的图书馆名、包名、类名,选择 Java 一个语言,然后 Finish 如下图: 然后,就可以看见我新建的 java Library 了,如下图: 第二步骤:马上写个测试程序 看看能不能运行

    2024年02月11日
    浏览(53)
  • Kotlin开发Android之基础问题记录

    1、Kotlin中如何直接通过组件id来操作组件? 解决方案:在build.gradle中添加对相应插件的使用即可。 2、Kotlin中Button设置背景颜色没有效果。 解决方案:在res-values-themes.xml文件中修改如下代码: 3、Kotlin中如何使用静态类或者静态方法? 解决方案: 4、Kotlin中EditText的赋值问题

    2024年02月09日
    浏览(42)
  • Android开发知识学习——Kotlin进阶

    申明前缀有construct修饰 如果有一个主构造函数,每个次构造函数需要委托给主构造函数,可以直接委托或者通过别的构造函数 主构造函数:是类头的一部分,跟在类名后面(可带参数),没有任何注解和可见性修饰符。如: 主构造函数中没有任何代码,初始化代码放在关键

    2024年02月06日
    浏览(58)
  • Android dex动态加载(Kotlin版)

    前言 环境 语言–Kotlin JDK11 SDK33 AndroidStudio版本 概述 libaray项目打包成jar jar通过dx/d8命令行工具转为dex.jar dex.jar放到assets目录下 App启动读取assets中的dex.jar复制到App可访问的文件夹中(建议内部存储的沙盒中,不受权限限制) 实例化DexClassLoader加载dex获取ClassLoader对象 通过Cla

    2023年04月12日
    浏览(39)
  • 切底掌握Android中的Kotlin DSL

    在这篇文章中,我们将学习如何在您的 Android 项目中编写 Kotlin DSL。 这个文章会很长,所以花点时间,让我们一起来写你的 DSL。我们将讨论以下主题, 什么是简单英语中的 DSL? 您使用任何 DSL 吗? 为什么我们使用 DSL? 我们如何编写自己的 DSL 基本示例说明。 那么让我们开

    2024年02月10日
    浏览(45)
  • 拥抱创新:用Kotlin开发高效Android应用

    在当今数字时代,移动应用已经成为人们生活中不可或缺的一部分。无论是社交媒体、电子商务还是健康管理,移动应用已经深刻地影响了我们的生活方式。随着移动设备的普及和功能的增强,Android平台作为最大的移动操作系统之一,扮演着举足轻重的角色。然而,随着用户

    2024年02月14日
    浏览(47)
  • [Android]网络框架之OkHttp(详细)(kotlin)

    目录 OkHttp的介绍 添加依赖 OkHttp的使用 get的同步与异步请求 post的同步与异步请求 POST请求的数据格式 POST请求上传文件 POST请求上传json对象 POST请求上传多个数据 OkHttp的配置 1.Builder构建器 2.自定义拦截器 3.自定义缓存 4. 自定义Cookie https://square.github.io/okhttp/ 由Square公司贡献的

    2024年02月12日
    浏览(82)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包