属性动画的使用和原理解析

Android 属性动画原理解析 目录 属性动画简介 什么是属性动画 属性动画的基本模型 android 属性动画使用示例 ValueAnimator ObjectAnimator ViewPropertyAnimator 属性动画的执行流程 动画循环 代码实现 帧刷新机制 Android Display系统的工作方式 早期帧刷新机制 优化:Project Butter 代码实现 根据动画已播放时长计算属性值 线性动画示例 非线性动画示例 时间插值器 类型估值器 代码实现 设置属性值 代码实现 阅读详情


前言

属性动画,顾名思义它是对于对象属性的动画。因此,所有补间动画的内容,都可以通过属性动画实现。


一、属性动画入门

首先我们来看看如何用属性动画实现上面补间动画的效果

    private void RotateAnimation() {
        ObjectAnimator anim = ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
        anim.setDuration(1000);
        anim.start();
    }

    private void AlpahAnimation() {
        ObjectAnimator anim = ObjectAnimator.ofFloat(myView, "alpha", 1.0f, 0.8f, 0.6f, 0.4f, 0.2f, 0.0f);
        anim.setRepeatCount(-1);
        anim.setRepeatMode(ObjectAnimator.REVERSE);
        anim.setDuration(2000);
        anim.start();
    }

这两个方法用属性动画的方式分别实现了旋转动画和淡入淡出动画,其中setDuration、setRepeatMode及setRepeatCount和补间动画中的概念是一样的。

可以看到,属性动画貌似强大了许多,实现很方便,同时动画可变化的值也有了更多的选择,动画所能呈现的细节也更多。

当然属性动画也是可以组合实现的

  ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(myView, "alpha", 1.0f, 0.5f, 0.8f, 1.0f);
                ObjectAnimator scaleXAnim = ObjectAnimator.ofFloat(myView, "scaleX", 0.0f, 1.0f);
                ObjectAnimator scaleYAnim = ObjectAnimator.ofFloat(myView, "scaleY", 0.0f, 2.0f);
                ObjectAnimator rotateAnim = ObjectAnimator.ofFloat(myView, "rotation", 0, 360);
                ObjectAnimator transXAnim = ObjectAnimator.ofFloat(myView, "translationX", 100, 400);
                ObjectAnimator transYAnim = ObjectAnimator.ofFloat(myView, "tranlsationY", 100, 750);
                AnimatorSet set = new AnimatorSet();
                set.playTogether(alphaAnim, scaleXAnim, scaleYAnim, rotateAnim, transXAnim, transYAnim);
//                set.playSequentially(alphaAnim, scaleXAnim, scaleYAnim, rotateAnim, transXAnim, transYAnim);
                set.setDuration(3000);
                set.start();

可以看到这些动画可以同时播放,或者是按序播放。


二、属性动画具体使用

1.步骤

  • 创建objectAnimator

  • 设置Interpolator(插值器)

  • 设置Evaluator(估值器)

  • 设置动画时长

  • 开启动画

//透明度起始为1,结束时为0
ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0f);
animator.setDuration(1000);//时间1s
animator.start();  
/*
ofFloat中的参数:
imageView:执行动画的View;
"alpha":表示透明动画;
1f:起始透明度;
0f:动画结束后的透明度
*/

2.组合动画

AnimatorSet:这个类提供了一个play()方法,调用后将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:

  • after(Animator anim) :将现有动画插入到传入的动画之后执行
  • after(long delay) :将现有动画延迟指定毫秒后执行
  • before(Animator anim): 将现有动画插入到传入的动画之前执行
  • with(Animator anim) :将现有动画和传入的动画同时执行
//沿x轴放大
ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 2f, 1f);
//沿y轴放大
ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(imageView, "scaleY", 1f, 2f, 1f);
//移动
ObjectAnimator translationXAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f, 0f);
//透明动画
ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0f, 1f);
AnimatorSet set = new AnimatorSet();
//同时沿X,Y轴放大,且改变透明度,然后移动
set.play(scaleXAnimator).with(scaleYAnimator).with(animator).before(translationXAnimator);
//都设置3s,也可以为每个单独设置
set.setDuration(3000);
set.start();

3.监听事件

        //添加监听事件
        set.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                //动画开始的时候调用
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                //画结束的时候调用
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                //动画被取消的时候调用
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                //动画重复执行的时候调用

            }
        });  

        //另一种设置监听的方式,里面的监听方法可以选择性重写
set.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationStart(Animator animation) {

    }
});

4.xml属性动画

这里提一下,属性动画当然也可以使用xml文件的方式实现,但是属性动画的属性值一般会牵扯到对象具体的属性,更多是通过代码动态获取,所以xml文件的实现会有些不方便。

<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

使用方法:

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.anim.property_animator);
set.setTarget(myObject);
set.start();

xml 文件中的标签也和属性动画的类相对应:

ValueAnimator --- <animator> 
ObjectAnimator --- <objectAnimator> 
AnimatorSet --- <set>

三.属性动画的核心原理

在上面实现属性动画的时候,我们反复的使用到了ObjectAnimator 这个类,这个类继承自ValueAnimator,使用这个类可以对任意对象的任意属性进行动画操作。而ValueAnimator是整个属性动画机制当中最核心的一个类;这点从下面的图片也可以看出。

1.核心类讲解

  • ValueAnimator:Animator的子类,实现了动画的整个处理逻辑,也是熟悉动画最为核心的类
  • ObjectAnimator:对象属性动画的操作类,继承自ValueAnimator,通过该类使用动画的形式操作对象的属性
  • TimeInterpolator:时间插值器,它的作用是根据时间流逝的百分比来计算当前属性值改变的百分比,系统预置的有线性插值器、加速减速插值器、减速插值器等。
  • TypeEvaluator:TypeEvaluator翻译为类型估值算法,它的作用是根据当前属性改变的百分比来计算改变后的属性值
  • Property:属性对象、主要定义了属性的set和get方法
  • PropertyValuesHolder:持有目标属性Property、setter和getter方法、以及关键帧集合的类。
  • KeyframeSet:**存储一个动画的关键帧集
    原理图
    属性动画核心原理,此图来自于Android SDK API 文档。

属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等。

从上图我们可以了解到,通过duration、startPropertyValue和endPropertyValue 等值,我们就可以定义动画运行时长,初始值和结束值。然后通过start方法开始动画。 那么ValueAnimator 到底是怎样实现从初始值平滑过渡到结束值的呢?这个就是由TypeEvaluator 和TimeInterpolator 共同决定的。

具体来说,TypeEvaluator 决定了动画如何从初始值过渡到结束值。个人觉得可以看作一个方向。

TimeInterpolator 决定了动画从初始值过渡到结束值的节奏。个人觉得可以看作一个速度。

说的通俗一点,你每天早晨出门去公司上班,TypeEvaluator决定了你是坐公交、坐地铁还是骑车;而当你决定骑车后,TimeInterpolator决定了你一路上骑行的方式,你可以匀速的一路骑到公司,你也可以前半程骑得飞快,后半程骑得慢悠悠。

2.插值器Interpolator介绍

Interpolator的概念其实我们并不陌生,在补间动画中我们就使用到了。他就是用来控制动画快慢节奏的;而在属性动画中,TimeInterpolator 也是类似的作用;TimeInterpolator 继承自Interpolator。我们可以继承TimerInterpolator 以自己的方式控制动画变化的节奏,也可以使用Android 系统提供的Interpolator。

下面都是系统帮我们定义好的一些Interpolator,我们可以通过setInterpolator 设置不同的Interpolator。

插值器
这里我们使用的Interpolator就决定了 前面我们提到的fraction。变化的节奏决定了动画所执行的百分比。不得不说,这么ValueAnimator的设计的确是很巧妙。

这些就是属性动画的核心内容。现在使用属性动画的特性自定义动画应该不是难事了。其余便签的含义,结合之前的内容应该不难理解了

3.估值器Evaluator介绍

设置属性从初始值到结束值变化的具体数值

插值器(Interpolator)决定 值 的变化规律(匀速、加速blabla),即决定的是变化趋势;

而估值器属性动画特有的属性


四、传统动画 VS 属性动画

相较于传统动画,属性动画有很多优势。那是否意味着属性动画可以完全替代传统动画呢。其实不然,两种动画都有各自的优势,属性动画如此强大,也不是没有缺点。

  • 补间动画中,虽然使用translate将图片移动了,但是点击原来的位置,依旧可以发生点击事件,而属性动画却不是。因此我们可以确定,属性动画才是真正的实现了view的移动,补间动画对view的移动更像是在不同地方绘制了一个影子,实际的对象还是处于原来的地方。
  • 当我们把动画的repeatCount设置为无限循环时,如果在Activity退出时没有及时将动画停止,属性动画会导致Activity无法释放而导致内存泄漏,而补间动画却没有问题。因此,使用属性动画时切记在Activity执行 onStop 方法时顺便将动画停止。(对这个怀疑的同学可以自己通过在动画的Update 回调方法打印日志的方式进行验证)。
  • xml 文件实现的补间动画,复用率极高。在Activity切换,窗口弹出时等情景中有着很好的效果。
  • 使用帧动画时需要注意,不要使用过多特别大的图,容易导致内存不足。
Android属性动画——ObjectAnimator类及浮动菜单的实现 今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。还有。 阅读详情

相关推荐

MDB协议中设备级别和选项(Levels and Options)

MDB协议的LEVEL级别介绍

hbstart的博客 939

ObjectAnimator实用方法

ObjectAnimator简单用法例子:        ObjectAnimator().ofFloat(view,"alpha",0.1f,0.8f,0.5f,1.0f).setDuration(800).start(); ofFloat()的参数: 第一个参数:控件 第二个参数:动画形式,注意:参数为字符串类型,不能写错。 rotationX   rotationY   旋

流影连月的博客 2720

Tera Term介绍和使用指南

Tera Term 是一个用于 Windows 操作系统的免费终端模拟器和串口通信软件。它主要用于在计算机和其他设备之间建立串口通信连接,例如通过串行线缆连接到设备如路由器、开发板或嵌入式系统。Tera Term 支持多种通信协议和功能,包括 Telnet、SSH 和串口通信。提供了一个终端窗口,模拟了常见的终端类型,如VT100。支持通过串口与设备进行通信,可用于与嵌入式系统、微控制器等设备进行连接。提供 Telnet 和 SSH 功能,可用于通过网络连接到远程主机。

weixin_49423593的博客 1万+

极简源码之Android ObjectAnimator实现原理

相对于View动画,属性动画有很多好处,比如:可以对任何对象操作,解决动画后控件点击区域问题等等。 我们以平移一个View展开来说: 调用ObjectAnimator.ofFloat时: 创建了一个ObjectAnimator对象,然后将传入的属性值保存下来,我们先看下anim.setFloatValues(values)里面具体干了什么 PropertyValuesHolder.ofFlo...

灵魂工程师 1288

Coppelia Sim (v-REP)仿真 机器人3D相机手眼标定与实时视觉追踪 (一)

先把这连个都添加到场景中,然后把kinect的depth相机拖到vision sensor中,把kinect的script也拖到vision sensor中,同时要注意修改深度相机的坐标,及修改script,删掉/rgb部分。由于需要导入到仿真场景中PDF文件不能直接导入,所以需要生成图下文件,使用windows自带的画图工具,生成以厘米为单位的图像格式,大小和标定板的设置大小一直。可以生成需要用的标定板的文件,设置标定板的大小,棋盘格的大小,等相关的参数,之后可以保存PDF文件。设置plane的大小。

hong3731的专栏 1753

C#实现串口通讯程序

1、自动获取本地串口。2、根据传入串口基本属性参数打开串口。3、串口数据的发送接收功能(ASCII和HEX格式)。4、ASCII和HEX相互转换。csharp体验AI代码助手代码解读复制代码[Category("UserProperty")] [Description("轮廓半径")] public int Radius { get => radius;

2401_89221445的博客 1765

自定义控件三部曲之动画篇(七)——ObjectAnimator基本使用

前言: 假如生活欺骗了你, 不要悲伤,不要心急! 忧郁的日子里须要镇静: 相信吧,快乐的日子终将会来临! 心儿永远向往着未来; 现在却常是忧郁。 一切都是瞬息,一切都将会过去; 而那过去了的,就会成为亲切的怀恋。 —–普希金 相关文章: 《Android自定义控件三部曲文章索引》:http://blog.youkuaiyun.com/harvic880925/article/details/5...

启舰 6万+

UsualToolCMS-验证码绕过漏洞复现

本文以UsualToolCMS为靶场,复现了验证码绕过漏洞

小小小屿屿屿的博客 918

Android 属性动画 原理 和 使用(Kotlin)

上面一篇文章写了 View动画 和 帧动画。。。。。 那这篇文章,就来一波属性动画。。。。。 我们应该都知道,View动画改变的只是View绘制的位置,并没有改变View的属性,比如四个顶点的坐标。就是你把一个View从他原始位置A,移动到新的位置B并停留在在B,你点击B位置,不是不会有点击事件的响应的。比如要点位置A才有。。。。 所以如果一个动画是有位置移动,但又必须有点击事件的处理的话,......

Leo_Liang_jie的博客 3656

FPGA AXI DDR3读写时序调试与FIFO缓存实战解析

本文深入解析FPGA中AXI协议与DDR3的读写时序调试,重点探讨FIFO缓存在数据流控制中的关键作用。通过实战案例详细讲解突发传输中的地址对齐、数据分片等常见问题,并提供状态机设计、仿真波形分析和性能优化技巧,帮助开发者高效解决高速数据存储场景下的时序同步与数据一致性问题。

blockchain9miner的博客 1117

AutosarMCAL开发——基于EB Wdg驱动

Autosar标准文件中提及的API共有5个,接口名传入参数说明返回参数函数功能ConfigPtr:Wdg配置指针,参数来自于EB生成代码voidWdg初始化ConfigPtr:Wdg配置指针Wdg初始化检查Wdg切换模式,需开启对应配置timeout:设置定时触发器计数(单位ms)void喂狗并重新设定计数值Wdg版本信息本文为博主个人学习总结记录,如有不正,欢迎指正。

qq_45583706的博客 2426

Android 动画 原理解析

Android 动画 原理解析一.View动画(一)基本使用1.代码实现2.xml实现(二)原理解析1.实现原理2.源码分析(1)Animation类(2)流程分析二.布局动画(一)基本使用1.xml实现2.代码实现(二)原理解析1.实现原理2.源码分析(1)解析设置LayoutAnimationController(2)dispatchDraw()三.属性动画(一)基本使用1.代码实现2.xml...

依生依世 924

13、控制依赖与切片的新基础及递归While程序的摘要

本文探讨了控制依赖和程序切片的新研究进展,并提出了一种用于递归While程序的广义过程摘要概念和验证框架。文章介绍了控制依赖的新定义及其优势,程序切片的相关工作,以及递归While程序的过程摘要在完全正确性和终止性证明中的应用。通过阶乘和插入排序程序的示例分析,展示了过程摘要的有效性。未来的研究将聚焦于并发程序的依赖计算、依赖关系的语义连接以及时间属性的保留。

kotlin6android的博客 85

Android动画框架:属性动画深入解析

属性动画(Property Animation)是Android 3.0(API 11)引入的一种新的动画机制。与传统的帧动画和补间动画相比,属性动画可以实现对对象的任意属性进行动画处理,从而实现更加丰富和灵活的动画效果。属性动画的核心类是和。用于动画化对象的属性,而则是一个更通用的动画引擎,它可以生成一个值序列,并通过监听器将这些值应用到对象的属性上。属性动画作为Android动画框架的核心组成部分,提供了强大的功能和灵活的用法。

2501_92489070的博客 522

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Android 属性动画详解

  引言:    现在市面类似功能的App太多,如果你想要在类似的App中脱颖而出的话,那么你就要在增强用户体验方面下功夫,动画是你最好的选择。它可以给用户增加指引、吸引眼球、提醒用户跟着你一步步操作等等,动画是一个成熟App必不可少的。   Android动画分类:        先简单介绍一下Android中的几种动画,大致可分为三种:  1.补间动画(也叫视图动画、Tween动画...

no_loafer的博客 3524

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Android动画之视图动画的缺点和属性动画的引入

本篇较短! 1视图动画添加监听 translate 动画 &amp;amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;amp;gt; &amp;amp;lt;translate xmlns:android=&amp;quot;http://schemas.android.com/apk/res/android&a

lidongxiu0714 4106

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

关于Android中动画探究(二)——属性动画

属性动画 上一节我们看过了Animation视图动画的原理浅析,了解到Animation的原理是通过ViewRootImpl来监听下一个屏幕刷新信号,然后DecorView对View树重新绘制并且顺带将动画绘制出来。 关于属性动画有两个常用类ValueAnimator和它的子类ObjectAnimator,还有view.animate()会得到一个ViewPropertyAnimator对象,需要...

Osborn_的博客 343

android Tween动画和属性动画

动画主要分为两种一种是帧动画,另外是属性动画

kai1ge1的博客 404

Android源码分析—属性动画的工作原理

Android源码分析—属性动画的工作原理

qq_43661383的博客 121

Property Animation(属性动画)

动画类型 View Animation(Tween Animation 补间动画) 只能支持简单的缩放、平移、旋转、透明度等基本的动画,且有一定的局限性 动画时View的真正的View的属性保持不变,实际位置未改变 原理:提供动画的起始和结束状态信息,中间的状态根据上述类里差值器算法填充 Drawable Animation(Frame Animation 帧动画) Property Anim

Wanny 1021

详解Android属性动画

前面我们讲到的属性动画都是使用代码的定义方式:Android属性动画之ValueAnimator和Android属性动画之ObjectAnimator和AnimatorSet,下面我们再来看看使用XML文件该怎么做。 (1) XML文件位置:res/animator/filename.xml(2) 文件编译后的类型:ValueAnimator, ObjectAnimator, Ani...

anzhenji7632的博客 155

【动画系列】属性动画

为什么要引入属性动画补间动画缺陷: 1.它只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,那如果我们希望可以对View的背景色进行动态地改变呢?很遗憾,我们只能靠自己去实现了。说白了,之前的补间动画机制就是使用硬编码的方式来完成的,功能限定死就是这些,基本上没有任何扩展性可言。 2.它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个

有心好书 1350

android 按钮动画效果_属性动画概览

属性动画系统是一个强健的框架,用于为几乎任何内容添加动画效果。您可以定义一个随时间更改任何对象属性的动画,无论其是否绘制到屏幕上。属性动画会在指定时长内更改属性(对象中的字段)的值。要添加动画效果,请指定要添加动画效果的对象属性,例如对象在屏幕上的位置、动画效果持续多长时间以及要在哪些值之间添加动画效果。借助属性动画系统,您可以定义动画的以下特性:时长:您可以指定动画的时长。默认时长为 ...

weixin_39664477的博客 711

浅谈Android动画之属性动画ValueAnimator和ObjectAnimator

前言 动画的使用 是 Android 开发中常用的知识 本文将详细介绍 Android 动画中 属性动画的原理 & 使用 简介 具体使用 而我们主要使用的就是ValueAnimator和ObjectAnimator类 ValueAnimator:可以设置开始值和结束值来动态改变view的移动位置 ObjectAnimator:功能更加强大,可以控制位移、透明度、旋转、缩放。 ValueAnimator的使用 实例说明: 1.设置布局文件在activity_main.xml修改 <?xml

fjnu_se的博客 536

属性动画

动画类型跟补间动画类型一样,但是补间动画只改变view的显示,并没有改变view本身。比如一个view在(200,300)- (200,500)的区域做平移动画(补间)。动画结束后 肉眼看到view停留在(200,500)的地方,但实际上view本身还是在(200,300)的地方。这就是view本身(属性)没有改变的原因。属性动画将弥补这个缺点。属性动画又有objectAnimator 与anim...

FooTyzZ 354

Android 动画之属性动画ValueAnimator和ObjectAnimator

布局就是一个ImageView,看看实现代码 public void flip(View view) { ObjectAnimator visibleToInVisable = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0f, 360.0f); //设置插值器 visibleToInVi

lzy的博客 3289
上一篇: 帧动画和补间动画的使用
下一篇: ReentrantLock的使用和原理详解
浮空over
博客等级 码龄7年 6粉丝 33原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值