Android开发笔记[10]-关于页

摘要

构建关于页、最终用户许可页(EULA)页和隐私协议页;Compose页面中嵌入xml布局;Compose页面中添加markdown文本.

关键信息

  • Android Studio:Iguana | 2023.2.1
  • Gradle:distributionUrl=https://services.gradle.org/distributions/gradle-8.4-bin.zip
  • jvmTarget = '1.8'
  • minSdk 21
  • targetSdk 34
  • compileSdk 34
  • 开发语言:Kotlin,Java
  • ndkVersion = '21.1.6352462'
  • kotlin版本:1.9.20
  • kotlinCompilerExtensionVersion '1.5.4'
  • com.android.library:8.3

原理简介

EULA(最终用户许可协议)

[https://blog.csdn.net/LingLing1301/article/details/124561891]
最终用户许可协议(End User Licence Agreement,EULA),指的是一家公司的软件与软件的使用者所达成的协议,此协议一般在软件安装时出现。如果使用者拒绝接受这家公司的EULA,那么便不能安装此软件。最终用户许可协议是软件应用程序作者或者发布者与应用程序使用者之间的合法合同。最终用户许可协议(EULA),通常是指“软件许可”,它与租赁协议类似;用户同意支付软件的使用费用,并且向软件作者或者发行者承诺遵守EULA中规定的所有约束条件。用户被告知,当他们打开软件包的包装、打开CD盒的封条、将卡片寄回给软件发行者、安装应用程序、执行下载文件或者简单的使用应用程序的时候就意味着他们已经“接受”了EULA中的条款。用户可以寄回软件产品或者在安装过程中当EULA提示接受按钮的时候点击“我不接受”来拒绝这个协议。

Markwon简介

[https://github.com/noties/Markwon]
[https://noties.io/Markwon/]
Markwon is a markdown library for Android. It parses markdown following commonmark-spec with the help of amazing commonmark-java library and renders result as Android-native Spannables. No HTML is involved as an intermediate step. No WebView is required. It's extremely fast, feature-rich and extensible.
It gives ability to display markdown in all TextView widgets (TextView, Button, Switch, CheckBox, etc), Toasts and all other places that accept Spanned content. Library provides reasonable defaults to display style of a markdown content but also gives all the means to tweak the appearance if desired. All markdown features listed in commonmark-spec are supported (including support for inlined/block HTML code, markdown tables, images and syntax highlight).
Markwon comes with a sample application. It is a collection of library usages that comes with search and source code for each code sample.
Since version 4.2.0 Markwon comes with an editor to highlight markdown input as user types (for example in EditText).
Markwon 是一个用于 Android 的 Markdown 库。它使用令人惊叹的 commonmark-java 库解析遵循 CommonMark 规范的 Markdown,并将结果渲染为 Android 本地的 Spannables。在此过程中不涉及 HTML 作为中间步骤,也不需要 WebView。它非常快速、功能丰富且可扩展。
Markwon 能够使所有 TextView 小部件(TextView、Button、Switch、CheckBox 等)显示 Markdown,以及 Toasts 和所有接受 Spanned 内容的其他地方。库提供了合理的默认样式来显示 Markdown 内容,但如果需要,也提供了所有方法来调整外观。Markwon 支持 CommonMark 规范中列出的所有 markdown 特性(包括对内联/块级 HTML 代码、Markdown 表格、图片和语法高亮的支持)。
Markwon 附带一个示例应用程序。这是一个包含库用法的集合,每个代码示例都带有搜索和源代码。
自版本 4.2.0 起,Markwon 附带了一个编辑器,用于在用户输入时高亮显示 Markdown(例如在 EditText 中)。

「关于」页面

[https://juejin.cn/post/7139019841284866055]
关于页Compose模板
APP的「关于」页面通常包含一些基础的信息,如APP的名称、版本号、开发者信息、更新日志、隐私政策、用户协议等内容。这个页面主要是为了向用户提供一些关于APP的基本信息和说明。
具体来说,「关于」页面可能包含以下几个部分:

  1. APP名称:显示APP的全称,有时也会配合APP的Logo一起展示。
  2. 版本信息:显示当前的APP版本号,有时候还会显示此版本的更新日期。
  3. 开发者信息:显示开发者的名称、地址、联系方式等信息,有时候也会提供开发者的官方网站链接。
  4. 更新日志:列出最近几次版本更新的内容,帮助用户了解APP的新功能和改进。
  5. 隐私政策:解释APP如何处理用户的个人信息,以及如何保护用户的隐私。
  6. 用户协议:列出用户在使用APP时需要遵守的条款和条件。
    以上这些信息可以帮助用户更好地理解和使用APP,同时也能让用户感到更加安全和信任。

app的隐私政策

[http://app.gjzwfw.gov.cn/jmopen/webapp/html5/yhysxy/index.html]
[https://zhuanlan.zhihu.com/p/670072425]
APP隐私合规现状与防范措施
[https://www.goupsec.com/news/5460.html]
2021年11月1《个人信息保护法》正式施行,标志信息保护进入强监管时代,同时,APP监管也被提升到前所未有的高度,数据安全、用户隐私、甚至功能体验等各个方面都出台了相应的规则规范,监管的初衷是:从各个层面保障用户的权益,避免用户的隐私、体验、数据被滥用,甚至威胁国家安全,一旦违规被查处面临的惩罚是非常严厉的,因此产品运营方必须高度重视。一方面,在产品设计、开发阶段就要充分考虑并满足各种监管要求;另一方面,一旦查出隐患问题,要积极响应,及时整改,否则可能面临工信部通报,甚至全面下架风险,首当其冲的一块是:APP隐私合规。
APP隐私合规指简单说就是:用户隐私的收集、存储、使用、加工、传输、提供、公开、删除等都要合乎法规个人信息保护法,遵守原则。从APP端来看,它关系到用户的切身体验,用户自身也能直观感受到自己的隐私是否被过分索取。比如,申请与自身功能毫不相关的权限,不给还拒绝提供服务等场景。
正常的开发是在APP启动之初就要初始化很多环境、参数,而这些初始化很可能涉及一些隐私API的调用,在隐私合规的框架下,这些调用只能被人为延后,或者取消。
示例文本:

本软件尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更有个性化的服务,本软件会按照本隐私权政策的规定使用和披露您的个人信息。但本软件将以高度的勤勉、审慎义务对待这些信息。除本隐私权政策另有规定外,在未征得您事先许可的情况下,本软件不会将这些信息对外披露或向第三方提供。本软件会不时更新本隐私权政策。您在同意本软件服务使用协议之时,即视为您已经同意本隐私权政策全部内容。本隐私权政策属于本软件服务使用协议不可分割的一部分。
1.适用范围
a)在您使用本软件网络服务,本软件自动接收并记录的您的手机上的信息,包括但不限于您的健康数据、使用的语言、访问日期和时间、软硬件特征信息及您需求的网页记录等数据;
2.信息的使用
a)在获得您的数据之后,本软件会将其上传至服务器,以生成您的排行榜数据,以便您能够更好地使用服务。
3.信息披露
a)本软件不会将您的信息披露给不受信任的第三方。
b)根据法律的有关规定,或者行政或司法机构的要求,向第三方或者行政、司法机构披露;
c)如您出现违反中国有关法律、法规或者相关规则的情况,需要向第三方披露;
4.信息存储和交换
本软件收集的有关您的信息和资料将保存在本软件及(或)其关联公司的服务器上,这些信息和资料可能传送至您所在国家、地区或本软件收集信息和资料所在地的境外并在境外被访问、存储和展示。
5.信息安全
a)在使用本软件网络服务进行网上交易时,您不可避免的要向交易对方或潜在的交易对方披露自己的个人信息,如联络方式或者邮政地址。请您妥善保护自己的个人信息,仅在必要的情形下向他人提供。如您发现自己的个人信息泄密,请您立即联络本软件客服,以便本软件采取相应措施。

Compose中嵌入xml布局

[https://blog.csdn.net/u010436867/article/details/120759187]

class ComposeAndXmlActivity : AppCompatActivity() {

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

    @SuppressLint("SetTextI18n")
    @Composable
    fun ComposeContent() {
        LazyColumn(modifier = Modifier.fillMaxWidth(), content = {
            item {
                Text(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(100.dp)
                        .background(color = Color.Green),
                    textAlign = TextAlign.Center,
                    text = "Compose 部分-头部",
                )    

                /* start XML布局部分 */
                AndroidView(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(8.dp),
                    factory = { ctx ->
                        val view =
                            LayoutInflater.from(ctx).inflate(R.layout.activity_compose_xml, null)
                        view.findViewById<TextView>(R.id.fromXML).text = "Xml布局部分"
                        view
                    },
                )
                /* end XML布局部分 */

            }
            item {
                Text(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(100.dp)
                        .padding(8.dp)
                        .background(color = Color.Yellow),
                    textAlign = TextAlign.Center,
                    text = "Compose 部分-尾部",
                )
            }
        })
    }
}

xml布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/fromXML"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="@color/black"
        android:gravity="center"
        android:text="..."
        android:textColor="@color/white"
        android:textSize="12sp" />
</androidx.appcompat.widget.LinearLayoutCompat>

实现

  1. 配置gradle
    build.gradle
/* start markdown相关 */
implementation "io.noties.markwon:core:4.6.2"
implementation 'io.noties.markwon:image:4.6.2'
implementation 'io.noties.markwon:image-glide:4.6.2'
/* end markdown相关 */
  1. 核心代码
    AboutActivity.kt
package cn.qsbye.alittlesmile_android

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.widget.TextView
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Star
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat.startActivity
import androidx.navigation.NavHostController
import cn.qsbye.alittlesmile_android.AboutPageClass.AboutScreen
import cn.qsbye.alittlesmile_android.AboutPageClass.BaseTextItem
import cn.qsbye.alittlesmile_android.AboutPageClass.LinkText
import cn.qsbye.alittlesmile_android.AboutPageClass.ModuleItem
import cn.qsbye.alittlesmile_android.AboutPageClass.MyViewModel
import cn.qsbye.alittlesmile_android.AboutPageClass.NormalSubItemWithStartIconData
import cn.qsbye.alittlesmile_android.AboutPageClass.NormalWithStartIconSubItemModule
import cn.qsbye.alittlesmile_android.AboutPageClass.RightsTextItem
import cn.qsbye.alittlesmile_android.AboutPageClass.boxSampleIcon
import cn.qsbye.alittlesmile_android.ui.theme.Alittlesmile_androidTheme
import io.noties.markwon.Markwon

class AboutActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Alittlesmile_androidTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    // 显示关于页内容
                    MyAboutPage(MyViewModel(),this@AboutActivity) // 这里假设你有一个 MyViewModel 实例
                }
            }
        }
    }
}

/* start 继承于关于页模板AboutPageClass */
// 添加context作为上下文参数传入
@Composable
fun MyAboutPage(myViewModel: MyViewModel, context_input: AboutActivity) {
    val list = remember {
        listOf(
            NormalSubItemWithStartIconData("给我好评👍", subText = "你的好评将会给予我们莫大的动力与帮助", startIcon = boxSampleIcon),
            NormalSubItemWithStartIconData("分享应用", startIcon = boxSampleIcon),
            NormalSubItemWithStartIconData("反馈&建议", startIcon = boxSampleIcon),
            NormalSubItemWithStartIconData("获取帮助💁", startIcon = boxSampleIcon),
            NormalSubItemWithStartIconData("更新日志📝", startIcon = boxSampleIcon),
            NormalSubItemWithStartIconData("版本状态",
                subText = context_input.resources.getString(R.string.app_version),
                startIcon = boxSampleIcon)
        ) // end ListOf
    }

    Column(
        Modifier
            .fillMaxSize()
            .background(Color.White)) {
        AboutScreen(
            topContent = {
                Column(
                    modifier = Modifier.background(
                        brush = Brush.verticalGradient(
                            listOf(
                                Color(0xFF89B6A2),
                                Color.White
                            )
                        )
                    )
                ) {
                    ModuleItem(
                        modifier = Modifier
                            .padding(12.dp)
                            .background(Color.White)) {
                        Column(modifier = Modifier.padding(bottom = 16.dp)) {
                            Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
                                Icon(painter = painterResource(id = R.drawable.ic_launcher_foreground), contentDescription = "logo")
                            }
                            BaseTextItem {
                                Text(
                                    text = "关于「晓晓爱学」",
                                    Modifier.padding(vertical = 6.dp),
                                    style = MaterialTheme.typography.titleLarge
                                )

                                /* start markdown显示 */
                                AndroidView(
                                    modifier = Modifier
                                        .fillMaxWidth()
                                        .padding(8.dp),
                                    factory = { ctx ->
                                        val layout_from_xml =
                                            LayoutInflater.from(ctx).inflate(R.layout.about_activity, null)
                                        val textview_from_xml = layout_from_xml.findViewById<TextView>(R.id.about_activity_textview) as TextView

                                        val markwon = Markwon.create(context_input)
                                        // render raw input to styled markdown
                                        // Set the styled markdown to the TextView
                                        markwon.setMarkdown(textview_from_xml, about_markdown_text)
                                        layout_from_xml // Return the TextView instance
                                    },
                                )
                                /* end markdown显示 */

                            }
                            Row(
                                Modifier
                                    .fillMaxWidth()
                                    .padding(top = 8.dp), horizontalArrangement = Arrangement.SpaceBetween) {
                                Row {
                                    Icon(
                                        imageVector = Icons.Outlined.Star,
                                        contentDescription = "star",
                                        modifier = Modifier.padding(start = 16.dp, end=12.dp),
                                        tint = Color.Cyan
                                    )

                                    Icon(
                                        imageVector = Icons.Outlined.Star,
                                        contentDescription = "star",
                                        tint = Color.Magenta
                                    )
                                }
                                LinkText(
                                    text = "第三方软件清单",
                                    modifier = Modifier.padding(end = 16.dp),
                                    color = Color(0xFF89B6A2)
                                ) {
                                    // onClick事件

                                }
                            }
                        }
                    }
                }
            },
            mainContent = {
                NormalWithStartIconSubItemModule(
                    itemList = list,
                    modifier = Modifier.padding(12.dp),
                    backGroundColor = Color.White,
                    showDivider = false,
                    elevation = 2.dp,
                    itemPadding = 24.dp,
                    textStyle = MaterialTheme.typography.titleLarge.copy(color = Color(0xFF404040)),
                    subTextStyle = MaterialTheme.typography.bodyMedium.copy(color = Color(0xFF797979)),
                    cardShape = RoundedCornerShape(12.dp), // 定义模块框的形状,这里使用圆角,且圆角大小为 12 dp
                    extraContent = {
                        Row(
                            Modifier
                                .fillMaxWidth()
                                .padding(top = 8.dp), horizontalArrangement = Arrangement.End) {
                            Row(Modifier.fillMaxWidth(0.5f), horizontalArrangement = Arrangement.SpaceEvenly) {
                                LinkText(text = "隐私政策", color = Color(0xFF89B6A2)){
                                    // onClick事件
                                    // 跳转页面
                                    val intent = Intent(context_input, PrivacyPolicyActivity::class.java)
                                    context_input.startActivity(intent) // 使用正确的上下文来启动活动
                                }
                                LinkText(text = "用户协议", color = Color(0xFF89B6A2)) {
                                    // onClick事件
                                    Toast.makeText(
                                        context_input,
                                        "用户协议",
                                        Toast.LENGTH_SHORT
                                    ).show()
                                    // 跳转页面
                                    val intent = Intent(context_input, EulaActivity::class.java)
                                    context_input.startActivity(intent) // 使用正确的上下文来启动活动
                                }
                                LinkText(text = "证照信息", color = Color(0xFF89B6A2)) {
                                    // onClick事件

                                }
                            }
                        }
                    },
                    onClick = { index ->
                        if (index == 4/* 更新日志项的索引 */) {
                            // 使用 Compose 的 NavController
                            //NavHostController.navigate(ChangelogActivity::class.java)

                            // 跳转页面
                            val intent = Intent(context_input, ChangelogActivity::class.java)
                            context_input.startActivity(intent) // 使用正确的上下文来启动活动
                            // finish()
                        }
                    } // end onClick
                )
            },
            bottomContent = {
                BaseTextItem {
                    Text(
                        text = "「晓晓爱学」所有贡献者 版权所有",
                        modifier = Modifier.padding(bottom = 16.dp, start = 8.dp),
                        color = Color(0xFF797979)
                    )
                    RightsTextItem(
                        dateText = "2021-2024",
                        name = "all contributors of _alittlesmile_",
                        style = LocalTextStyle.current.copy(color = Color(0xFF797979))
                    )
                }
            }
        )
    }
}
/* end 继承于关于页模板AboutPageClass */

/* start 关于信息 markdown */
val about_markdown_text = """
    ### 「晓晓爱学」是一款帮助你更好学习的APP。具体功能包括:
    ### 省略
""".trimIndent()
/* end 关于信息 markdown */

PrivacyPolicyActivity.kt

package cn.qsbye.alittlesmile_android
import android.app.Activity
import android.os.Bundle
import android.view.LayoutInflater
import android.widget.TextView
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import cn.qsbye.alittlesmile_android.MyEulaPage
import cn.qsbye.alittlesmile_android.R
import cn.qsbye.alittlesmile_android.eula_markdown_text
import cn.qsbye.alittlesmile_android.ui.theme.Alittlesmile_androidTheme
import io.noties.markwon.Markwon

class PrivacyPolicyActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Alittlesmile_androidTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    // 显示隐私政策页内容
                    MyPrivacyPolicyPage(context_input = this@PrivacyPolicyActivity)
                }
            }
        }
    }
}

/* start PrivacyPolicy页面布局 */
@Composable
fun MyPrivacyPolicyPage(context_input: Activity, modifier: Modifier = Modifier) {
    /* start markdown显示 */
    AndroidView(
        modifier = Modifier
            .fillMaxWidth()
            .padding(8.dp),
        factory = { ctx ->
            val layout_from_xml =
                LayoutInflater.from(ctx).inflate(R.layout.about_activity, null)
            val textview_from_xml = layout_from_xml.findViewById<TextView>(R.id.about_activity_textview) as TextView

            val markwon = Markwon.create(context_input)
            // render raw input to styled markdown
            // Set the styled markdown to the TextView
            markwon.setMarkdown(textview_from_xml, privacy_policy_markdown_text)
            layout_from_xml // Return the TextView instance
        },
    )
    /* end markdown显示 */

}
/* end PrivacyPolicy页面布局 */

/* start 隐私政策 markdown */
val privacy_policy_markdown_text = """
    ## 隐私政策
    - 更新日期:2024年3月16日
    
    「晓晓爱学」所有贡献者(简称“我们”)作为「晓晓爱学」的运营者,深知个人信息的重要性,我们将按照法律法规的规定,保护您的个人信息及隐私安全。我们制定本“隐私政策”并特别提示:希望您在使用「晓晓爱学」(以下简称"本应用")服务前仔细阅读并理解本隐私政策,以便做出适当选择。

    0. 所需权限
    请您仔细阅读以下权限说明为提供某些服务,我们需要向您获取以下权限
    * (1)读取/修改手机存储权限,用于保存下载文件(图片/视频/音频)。
    * (2)读取/修改相册、音频权限,用于选取手机中的图片/视频/音频进行编辑,编辑完成后保存到手机。 
    * (3)剪切板权限,用于检测下载的任务,减少交互步骤。 
    * (4)读取安装应用列表,用于三方登录、支付前判断是否安装相关客户端,避免程序错误。
    * (5)读取设备识别码,设备MAC地址,用于确定设备唯一性,用于友盟统计,Bugly错误日志上传定位。

    1. 我们如何收集和使用您的信息
    * 1.1 登录 
    您可以使用第三方账号(微信)登录并使用本应用,您将授权我们获取您在第三方平台注册的公开信息(头像,昵称),用于与本应用账号绑定,使您可以直接登录并使用本产品和相关服务。
    * 1.2 提供商品或服务
    我们希望为您提供优质而可靠的服务,为实现安全保障功能所收集的信息是必要信息。
    * 1.2.1 设备信息与日志信息
    > a.为了保障软件与服务的安全运行,我们会收集您的硬件型号、操作系统版本号、国际移动设备识别码、唯一设备标识符、网络设备硬件地址、IP 地址、WLAN接入点、蓝牙、基站、软件版本号、网络接入方式、类型、状态、网络质量数据、操作、使用、服务日志。 
    > b.为了预防恶意程序及安全运营所必需,我们会收集安装的应用信息或正在运行的进程信息、应用程序的总体运行、使用情况与频率、应用崩溃情况、总体安装使用情况、性能数据、应用来源。
    > c.我们可能使用您的账户信息、设备信息、服务日志信息以及我们关联方、合作方在获得您授权或依法可以共享的信息,用于判断账户安全、进行身份验证、检测及防范安全事件。
    * 1.3 收集、使用个人信息目的变更
    请您了解,随着我们业务的发展,可能会对本应用的功能和提供的服务有所调整变化。原则上,当新功能或服务与我们当前提供的功能或服务相关时,收集与使用的个人信息将与原处理目的具有直接或合理关联。在与原处理目的无直接或合理关联的场景下,我们收集、使用您的个人信息,会再次进行告知,并征得您的同意。
    * 1.4 依法豁免征得同意收集和使用的个人信息
    请您理解,在下列情形中,根据法律法规及相关国家标准,我们收集和使用您的个人信息无需征得您的授权同意:
    > a.与国家安全、国防安全直接相关的;
    > b.与公共安全、公共卫生、重大公共利益直接相关的;
    > c.与犯罪侦查、起诉、审判和判决执行等直接相关的;
    > d.出于维护个人信息主体或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;
    > e.所收集的您的个人信息是您自行向社会公众公开的;
    > f.从合法公开披露的信息中收集的您的个人信息的,如合法的新闻报道、政府信息公开等渠道;
    > g.根据您的要求签订或履行合同所必需的;
    > h.用于维护软件及相关服务的安全稳定运行所必需的,例如发现、处置软件及相关服务的故障;
    > i.为合法的新闻报道所必需的;
    > j.学术研究机构基于公共利益开展统计或学术研究所必要,且对外提供学术研究或描述的结果时,对结果中所包含的个人信息进行去标识化处理的;
    > k.法律法规规定的其他情形

    2. 我们如何使用Cookie等同类技术

    * 2.1 在您未拒绝接受cookies的情况下,本应用会在您的计算机上设定或取用cookies ,以便您能登录或使用依赖于cookies的本应用平台服务或功能。
    * 2.2 您有权选择接受或拒绝接受cookies。您可以通过修改浏览器设置的方式拒绝接受cookies。但如果您选择拒绝接受cookies,则您可能无法登录或使用依赖于cookies的本应用网络服务或功能。
    * 2.3通过本应用所设cookies所取得的有关信息,将适用本政策。

    3. 我们如何共享、转让、公开披露个人信息
    * 3.1共享
    * 3.1.1 共享原则
    > a.授权同意原则: 未经您的同意,我们不会共享您的个人信息,除非共享的个人信息是去标识化处理后的信息,且共享第三方无法重新识别此类信息的自然人主体。如果第三方使用信息的目的超越原授权同意范围,他们需要重新征得您的同意。
    > b.安全审慎原则: 我们将审慎评估第三方使用共享信息的目的,对这些合作方的安全保障能力进行综合评估,并要求其遵循合作法律协议。我们会对合作方获取信息的软件工具开发包(SDK)、应用程序接口(API)进行严格的安全监测,以保护数据安全。
    * 3.1.2 实现功能或服务的共享信息
    支付功能:支付功能由与我们合作的第三方支付机构向您提供服务。第三方支付机构可能需要收集您的姓名、银行卡类型及卡号、有效期及手机号码。银行卡号、有效期及手机号码 是个人敏感信息,这些信息是支付功能所必需的信息,拒绝提供将导致您无法使用该功能,但不影响其他功能的正常使用。
    * 3.2 转让
    我们不会转让您的个人信息给任何其他第三方,除非征得您的明确同意。
    * 3.3 公开展示
    我们不会公开披露您的信息,除非遵循国家法律法规规定或者获得您的同意。我们公开披露您的个人信息会采用符合行业内标准的安全保护措施。
    * 3.4 依法豁免征得同意共享、转让、公开披露的个人信息
    请您理解,在下列情形中,根据法律法规及国家标准,我们共享、转让、公开披露您的个人信息无需征得您的授权同意:
    > a.与国家安全、国防安全直接相关的;
    > b.与公共安全、公共卫生、重大公共利益直接相关的;
    > c.与犯罪侦查、起诉、审判和判决执行等直接相关的;
    > d.出于维护您或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;
    > e.您自行向社会公众公开的个人信息;
    > f.从合法公开披露的信息中收集个人信息的,如合法的新闻报道、政府信息公开等渠道。
    4. 我们如何存储个人信息

    * 4.1 存储地点
    我们依照法律法规的规定,将在境内运营过程中收集和产生的您的个人信息存储于中华人民共和国境内。目前,我们不会将上述信息传输至境外,如果我们向境外传输,我们将会遵循相关国家规定或者征求您的同意。
    * 4.2 存储期限
    我们仅在为提供便捷下载及服务之目的所必需的期间内保留您的授权登录信息

    5. 您如何管理您的个人信息

    * 5.1您可以在关于页面最底部查看到「隐私政策」,点击即可查看
    * 5.2停止运营向您告知
    如我们停止运营,我们将及时停止收集您个人信息的活动,将停止运营的通知以逐一送达或公告的形式通知您,并对所持有的您的个人信息进行删除或匿名化处理。

    6. 未成年人条款

    * 6.1未成年人通用条款

    > a.若您是未满18周岁的未成年人,在使用本应用及相关服务前,应在您的父母或其他监护人监护、指导下共同阅读并同意本隐私政策。
    > b.我们根据国家相关法律法规的规定保护未成年人的个人信息,只会在法律允许、父母或其他监护人明确同意或保护未成年人所必要的情况下收集、使用、共享或披露未成年人的个人信息;如果我们发现在未事先获得可证实的父母或其他监护人同意的情况下收集了未成年人的个人信息,则会设法尽快删除相关信息。
    > c.若您是未成年人的监护人,当您对您所监护的未成年人的个人信息有相关疑问时,请通过本隐私政策公示的联系方式与我们联系。

    7. 隐私政策的修订和通知

    * 7.1.为了给您提供更好的服务,本应用及相关服务将不时更新与变化,我们会适时对本隐私政策进行修订,这些修订构成本隐私政策的一部分并具有等同于本隐私政策的效力,未经您明确同意,我们不会削减您依据当前生效的本隐私政策所应享受的权利。
    * 7.2.本隐私政策更新后,会以适当的方式提醒您更新的内容,以便您及时了解本隐私政策的最新版本。
    8. 关于第三方SDK名称及获取的相关信息和使用的目的范围

    * 8.1.友盟SDK
    合作目的:提供统计分析、社会化分享服务
    个人信息收集方式:嵌入第三方SDK,SDK收集传输个人信息
    个人信息收集范围:设备Mac地址、唯一设备识别码(IMEI/Mac/android ID/IDFA/OPENUDID/GUID/BSSID/SSID/SN、SIM 卡 IMSI 信息)
    隐私政策:https://www.umeng.com/page/policy

    * 8.2.Bugly SDK
    合作目的:为应用App提供了稳定性监控,追踪奔溃闪退的问题
    个人信息收集方式:嵌入第三方SDK,SDK收集传输个人信息
    个人信息收集范围:设备MAC地址、唯一设备标识码,App奔溃闪退日志
    隐私政策:https://bugly.qq.com/v2/contract

    * 8.3.支付宝SDK
    合作目的:提供支付宝手机App第三方支付服务
    个人信息收集方式:嵌入第三方SDK,SDK收集传输个人信息
    个人信息收集范围:常用设备信息(如IMEI/IMSI、SIM卡序列号/MAC地址)、网络信息以及地理位置信息
    隐私政策:https://render.alipay.com/p/c/k2cx0tg8

    * 8.4.微信开放平台SDK
    合作目的:支持微信授权登录、微信分享和微信支付
    个人信息收集方式:嵌入第三方SDK,SDK收集传输个人信息
    个人信息收集范围:设备型号、操作系统、唯一设备标识符(指由设备制造商编入到设备中的一串字符,可用于以独有方式标识相应设备)
    隐私政策:https://weixin.qq.com/cgi-bin/readtemplate?lang=zh_CN&t=weixin_agreement&s=privacy

    * 8.5.七牛云SDK
    合作目的:为应用提供图片上传功能
    个人信息收集方式:嵌入第三方SDK,SDK收集传输个人信息
    个人信息收集范围:设备型号、操作系统、唯一设备标识符(指由设备制造商编入到设备中的一串字符,可用于以独有方式标识相应设备)
    隐私政策:https://developer.qiniu.com/pili/8027/sdk-privacy-policy

    9. 账号注销说明
    您可以按照操作流程【"个人中心"->"头像"->"注销账号"】进行账号注销。在您点击注销账号时,我们会再次确认注销(避免误操作)。在您注销成功后,我们将停止为您提供产品或服务,并根据适用法律的要求删除您的个人信息。

    10. 联系我们
    如果您对个人信息保护问题有投诉、建议、疑问,您可以通过以下方式与我们取得联系:
    
    > 负责开发者:qsbye,
    > 邮箱地址:2557877116@qq.com
""".trimIndent()
/* end 隐私政策 markdown */

EulaActivity.kt

package cn.qsbye.alittlesmile_android

import android.app.Activity
import android.os.Bundle
import android.view.LayoutInflater
import android.widget.TextView
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Star
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import cn.qsbye.alittlesmile_android.AboutPageClass.AboutScreen
import cn.qsbye.alittlesmile_android.AboutPageClass.BaseTextItem
import cn.qsbye.alittlesmile_android.AboutPageClass.LinkText
import cn.qsbye.alittlesmile_android.AboutPageClass.ModuleItem
import cn.qsbye.alittlesmile_android.AboutPageClass.MyViewModel
import cn.qsbye.alittlesmile_android.AboutPageClass.NormalSubItemWithStartIconData
import cn.qsbye.alittlesmile_android.AboutPageClass.NormalWithStartIconSubItemModule
import cn.qsbye.alittlesmile_android.AboutPageClass.RightsTextItem
import cn.qsbye.alittlesmile_android.AboutPageClass.boxSampleIcon
import cn.qsbye.alittlesmile_android.ui.theme.Alittlesmile_androidTheme
import io.noties.markwon.Markwon

class EulaActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Alittlesmile_androidTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    // 显示用户协议页内容
                    // MyEulaPage(MyViewModel(),this@EulaActivity) // 这里假设你有一个 MyViewModel 实例
                    MyEulaPage(this@EulaActivity)

                }
            }
        }
    }
}

/* start EULA页面布局 */
@Composable
fun MyEulaPage(context_input:Activity, modifier: Modifier = Modifier) {
    /* start markdown显示 */
    AndroidView(
        modifier = Modifier
            .fillMaxWidth()
            .padding(8.dp),
        factory = { ctx ->
            val layout_from_xml =
                LayoutInflater.from(ctx).inflate(R.layout.about_activity, null)
            val textview_from_xml = layout_from_xml.findViewById<TextView>(R.id.about_activity_textview) as TextView

            val markwon = Markwon.create(context_input)
            // render raw input to styled markdown
            // Set the styled markdown to the TextView
            markwon.setMarkdown(textview_from_xml, eula_markdown_text)
            layout_from_xml // Return the TextView instance
        },
    )
    /* end markdown显示 */

}
/* end EULA页面布局 */

/* start 用户许可 markdown */
val eula_markdown_text = """
    ## 用户协议
    感谢你对「晓晓爱学」(以下简称"本应用")的支持。本应用作为一款学习辅助软件,旨在提升你的学习体验。
    
    ### 关于账号系统
    本应用从1.0.0开始提供了账号系统,目前仅仅用于记录用户身份(激活状态),该账号使用邮箱注册,注册账号时,请务必自行保证邮箱的有效性,后续将用于您忘记密码时接收重置密码链接,本应用不会将邮箱用于账号以外的其他功能。
    为了公平和更好的运营下去,账号只能自用,不允许分享和转卖,若发现有人公开分享和转卖,将取消或禁封账号,避免给双方带来不必要的麻烦,请大家遵守契约。
    
    ### 用户禁止行为
    你在使用本应用和/或本服务的过程中,应遵守相关法律法规、用户协议、规则规范等,不得从事包括但不限于以下任何行为,也不得为以下任何行为提供便利
    1. 发布、传送、传播、储存危害国家安全统一、破坏社会稳定、违反公序良俗、侮辱、诽谤、淫秽、暴力以及任何违反国家法律法规的内容;
    2. 发布、传送、传播、储存侵害他人知识产权、商业秘密等合法权利的内容;
    3. 虚构事实、隐瞒真相以误导、欺骗他人;
    4. 发布、传送、传播广告信息及垃圾信息;
    5. 其他法律法规禁止的行为。
    
    ### 协议的生效与变更
    你使用本应用的服务即视为你已阅读本协议并接受本协议的约束。
    本应用有权在必要时修改本协议条款。你可以在相关服务页面查阅最新版本的协议条款。
    本协议条款变更后,如果你继续使用本应用提供的软件或服务,即视为你已接受修改后的协议。如果你不接受修改后的协议,应当停止使用本应用提供的软件或服务。
""".trimIndent()
/* end 用户许可信息 markdown */

「关于」页模板部分
AboutPageClass/SampleDemo.kt

package cn.qsbye.alittlesmile_android.AboutPageClass

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.KeyboardArrowRight
import androidx.compose.material.icons.outlined.Star
import androidx.compose.material3.*
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import cn.qsbye.alittlesmile_android.R

@Composable
fun HomeScreen(viewModel: MyViewModel) {
    Column(
        Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(onClick = { viewModel.pagerIndex = 4 }) {
            Text(text = "仿 一个木函")
        }
    }
}

@Composable
fun AboutQqPager(myViewModel: MyViewModel) {
    val list = remember {
        listOf(
            NormalSubItemData("功能介绍", endIcon = endArrow),
            NormalSubItemData("官网", endIcon = endArrow, endText = "有新版本可用"),
            NormalSubItemData("帮助", endIcon = endArrow),
            NormalSubItemData("反馈", endIcon = endArrow)
        )
    }

    Column(
        Modifier
            .fillMaxSize()
            .background(Color(0xFFF6F7FB))) {
        AboutScreen(
            topContent = {
                AppInfoVerticalItem(
                    iconPaint = painterResource(id = R.drawable.ic_launcher_foreground),
                    subText = "V 8.9.3.8730",
                    modifier = Modifier.padding(bottom = 32.dp, top=16.dp),
                    subTextStyle = MaterialTheme.typography.bodyMedium.copy(color = Color(0xFFB3B3BD))
                )
            },
            mainContent = {
                NormalSubItemModule(
                    itemList = list,
                    backGroundColor = Color.White,
                    modifier = Modifier
                        .padding(horizontal = 12.dp)
                        .padding(bottom = 6.dp),
                    startTextStyle = LocalTextStyle.current.copy(fontSize = 16.sp),
                    endTextStyle = LocalTextStyle.current.copy(color = Color(0xFF939393)),
                    border = BorderStroke(width = 0.dp, color = Color.Transparent)
                )
            },
            bottomContent = {
                BaseTextItem {
                    Row(verticalAlignment = Alignment.CenterVertically) {
                        LinkText(text = "服务协议", color = Color(0xFF115AA9)) {}

                        Text(text=" | ", color = Color(0xFF5A9CD9))

                        LinkText(text = "隐私政策", color = Color(0xFF115AA9)) {}
                    }
                    Text(text = "客户服务热线-4006700700", color = Color(0xFFAFAFAF))
                    RightsTextItem(
                        dateText = "2009-2022",
                        name = "Tencent.",
                        style = LocalTextStyle.current.copy(color = Color(0xFFAFAFAF))
                    )
                }
            },
            keepBottomInBottom = true
        )
    }
}

@Composable
fun AboutBoxPager(myViewModel: MyViewModel) {
    val list = remember {
        listOf(
            NormalSubItemWithStartIconData("给我好评", subText = "您的好评将会给予我们莫大的动力与帮助", startIcon = boxSampleIcon),
            NormalSubItemWithStartIconData("加入群组", subText = "加入官方群组,与四海函友交友互水", startIcon = boxSampleIcon),
            NormalSubItemWithStartIconData("分享应用", startIcon = boxSampleIcon),
            NormalSubItemWithStartIconData("小程序", startIcon = boxSampleIcon),
            NormalSubItemWithStartIconData("反馈&建议", startIcon = boxSampleIcon),
            NormalSubItemWithStartIconData("获取帮助", startIcon = boxSampleIcon),
            NormalSubItemWithStartIconData("版本状态", subText = "7.10.2-normal", startIcon = boxSampleIcon)
        )
    }

    Column(
        Modifier
            .fillMaxSize()
            .background(Color.White)) {
        AboutScreen(
            topContent = {
                Column(
                    modifier = Modifier.background(
                        brush = Brush.verticalGradient(
                            listOf(
                                Color(0xFF89B6A2),
                                Color.White
                            )
                        )
                    )
                ) {
                    ModuleItem(
                        modifier = Modifier
                            .padding(12.dp)
                            .background(Color.White)) {
                        Column(modifier = Modifier.padding(bottom = 16.dp)) {
                            Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
                                Icon(painter = painterResource(id = R.drawable.ic_launcher_foreground), contentDescription = "logo")
                            }
                            BaseTextItem {
                                Text(
                                    text = "拥有很多,不如有我。",
                                    Modifier.padding(vertical = 6.dp),
                                    style = MaterialTheme.typography.titleLarge
                                )
                            }
                            Row(
                                Modifier
                                    .fillMaxWidth()
                                    .padding(top = 8.dp), horizontalArrangement = Arrangement.SpaceBetween) {
                                Row {
                                    Icon(
                                        imageVector = Icons.Outlined.Star,
                                        contentDescription = "star",
                                        modifier = Modifier.padding(start = 16.dp, end=12.dp),
                                        tint = Color.Cyan
                                    )

                                    Icon(
                                        imageVector = Icons.Outlined.Star,
                                        contentDescription = "star",
                                        tint = Color.Magenta
                                    )
                                }
                                LinkText(
                                    text = "开源许可",
                                    modifier = Modifier.padding(end = 16.dp),
                                    color = Color(0xFF89B6A2)
                                ) {}
                            }
                        }
                    }
                }
            },
            mainContent = {
                NormalWithStartIconSubItemModule(
                    itemList = list,
                    modifier = Modifier.padding(12.dp),
                    backGroundColor = Color.White,
                    showDivider = false,
                    elevation = 2.dp,
                    itemPadding = 24.dp,
                    textStyle = MaterialTheme.typography.titleLarge.copy(color = Color(0xFF404040)),
                    subTextStyle = MaterialTheme.typography.bodyMedium.copy(color = Color(0xFF797979)),
                    cardShape = RoundedCornerShape(12.dp), // 定义模块框的形状,这里使用圆角,且圆角大小为 12 dp
                    extraContent = {
                        Row(
                            Modifier
                                .fillMaxWidth()
                                .padding(top = 8.dp), horizontalArrangement = Arrangement.End) {
                            Row(Modifier.fillMaxWidth(0.5f), horizontalArrangement = Arrangement.SpaceEvenly) {
                                LinkText(text = "隐私政策", color = Color(0xFF89B6A2)) { }
                                LinkText(text = "用户协议", color = Color(0xFF89B6A2)) { }
                            }
                        }
                    }
                )
            },
            bottomContent = {
                BaseTextItem {
                    Text(
                        text = "XX科技 版权所有",
                        modifier = Modifier.padding(bottom = 16.dp, start = 8.dp),
                        color = Color(0xFF797979)
                    )
                    RightsTextItem(
                        dateText = "2017-2022",
                        name = "XXkuraft",
                        style = LocalTextStyle.current.copy(color = Color(0xFF797979))
                    )
                }
            }
        )
    }
}

val endArrow =  @Composable {
    Icon(imageVector = Icons.AutoMirrored.Outlined.KeyboardArrowRight, contentDescription = "arrow", tint = Color(0xFFD0D0D0))
}

val boxSampleIcon = @Composable {
    Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
        Icon(imageVector = Icons.Outlined.Star, contentDescription = "star", tint = Color(0xFF797979))
    }
}

@Preview
@Composable
fun PreviewDemo() {
    Column(modifier = Modifier.fillMaxSize()) {
        AboutLibPager()
    }
}

AboutScreen.kt

package cn.qsbye.alittlesmile_android.AboutPageClass

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview

/**
 * AboutPager 的主入口,所有的子模块都应该放置于其中
 *
 * @param modifier Modifier
 * @param topContent 顶部内容(一般用于放置APP ico 、名称、版本号等)
 * @param mainContent 中间内容(用于存放主要内容)
 * @param bottomContent 底部内容(一般用于存放版权信息)
 * @param keepBottomInBottom 是否固定底部内容始终在底部可见,设置为 true 可能出现内容叠加
 * */
@Composable
fun AboutScreen(
    modifier: Modifier = Modifier,
    topContent: (@Composable () -> Unit)? = null,
    mainContent: (@Composable () -> Unit)? = null,
    bottomContent: (@Composable () -> Unit)? = null,
    keepBottomInBottom: Boolean = false
) {
    Box {
        Column(modifier = modifier.verticalScroll(rememberScrollState())) {
            if (topContent != null) {
                topContent()
            }
            if (mainContent != null) {
                mainContent()
            }
            if (bottomContent != null && !keepBottomInBottom) {
                bottomContent()
            }
        }

        if (bottomContent != null && keepBottomInBottom) {
            Column(Modifier.fillMaxSize(), verticalArrangement = Arrangement.Bottom) {
                bottomContent()
            }
        }
    }
}

AppinfoItem.kt

package cn.qsbye.alittlesmile_android.AboutPageClass

import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import cn.qsbye.alittlesmile_android.R

/**
 * 头部 APP 信息垂直显示基础 Item
 *
 * @param iconContent 图标内容
 * @param textContent 文本内容
 * @param modifier Modifier
 * */
@Composable
fun AppInfoVerticalItem(
    iconContent: @Composable () -> Unit,
    textContent: @Composable () -> Unit,
    modifier: Modifier = Modifier
) {
    Column(modifier = modifier) {
        iconContent()
        textContent()
    }
}

/**
 * 头部 APP 信息垂直显示 item
 *
 * @param iconPaint APP 图标
 * @param modifier Modifier
 * @param text 主文本
 * @param subText 主文本下方的补充文本
 * @param textStyle 主文本样式
 * @param subTextStyle 补充文本样式
 * */
@Composable
fun AppInfoVerticalItem(
    iconPaint: Painter,
    modifier: Modifier = Modifier,
    text: String? = null,
    subText: String? = null,
    textStyle: TextStyle = MaterialTheme.typography.titleMedium,
    subTextStyle: TextStyle = MaterialTheme.typography.bodyMedium
) {
    AppInfoVerticalItem(
        iconContent = {
            Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
                Icon(
                    painter = iconPaint,
                    contentDescription = "icon"
                )
            }
        },
        textContent = {
            Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {

                if (text != null) {
                    Text(
                        text = text,
                        style = textStyle,
                        maxLines = 1
                    )
                }

                if (subText != null) {
                    Text(
                        text = subText,
                        style = subTextStyle
                    )
                }

            }
        },
        modifier = modifier
    )
}

/**
 * 头部 APP 信息水平显示基础 Item
 *
 * @param iconContent 图标内容
 * @param textContent 文本内容
 * @param modifier Modifier
 * */
@Composable
fun AppInfoHorizontalItem(
    iconContent: @Composable () -> Unit,
    textContent: @Composable () -> Unit,
    modifier: Modifier = Modifier
) {
    Row(modifier = modifier, horizontalArrangement = Arrangement.SpaceBetween) {
        iconContent()
        textContent()
    }
}

/**
 * 头部 APP 信息水平显示 item
 *
 * @param iconPaint APP 图标
 * @param modifier Modifier
 * @param text 主文本
 * @param subText 主文本下方的补充文本
 * */
@Composable
fun AppInfoHorizontalItem(
    iconPaint: Painter,
    text: String,
    modifier: Modifier = Modifier,
    subText: String? = null
) {
    AppInfoHorizontalItem(
        iconContent = {
            Icon(
                painter = iconPaint,
                contentDescription = "icon"
            )
        },
        textContent = {
            Column(
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally,
                modifier = Modifier.fillMaxHeight().padding(6.dp)
            ) {
                Text(
                    text = text,
                    style = MaterialTheme.typography.titleMedium,
                    maxLines = 1
                )

                if (subText != null) {
                    Text(
                        text = subText,
                        style = MaterialTheme.typography.bodyMedium
                    )
                }

            }
        },
        modifier = modifier.fillMaxWidth()
    )
}


@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true)
@Composable
fun PreviewVerticalAppInfoItem() {
    AppInfoVerticalItem(
        iconPaint = painterResource(id = R.drawable.ic_launcher_foreground),
        text = "示例APP",
        subText = "V1.0.0"
    )
}

@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true)
@Composable
fun PreviewHorizontalAppInfoItem() {
    AppInfoHorizontalItem(
        iconPaint = painterResource(id = R.drawable.ic_launcher_foreground),
        text = "示例APP",
        subText = "V1.0.0",
        modifier = Modifier.height(80.dp)
    )
}

ModuleItem.kt

package cn.qsbye.alittlesmile_android.AboutPageClass

import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.KeyboardArrowRight
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import cn.qsbye.alittlesmile_android.R

/**
 * 一个独立的模块,使用 Card 作为载体
 *
 * @param modifier Modifier
 * @param title 模块标题,会被置于载体 Card 之外
 * @param backGroundColor Card 背景颜色
 * @param border Card 变宽
 * @param elevation Card 高度(会影响 Card 的阴影深度)
 * @param shape Card 形状(可自定义形状或增加圆角)
 * @param titleStyle 标题文本的样式
 * @param content 内容
 * */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ModuleItem(
    modifier: Modifier = Modifier,
    title: String? = null,
    backGroundColor: Color = Color.Unspecified,
    border: BorderStroke? = null,
    elevation: Dp = 1.dp,
    shape: Shape = MaterialTheme.shapes.medium,
    titleStyle: TextStyle = MaterialTheme.typography.titleMedium,
    content: @Composable () -> Unit
) {
    Column {
        if (title != null) {
            // 将标题文本放到 Card 外面,这样比较美观
            Text(
                text = title,
                style = titleStyle,
                modifier = Modifier.padding(start = 6.dp)
            )
        }

        Card(
            // onClick = { }, // 其实这里只是将 card 作为外部载体使用,不需要直接点击 card,但是这个参数是必须的,所以直接传一个空的给它
            // enabled = false, // 禁用点击
            modifier = modifier.fillMaxWidth(),
            colors = CardDefaults.cardColors(containerColor = backGroundColor),
            border = border,
            elevation = CardDefaults.cardElevation(elevation), // 这里需要将 Dp 类型的值转换为 CardElevation
            shape = shape
        ) {
            Column(modifier = Modifier.padding(8.dp)) {
                content()
            }
        }
    }
}

/**
 * 基础 Item 载体,使用 Row 作为载体承载内容
 *
 * @param startContent 左边的内容
 * @param endContent 右边的内容
 * @param modifier Modifier
 * @param horizontalArrangement 水平对齐方式
 * @param verticalAlignment 垂直对齐方式
 * */
@Composable
fun BaseSubItem(
    startContent: @Composable () -> Unit,
    endContent: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    horizontalArrangement: Arrangement.Horizontal = Arrangement.SpaceBetween,
    verticalAlignment: Alignment.Vertical = Alignment.Top
) {
    Row(
        modifier = modifier.fillMaxWidth(),
        horizontalArrangement = horizontalArrangement,
        verticalAlignment = verticalAlignment
    ) {
        startContent()
        endContent()
    }
}

/**
 * 一般 Item,显示效果形如普通文本列表,但是可以添加尾部图标与尾部文字,建议包裹在 [NormalSubItemModule] 使用
 *
 * @param startText 主文本,位于列表最左边
 * @param modifier Modifier
 * @param endText 辅助文本,位于列表最右边,但是在 [endIcon] 左边
 * @param startTextStyle 主文本样式
 * @param endTextStyle 辅助文本样式
 * @param endIcon 尾部图标
 * @param onClick 点击回调
 * */
@Composable
fun NormalSubItem(
    startText: String,
    modifier: Modifier = Modifier,
    endText: String? = null,
    startTextStyle: TextStyle = LocalTextStyle.current,
    endTextStyle: TextStyle = LocalTextStyle.current,
    endIcon: (@Composable () -> Unit)? = null,
    onClick: (() -> Unit)? = null
) {
    BaseSubItem(
        // 这里如果没有设置点击回调则不添加 clickable,因为如果即使添加的是空 lambda 也会有点击涟漪效果,不太美观
        modifier = if (onClick != null) modifier.clickable { onClick() } else modifier,
        startContent = {
            Text(text = startText, style = startTextStyle)
        },
        endContent = {
            Row(verticalAlignment = Alignment.CenterVertically) {
                if (endText != null) {
                    Text(text = endText, style = endTextStyle)
                }
                if (endIcon != null) {
                    endIcon()
                }
            }
        },
        verticalAlignment = Alignment.CenterVertically
    )
}

/**
 * 带有前导图标的一般 Item ,显示效果类似对话框,建议包裹在 [NormalWithStartIconSubItemModule] 中使用
 *
 * @param text 主文本
 * @param modifier Modifier
 * @param subText 辅助文本
 * @param textStyle 主文本样式
 * @param subTextStyle 辅助文本样式
 * @param isAlignIcon 是否留空,如果设置为 true,那么即使没有图标也会将图标位置留空占位
 * @param startIcon 图标
 * @param onClick 点击回调
 * */
@Composable
fun NormalWithStartIconSubItem(
    text: String,
    modifier: Modifier = Modifier,
    subText: String? = null,
    textStyle: TextStyle = MaterialTheme.typography.titleMedium,
    subTextStyle: TextStyle = MaterialTheme.typography.bodySmall,
    isAlignIcon: Boolean = true,
    startIcon: (@Composable () -> Unit)? = null,
    onClick: (() -> Unit)? = null
) {
    BaseSubItem(
        modifier = if (onClick != null) modifier.clickable { onClick() } else modifier,
        horizontalArrangement = Arrangement.Start,
        verticalAlignment = Alignment.CenterVertically,
        startContent = {
            // 如果条件满足的话,即使图标为空,也要把位置留空出来,即 fillMaxWidth(0.2f)
            Column(Modifier.fillMaxWidth(if (!isAlignIcon && startIcon == null) 0f else 0.2f)) {
                if (startIcon != null) {
                    startIcon()
                }
            }

            Column(modifier = Modifier.padding(start = 6.dp).fillMaxWidth(0.8f)) {
                Text(text = text, style = textStyle)
                if (subText != null) {
                    Text(text = subText, style = subTextStyle)
                }
            }
        },
        // 由于该组件风格不是严格的左右布局,所以这里将内容全放入 startContent 中,不使用 endContent
        endContent = {  }
    )
}

/**
 *
 * 用于承载 [NormalSubItem] 的模块,会将传入的所有 [NormalSubItem] 放入同一个 Card 中
 *
 * @param itemList 内容列表
 * @param modifier 承载这个 module 的父 composable 的 modifier
 * @param title 该模块标题
 * @param showDivider 是否在 item 之间显示分割线
 * @param showAllDivider 是否在第一个 item 之前 和 最后一个 item 之后显示分割线
 * @param backGroundColor 模块背景颜色
 * @param border 模块边框
 * @param elevation 模块高度(影响阴影深度)
 * @param itemPadding item 之间的 padding
 * @param startTextStyle 主文本样式
 * @param endTextStyle 辅助文本样式
 * @param cardShape 模块形状
 * @param titleStyle 标题样式
 * @param extraContent 额外的子定义内容,会在最后显示
 * @param onClick 点击回调,参数为点击的 item 索引
 * */
@Composable
fun NormalSubItemModule(
    itemList: List<NormalSubItemData>,
    modifier: Modifier = Modifier,
    title: String? = null,
    showDivider: Boolean = true,
    showAllDivider: Boolean = false,
    backGroundColor: Color = Color.Unspecified,
    border: BorderStroke? = null,
    elevation: Dp = 1.dp,
    itemPadding: Dp = 6.dp,
    startTextStyle: TextStyle = LocalTextStyle.current,
    endTextStyle: TextStyle = LocalTextStyle.current,
    cardShape: Shape = MaterialTheme.shapes.medium,
    titleStyle: TextStyle = MaterialTheme.typography.titleMedium,
    extraContent: (@Composable () -> Unit)? = null,
    onClick: ((clickNo: Int) -> Unit)? = null
) {
    ModuleItem(
        title = title,
        modifier = modifier,
        backGroundColor = backGroundColor,
        border = border,
        elevation = elevation,
        shape = cardShape,
        titleStyle = titleStyle
    ) {
        if (showAllDivider) {
            Divider(modifier = Modifier.padding(bottom = itemPadding))
        }
        itemList.forEachIndexed { index: Int, normalSubItemData: NormalSubItemData ->
            NormalSubItem(
                normalSubItemData.startText,
                endIcon = normalSubItemData.endIcon,
                onClick =
                    if (onClick == null) null
                    else { { onClick(index) } },
                endText = normalSubItemData.endText,
                modifier = Modifier.padding(bottom = if (!showAllDivider && index == itemList.lastIndex) 0.dp else itemPadding),
                startTextStyle = startTextStyle,
                endTextStyle = endTextStyle
            )
            if (showDivider && index != itemList.lastIndex) {
                Divider(modifier = Modifier.padding(bottom = itemPadding))
            }
        }
        if (showAllDivider) {
            Divider(modifier = Modifier.padding(bottom = itemPadding))
        }
        if (extraContent != null) {
            extraContent()
        }
    }
}

/**
 *
 * 用于承载 [NormalWithStartIconSubItem] 的模块,会将传入的所有 [NormalWithStartIconSubItem] 放入同一个 Card 中
 *
 * @param itemList 内容列表
 * @param modifier 承载这个 module 的父 composable 的 modifier
 * @param title 该模块标题
 * @param showDivider 是否在 item 之间显示分割线
 * @param showAllDivider 是否在第一个 item 之前 和 最后一个 item 之后显示分割线
 * @param backGroundColor 模块背景颜色
 * @param border 模块边框
 * @param elevation 模块高度(影响阴影深度)
 * @param itemPadding item 之间的 padding
 * @param textStyle 主文本样式
 * @param subTextStyle 辅助文本样式
 * @param cardShape 模块形状
 * @param titleStyle 标题样式
 * @param extraContent 额外的子定义内容,会在最后显示
 * @param onClick 点击回调,参数为点击的 item 索引
 * */
@Composable
fun NormalWithStartIconSubItemModule(
    itemList: List<NormalSubItemWithStartIconData>,
    modifier: Modifier = Modifier,
    title: String? = null,
    showDivider: Boolean = true,
    showAllDivider: Boolean = false,
    backGroundColor: Color = Color.Unspecified,
    border: BorderStroke? = null,
    elevation: Dp = 1.dp,
    itemPadding: Dp = 6.dp,
    isAlignIcon: Boolean = true,
    textStyle: TextStyle = MaterialTheme.typography.titleMedium,
    subTextStyle: TextStyle = MaterialTheme.typography.bodyMedium,
    cardShape: Shape = MaterialTheme.shapes.medium,
    titleStyle: TextStyle = MaterialTheme.typography.titleMedium,
    extraContent: (@Composable () -> Unit)? = null,
    onClick: ((clickNo: Int) -> Unit)? = null
) {
    ModuleItem(
        title = title,
        modifier = modifier,
        backGroundColor = backGroundColor,
        border = border,
        elevation = elevation,
        shape = cardShape,
        titleStyle = titleStyle
    ) {
        if (showAllDivider) {
            Divider(modifier = Modifier.padding(bottom = itemPadding))
        }
        itemList.forEachIndexed { index: Int, normalSubItemWithStartIconData: NormalSubItemWithStartIconData ->
            NormalWithStartIconSubItem(
                normalSubItemWithStartIconData.text,
                subText = normalSubItemWithStartIconData.subText,
                startIcon = normalSubItemWithStartIconData.startIcon,
                onClick =
                if (onClick == null) null
                else { { onClick(index) } },
                modifier = Modifier.padding(bottom = itemPadding),
                textStyle = textStyle,
                subTextStyle = subTextStyle,
                isAlignIcon = isAlignIcon
            )
            if (showDivider && index != itemList.lastIndex) {
                Divider(modifier = Modifier.padding(bottom = itemPadding))
            }
        }
        if (showAllDivider) {
            Divider(modifier = Modifier.padding(bottom = itemPadding))
        }
        if (extraContent != null) {
            extraContent()
        }
    }
}

data class NormalSubItemData(
    val startText: String,
    val endText: String? = null,
    val endIcon: (@Composable () -> Unit)? = null,
)

data class NormalSubItemWithStartIconData(
    val text: String,
    val subText: String? = null,
    val startIcon: (@Composable () -> Unit)? = null,
)


// ----------- 预览 ------------


@Preview(showBackground = true, backgroundColor = 0xFF000000)
@Composable
fun PreviewModuleItem() {
    Column(Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) {
        ModuleItem(
            modifier = Modifier.padding(horizontal = 8.dp)
        ) {
            Text(text = "hahah")
        }
    }
}

@Preview(showBackground = true, backgroundColor = 0xFF000000)
@Composable
fun PreviewNormalSubItem() {
    val text = remember { mutableStateOf("测试列表项") }

    Column(Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center) {
        ModuleItem {
            NormalSubItem(
                text.value,
                endIcon = {
                    Icon(
                        imageVector = Icons.Outlined.KeyboardArrowRight,
                        contentDescription = "arrow"
                    )
                },
                onClick = {
                    text.value = "点击了:${System.currentTimeMillis()}"
                }
            )
        }
    }
}

@Preview(showBackground = true, backgroundColor = 0xFFCCCCCC)
@Composable
fun PreviewNormalWithStartIconSubItem() {
    val text = remember { mutableStateOf("测试列表项") }

    Column(Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center) {
        ModuleItem(title = "模块1") {
            NormalWithStartIconSubItem(
                text = text.value,
                startIcon = {
                    Icon(
                        painter = painterResource(id = R.drawable.ic_launcher_foreground),
                        contentDescription = "arrow"
                    )
                },
                subText = "这里是一些其他的描述内容",
                onClick = {
                    text.value = "点击了:${System.currentTimeMillis()}"
                }
            )
        }
    }
}

@Preview(showBackground = true, backgroundColor = 0xFFCCCCCC)
@Composable
fun PreviewNormalSubItemModule() {
    val endIcon = @Composable {
        Icon(imageVector = Icons.Outlined.KeyboardArrowRight, contentDescription = "arrow")
    }
    val itemList1 = listOf(
        NormalSubItemData("列表1", "1.0.0", endIcon),
        NormalSubItemData("列表2", null, endIcon),
        NormalSubItemData("列表3", "1.0.0", endIcon),
        NormalSubItemData("列表4", "1.0.0", endIcon),
        NormalSubItemData("列表5", "1.0.0", endIcon),
        NormalSubItemData("列表6", "1.0.0"),
        NormalSubItemData("列表7", null, endIcon),
        NormalSubItemData("列表8", "1.0.0",),
        NormalSubItemData("列表9", "1.0.0", endIcon),
        NormalSubItemData("列表10", "1.0.0"),
    )

    Column(Modifier.fillMaxSize()) {
        NormalSubItemModule(
            itemList1, title = "模块1"
        )
        NormalSubItemModule(
            itemList1, title = "模块2"
        )
    }
}

@Preview(showBackground = true, backgroundColor = 0xFFCCCCCC)
@Composable
fun PreviewNormalWithStartIconSubItemModule() {
    val startIcon = @Composable {
        Icon(painter = painterResource(id = R.drawable.ic_launcher_foreground), contentDescription = "arrow")
    }
    val itemList1 = listOf(
        NormalSubItemWithStartIconData("列表1", "这是描述文本", startIcon),
        NormalSubItemWithStartIconData("列表2", null, startIcon),
        NormalSubItemWithStartIconData("列表3", "这是描述文本"),
        NormalSubItemWithStartIconData("列表4", "这是描述文本", startIcon),
        NormalSubItemWithStartIconData("列表5", "这是描述文本", startIcon),
        NormalSubItemWithStartIconData("列表6", "这是描述文本"),
        NormalSubItemWithStartIconData("列表7", null, startIcon),
        NormalSubItemWithStartIconData("列表8", "1.0.0",),
        NormalSubItemWithStartIconData("列表9", "1.0.0", startIcon),
        NormalSubItemWithStartIconData("列表10", "1.0.0"),
    )

    Column(Modifier.fillMaxSize()) {
        NormalWithStartIconSubItemModule(
            itemList1, title = "模块1", isAlignIcon = false
        )
        NormalWithStartIconSubItemModule(
            itemList1, title = "模块2"
        )
    }
}

MyViewModel.kt

package cn.qsbye.alittlesmile_android.AboutPageClass

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.ViewModel
import cn.qsbye.alittlesmile_android.ui.theme.Alittlesmile_androidTheme

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

        val viewModel = MyViewModel()

        setContent {
            HomeView(viewModel)
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeView(viewModel: MyViewModel) {
    Alittlesmile_androidTheme {
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = MaterialTheme.colorScheme.background
        ) {
            MaterialTheme {
                Scaffold(
                    topBar = {
                        TopAppBar (
                            title = {
                                Text(text = "About pager Demo", maxLines = 1, overflow = TextOverflow.Ellipsis)
                            },
                            navigationIcon = {
                                IconButton(onClick = {
                                    viewModel.pagerIndex = 0
                                }) {
                                    Icon(Icons.Outlined.ArrowBack, "返回")
                                }
                            },
                        )
                    }
                ) {
                    Column(Modifier.fillMaxSize().padding(it)) {
                        when (viewModel.pagerIndex) {
                            0 -> HomeScreen(viewModel)
                            1 -> AboutChengPager(MyViewModel())
                            2 -> AboutWechatPager(MyViewModel())
                            3 -> AboutQqPager((MyViewModel()))
                            4 -> AboutBoxPager(MyViewModel())
                            5 -> AboutLibPager()
                            6 -> AboutVspPager()
                        }
                    }
                }
            }
        }
    }
}

class MyViewModel: ViewModel() {
    var pagerIndex by  mutableStateOf(0)
}


@Preview
@Composable
fun PreviewHomeView() {
    HomeView(viewModel = MyViewModel())
}

TextItem.kt

package cn.qsbye.alittlesmile_android.AboutPageClass

import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

/**
 * 基础纯文本 item
 * */
@Composable
fun BaseTextItem(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
        content()
    }
}

/**
 * 版权信息文本
 * */
@Composable
fun RightsTextItem(
    dateText: String,
    name: String,
    style: TextStyle = LocalTextStyle.current,
) {
    Box(
        modifier = Modifier
            .fillMaxSize() // 使 Box 占据可用空间
            .padding(16.dp) // 添加内边距
    ) {
        Text(
            text = "Copyleft © $dateText $name",
            style = style,
            modifier = Modifier.align(Alignment.Center) // 确保文本在 Box 内居中
        )
//        Text(
//            text = "All Rights Reserved.",
//            style = style,
//            modifier = Modifier.align(Alignment.Center) // 确保文本在 Box 内居中
//        )
    }
}

/**
 * 可点击的链接文本
 * */
@Composable
fun LinkText(
    text: String,
    modifier: Modifier = Modifier,
    fontSize: TextUnit = 12.sp,
    color: Color = MaterialTheme.colorScheme.primary,
    onClick: () -> Unit) {

    Text(
        text = text,
        color = color,
        fontSize = fontSize,
        modifier = modifier.noRippleClickable(onClick = onClick) )
}

/**
 * 没有点击涟漪效果的 Clickable
 * */
inline fun Modifier.noRippleClickable(crossinline onClick: ()->Unit): Modifier = composed {
    this.clickable(indication = null,
        interactionSource = remember { MutableInteractionSource() }) {
        onClick()
    }
}

@Preview(showBackground = true, backgroundColor = 0xFFCCCCCC)
@Composable
fun PreviewTextItem() {
    Column(Modifier.fillMaxSize(), verticalArrangement = Arrangement.Bottom) {
        BaseTextItem {
            Row {
                LinkText(text = "隐私协议") { }
                Text(text = "|", modifier = Modifier.padding(horizontal = 4.dp))
                LinkText(text = "隐私政策") { }
            }

            RightsTextItem(dateText = "2022", name = "equationl")
        }
    }
}

效果

关于页 用户协议页
posted @   qsBye  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示