Eclipse 插件开发

Eclipse 是一个很让人着迷的开发环境,它提供的核心框架和可扩展的插件机制给广大的程序员提供了无限的想象和创造空间。目前网上流传相当丰富且全面的开发工具方面的插件,但是 Eclipse 已经超越了开发环境的概念,可以想象 Eclipse 将成为未来的集成的桌面环境。目前的 Eclipse 本身就具备资源管理和外部程序的功能,加上无所不能的插件,将构成一个丰富多彩的工作环境而不仅仅是一个 IDE。


1.Eclipse 简介和插件开发

Eclipse 是一个很让人着迷的开发环境,它提供的核心框架和可扩展的插件机制给广大的程序员提供了无限的想象和创造空间。目前网上流传相当丰富且全面的开发工具方面的插件,但是 Eclipse 已经超越了开发环境的概念,可以想象 Eclipse 将成为未来的集成的桌面环境。目前的 Eclipse 本身就具备资源管理和外部程序的功能,加上无所不能的插件,将构成一个丰富多彩的工作环境而不仅仅是一个 IDE。对于程序员来说,没有什么比可以随心所欲的定制的工作环境更重要,你的决心,勇气和创造力在与别人分享成果的过程中一览无余。好了,你是不是心动了,如果你已经对 Eclipse 有一定的认识,那么,和我一起打造自己的个性化工作环境吧,首先我们一起开发一个天气预报的插件,然后我们打造属于自己的邮件快速监控功能。

以下的工作基于一定的前提,那就是你是一名 Java 程序员,你很欣赏并正开始使用 Eclipse 这个超酷的工作环境,别忘了下载最新版的 Eclipse3.0,本文基于 Eclipse3.0 开发。

2. 天气预报插件

如果你已经厌倦了总是要登录某些网站从相关网页上获取信息,这里有一个让你通过 Eclipse 快速获取信息的新方法。让我们从头开始,做一个属于自己的天气预报插件吧,你的 Eclipse 将具有天气预报功能,是不是很酷呢?

在这一部分,我们将要实现一个 Eclipse 插件,他可以在任何我们想知道的时候通过简单的点击鼠标告诉我们本地区的天气预报,这当然很刺激。对于一个程序员而言,事情就应该如此。让我们开始吧,我们首先要定义一个插件,把他加到菜单和工具栏中。对于没有插件开发经验的你,可以参考《 开发 Eclipse 插件》,树立基本的插件开发意识,当然,本文将详细的辅助你完成这一创造性的工作。

2.1 最基础的插件

你可以完全参考《 开发 Eclipse 插件》的插件示例,制作你的第一个 hello Eclipse 插件,幸运的是,Eclipse3.0 完全考虑到你的需求,通过菜单 File -> New-> Other ->Plug-in Project,输入项目名称,next 出现对话框,只要在插件名处输入"muplugin",next 以后选择 "Hello,World"的插件模板你可以直接新建一个名为 myplugin 的最简单的插件,但其实我们的天气预报并不比它复杂多少,建完改插件以后的效果如下图。

现在,将项目作为运行时工作台运行(run - run as runtime workbench),在一个全新的 Eclipse 窗口中,通过点击菜单 sample menu 的 sample Action 或者工具栏中的圆形 Eclipse 图标,你将看到如下效果的对话框。

到此为止,天气预报插件的原始版做成了,通过修改 plugin.xml,我们将菜单改成中文形式,需要修改的地方就 2 处,详见表格。

               <actionSet 
            label="Sample Action Set"
            visible="true"
            id="myplugin.actionSet"> 
         <menu 
               label="我的空间"
               id="sampleMenu"> 
            <separator 
                  name="sampleGroup"> 
            </separator> 
         </menu> 
         <action 
               label="天气预报"
               icon="icons/sample.gif"
               class="myplugin.actions.SampleAction"
               tooltip="Hello, Eclipse world"
               menubarPath="sampleMenu/sampleGroup"
               toolbarPath="sampleGroup"
               id="myplugin.actions.SampleAction"> 
         </action>

此时在运行时工作台,我们的菜单已经改变。

2.2 用 VisualEditer 制作天气预报对话框

虽然菜单是天气预报,但是我们需要的不是 hello Eclispe 对话框,我们需要的是告诉我们天气的对话框,当然需要我们从头开始,于是我们需要重新构建一个对话框,这个就需要 Visual Editor 来帮助进行界面的开发(关于 Visual Editer 参考《 Build GUIs with the Eclipse Visual Editor project》) 。我们将使用 Visual Editor 实现一个 Swing 对话框,当然只用 VE 做一个对话框是有点大材小用,但是作为起点,已经合适了。

首先参考《 Build GUIs with the Eclipse Visual Editor project》构建 Visual Editer 开发环境,当一切准备齐全,鼠标右键点击 PackgeExpoler 中的 "muplugin.actions"java 文件,从弹出式菜单中选择 new->other->VisualClass,新建一个可视化的类,弹出界面如下图:

选择 next,然后在 name 中输入 WeatherDialog,这个就是我们用来显示天气预报的 dialog

选择该对话框的超类为 javax.swing.JDiaog, 点击 Finish 按钮。 等待一段时间后,我们的对话框就基本生成了,鼠标点击左上角图标,直接输入天气预报就是对话框的标题,同时 我们可以看到左侧的 VisualEditor 面板。

然后我们将该对话框于与刚才的天气预报菜单连接 找到 SampleAction 的 run 函数,如下所示:

		 public void run(IAction action) { 
		 MessageDialog.openInformation( 
			 window.getShell(), 
			"Myplugin Plug-in", 
			"Hello, Eclipse world"); 
	 }

替换成如下代码

    	 public void run(IAction action) 
    { 
        WeatherDialog wd=new WeatherDialog(); 
        wd.setSize(400, 335); 
        wd.show();        
    }

此时,点击菜单运行,我们的对话框看起来象这个样子,在此基础上我们还要在上面增加天气预报信息

2.3 增加天气预报功能

下面的部分是重点,我们将使用具有解析 Html 功能的 Swing 组件 JEditPane,来获取网络上的现成的天气预报信息,根据上图,从 VisualEditor 的面板中 Swing Components 组点击 JEditPane,加入到对话框中。并修改对话框代码使得最终的代码如下:

 /* 
 * Created on 2004-9-23 
 * */ 
 package myplugin; 
 import java.io.BufferedReader; 
 import java.io.InputStreamReader; 
 import java.net.URL; 
 import javax.swing.JDialog; 
 import javax.swing.JEditorPane; 
 /** 
 * <p>Title: WatherDialog</p> 
 * <p>Description: 这个是对话框类,用于显示指定城市的当天的天气预报 </p>
 * <p>Copyright: Copyright (c) 2004</p> 
 * <p>Company:UF SOFT</p> 
 * @author 赵勇
 * @version 1.0 
 */ 
 public class WatherDialog extends JDialog 
 { 
    String city="北京"; 
    
    private JEditorPane jEditorPane = null; 
    /** 
     * This method initializes 
     * / 
    public WatherDialog(String city) 
    { 
        super(); 
        this.city=city; 
        initialize(); 
    } 
    /** 
     * This method initializes this 
     * @return void 
     */ 
    private void initialize() 
    { 
        this.setContentPane(getJEditorPane()); 
        try 
        { 
            // 构建 URL 对象
            URL url =
new URL("http://weather.news.sina.com.cn//cgi-bin/figureWeather/simpleSearch.cgi?city="
+city); 
            String temp=""; 
            BufferedReader in 
                 = new BufferedReader(new InputStreamReader(url.openStream()));
            // 使用 openStream 得到一输入流并由此构造一个 BufferedReader 对象
            String inputLine; 
            // 从输入流不断的读数据,直到读完为止
            while ((inputLine = in.readLine()) != null) 
                temp=temp+inputLine+"\n"; 
            // 关闭输入流
            in.close();  
            String  weather     
                 =temp.substring ( temp.indexOf( "<body"), 
                                  temp.lastIndexOf( "body>")+5); 
            
            this.jEditorPane .setText(weather); 
        } 
        catch (Exception e) 
        { 
            e.printStackTrace(); 
        } 
        this.setTitle("天气预报"); 
        this.setSize(400, 166); 
        
    } 
    /** 
     * This method initializes jEditorPane     
     *     
     * @return javax.swing.JEditorPane     
     */    
    private JEditorPane getJEditorPane() 
    { 
        if (jEditorPane == null) 
        { 
            jEditorPane = new JEditorPane(); 
            jEditorPane.setContentType( "text/html"); 
        } 
        return jEditorPane; 
    } 
 }  //  @jve:decl-index=0:visual-constraint="70,19"

以上代码中最关键的部分就是对话框中的 JEditorPane 对象,在初始化时,从一个 URL 获取天气预报信息,表现为 Html 标记片段,不用解析,直接调用 JEditorPane 的 setText 方法,就可以将 Html 格式的信息直接按解析过的方式显示,也就是天气预报信息了,

此时 Action 中的调用需要做修改

    	 public void run(IAction action) 
    { 
        WeatherDialog wd=new WeatherDialog("北京"); 
        wd.setSize(400, 335); 
        wd.show();        
    }

现在以运行时工作台的方式运行,点击天气预报菜单,可以看到下图:

如果你在上海或者其他城市,试着修改 city 参数为"上海",再次运行,你将发现,你仍然能够得到该城市的天气预报(这里我们从网站上提取的信息,有点投机取巧了)。值得注意的是,Xmethod 网站提供了一个天气预报的 WebService,可惜只有美国的城市,不然我们可以使用 Web Service 调用获取天气预报,将会更酷。

现在运行是工作台已经具备了天气预报的功能,还需要更进一步,将改插件导出发布,拷贝到 Eclipse 根目录的 plugins 目录中,重新启动(具体参见 Eclipse 帮助)。现在你自己的 Eclipse,就具备了天气预报的功能,只要你点击鼠标,就可以在编程之余轻松的获取天气信息。 除非你的老板认为你在工作时间随时了解天气情况不是一个好主意,我认为你完全可以将这个插件纳入个人收藏的插件之列。你也可以在此基础上扩展,增加一些配置文件和属性设置,定制出满足自己要求的插件。如果能够增加信息的自动过滤和筛选,那将是一次很愉快的体验,如果你有时间和兴趣,不妨一试。

3. 邮件快速监控插件

现在你的工作因为 Eclipse 而更加惬意,更具创造力,那么你还有什么不满?你是否厌倦了各种邮件客户端随时随地的骚扰你呢?你希望你在高兴的时候适时的了解一下邮件的概况?好了,既然想到了为什么犹豫呢,因为你是程序员,你就是要用 Eclipse 享受完全 DIY 的乐趣。

3.1 生成插件

本部分我们将在以上 myplugin 插件的基础上增加一个邮件过滤显示的对话框,类似的我们通过 VisualEditer 创建一个名为 MailDialog 的对话框,并增加一个 JEditPane 用来显示邮箱中我们关注的信息。

修改 plugin.xml,增加一个"我的邮件"菜单

             <action 
               label="邮件信息"
               icon="icons/sample.gif"
               class="myplugin.actions.MailAction"
               tooltip="邮件信息"
               menubarPath="sampleMenu/sampleGroup"
               toolbarPath="sampleGroup"
               id="myplugin.actions.MailAction"> 
         </action>

现在,你知道要创建一个 MailAction 的 Action 类,并在 在 Run 中增加如下代码

              MailConfig mail=new MailConfig(); 
        
        String popServer="server"; 
        String popUser="zhaoyong"; 
        String popPassword="1234"; 
        
        // 设置需要过滤的关键字:发件人和邮件主题
        String [] strFrom=new String[] {"zhaoyong"}; 
        String [] strSubject=new String[] {"测试"}; 
        
        MailConfig[] mc =new MailConfig [] { mail }; 
        MailDialog md=new MailDialog(mc); 
        System.err.println("run run run ") ; 
        md.setSize(400, 335); 
         md.show();

以上的代码编译不会通过,但是别着急,慢慢来,很快了。

3.2 构建邮件监控对话框

当然你需要建立一个 MailConfig 类用来表示一个邮箱的具体设置已及相关信息,这里就不在累述说明,详情参见参考资料中的代码。需要说明的式 MailConfig 除了要记录一个邮箱的地址,用户名和密码外,还提供 2 个关键字数组,如果为空,不加过滤,如果关键字有值,系统会根据发件人和邮件标题中是否包含关键字来进行显示邮件信息,已保证你的绝对自由。

首先我们需要实现一个 MailConfig 类,表示邮件配置,每个 MailConfig 的对象代表一个邮件帐户,我们的系统将能显示多个邮箱的配置,每个 MailConfig 中使用一个数组来保存需要过滤的收件人和邮件地址。

MailConfig 类的中的变量如下:

        String popServer; 
    String popUser; 
    String popPassword; 
    
 // 设 置 需 要 过 滤 的 关 键 字 : 发 件 人 和 邮 件 主 题
    String [] strFrom; 
    String [] strSubject;    
    
    // 是 否 显 示 邮 件 内 容 
 boolean isViewContent=false;

同样,我们将使用一个对话框来显示邮件信息,MailDialog 需要引用 javaMail.jar, 和 activation.jar 这两个类包,确保已经有这两个类包并加入到项目的类路径中。最后的 MailDialog 代码如下:

 package myplugin; 
 import java.io.IOException; 
 import java.util.Properties; 
 import javax.mail.Folder; 
 import javax.mail.Message; 
 import javax.mail.MessagingException; 
 import javax.mail.Session; 
 import javax.mail.Store; 
 import javax.mail.internet.InternetAddress; 
 import javax.swing.JDialog; 
 import javax.swing.JEditorPane; 
 import javax.swing.JTextPane; 
 /** 
 * @author zhaoyong 
 * 
 * TODO To change the template for this generated type comment go to 
 * Window - Preferences - Java - Code Style - Code Templates 
 */ 
 public class MailDialog extends JDialog 
 { 
    
    private JEditorPane jEditorPane = null; 
    private JTextPane jTextPane = null; 
    
    // 可以显示多个邮件配置
    MailConfig[]  mc= null; 
   
    
    /** 
     * This method initializes 
     * 构造函数
     * @param mc : 需要显示的多个邮箱配置对象。
     */ 
    public MailDialog(MailConfig[] mc) 
    { 
    	
        super(); 
      
        if(mc!=null) 
            this.mc = mc; 
        else 
            System.err.println("邮件配置错误!") ; 
        
        initialize(); 
    } 
    /** 
     * This method initializes this 
     * 初始化
     * @return void 
     */ 
    private void initialize() 
    { 
        try 
        { 
            // 设定显示内容的面板
            this.setContentPane(getJTextPane()); 
            // 取得所有的新邮件信息
            String s= getAllMailInfo();            
            // 将信息显示在对话框中
            this.jTextPane .setText(s); 
           
            this.setTitle("邮件信息"); 
            this.setSize(251, 100); 
        } 
        catch (Exception e) 
        {           
        	 // 发生错误显示错误信息
        	 this.jTextPane .setText(e.toString()); 
            e.printStackTrace(); 
        } 
        
    } 
    /** 取得所有的邮箱的需要监控的邮件信息
	 * 
	 * @return String 
	 */ 
    private String getAllMailInfo() 
    { 
    	 String allMailInfo=""; 
    	
    	 if (mc.length <1) 
    		 allMailInfo="没有配置邮箱!"; 
    	 else 
    	 { 
    		 for(int i=0;i<mc.length;i++) 
    		 { 
    			 // 循环获取每个邮箱的邮件信息
    			 allMailInfo=allMailInfo+getMailInfo(mc[i]); 
    		 } 
    	 } 
    	 // 还没有收到相关的邮件
    	 if (allMailInfo.trim().length() ==0) 
    		 allMailInfo="未检测到相关新邮件!"; 
    	 return allMailInfo; 
    	
    } 
    /* 
     * 得到一个邮箱中满足条件的所有新邮件的字符串形式
     **/ 
    private String getMailInfo(MailConfig mc) 
    { 
    	 // 最终输出的邮件信息
        String mailInfo=""; 
    	
        // 每个邮箱服务器上的 Store 和 Folder 对象
    	 Store store=null; 
        Folder folder=null; 
        
        try 
        { 
           
            Properties props = System.getProperties(); 
            // 与邮件服务器生成一个 Session 
            Session session = Session.getDefaultInstance( props,null); 
     
           // 给出服务器,用户名,密码连接服务器
            store = session.getStore("pop3"); 
            store.connect(mc.getPopServer(), mc.getPopUser(),mc.getPopPassword()); 
            
            // 取得默认的邮件 Folder 
            folder = store.getDefaultFolder(); 
            if (folder == null) 
                throw new Exception("No default folder"); 
            
            // 取得收件箱
            folder = folder.getFolder("INBOX"); 
            if (folder == null) 
                throw new Exception("No POP3 INBOX"); 
            
            // 以只读方式打开收件箱
            folder.open(Folder.READ_ONLY); 
            
            // 获取所有新邮件并处理
            Message[] msgs = folder.getMessages(); 
           
            for (int i = 0; i < msgs.length; i++) 
            { 
                Message message= msgs[i]; 
                // 取得每封邮件的信息,需要引用 MailConfig 对象进行关键字过滤
                mailInfo = mailInfo+ getMessageInfo( message,mc);                
            } 
            
        } 
        catch (Exception ex) 
        { 
            ex.printStackTrace(); 
        } 
        finally 
        { 
            // 安全的关闭邮件服务器资源
            try 
            { 
                if (folder!=null) folder.close(true); 
                if (store!=null) store.close(); 
            } 
            catch (Exception ex2) {ex2.printStackTrace();} 
        } 
        return mailInfo; 
    } 
    
    /** 
     * 得到一封邮件的信息,需要根据 MailConfig 过滤
	 * @param mailInfo 
	 * @param message 
	 * @return 邮件信息
	 * @throws MessagingException 
	 * @throws IOException 
	 */ 
	 private String getMessageInfo( final Message message ,final MailConfig mc) 
		 throws MessagingException, IOException 
	 { 
		 // 返回的改邮件信息
		 String mailInfo=""; 
		
		 String from=((InternetAddress)message.getFrom()[0]).getPersonal(); 
            
		 if (from==null) 
		    from=((InternetAddress)message.getFrom()[0]).getAddress(); 
		
		 String subject=message.getSubject(); 
		
		 // 如果满足过滤信息则显示,否则返回空
        if(isElementinString(from,mc.getStrFrom()) 
        		 ||isElementinString(subject,mc.getStrSubject()) )   
        {        	
        	 mailInfo=mailInfo+"发件人 : "+from+"\n";                
        	 mailInfo=mailInfo+"邮件主题 : "+subject+"\n"; 
        	 mailInfo=mailInfo+"发送时间 : "+message.getSentDate() +"\n"; 
        	
        	 // 如果显示内容,则打印内容
        	 if(mc.isViewContent) 
        		 mailInfo=mailInfo+message.getContent() +"\n"; 
        	
        	 mailInfo=mailInfo+"------------------------------------\n"; 
        } 
		 return mailInfo; 
	 } 
	
	 private JTextPane getJTextPane() 
    { 
        if (jTextPane == null) 
        { 
            jTextPane = new JTextPane(); 
        } 
        
        return jTextPane; 
    } 
   
	 /** 
     * 判断目标关键字数组中是否有指定的字符串 , 进行过滤
	 * @param targetStr :
	 * @param keys :
	 * @return 	如果有,返回 true, 否则返回 false 
	 */ 
 
    private boolean isElementinString(String targetStr,String [] keys) 
    { 
    	 // 没指定过滤条件,显示所有
    	 if (keys==null) 
    		 return true; 
    	 // 指定字符串为空,直接返回 false 
    	 if (targetStr==null) 
    		 return false; 
    	 for(int i=0;i<keys.length ;i++) 
    	 { 
    		 if (targetStr.indexOf(keys[i])>-1) 
    			 return true; 
    	 } 
    	 return false; 
    } 
 
 }  
 //  @jve:decl-index=0:visual-constraint="10,10"--说明,这是 Visual Editor 添加的控制信息

以上代码的注释已经保证你能够看清楚,这里就不加累述,有兴趣的可以自己试试,体验一切尽在掌握的快感。当然这个例子做的实在简单,因此也为你的进一步开发留有足够的余地。

3.3 打包和发布

到此,在 mypulgin 中增加了邮件信息菜单和对话框,系统的 plugin.xml 如下:

 <?xml version="1.0" encoding="UTF-8"?> 
 <?eclipse version="3.0"?> 
 <plugin 
   id="myplugin"
   name="Myplugin Plug-in"
   version="1.0.0"
   provider-name=""
   class="myplugin.MypluginPlugin"> 
   <runtime> 
      <library name="myplugin.jar"> 
         <export name="*"/> 
      </library> 
      <library name="lib/javaMail.jar"> 
         <export name="*"/> 
      </library> 
      <library name="lib/activation.jar"> 
         <export name="*"/> 
      </library> 
   </runtime> 
   <requires> 
      <import plugin="org.eclipse.ui"/> 
      <import plugin="org.eclipse.core.runtime"/> 
   </requires> 
   <extension 
         point="org.eclipse.ui.actionSets"> 
      <actionSet 
            label="Sample Action Set"
            visible="true"
            id="myplugin.actionSet"> 
         <menu 
               label="我的空间"
               id="sampleMenu"> 
            <separator 
                  name="sampleGroup"> 
            </separator> 
         </menu> 
         <action 
               label="天气预报"
               icon="icons/sample.gif"
               class="myplugin.actions.SampleAction"
               tooltip="Hello, Eclipse world"
               menubarPath="sampleMenu/sampleGroup"
               toolbarPath="sampleGroup"
               id="myplugin.actions.SampleAction"> 
         </action> 
          <action 
               label="邮件信息"
               icon="icons/sample.gif"
               class="myplugin.actions.MailAction"
               tooltip="邮件信息"
               menubarPath="sampleMenu/sampleGroup"
               toolbarPath="sampleGroup"
               id="myplugin.actions.MailAction"> 
         </action> 
      </actionSet> 
   </extension> 
 </plugin>

实际上,我们在一个插件中加入了 2 个功能,因此就实现了我们的开发环境的自我扩展和定制。 同样,参考 Eclipse 的帮助,你可以轻松的再次将插件打包导出,并将其加入自己的 Eclipse 的 plugins 目录(可能需要解压缩),或通过 help 菜单的 Update 选项进行安装,注意导出时需要选定相关的类包。重新启动,你将发现自己的 IDE 已经多了自己的菜单,开发环境已经随着自己的意愿在改变了,程序员天生的满足感油然而生。

现在,你可以在需要的时候点击菜单,了解你希望监控的邮件情况或者最近的天气情况,一切轻松的尽在掌握,Eclipse 的插件,就是这样全能。

4. 总结

那么,聪明的你有没有发现什么问题,对,上面的东西太粗糙,太简单了,你可以做进一步的优化设计和功能加强,比如,自己增加邮件配置文件而不是写在代码里面,动态监控弹出邮件预警(通过事先设定的紧急状态),你也许还会想起来很多的新的主意,比如我为什么不能有个能看电影的插件?或是 Eclipse 中飘出动听的音乐?别急,这些不一定需要你亲手去做, http://sourceforge.net/projects/rocexwang/有一个播放器插件,但是现在仅能播放音乐,国外已经有人用 Eclipse 开发游戏,也有人用 Eclipse 来做 MIS 系统的。http://www.matrix.org.cn/forum_view.asp?forum_id=25&view_id=10510 有一个国人开发的俄罗斯方块游戏,虽然简单了一点。当然,通过网址http://eclipse-plugins.2y.net/eclipse/index.jsp和 http://www.eclipseplugincentral.com/你可以找到很多的插件,如果你知道什么更好的插件,请告诉我。

Eclipse 提供了一个纯的框架和插件结构,使得开发任何功能的插件都能成为现实。本文介绍了 2 个有趣的 Eclipse 插件的开发,可以使我们的工作环境增加了两个可爱的小功能,同时也使得你具备了基础的插件开发能力,借助 Eclipse 的强大功能,从此你可以把你的任何想法变为现实。同时请保持与别人的交流,我会很乐意了解你的新奇的插件,并收藏和学习任何好的插件,打造一个完全属于自己的个性化的开发环境。Eclipse 将在不久的将来成为一个全能的 Platform,这一点在全世界数以万计的开发人员的手中,正一点一点变为现实。

在这里下载代码: myplugin.rar,这里打包的是整个 myplugin 项目,解压缩后导入 Eclipse,你可能需要重新配置类路径等工作。

参考资料


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值