android Graphics(三):区域(Range)

安卓判断某个点是否在扇形或者区域内 安卓判断某个点是否在扇形或者区域内,系统自带判断,效率高 立即下载

我自己的一句警言,送给大家:

想要跟别人不一样,你就要跟别人不一样。----- Harvic 


相关文章:

1、《android Graphics(一):概述及基本几何图形绘制》
2、《android Graphics(二):路径及文字》
3、《android Graphics(三):区域(Range)》
4、《android Graphics(四):canvas变换与操作》


一、构造Region

1、基本构造函数

 public Region()  //创建一个空的区域  
 public Region(Region region) //拷贝一个region的范围  
 public Region(Rect r)  //创建一个矩形的区域  
 public Region(int left, int top, int right, int bottom) //创建一个矩形的区域  

上面的四个构造函数,第一个还要配合其它函数使用,暂时不提。
第二个构造函数是通过其它的Region来复制一个同样的Region变量
第三个,第四个才是正规常的,根据一个矩形或矩形的左上角和右下角点构造出一个矩形区域

2、间接构造

 public void setEmpty()  //置空
 public boolean set(Region region)   
 public boolean set(Rect r)   
 public boolean set(int left, int top, int right, int bottom)   
 public boolean setPath(Path path, Region clip)//后面单独讲

这是Region所具有的一系列Set方法,我这里全部列了出来,下面一一对其讲解:
注意:无论调用Set系列函数的Region是不是有区域值,当调用Set系列函数后,原来的区域值就会被替换成Set函数里的区域。
SetEmpty():从某种意义上讲置空也是一个构造函数,即将原来的一个区域变量变成了一个空变量,要再利用其它的Set方法重新构造区域。
set(Region region):利用新的区域值来替换原来的区域
set(Rect r):利用矩形所代表的区域替换原来的区域
set(int left, int top, int right, int bottom):同样,根据矩形的两个点构造出矩形区域来替换原来的区域值
setPath(Path path, Region clip):根据路径的区域与某区域的交集,构造出新区域,这个后面具体讲解

举个小例子,来说明一个Set系列函数的替换概念:

关于重写新建一个类,并派生自view,并且要重写OnDraw函数的问题我就不再讲了,有问题的同学,可以参考下《android Graphics(一):概述及基本几何图形绘制》,当然最后我也会给出相关的源码,直接看源码也行。

下面写了一个函数,先把Set函数注释起来,看看画出来的区域的位置,然后开启Set函数,然后再看画出来的区域
注:里面有个函数drawRegion(Canvas canvas,Region rgn,Paint paint),只知道它可以画出指定的区域就可以了,具体里面是什么意思,后面我们再仔细讲。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class MyRegionView extends View {  
  2.   
  3.     public MyRegionView(Context context) {  
  4.         super(context);  
  5.         // TODO Auto-generated constructor stub  
  6.     }  
  7.       
  8.     @Override  
  9.     protected void onDraw(Canvas canvas) {  
  10.         // TODO Auto-generated method stub  
  11.         super.onDraw(canvas);  
  12.           
  13.         //初始化画笔  
  14.         Paint paint = new Paint();  
  15.         paint.setColor(Color.RED);  
  16.         paint.setStyle(Style.FILL);  
  17.         paint.setStrokeWidth(2);  
  18.           
  19.         Region rgn = new Region(10,10,100,100);  
  20.   
  21. //      rgn.set(100, 100, 200, 200);  
  22.         drawRegion(canvas, rgn, paint);  
  23.           
  24.     }  
  25.       
  26.     //这个函数不懂没关系,下面会细讲  
  27.     private void drawRegion(Canvas canvas,Region rgn,Paint paint)  
  28.     {  
  29.         RegionIterator iter = new RegionIterator(rgn);  
  30.         Rect r = new Rect();  
  31.           
  32.         while (iter.next(r)) {  
  33.           canvas.drawRect(r, paint);  
  34.         }   
  35.     }  
  36.   
  37. }  
看下效果:
                            未开启Set函数时                                                                                           

                       使用Set函数后,替换为新区域

3、使用SetPath()构造不规则区域

boolean setPath (Path path, Region clip)

参数说明:
Path path:用来构造的区域的路径
Region clip:与前面的path所构成的路径取交集,并将两交集设置为最终的区域

由于路径有很多种构造方法,而且可以轻意构造出非矩形的路径,这就摆脱了前面的构造函数只能构造矩形区域的限制。但这里有个问题是要指定另一个区域来取共同的交集,当然如果想显示路径构造的区域,Region clip参数可以传一个比Path范围大的多的区域,取完交集之后,当然是Path参数所对应的区域喽。机智的孩子。

下面,先构造一个椭圆路径,然后在SetPath时,传进去一个比Path小的矩形区域,让它们两个取交集

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class MyRegionView extends View {  
  2.   
  3.     public MyRegionView(Context context) {  
  4.         super(context);  
  5.         // TODO Auto-generated constructor stub  
  6.     }  
  7.       
  8.     @Override  
  9.     protected void onDraw(Canvas canvas) {  
  10.         // TODO Auto-generated method stub  
  11.         super.onDraw(canvas);  
  12.         //初始化Paint  
  13.         Paint paint = new Paint();  
  14.         paint.setColor(Color.RED);  
  15.         paint.setStyle(Style.FILL);  
  16.         paint.setStrokeWidth(2);  
  17.         //构造一个椭圆路径  
  18.         Path ovalPath = new Path();  
  19.         RectF rect =  new RectF(50, 50, 200, 500);    
  20.         ovalPath.addOval(rect, Direction.CCW);  
  21.         //SetPath时,传入一个比椭圆区域小的矩形区域,让其取交集  
  22.         Region rgn = new Region();  
  23.         rgn.setPath(ovalPath,new  Region(50, 50, 200, 200));  
  24.         //画出路径  
  25.         drawRegion(canvas, rgn, paint);  
  26.     }  
  27.       
  28.     //这个函数不懂没关系,下面会细讲  
  29.     private void drawRegion(Canvas canvas,Region rgn,Paint paint)  
  30.     {  
  31.         RegionIterator iter = new RegionIterator(rgn);  
  32.         Rect r = new Rect();  
  33.           
  34.         while (iter.next(r)) {  
  35.           canvas.drawRect(r, paint);  
  36.         }   
  37.     }  
  38. }  
结果如下:

二、矩形集枚举区域——RegionIterator类

对于特定的区域,我们都可以使用多个矩形来表示其大致形状。事实上,如果矩形足够小,一定数量的矩形就能够精确表示区域的形状,也就是说,一定数量的矩形所合成的形状,也可以代表区域的形状。RegionIterator类,实现了获取组成区域的矩形集的功能,其实RegionIterator类非常简单,总共就两个函数,一个构造函数和一个获取下一个矩形的函数;
RegionIterator(Region region) //根据区域构建对应的矩形集
boolean next(Rect r) //获取下一个矩形,结果保存在参数Rect r 中

由于在Canvas中没有直接绘制Region的函数,我们想要绘制一个区域,就只能通过利用RegionIterator构造矩形集来逼近的显示区域。用法如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private void drawRegion(Canvas canvas,Region rgn,Paint paint)  
  2. {  
  3.     RegionIterator iter = new RegionIterator(rgn);  
  4.     Rect r = new Rect();  
  5.       
  6.     while (iter.next(r)) {  
  7.       canvas.drawRect(r, paint);  
  8.     }   
  9. }  
上面我们也都看到了它的用法,首先,根据区域构建一个矩形集,然后利用next(Rect r)来逐个获取所有矩形,绘制出来,最终得到的就是整个区域,如果我们将上面的画笔Style从FILL改为STROKE,重新绘制椭圆路径,会看得更清楚。


三、区域的合并、交叉等操作

无论是区域还是矩形,都会涉及到与另一个区域的一些操作,比如取交集、取并集等,涉及到的函数有:

public final boolean union(Rect r)   
public boolean op(Rect r, Op op) {  
public boolean op(int left, int top, int right, int bottom, Op op)   
public boolean op(Region region, Op op)   
public boolean op(Rect rect, Region region, Op op)   

除了Union(Rect r)是指定合并操作以外,其它四个op()构造函数,都是指定与另一个区域的操作。其中最重要的指定Op的参数,Op的参数有下面四个:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 假设用region1  去组合region2     
  2. public enum Op {    
  3.         DIFFERENCE(0), //最终区域为region1 与 region2不同的区域    
  4.         INTERSECT(1), // 最终区域为region1 与 region2相交的区域    
  5.         UNION(2),      //最终区域为region1 与 region2组合一起的区域    
  6.         XOR(3),        //最终区域为region1 与 region2相交之外的区域    
  7.         REVERSE_DIFFERENCE(4), //最终区域为region2 与 region1不同的区域    
  8.         REPLACE(5); //最终区域为为region2的区域    
  9.  }   
至于这六个参数的具体意义,后面给个具体的图给大家显示出来,先举个取交集的例子。

效果图:


先构造两个相交叉的矩形,并画出它们的轮廓

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //构造两个矩形  
  2. Rect rect1 = new Rect(100,100,400,200);  
  3. Rect rect2 = new Rect(200,0,300,300);  
  4.   
  5. //构造一个画笔,画出矩形轮廓  
  6. Paint paint = new Paint();  
  7. paint.setColor(Color.RED);  
  8. paint.setStyle(Style.STROKE);  
  9. paint.setStrokeWidth(2);  
  10.   
  11. canvas.drawRect(rect1, paint);  
  12. canvas.drawRect(rect2, paint);  
然后利用上面的两年rect,(rect1和rect2)来构造区域,并在rect1的基础上取与rect2的交集

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //构造两个Region  
  2. Region region = new Region(rect1);  
  3. Region region2= new Region(rect2);  
  4.   
  5. //取两个区域的交集        
  6. region.op(region2, Op.INTERSECT);  
最后构造一个填充画笔,将所选区域用绿色填充起来

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint_fill = new Paint();  
  2. paint_fill.setColor(Color.GREEN);  
  3. paint_fill.setStyle(Style.FILL);  
  4. drawRegion(canvas, region, paint_fill);  
全部代码为:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * created by harvic 
  3.  * 2014/9/4 
  4.  */  
  5. import android.content.Context;  
  6. import android.graphics.Canvas;  
  7. import android.graphics.Color;  
  8. import android.graphics.Paint;  
  9. import android.graphics.Rect;  
  10. import android.graphics.Region;  
  11. import android.graphics.Paint.Style;  
  12. import android.graphics.Region.Op;  
  13. import android.graphics.RegionIterator;  
  14. import android.view.View;  
  15.   
  16. public class MyRegionView extends View {  
  17.   
  18.     public MyRegionView(Context context) {  
  19.         super(context);  
  20.         // TODO Auto-generated constructor stub  
  21.     }  
  22.       
  23.     @Override  
  24.     protected void onDraw(Canvas canvas) {  
  25.         // TODO Auto-generated method stub  
  26.         super.onDraw(canvas);  
  27.           
  28.         //构造两个矩形  
  29.         Rect rect1 = new Rect(100,100,400,200);  
  30.         Rect rect2 = new Rect(200,0,300,300);  
  31.           
  32.         //构造一个画笔,画出矩形轮廓  
  33.         Paint paint = new Paint();  
  34.         paint.setColor(Color.RED);  
  35.         paint.setStyle(Style.STROKE);  
  36.         paint.setStrokeWidth(2);  
  37.           
  38.         canvas.drawRect(rect1, paint);  
  39.         canvas.drawRect(rect2, paint);  
  40.           
  41.           
  42.           
  43.         //构造两个Region  
  44.         Region region = new Region(rect1);  
  45.         Region region2= new Region(rect2);  
  46.   
  47.         //取两个区域的交集        
  48.         region.op(region2, Op.INTERSECT);  
  49.           
  50.         //再构造一个画笔,填充Region操作结果  
  51.         Paint paint_fill = new Paint();  
  52.         paint_fill.setColor(Color.GREEN);  
  53.         paint_fill.setStyle(Style.FILL);  
  54.         drawRegion(canvas, region, paint_fill);  
  55.   
  56.     }  
  57.       
  58.   
  59. private void drawRegion(Canvas canvas,Region rgn,Paint paint)  
  60. {  
  61.     RegionIterator iter = new RegionIterator(rgn);  
  62.     Rect r = new Rect();  
  63.       
  64.     while (iter.next(r)) {  
  65.       canvas.drawRect(r, paint);  
  66.     }   
  67. }  
  68. }  
其它参数的操作与这个类似,其实只需要改动region.op(region2, Op.INTERSECT);的Op参数值即可,下面就不再一一列举,给出操作后的对比图。


四、其它一些方法

Region类除了上面的一些重要的方法以外,还有一些比较容易理解的方法,我就不再一一列举用法了,下面一并列出给大家

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /**几个判断方法*/    
  2. public native boolean isEmpty();//判断该区域是否为空    
  3. public native boolean isRect(); //是否是一个矩阵    
  4. public native boolean isComplex();//是否是多个矩阵组合    
  5.     
  6.     
  7. /**一系列的getBound方法,返回一个Region的边界*/    
  8. public Rect getBounds()     
  9. public boolean getBounds(Rect r)     
  10. public Path getBoundaryPath()     
  11. public boolean getBoundaryPath(Path path)     
  12.     
  13.     
  14. /**一系列的判断是否包含某点 和是否相交*/    
  15. public native boolean contains(int x, int y);//是否包含某点    
  16. public boolean quickContains(Rect r)   //是否包含某矩阵    
  17. public native boolean quickContains(int left, int top, int right,    
  18.                                         int bottom) //是否没有包含某矩阵    
  19.  public boolean quickReject(Rect r) //是否没和该矩阵相交    
  20.  public native boolean quickReject(int left, int top, int right, int bottom); //是否没和该矩阵相交    
  21.  public native boolean quickReject(Region rgn);  //是否没和该矩阵相交    
  22.     
  23. /**几个平移变换的方法*/    
  24. public void translate(int dx, int dy)     
  25. public native void translate(int dx, int dy, Region dst);    
  26. public void scale(float scale) //hide    
  27. public native void scale(float scale, Region dst);//hide    

参考文章:《Android 2D Graphics学习(二)、Canvas篇2、Canvas裁剪和Region、RegionIterator》


本篇所涉及到的代码,我集合在了一个工程中,大家可以下载

源码下载地址:http://download.youkuaiyun.com/detail/harvic880925/7866523

请大家尊重原创者版权,转载请标时出处:http://blog.youkuaiyun.com/harvic880925/article/details/39056701   谢谢!

DDR内存Prefetch机制详解:从DDR1到DDR4的技术演进与性能提升 本文深入解析DDR内存Prefetch机制从DDR1到DDR4的技术演进与性能提升。通过分析Prefetch技术的实现原理与Burst传输的协同机制,揭示DDR1的2n Prefetch、DDR2的4n Prefetch、DDR3的8n Prefetch以及DDR4的Bank Group架构创新如何显著提升内存带宽与系统性能。 阅读详情

相关推荐

私有云平台搭建——OpenStack

私有云平台搭建——OpenStack 搭建过程

2301_79739036的博客 3089

自定义控件之绘图篇(三):区域(Range)

前言:最近几天对画图的研究有些缓慢,项目开始写代码了,只能在晚上空闲的时候捯饬一下自己的东西,今天给大家讲讲区域的相关知识,已经想好后面两篇的内容了,这几天有时间赶紧写出来给大家。有关界面开发的东东内容确实比较多,慢慢来吧,总有一天会不一样。 我自己的一句警言,送给大家: 想要跟别人不一样,你就要跟别人不一样。----- Harvic 相关文章: 《Android自定义控件三...

启舰 2万+

TSMC 40nm LP工艺下,新手画版图最容易踩的5个坑(附Cadence避坑指南)

本文详细解析了TSMC 40nm LP工艺下版图设计的5个常见陷阱,包括MOS管尺寸转换、DNW隔离、Guard Ring设计、金属布线和DRC检查。通过Cadence实战技巧和设计规则优化,帮助新手工程师规避典型错误,提升版图设计效率和质量。

weixin_26800111的博客 456

Android 在Canvas指定区域绘制出图片

思路: 获取一个canvas对象 获取一个paint对象,并设置好相关属性 获取Bitmap 设置绘制区域 设置裁剪区域 核心代码: 注意事项: 上图中img表示的是要绘制的图片宽高(如设置的是图片实际宽度那个就是绘制整张图片,反之设置的小于图片宽高则绘制的是实际图片的一部分。按坐标系计算) 上图中reg表示的是指定绘制的区域(指定后图片不会绘制到这个区域以外的地方) 也有个小例子需要的自取:Demo 就些这么多,day day up! .........

wulileaf的博客 4321

Linux 系统管理员必备:Chrome 企业版离线部署全攻略(含 RPM/DEB 双版本)

本文详细介绍了Linux系统管理员如何离线部署Chrome企业版,涵盖RPM和DEB双版本。从获取官方离线安装包到静默安装、权限配置及企业级策略管理,提供全流程解决方案,特别适合Ubuntu等DEB系系统管理员参考。

f9g0h的博客 483

E-Prime脑电实验打mark全攻略:从端口查询到串并口实战(附常见问题解决)

本文提供了E-Prime脑电实验打mark的完整实战指南,涵盖端口查询、并口与串口配置的核心步骤及常见问题解决。文章深入解析了mark原理,并详细介绍了通过E-Basic脚本和Serial设备对象发送信号的两种方法,帮助研究者实现E-Prime与脑电设备的高精度时间同步,确保实验数据可靠性。

4k5l6j7h8的博客 354

Android Canvas沿着Path绘制文字

Canvas.drawTextOnPath()package shortcut.song.com.myapplication;import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android

若有博文对您有帮助,欢迎点赞关注,收藏评论。 4532

微信聊天记录解密,必须是root的手机

手机上安装x-plore软件,将/data/data目录的相关EnMicroMsg.db auth_info_key_prefs.xml拷贝到/storge/emulated/0目录后才可以拷贝到电脑。路径:/data/data/com.tencent.mm/shared_prefs/auth_info_key_prefs.xml。但是是加密过的 需要解密 解密密码生成规则是手机IMEI+微信UIN md5(32位小写)后的前7位。安卓手机要root测试版本微信要7或者8版本都可以。选择message表。

venus60的专栏 1004

C# 使用GDI+绘制不规则图形

C# 使用GDI+ 完成不规则绘图

lainiao110的专栏 986

【项目实战】Java9的新特性模块化(Module)

Java 9引入了模块化系统,称为"Java Platform Module System"(JPMS)这个系统允许将Java程序分成模块,每个模块都有自己的规范,可以明确地声明它依赖于哪些其他模块,并且只能访问被其所依赖的模块暴露的API。这样可以模块化就是增加了更高级别的聚合,是Package的封装体。Package是一些类路径名字的约定,而模块是一个或多个Package组成的封装体。

奶爸的编程之路,也就一周冷个三天 1896

深度探索:VS2022 牵手 DeepSeek,开启开发新征程

通过将 VS2022 与 DeepSeek 接入,我们成功开启了智能开发的新征程。从前期的硬件准备、软件环境搭建,到实战环节的一步步配置与整合,再到全面的测试与验证,每一个步骤都充满挑战,但也带来了满满的收获。在实际应用中,VS2022 与 DeepSeek 的结合展现出了强大的优势,为开发者带来了更高的效率和更出色的代码质量。无论是代码自动补全、智能代码审查,还是自然语言交互编程,都让开发过程变得更加智能、便捷,极大地提升了开发体验。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 3522

(4.1.36.5)android Graphics(三):区域(Range)

前言:最近几天对画图的研究有些缓慢,项目开始写代码了,只能在晚上空闲的时候捯饬一下自己的东西,今天给大家讲讲区域的相关知识,已经想好后面两篇的内容了,这几天有时间赶紧写出来给大家。有关界面开发的东东内容确实比较多,慢慢来吧,总有一天会不一样。 我自己的一句警言,送给大家: 想要跟别人不一样,你就要跟别人不一样。----- Harvic  相关文章:

JAVA成长之路 489

ADT MCP Server 把 ABAP 开发带进 Agent 模式

今天讨论 ADT MCP Server,不能只把它看成 Visual Studio Code 里多出来的一个开关。它真正有意思的地方,在于它把 ABAP 后端开发里那些原本需要手工点击、手工创建、手工激活、手工查服务元数据的动作,包装成一组可以被 GitHub Copilot 这类 coding agent 调用的工具。SAP 在 RAP130 练习里把它描述为运行在 ADT for Visual Studio Code 扩展内部的本地 HTTP Server,它实现的是 Model Context Pro

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 183

android Graphics:区域(Range)

目录(?)[+] 一构造Region 基本构造函数间接构造使用SetPath构造不规则区域 二矩形集枚举区域RegionIterator类三区域的合并交叉等操作四其它一些方法 前言:最近几天对画图的研究有些缓慢,项目开始写代码了,只能在晚上空闲的时候捯饬一下自己的东西,今天给大家讲讲区域的相关知识,已经想好后面两篇的内容了,这几天有时间赶紧写出来给大家。有关界面开发的东

u013598111的专栏 365

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

android Graphics

志不强者智不达,言不信者行不果。——墨翟 (凡诸事有成者都有惊人的毅力做后盾,相信自己,持之以恒,不久的将来,我们都将有一番成就,勤不富也饱,懒不死也饿) 相关文章: 1、《android Graphics(一):概述及基本几何图形绘制》 2、《android Graphics(二):路径及文字》 3、《android Graphics(三):区域(Range)》 4、

langjian2012的专栏 840

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

《android Graphics(一):概述及基本几何图形绘制》

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34347651的博客 102

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Unity Shader性能瓶颈深度分析:Nsight Graphics实战与RTX显卡优化指南

在实时渲染和游戏开发中,Shader性能优化是提升帧率与用户体验的核心环节。其原理在于,Shader作为在GPU上执行的小程序,直接决定了顶点与像素的处理效率,不当的实现会导致算术逻辑单元(ALU)过载或纹理带宽瓶颈。这项技术的价值在于,通过精准定位瓶颈,开发者可以显著降低GPU负载,提升渲染效率与能效比。应用场景广泛覆盖了手游、PC/主机游戏以及XR应用的性能调优。本文聚焦于利用NVIDIA Nsight Graphics这一专业GPU分析工具,结合RTX显卡的硬件特性,深入指令级剖析Unity项目中的S

weixin_30363509的博客 342
上一篇: 深入Log4J源码之Log4J Core
下一篇: android studio导入 so ,jar 文件。
method_chen
博客等级 码龄15年 25粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值