Project 6:Mobile APP

这篇博客详细介绍了如何使用Kivy和Python创建一个移动APP,包括定义APP逻辑、编辑显示界面、处理屏幕切换、账号注册登录功能、JSON文件交互、登录验证、显示TXT文件内容、界面优化和兼容性处理,以及最终将项目打包成APK文件的步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Kivy+Python

Python文件控制逻辑
定义类;
MainApp(APP)控制 Rootwidget(ScreenManager)控制每一个(Screen);

#转为执行文件时需要命名为main.py
from kivy.app import App
from kivy.lang import Builder  #连接kv
from kivy.uix.screenmanager import ScreenManager, Screen

Builder.load_file("design.kv")  #引入kvfile

class LoginScreen(Screen):
    def sign_up(self):
        self.manager.current="sign_up_screen" #manager为当前页面
        #使用sign_up函数会跳转到signup页面

class SignUpScreen(Screen):
    def back(self):
        self.manager.current="login_screen"

class Rootwidget(ScreenManager):
    pass

class MainApp(App):
    def build(self):
        return Rootwidget()  #返回一个对象

if __name__=="__main__":
    MainApp().run()

kv文件用于编辑app的显示图像
每一个screen都为Python文件中的一类;
布置每一个screen的布局;

注意点
对每个screen命名,以便于在python文件中调用;

<LoginScreen>:
    GridLayout:
        cols:1
        GridLayout:
            cols:1
            Label:
                text:"User Login"
            TextInput:
                hint_text:"User name"
            TextInput:
                hint_text:"User password"
            Button:
                text:"Login"
        GridLayout:
            cols: 2
            Button:
                text:"Forgot password"
            Button:
                text:"Sign Up"
                on_press: root.sign_up()

<SignUpScreen>:
    GridLayout:
        cols:1
        Label:
            text:"Sign up for a space journey!"
        TextInput:
            hint_text:"Username"
        TextInput:
            hint_text:"Password"
        Button:
            text:"Submit"
            on_press:root.back()

<Rootwidget>:
    LoginScreen:
        name:"login_screen"
    SignUpScreen:
        name:"sign_up_screen"

调整signup screen,使输出signup的名字和密码

main.py

class SignUpScreen(Screen):
    def add_user(self,uname,pword):
        print(uname,pword)

design.ky

<SignUpScreen>:
    GridLayout:
        cols:1
        Label:
            text:"Sign up for a space journey!"
        TextInput:
            id:username
            hint_text:"Username"
        TextInput:
            id:password
            hint_text:"Password"
        Button:
            text:"Submit"
            on_press:root.add_user(root.ids.username.text,root.ids.password.text)
                                    #.text,从object中提出text
                #root  代表signupscreen , adduser 来自signupscreen类
                # ids是Screen 中的properties,username为上面创建的id,text为strvalue

改变add_user函数,使得输入的info可以传入jsonfile
load得到jsonfile中的字典数据;
dump对jsonfile进行添加关键词:值;

import json
from datetime import datetime
class SignUpScreen(Screen):
    def add_user(self,uname,pword):
        with open("user.json") as file:
            users = json.load(file)
        users[uname]={"username":uname,"password":pword,
            "created":datetime.now().strftime("%Y-%m-%d %H-%M-%S")}
        #覆盖原本jsonfile
        with open("user.json",'w') as file:
            json.dump(users,file)

添加注册成功页面

按钮从右往左返回login页面

class SignUpScreenSuccess(Screen):
    def go_to_login(self):
        self.manager.transition.direction="right"
        self.manager.current="login_screen"

显示——success,下方一个返回按钮;

<SignUpScreenSuccess>:
    GridLayout:
        cols:1
        Label:
            text:"Sign up Successful!"
        Button:
            text:"Login page"
            on_press:root.go_to_login()

添加账号密码的判断——login按钮

main.py
密码正确则进入successpage;
失败则在label显示wrongname;

class LoginScreen(Screen):
    def sign_up(self):
        self.manager.current="sign_up_screen"
    def login(self,uname,pword):
        with open("user.json") as file:
            users = json.load(file)
        if uname in users and users[uname]['password'] == pword:      
            self.manager.transition.direction="left"
            self.manager.current="login_screen_success"
        else:
            self.ids.login_wrong.text="Wrong username or password!"

design.ky
为loginpage增加一个label,用于显示账号密码是否正确;

<LoginScreen>:
    GridLayout:
        cols:1
        GridLayout:
            cols:1
            Label:
                text:"User Login"
            TextInput:
                id:username
                hint_text:"User name"
            TextInput:
                id:password
                hint_text:"User password"
            Button:
                text:"Login"
                on_press:root.login(root.ids.username.text,root.ids.password.text)
            Label:
                id:login_wrong
                text:""
        GridLayout:
            cols: 2
            Button:
                text:"Forgot password"
            Button:
                text:"Sign Up"
                on_press: root.sign_up()

设置登入后界面,实现随机输出txt文本的内容
main.py
glob得到给定目录下的所有txt的相对路径;
path.stem得到路径下的文件名;
打开文件,readlines()得到文件中的行列表;
random.choice随机选择文件中的一行;

import glob
from pathlib import Path
import random

class LoginScreenSuccess(Screen):
    def log_out(self):
        self.manager.transition.direction="right"
        self.manager.current="login_screen"

    def get_quotes(self,feel):
        feel=feel.lower() #转为小写
        available_feelings=glob.glob("quotes/*txt")
        #glob得到目录下每一个txt
        #path.stem得到文件名
        #name得到文件名+后缀
        available_feelings=[Path(filename).stem for filename in available_feelings]
        #去掉后缀
        if feel in available_feelings:
            with open(f"quotes/{feel}.txt",'r',encoding='UTF-8') as file:
                quotes=file.readlines()
                #得到list
            self.ids.quote.text=random.choice(quotes)
            #print(feel)
        else:
            self.ids.quote.text="Try another feeling"

design.kv

<LoginScreenSuccess>:
    GridLayout:
        cols:1
        Button:
            text:"Logout"
            on_press:root.log_out()
        Label:
            text:"How do you feel!"
        TextInput:
            id:feeling
            hint_text:"Things to try: happy, sad, unloved..."
        Button:
            text:"Enlighten me"
            on_press:root.get_quotes(root.ids.feeling.text)
        Label:
            id:quote
            text:""

上述代码可实现app所有功能

main.py

#转为执行文件时需要命名为main.py
from kivy.app import App
from kivy.lang import Builder  #连接kv
from kivy.uix.screenmanager import ScreenManager, Screen
import json
from datetime import datetime
import glob
from pathlib import Path
import random


Builder.load_file("design.kv")  #引入kvfile

class LoginScreen(Screen):
    def sign_up(self):
        self.manager.current="sign_up_screen"
    def login(self,uname,pword):
        with open("user.json") as file:
            users = json.load(file)
        if uname in users and users[uname]['password'] == pword:      
            self.manager.transition.direction="left"
            self.manager.current="login_screen_success"
        else:
            self.ids.login_wrong.text="Wrong username or password!"


class SignUpScreen(Screen):
    def add_user(self,uname,pword):
        with open("user.json") as file:
            users = json.load(file)
        users[uname]={"username":uname,"password":pword,
            "created":datetime.now().strftime("%Y-%m-%d %H-%M-%S")}
        #覆盖原本jsonfile
        with open("user.json",'w') as file:
            json.dump(users,file)
        self.manager.current="sign_up_screen_success"

class SignUpScreenSuccess(Screen):
    def go_to_login(self):
        self.manager.transition.direction="right"
        self.manager.current="login_screen"

class LoginScreenSuccess(Screen):
    def log_out(self):
        self.manager.transition.direction="right"
        self.manager.current="login_screen"

    def get_quotes(self,feel):
        feel=feel.lower() #转为小写
        available_feelings=glob.glob("quotes/*txt")
        #glob得到目录下每一个txt
        #path.stem得到文件名
        #name得到文件名+后缀
        available_feelings=[Path(filename).stem for filename in available_feelings]
        #去掉后缀
        if feel in available_feelings:
            with open(f"quotes/{feel}.txt",'r',encoding='UTF-8') as file:
                quotes=file.readlines()
                #得到list
            self.ids.quote.text=random.choice(quotes)
            #print(feel)
        else:
            self.ids.quote.text="Try another feeling"


class Rootwidget(ScreenManager):
    pass

class MainApp(App):
    def build(self):
        return Rootwidget()  #返回一个对象

if __name__=="__main__":
    MainApp().run()

design.kv

<LoginScreen>:
    GridLayout:
        cols:1
        GridLayout:
            cols:1
            Label:
                text:"User Login"
            TextInput:
                id:username
                hint_text:"User name"
            TextInput:
                id:password
                hint_text:"User password"
            Button:
                text:"Login"
                on_press:root.login(root.ids.username.text,root.ids.password.text)
            Label:
                id:login_wrong
                text:""
        GridLayout:
            cols: 2
            Button:
                text:"Forgot password"
            Button:
                text:"Sign Up"
                on_press: root.sign_up()
            

<SignUpScreen>:
    GridLayout:
        cols:1
        Label:
            text:"Sign up for a space journey!"
        TextInput:
            id:username
            hint_text:"Username"
        TextInput:
            id:password
            hint_text:"Password"
        Button:
            text:"Submit"
            on_press:root.add_user(root.ids.username.text,root.ids.password.text)
                                    #.text,从object中提出text
                #root  代表signupscreen , adduser 来自signupscreen类
                # ids是Screen 中的properties,username为上面创建的id,text为strvalue
<SignUpScreenSuccess>:
    GridLayout:
        cols:1
        Label:
            text:"Sign up Successful!"
        Button:
            text:"Login page"
            on_press:root.go_to_login()

<LoginScreenSuccess>:
    GridLayout:
        cols:1
        Button:
            text:"Logout"
            on_press:root.log_out()
        Label:
            text:"How do you feel!"
        TextInput:
            id:feeling
            hint_text:"Things to try: happy, sad, unloved..."
        Button:
            text:"Enlighten me"
            on_press:root.get_quotes(root.ids.feeling.text)
        Label:
            id:quote
            text:""

<Rootwidget>:
    LoginScreen:
        name:"login_screen"
    SignUpScreen:
        name:"sign_up_screen"
    SignUpScreenSuccess:
        name:"sign_up_screen_success"
    LoginScreenSuccess:
        name:"login_screen_success"

下面将优化app显示界面和兼容性

布置login界面
degin.kv

<LoginScreen>:
    GridLayout:
        cols:1
        GridLayout: #等分子layout
            cols:1
            padding:15,15  #此grid相对外面的layout设置padding
            #左右  上下
            spacing:20,20
        #grid内部每个widget的距离
            Label:
                text:"User Login"
                font_size:'20sp'
                #sp=space-independent pixels
            TextInput:
                id:username
                hint_text:"User name"
            TextInput:
                id:password
                password: True
                hint_text:"User password"
            RelativeLayout:#设定相对layout
                Button:
                    text:"Login"
                    on_press:root.login(root.ids.username.text,root.ids.password.text)
                    #需要设定size
                    size_hint:0.3,0.5
                    #   百分比缩短 wide  height
                    pos_hint:{"center_x":0.5,"center_y":0.6}
                    #0.5 0.6比较好
        

            Label:
                id:login_wrong
                text:""
        GridLayout:
            cols: 2
            size_hint:0.2,0.2
            #该layout占20%的比例,相对的另外的平分80%
            padding:10,10
            spacing:10,0
            Button:
                text:"Forgot password"
                background_color:1,1,1,0
                #RGB+背景透明度
                opacity:1 if self.state == 'normal' else 0.5
                #normal时透明度1,其他时候透明度0.5
                color:0.1,0.7,1,1
            Button:
                text:"Sign Up"
                on_press: root.sign_up()
                background_color:1,1,1,0
                #RGB+背景透明度
                opacity:1 if self.state == 'normal' else 0.5
                #normal时透明度1,其他时候透明度0.5
                color:0.1,0.7,1,1

显示效果:
在这里插入图片描述

sign-up page的布置

<SignUpScreen>:
    GridLayout:
        cols:1
        padding:20,20
        spacing:20,20
        Label:
            text:"Sign up for a space journey!"
        TextInput:
            size_hint:0.4,0.4
            id:username
            hint_text:"Username"
        TextInput:
            size_hint:0.4,0.4
            id:password
            hint_text:"Password"
        Button:
            size_hint:0.4,0.4
            text:"Submit"
            on_press:root.add_user(root.ids.username.text,root.ids.password.text)
                                    #.text,从object中提出text
                #root  代表signupscreen , adduser 来自signupscreen类
                # ids是Screen 中的properties,username为上面创建的id,text为strvalue

设置图像按钮——执行hover监测
main.py
定义imagebutton类,继承自三个类

from hoverable import HoverBehavior 
#引入hover
from kivy.uix.image import Image
from kivy.uix.behaviors import ButtonBehavior


class ImageButton(ButtonBehavior,HoverBehavior,Image):
    #ButtonBehavior要放在最前面!
    #image的顺序会影响behavior
    pass
#imagebutton 可以使用上述三种类的功能

design.kv
使用两个图像,在hover中切换;
调用的button为imagebutton类,继承三个类的功能;

<LoginScreenSuccess>:
    GridLayout:
        cols:1
        padding:30,30
        spacing:30,30
        RelativeLayout:
            ImageButton:
                #text:"Logout"  #无用
                size_hint:0.35,0.35
                #pos_hint需要在relateivelayout中使用
                pos_hint:{'center_x':0.93,'center_y':0.8}
                on_press:root.log_out()
                source:"logout_hover.png" if self.hovered else "logout_nothover.png"

        Label:
            text:"How do you feel!"
        TextInput:
            id:feeling
            hint_text:"Things to try: happy, sad, unloved..."
        Button:
            text:"Enlighten me"
            on_press:root.get_quotes(root.ids.feeling.text)
        Label:
            id:quote
            text:""

为enlighten页面添加滚轮+动态text
text_size:可设置label中text的大小;
其中None表示text不受label高度的限制;

size_hint_y:None表示label的高度不受hiny的限制(可高于分配的20%);
设置height参数可设置fixed height,即固定label 的高度;

动态高度:height:self.texture_size[1]; 高度为1,宽度为0;
则当前label高度受text变化而变化;

将整个label放入ScrollView中,即可使text动态缩进,且label高度为scrollview高度,通过滚轮访问;

ScrollView: #添加滚轮
        #滚轮作为label中text大小的限制
            Label:
                id:quote
                text:""
                #text_size:self.width, self.height
                #self指label,则width和height会随label变化
                text_size:self.width, None
                #height不受label限制
                
                size_hint_y:None 
                #labelheight不受hinty限制(可超过20%)  默认100px
                #height:200
                #设置label 的高度

                #设置label动态高度
                height:self.texture_size[1]
                #0 - width   1-height 动态高度

上面已完成app的所有界面

添加forget password
main.py

#转为执行文件时需要命名为main.py
from kivy.app import App
from kivy.lang import Builder  #连接kv
from kivy.uix.screenmanager import ScreenManager, Screen
import json
from datetime import datetime
import glob
from pathlib import Path
import random
from hoverable import HoverBehavior 
#引入hover
from kivy.uix.image import Image
from kivy.uix.behaviors import ButtonBehavior

Builder.load_file("design.kv")  #引入kvfile

class LoginScreen(Screen):
    def sign_up(self):
        self.manager.transition.direction="left"
        self.manager.current="sign_up_screen"
    def login(self,uname,pword):
        with open("user.json") as file:
            users = json.load(file)
        if uname in users and users[uname]['password'] == pword:      
            self.manager.transition.direction="left"
            self.manager.current="login_screen_success"
        else:
            self.ids.login_wrong.text="Wrong username or password!"
    def forget(self):
        self.manager.transition.direction="left"
        self.manager.current="forget_password"

class SignUpScreen(Screen):
    def add_user(self,uname,pword):
        with open("user.json") as file:
            users = json.load(file)
        users[uname]={"username":uname,"password":pword,
            "created":datetime.now().strftime("%Y-%m-%d %H-%M-%S")}
        #覆盖原本jsonfile
        with open("user.json",'w') as file:
            json.dump(users,file)
        self.manager.transition.direction="left"
        self.manager.current="sign_up_screen_success"
    def back(self):
        self.manager.transition.direction="right"
        self.manager.current="login_screen"

class SignUpScreenSuccess(Screen):
    def go_to_login(self):
        self.manager.transition.direction="right"
        self.manager.current="login_screen"
    


class LoginScreenSuccess(Screen):
    def log_out(self):
        self.manager.transition.direction="right"
        self.manager.current="login_screen"

    def get_quotes(self,feel):
        feel=feel.lower() #转为小写
        available_feelings=glob.glob("quotes/*txt")
        #glob得到目录下每一个txt
        #path.stem得到文件名
        #name得到文件名+后缀
        available_feelings=[Path(filename).stem for filename in available_feelings]
        #去掉后缀
        if feel in available_feelings:
            with open(f"quotes/{feel}.txt",'r',encoding='UTF-8') as file:
                quotes=file.readlines()
                #得到list
            self.ids.quote.text=random.choice(quotes)
            #print(feel)
        else:
            self.ids.quote.text="Try another feeling"

class ImageButton(ButtonBehavior,HoverBehavior,Image):
    #ButtonBehavior要放在最前面!
    #image的顺序会影响behavior
    pass
#imagebutton 可以使用上述三种类的功能


class FindPassword(Screen):
    def change(self,uname,pword):
        with open("user.json") as file:
            users=json.load(file)
        if uname not in users.keys():
            self.ids.board.text="This username is not exist! Please try again!"
        else:
            users[uname]={"username":uname,"password":pword,
                "created":datetime.now().strftime("%Y-%m-%d %H-%M-%S")}
            with open("user.json",'w') as file:
                json.dump(users,file)
            self.manager.transition.direction="right"
            self.manager.current="login_screen"
    def back(self):
        self.manager.transition.direction="right"
        self.manager.current="login_screen"


class Rootwidget(ScreenManager):
    pass

class MainApp(App):
    def build(self):
        return Rootwidget()  #返回一个对象

if __name__=="__main__":
    MainApp().run()

design.kv

<LoginScreen>:
    GridLayout:
        cols:1
        GridLayout: #等分子layout
            cols:1
            padding:15,15  #此grid相对外面的layout设置padding
            #左右  上下
            spacing:20,20
        #grid内部每个widget的距离
            Label:
                text:"User Login"
                font_size:'20sp'
                #sp=space-independent pixels
            TextInput:
                id:username
                hint_text:"User name"
            TextInput:
                id:password
                password: True
                hint_text:"User password"
            RelativeLayout:#设定相对layout
                Button:
                    text:"Login"
                    on_release:root.login(root.ids.username.text,root.ids.password.text)
                    #需要设定size
                    size_hint:0.3,0.5
                    #   百分比缩短 wide  height
                    pos_hint:{"center_x":0.5,"center_y":0.6}
                    #0.5 0.6比较好

            Label:
                id:login_wrong
                text:""
        GridLayout:
            cols: 2
            size_hint:0.2,0.2
            #该layout占20%的比例,相对的另外的平分80%
            padding:10,10
            spacing:10,0
            Button:
                text:"Forgot password"
                on_release:root.forget()
                background_color:1,1,1,0
                #RGB+背景透明度
                opacity:1 if self.state == 'normal' else 0.5
                #normal时透明度1,其他时候透明度0.5
                color:0.1,0.7,1,1
            Button:
                text:"Sign Up"
                on_release: root.sign_up()
                background_color:1,1,1,0
                #RGB+背景透明度
                opacity:1 if self.state == 'normal' else 0.5
                #normal时透明度1,其他时候透明度0.5
                color:0.1,0.7,1,1

<SignUpScreen>:
    GridLayout:
        cols:1
        padding:20,20
        spacing:20,20
        RelativeLayout:   
            size_hint:0,0.35
            Button:
                size_hint:0.3,0.3
                pos_hint:{'center_x':0.17,'center_y':0.8}
                text:"BACK"
                on_release:root.back()
        Label:
            #font_size:'40sp'
            text:"Sign up for a space journey!"

        TextInput:
            #size_hint:0.4,0.4
            id:username
            hint_text:"Username"
        TextInput:
            #size_hint:0.4,0.4
            id:password
            hint_text:"Password"
        Button:
            #size_hint:0.4,0.4
            text:"Submit"
            on_release:root.add_user(root.ids.username.text,root.ids.password.text)
                                    #.text,从object中提出text
                #root  代表signupscreen , adduser 来自signupscreen类
                # ids是Screen 中的properties,username为上面创建的id,text为strvalue

<SignUpScreenSuccess>:
    GridLayout:
        cols:1
        padding:20,20
        spacing:20,20
        Label:
            text:"Sign up Successful!"
        RelativeLayout:
            Button:
                size_hint:0.6,0.35
                pos_hint:{'center_x':0.5,'center_y':0.6}
                text:"Login page"
                on_release:root.go_to_login()
<FindPassword>:
    GridLayout:
        cols:1
        padding:20,20
        spacing:20,20
        RelativeLayout:   
            size_hint:0,0.35
            Button:
                size_hint:0.3,0.3
                pos_hint:{'center_x':0.17,'center_y':0.8}
                text:"BACK"
                on_release:root.back()
        Label:
            id:board
            text:"Change your password"
        TextInput:
            hint_text:"Enter your username"
            id:username
        TextInput:
            hint_text:"Enter your new password"
            id:password
        Button:
            text:"Change password"
            on_release:root.change(root.ids.username.text,root.ids.password.text)


<LoginScreenSuccess>:
    GridLayout:
        cols:1
        padding:30,30
        spacing:30,30
        RelativeLayout:
            ImageButton:
                #text:"Logout"  #无用
                size_hint:0.35,0.35
                #pos_hint需要在relateivelayout中使用
                pos_hint:{'center_x':0.93,'center_y':0.8}
                on_press:root.log_out()
                source:"logout_hover.png" if self.hovered else "logout_nothover.png"

        Label:
            text:"How do you feel!"
        TextInput:
            id:feeling
            hint_text:"Things to try: happy, sad, unloved..."
        Button:
            text:"Enlighten me"
            on_release:root.get_quotes(root.ids.feeling.text)
        ScrollView: #添加滚轮
        #滚轮作为label中text大小的限制
            Label:
                id:quote
                text:""
                #text_size:self.width, self.height
                #self指label,则width和height会随label变化
                text_size:self.width, None
                #height不受label限制
                
                size_hint_y:None 
                #labelheight不受hinty限制(可超过20%)  默认100px
                #height:200
                #设置label 的高度

                #设置label动态高度
                height:self.texture_size[1]
                #0 - width   1-height 动态高度

<Rootwidget>:
    LoginScreen:
        name:"login_screen"
    SignUpScreen:
        name:"sign_up_screen"
    SignUpScreenSuccess:
        name:"sign_up_screen_success"
    LoginScreenSuccess:
        name:"login_screen_success"
    FindPassword:
        name:"forget_password"

下面将files包装为APK file

进入linux——ubuntu系统;
按如下顺序在ubuntu中下载;

buildozer init

Just in case: Ubuntu version 20.04 LTS: checked by lsb_release -a

  1. This one helped me to run kivy with Python3 on Ubuntu at least
sudo apt-get install python3-kivy
  1. Another line to install buildozer
pip3 install -U buildozer
  1. After running buildozer android debug
    that very new Ubuntu installation messaged about missing “git” and “java-jdk”
    So, installed “git” and “jdk”
sudo apt install git
sudo apt install default-jdk
  1. …and run “buildozer” again with:
buildozer appclean
buildozer android debug

the process will install lots of missing things as python-for-android and Android SDK, NDK, etc

still an error but different from what Ardit had with "android.arch = armeabi-v7a" in buildozer.spec file.

the only good thing the command:

bash kivy-buildozer-installer.sh

works after all, but cannot go further yet (

  1. Thanks to David and some Internet articles: need to install a bit more to avoid of missing autoconf, automake,ctypes errors:
sudo apt install cython3
sudo apt-get install autoconf
sudo apt-get install automake
sudo apt-get install g++
sudo apt-get install libtool m4 automake
sudo apt-get install lld
sudo apt install libssl-dev
sudo apt-get install libffi-devel

the last line cleared _ctypes file missing problem

buildozer appclean
buildozer android debug

after work bin/ folder will have an *.apk file

结束并得到bin,里面放着apk文件;
传入github;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值