Android row column onSelect background

本文介绍了一种实现表格视图中偶数行和奇数行不同背景颜色的方法,并通过XML定义了行的选择器样式。同时提供了Java代码示例,展示了如何根据不同状态设置TextView背景。
<!--Even row -->
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">        
    <item android:state_pressed="true" android:drawable="@color/selected" />
    <item android:state_selected="true" android:drawable="@color/selected" />
    <item android:state_focused="true" android:drawable="@color/selected" />
    <item android:drawable="@color/even_row" />
    <item android:state_selected="true" android:state_focused="false" android:state_pressed="false" android:drawable="@color/even_row" />
</selector>


<!--Odd row -->
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@color/selected" />
<item android:state_selected="true" android:drawable="@color/selected" />
<item android:state_focused="true" android:drawable="@color/selected" />
<item android:state_selected="true" android:state_focused="false" android:state_pressed="false" android:drawable="@android:color/transparent" />
</selector>


java code:

for (String data:rowData){
			   
				   TextView col = new TextView(context);
				   col.setTextAlignment(TextView.TEXT_ALIGNMENT_CENTER);
				   col.setText(data);
				   col.setTextSize(24);
				   if (clickable){
					   if (table.getChildCount()%2==0)
						   col.setBackgroundResource(R.drawable.table_selector_odd);
					   else
						   col.setBackgroundResource(R.drawable.table_selector_even);
				   }
				   
			   row.addView(col, col1Params);
		   }



package com.desaysv.hvac.ui.widget import android.util.Log import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior import androidx.compose.foundation.gestures.waitForUpOrCancellation import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalDensity import com.desaysv.hvac.HvacScopeViewModelProvider import com.desaysv.hvac.intent.MainIntent import com.desaysv.hvac.ui.theme.HvacThemeM32T import com.desaysv.hvac.viewmodel.MainViewModel import kotlin.math.roundToInt /** * 滚轮切换视图 */ @OptIn(ExperimentalFoundationApi::class) @Composable fun <T> M32T_WheelPicker( data: List<T>, selectIndex: Int, visibleCount: Int, modifier: Modifier = Modifier, zoomItem: Boolean = true, /** * 用户操作时且与上次索引相同时是否回调 onSelect 函数,默认为 false */ sameIndexInvoke: Boolean = false, contentAlignment: Alignment = Alignment.Center, onSelect: (index: Int, item: T, fromUser: Boolean) -> Unit, contentUnSel: @Composable (item: T) -> Unit, contentUnSelDown: @Composable (item: T) -> Unit, content: @Composable (item: T) -> Unit, ) { val mainViewModel = HvacScopeViewModelProvider.getHvacScopeViewModel(MainViewModel::class.java) val isDriver = HvacThemeM32T.uiTheme.isShownInMainScreen Log.d("WheelPicker", "selectIndex: $selectIndex") if (visibleCount % 2 == 0) { throw IllegalArgumentException("visibleCount must be an odd number") } val newDataList = mutableListOf<AddHeadFootBeanM32T<T>>() for (item in data) { newDataList.add(AddHeadFootBeanM32T(item)) } for (i in 0 until visibleCount / 2) { newDataList.add(0, AddHeadFootBeanM32T(null)) newDataList.add(AddHeadFootBeanM32T(null)) } val newSelectIndex = selectIndex + visibleCount / 2 val newOnSelect: (index: Int, item: AddHeadFootBeanM32T<T>, fromUser: Boolean) -> Unit = { index, item, fromUser -> if (item.item != null) { onSelect.invoke(index - visibleCount / 2, item.item, fromUser) } } val newContent: @Composable (bean: AddHeadFootBeanM32T<T>) -> Unit = { if (it.item != null) { content.invoke(it.item) } } val unSelContent: @Composable (bean: AddHeadFootBeanM32T<T>) -> Unit = { if (it.item != null) { contentUnSel.invoke(it.item) } } val unSelDownContent: @Composable (bean: AddHeadFootBeanM32T<T>) -> Unit = { if (it.item != null) { contentUnSelDown.invoke(it.item) } } var fromUser by remember { mutableStateOf(false) } BoxWithConstraints( modifier = modifier.pointerInput(Unit) { awaitEachGesture { awaitFirstDown().also { it.consume() } val up = waitForUpOrCancellation() up?.consume() } }, propagateMinConstraints = true ) { val density = LocalDensity.current val size = newDataList.size val count = size * 1 val pickerHeight = maxHeight val pickerHeightPx = density.run { pickerHeight.toPx() } val pickerCenterLinePx = pickerHeightPx / 2 val itemHeight = pickerHeight / visibleCount val itemHeightPx = pickerHeightPx / visibleCount val startIndex = 0 val index = startIndex - startIndex.floorMod(size) + newSelectIndex.coerceAtMost(size - 1).coerceAtLeast(0) val scrollOffset = ((itemHeightPx - pickerHeightPx) / 2).roundToInt() val listState = rememberLazyListState( initialFirstVisibleItemIndex = index, initialFirstVisibleItemScrollOffset = scrollOffset, ) var lastSelectedIndex by remember { mutableIntStateOf(newSelectIndex) } val layoutInfo by remember { derivedStateOf { listState.layoutInfo } } LaunchedEffect(newSelectIndex) { if (!listState.isScrollInProgress) { listState.animateScrollToItem(index, scrollOffset) } } // 滚动结束(且停止触摸),重置 fromUser LaunchedEffect(listState.isScrollInProgress) { Log.i( "WheelPicker", "listState.isScrollInProgress: ${listState.isScrollInProgress}, fromUser=$fromUser" ) //防止在滑动时退出空调界面 mainViewModel.sendAction(MainIntent.TouchIntent(isDriver = isDriver, touched = listState.isScrollInProgress)) fromUser = listState.isScrollInProgress } LazyColumn( modifier = Modifier, state = listState, flingBehavior = rememberSnapFlingBehavior(listState), ) { items(count) { index -> Log.i( "WheelPicker", "items listState.isScrollInProgress: ${listState.isScrollInProgress}, fromUser=$fromUser" ) val currIndex = (index - startIndex).floorMod(size) val isSelected = currIndex == newSelectIndex var percent = 1f val item = layoutInfo.visibleItemsInfo.find { it.index == index } Log.i("WheelPicker", "WheelPicker() called with: index = $index") if (item != null) { val itemCenterY = item.offset + item.size / 2 percent = if (itemCenterY < pickerCenterLinePx) { itemCenterY / pickerCenterLinePx } else { 1 - (itemCenterY - pickerCenterLinePx) / pickerCenterLinePx } Log.i( "WheelPicker", "WheelPicker() pickerCenterLinePx = $pickerCenterLinePx item.offset = ${item.offset} item.size = ${item.size}" ) Log.i( "WheelPicker", "WheelPicker22() condition1 = ${!listState.isScrollInProgress} " + "condition2 = ${item.offset < pickerCenterLinePx} " + "condition3 = ${item.offset + item.size > pickerCenterLinePx} " + "lastSelectedIndex = $lastSelectedIndex " + "currIndex = $currIndex" ) if (!listState.isScrollInProgress && item.offset < pickerCenterLinePx && item.offset + item.size > pickerCenterLinePx ) { if (lastSelectedIndex != currIndex) { Log.i("WheelPicker", "fromUser: $fromUser, currIndex: $currIndex") newOnSelect(currIndex, newDataList[currIndex], fromUser) lastSelectedIndex = currIndex } else if (sameIndexInvoke && fromUser) { // 手动控制选择和上次一样的位置 Log.i("WheelPicker", "sameIndexInvoke currIndex: $currIndex") newOnSelect(currIndex, newDataList[currIndex], true) } } } Box( modifier = Modifier .graphicsLayer { if (zoomItem) { // alpha = 0.05f + 0.95f * percent scaleX = 0.7f + 0.3f * percent scaleY = 0.7f + 0.3f * percent } } .fillMaxWidth() .height(itemHeight), contentAlignment = contentAlignment, ) { if (isSelected) newContent(newDataList[currIndex]) else { if (currIndex < newSelectIndex) unSelContent(newDataList[currIndex]) else unSelDownContent(newDataList[currIndex]) } } } } } } private fun Int.floorMod(other: Int): Int = when (other) { 0 -> this else -> this - floorDiv(other) * other } data class AddHeadFootBeanM32T<T>(val item: T?) 上述代码修改为左对齐(注意通过scale放大的部分也需要和其他部分左对齐)
10-16
@Entry @Component struct QRCodeGenerator { // 用户信息相关状态 @State userName: string = '' @State userPhone: string = '' @State userInfo: string = '' // UI控制相关状态 @State isQRCodeGenerated: boolean = false @State qrSize: string = '40%' @State selectedAvatar: Resource = $r("app.media.avatar12") @State currentColorIndex: number = 0 // 尺寸选择选项 private sizeOptions: SelectOption[] = [ { value: '40%' }, { value: '50%' }, { value: '60%' }, { value: '70%' }, { value: '80%' }, { value: '90%' }, { value: '100%' } ] // 二维码颜色选项 private qrColors: Color[] = [ Color.Orange, Color.Blue, Color.Brown, Color.Gray, Color.Green, Color.Pink, Color.Red, Color.Yellow, Color.Blue, Color.Green ] // 颜色名称对应表 private colorNames: string[] = [ '橙色', '蓝色', '棕色', '灰色', '绿色', '粉色', '红色', '黄色', '蓝色', '绿色' ] build() { Row() { // 左侧表单区域 Column() { // 姓名输入 this.buildInputRow('姓名:', '请输入姓名', (value) => this.userName = value) // 电话输入 this.buildInputRow('电话:', '请输入电话', (value) => this.userPhone = value) // 尺寸选择 Row({ space: 20 }) { Text("尺寸:") .fontSize(40) .margin({ left: 20 }) Select(this.sizeOptions) .font({ size: 30 }) .selectedOptionFont({ size: 30 }) .optionFont({ size: 26 }) .selected(0) // 初始选中第一个选项(40%) .onSelect((_, value) => { this.qrSize = value console.log(`选择尺寸: ${value}`) }) }.width('100%') // 颜色选择 Column() { Text(`颜色: ${this.colorNames[this.currentColorIndex]}`) .fontSize(40) .margin({ left: 20, bottom: 10 }) Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, wrap: FlexWrap.Wrap }) { ForEach(this.qrColors, (color: Color, index: number) => { Text() .width(30) .height(30) .backgroundColor(color) .margin({ right: 10, bottom: 10 }) .border({ width: this.currentColorIndex === index ? 2 : 0, color: Color.Black }) .onClick(() => { this.currentColorIndex = index console.log(`选择颜色: ${this.colorNames[index]}`) }) }) }.width('80%') // 自适应宽度 }.width('100%') // 头像选择 Row({ space: 20 }) { Text(`头像:`) .fontSize(40) .margin({ left: 20 }) this.buildAvatarOption($r('app.media.avatar1')) this.buildAvatarOption($r('app.media.avatar16')) this.buildAvatarOption($r("app.media.avatar11")) }.width('100%') // 生成按钮 Button('生成二维码') .width('80%') .fontSize(30) .backgroundColor('#1677ff') // 增加按钮背景色 .onClick(() => { this.isQRCodeGenerated = true this.userInfo = `姓名:${this.userName},电话:${this.userPhone}` console.log(`生成信息: ${this.userInfo}`) }) } .height('100%') .width('50%') .justifyContent(FlexAlign.SpaceEvenly) // 右侧二维码展示区域 Column() { if (this.isQRCodeGenerated) { // 二维码与头像叠层 Stack() { QRCode(this.userInfo) .color(this.qrColors[this.currentColorIndex]) .width(this.qrSize) Image(this.selectedAvatar) .width(20) // 头像大小 } } else { // 未生成时显示提示 Text('请输入信息并生成二维码') .fontSize(28) } } .height('100%') .width('50%') .justifyContent(FlexAlign.Center) } .height('100%') .width('100%') .padding(20) // 增加整体内边距 } /** * 构建输入行组件 * @param label 输入框标签 * @param placeholder 占位文本 * @param onChange 输入变化回调 */ @Builder private buildInputRow(label: string, placeholder: string, onChange: (value: string) => void) { Row({ space: 20 }) { Text(label) .fontSize(40) .margin({ left: 20 }) TextInput({ placeholder: placeholder }) .width('70%') .fontSize(30) .placeholderFont({ size: 30 }) .onChange(onChange) // 修复原bug:使用传入的回调而非硬编码 }.width('100%') } /** * 构建头像选择项 * @param avatar 头像资源 */ @Builder private buildAvatarOption(avatar: Resource) { Row() { Image(avatar) .width(40) Radio({ value: `${avatar}`, group: 'avatar' }) // 增加唯一value确保单选正常 .onChange((isSelected) => { if (isSelected) { this.selectedAvatar = avatar console.log(`选择头像: ${JSON.stringify(avatar)}`) } }) } } }在原基础上多添加一些效果保持别出错
最新发布
10-18
import React, { useState, useEffect } from 'react'; import { ProCard, PageContainer } from '@ant-design/pro-components'; import {Form, Radio, Tree, Row, Col, message, List, Card, Tag, Typography, Space} from 'antd'; import {request} from "@@/plugin-request/request"; import CustomPagination from "./CustomPagination"; const { Title, Text, Paragraph } = Typography; interface SearchResultListProps { keyword?: string; } interface AssetsItem { id: string; dsName: string; dsTableName: string; dsType: string; areaId: string; areaName: string; sysId: string; sysName: string; bussinessObjName: string; bussinessObjId: string; processPointName: string; dsOwnerName: string; dsOwnerNum: string; isConfirm: string; // 后端返回的是字符串 "true"/"false" dsMemo: string; dsRange: string; tags: string; demoData: string; createUser: string; createTime: number; // 时间戳 updateUser: string; updateTime: number; // 时间戳 deleted: number; // 删除标志,0表示未删除 demoFileName: string; demoFilePath: string; demoOssFilePath: string; } // 树形结构组件 const SearchResultList: React.FC<SearchResultListProps> = ({keyword}) => { const [treeData, setTreeData] = useState<any[]>([]); const [assetsData, setAssetsData] = useState<AssetsItem[]>([]); const [expandedKeys, setExpandedKeys] = useState<string[]>([]); // ✅ 新增两个筛选状态 const [isEmpty, setIsEmpty] = useState<boolean>(false); const [dsType, setDsType] = useState<string>(''); const [isConfirm, setIsConfirm] = useState<string>(''); //数据资产分页 const [pageNum, setPageNum] = useState<number>(1); const [totalItems, setTotalItems] = useState<number>(5); // 从接口获取 // 组件加载时自动请求一次树数据 useEffect(() => { const loadInitialData = async () => { // 更新 requestAssetsParam // 请求树结构 const treeData = await fetchTreeData({ keyWord: keyword ?? '', dsType: dsType, isConfirm: isConfirm }); if(treeData){ setTreeData(treeData); }else{ message.error("查询数据为空") } // 请求数据资产 const assetsData = await fetchAssetsData({ keyWord: keyword, dsType: dsType, isConfirm: isConfirm, pageNum:pageNum, pageSize:10 }); if(assetsData){ setAssetsData(assetsData); } }; loadInitialData(); }, [keyword, dsType, isConfirm ,pageNum]); // 当筛选条件变化时重新请求 // 树形数据处理函数,遍历节点 将节点的名称进行拼接 // @ts-ignore const filterTreeData = (nodes: any[], level = 1) => nodes.map((node) => { if (node.count <= 0 || node.count == null) { node.title = node.title.replace(/[\s\(\(]*$$(\d+)$$[\s\)\)]*/g, '').trim(); } return { ...node, isLink: level === 1, // 第一层节点添加 isLink children: node.children ? filterTreeData(node.children, level + 1) : [], }; }); // 获取树形结构数据的函数 const fetchTreeData = async (param: { keyWord: string; dsType: string; isConfirm: string; }) => { const res = await request('/ds/dsbaseinfo/searchAssetsTree', { method: 'POST', data:param }); if(res && res.length>0){ return filterTreeData(res || []); } return [] }; // 获取资产详情的函数, const fetchAssetsData = async (param: { keyWord: any; dsType: string; isConfirm: string; pageNum: number; pageSize: number; areaName?: any; }) => { try { const res = await request('/ds/dsbaseinfo/globalSearchAssets', { method: 'POST', data: param, }); if (res && res.list) { setIsEmpty(res.list.length === 0); setTotalItems(res.list.length) return res.list as AssetsItem[]; } else { setIsEmpty(true); return []; } } catch (err) { setIsEmpty(true); message.error('请求失败'); return []; } } const showModal = (areaName: string) => { // 模拟从后端获取领域清单数据 const list = [ { name: '销售领域', count: 120 }, { name: '财务领域', count: 80 }, { name: '人力资源领域', count: 95 }, { name: '供应链领域', count: 70 }, ]; }; //翻页函数 const handlePageChange = (page: number) => { setPageNum(page); }; // 高亮关键词函数 const highlightKeyword = (text: string, keyword: string | null | undefined): React.ReactNode => { if (!keyword || !text || typeof text !== 'string') return text; const regex = new RegExp(`(${keyword})`, 'gi'); // 忽略大小写匹配 const parts = text.split(regex); return parts.map((part, index) => part.toLowerCase() === keyword.toLowerCase() ? ( <span key={index} style={{color: 'red', fontWeight: 'bold'}}> {part} </span> ) : ( part ) ); }; // 预览文件函数 const handlePreview = (url: string) => { if (url) { const base64Url = btoa(unescape(encodeURIComponent(url))); window.open('http://10.255.96.51:8012/onlinePreview?url=' + encodeURIComponent(base64Url)); } else { message.error('无法预览文件'); } }; // 点击树节点时触发 const onSelect = async (selectedKeys: string[], info: any) => { const selectedNode = info.node; // ✅ 判断是否是叶子节点或倒数第二层节点 const isLeafNode = selectedNode.isLeaf || !selectedNode.children?.length; const isPenultimateNode = selectedNode.children?.length > 0 && selectedNode.children.every((child: any) => child.isLeaf || !child.children?.length); let param = { keyWord:keyword, pageNum:pageNum, pageSize:10, dsType:dsType, isConfirm:isConfirm, areaName: selectedNode.key.split('_')[0], } if (isLeafNode){ param = { ...param, // @ts-ignore dsName:selectedNode.title } } if (isPenultimateNode) { if(selectedNode.key.includes("系统")){ // @ts-ignore param = {...param,sysName:selectedNode.key.split('_')[2]} } if (selectedNode.key.includes("对象")){ // @ts-ignore param = {...param,bussinessObjName:selectedNode.key.split('_')[2]} } if (selectedNode.key.includes("流程")){ // @ts-ignore param = {...param,processPointName:selectedNode.key.split('_')[2]} } } const assetsData = await fetchAssetsData(param); setAssetsData(assetsData); } // 控制树节点展开 const onExpand = (keys: React.Key[]) => { setExpandedKeys(keys as string[]); }; // ✅ 处理筛选项变化 const handleDsTypeChange = (e: any) => { setDsType(e.target.value) }; const handleIsConfirmChange = (e: any) => { if(e.target.value == '是'){ setIsConfirm('true') }else if (e.target.value == '否'){ setIsConfirm('false') }else{ setIsConfirm('') } }; return ( <PageContainer style={{marginTop: 5}}> <ProCard style={{margin: '0 auto', width: 1300, border: 1}}> <Form layout="inline" style={{padding: 3, flexDirection: 'column'}}> {/* 数据类型 */} <Form.Item label="数据类型" style={{marginBottom: 16}}> <Radio.Group defaultValue={''} onChange={handleDsTypeChange}> <Radio value="">不限</Radio> <Radio value="原始数据">原始数据</Radio> <Radio value="清洗后数据">清洗后数据</Radio> </Radio.Group> </Form.Item> {/* 是否认证 */} <Form.Item label="是否认证" style={{marginBottom: 16}}> <Radio.Group defaultValue={''} onChange={handleIsConfirmChange}> <Radio value="">不限</Radio> <Radio value="是">是</Radio> <Radio value="否">否</Radio> </Radio.Group> </Form.Item> </Form> </ProCard> <Row gutter={5} style={{marginTop: 5}}> <Col span={5}> <ProCard title="各领域树状结构"> <Tree showLine treeData={treeData} // @ts-ignore onSelect={onSelect} expandedKeys={expandedKeys} onExpand={onExpand} title={(node) => { if (node.isFirstLevel) { return ( <span> <span>{node.title}</span> <button type="button" style={{ marginLeft: 10, padding: '2px 8px', fontSize: 12, color: '#1890ff', background: 'none', border: '1px solid #1890ff', borderRadius: 4, cursor: 'pointer', }} onClick={(e) => { e.preventDefault(); e.stopPropagation(); // 阻止触发 onSelect showModal(node.title); }} >        领域清单 </button> </span> ); } return <span>{node.title}</span>; }} /> </ProCard> </Col> <Col span={19}> <ProCard title="数据资产列表"> <div> {isEmpty ? ( <div style={{ textAlign: 'center', padding: '50px 20px', color: '#999', fontSize: '16px', }}> 提示您搜索的关键词暂无相关数据资产 </div> ) : ( <List dataSource={assetsData} renderItem={(item) => ( <List.Item style={{marginBottom: -5}} key={item.id}> <Card style={{width: '100%', padding: 10}} bodyStyle={{padding: 5}}> <Title level={4}> <a href="#">{highlightKeyword(item.dsName, keyword)}</a>    {item.isConfirm === 'true' ? ( <Tag color="red" key={item.id}> 数据已确认 </Tag> ) : null} {item.tags.split(',').map((tag, index) => ( <Tag color="blue" key={index}> {highlightKeyword(tag, keyword)} </Tag> ))} </Title> <Text type="secondary">{highlightKeyword(item.dsTableName, keyword)}</Text> <Paragraph>{highlightKeyword(item.dsMemo, keyword)}</Paragraph> <Space direction="vertical" size="small"> <Text> 领域:<Text strong>{highlightKeyword(item.areaName, keyword)}</Text>     系统:<Text strong>{highlightKeyword(item.sysName, keyword)}</Text>     对象:<Text strong>{highlightKeyword(item.bussinessObjName, keyword)}</Text>     流程:<Text strong>{highlightKeyword(item.processPointName, keyword)}</Text> </Text> {/* 如果有样例文件则显示 */} {item.demoFileName && item.demoFileName !== '' ? ( <Text> 样例数据:{' '} <a href="#">{highlightKeyword(item.demoFileName, keyword)}</a>       <span onClick={() => handlePreview(item.demoOssFilePath)} style={{color: 'blue', cursor: 'pointer', marginRight: 10}} > 预览 </span>     <span> <a href={item.demoOssFilePath}>下载</a> </span> </Text> ) : null} </Space> </Card> </List.Item> )} /> )} <CustomPagination current={pageNum} total={totalItems} pageSize={10} onPageChange={handlePageChange} /> </div> </ProCard> </Col> </Row> </PageContainer> ); } export default SearchResultList; 我这么写 为啥么有显示出链接
08-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值