android中通过属性动画来旋转控件,【Android 动画片】Property Animation详解(二)

上一篇【Android 动画】View Animation详解(一)我们介绍了Android View Animation动画,包括Tween动画和帧动画,今天我们来介绍一下另一种非常 好用的动画——-Property Animation(属性动画)。

一.属性动画概念

所谓属性动画,就是通过不断修改组件的私有属性来调整组件的大小,位置,缩放,清晰度等等效果,从而达到一个动画的效果,属性动画可以轻而易举的办到许多View动画做不到的事,今天我们就来学习一下属性动画。

首先我们要先了解关于View在3.0之后引入的几个新的属性,并设置了其getter和setter方法:

translationX 和 translationY:这两个属性控制了View所处的位置,它们的值是由layout容器设置的,是相对于坐标原点(0,0左上角)的一个偏移量。

rotation, rotationX 和 rotationY:控制View绕着轴点(pivotX和pivotY)旋转.

scaleX 和 scaleY:控制View基于pivotX和pivotY的缩放。

pivotX 和 pivotY:旋转的轴点和缩放的基准点,默认是View的中心点。

x 和 y:描述了view在其父容器中的最终位置,是左上角左标和偏移量(translationX,translationY)的和。

aplha:透明度,1是完全不透明,0是完全透明。

属性动画系统是一个允许你做几乎任何事情的强大的框架。你可以定义一个在一定时间内对象的属性不断变化的动画。属性动画的实现是在指定的时间内更改属性(一个对象中的字段)。想要执行什么动作,你就应该指定你希望动画变化的对象的指定属性。

属性动画有以下几个重要的属性:

Duration(持续时间):你可以指定动画的持续时间。默认大小为300毫秒。

Time interpolation(时间插值):时间插值函数. (指定属性值变化的快慢) 如:AccelerateDecelerateInterpolator。

Repeat count and behavior(重复计数和行为):你可以指定当动画结束的时候是否重复该动画,重复多少次该动画。您还可以指定是否希望动画反向回放。设置为反向播放动画向前然后向后地重复,直到到达重复次数。

Animator sets(动画设置):你可以把动画放在一个组内,可以同时执行多个动画效果。

Frame refresh delay(帧刷新延迟):可以指定多长时间刷新你的动画帧。默认设置为每10毫秒刷新一次动画帧,但最终依赖系统的当前状态。

二.属性动画如何工作?

1.首先,我们看一个简单的例子。图1描述了一个假想的对象,动画和X属性(它在屏幕上的水平位置)。动画的持续时间设置为40毫秒,距离是40像素。默认的帧刷新率每10毫秒刷新一次,物体水平移动10像素。40ms后结束,动画停止,物体水平移动了40个像素。这是一个带线性插值的动画例子,所展现的是一个以恒定的速度运动的目标。

011758285

图1

2.上面的例子是一个线性插值动画的例子,你也可以指定有一个非线性插值动画。图2说明了一个假设的对象,在动画开始时加速,结束时减速的动画。对象还是在40毫秒移动40像素,但非线性。在开始的时候,这个动画加速到中途点,然后从中途点减速直到动画结束。如图2所示,在开始和结束的动画距离是小于在中间时的。

011758286

图2

3.让我们来详细看看属性动画系统的重要组成部分,图3描述了主要的类如何与另一个工作。

011758287

图3

ValueAnimator本身不作用于任何对象,也就是说直接使用它没有任何动画效果。它可以对一个值做动画,然后我们可以监听其动画过程,在动画过程中修改我们的对象的属性值,这样也就相当于我们的对象做了动画。

ValueAnimator封装timeinterpolator,定义动画插值和TypeEvaluator(可以理解为估值器),它定义了如何计算被修改了的组件的属性的值。

当想要开始一个动画的时候,应该先创建一个ValueAnimator并给它开始和结束值的属性值,你想动画做什么动作,以及动画的持续时间。当你调用start()动画开始。整个动画中,ValueAnimator会基于动画的持续时间和经过了多少时间计算出一个0和1之间分数。用分数表示动画已经完成了的时间的百分比,0就是0%,1就是100%。例如,在图1中,在t = 10毫秒时,该值应该是0.25,因为总时间t = 40毫秒。

当ValueAnimator完成计算一个逝去分数(当前执行时间占动画总执行时间的比例),它调用当前设置的TimeInterpolator,计算插值分数。一个被插值分数映射逝去分数到一个新的分数,考虑了被设置的时间插值。例如,在图2中,因为动画慢慢加速,插值后的分数0.15小于逝去分数0.25,如图1,在t = 10 ms,插值分数等于逝去分数。

当插值分数被计算的时候,ValueAnimator调用合适的TypeEvaluator,基于插值分数的初始值以及动画的结束值动态的计算该属性的值。例如,在图2中,在t = 10毫秒时分数为0.15,所以实际的属性值将是0.15 *(40 - 0)=6。

三.属性动画与View 动画不同

View动画系统只对动画视图对象有用,所以如果你想非视图对象动画,就需要实现你自己的代码来做。视图动画系统也受到限制,只公开了几方面的动画视图对象,,如一个视图的缩放和旋转而无法修改背景色。

视图动画系统的另一个缺点是,它只在视图被修改,而不是实际的视图。例如,如果你的动画按钮在屏幕上移动,按钮绘制正确,但实际的位置,你可以点击按钮并没有改变,所以你要实现你自己的逻辑来处理这个。

使用属性动画系统,这些约束完全可以解除,你可以对任何对象的任何属性进行操作。属性动画系统在一个较高的水平以更稳健的方式进行动画。指定你想要操作的动画属性,如颜色,大小,位置等等。

三.API 概述

你可以在在android.animation属性动画系统找到大部分的API。因为视图动画系统已经在android.view.animation定义了多种插值算法,你可以直接使用这些封装好的动画效果。

Animator类提供了用于创建动画的基本结构。你通常不直接使用这个类,它只提供最低限度的功能,通常根据自己的需要对该类进行扩展。下面介绍一下Animator的子类:

ValueAnimator

属性动画,它的所有核心功能是计算动画值和每个动画的时间细节,关于一个动画是否有重复的信息,监听并接收更新事件,并且能够自定义动画。属性动画有两个步骤:计算属性的值和设定属性的值。ValueAnimator只进行计算,所以你必须使用ValueAnimator计算值不断的来设置更新该对象的值。

ObjectAnimator

ObjectAnimator是ValueAnimator的子类,它允许你设定一个目标对象和对象的属性动画。当它为动画计算新值时,就可以进行相应地属性更新。

AnimatorSet

AnimatorSet提供了一种机制,可以把多个动画放在一个群组里一起执行,可以实现更多的动画效果。你可以设置一个顺序动画(也可以指定动画的延迟)。

Evaluators告诉属性动画系统如何计算属性值。他们是由动画类提供的时序数据,动画的开始和结束值,在这个数据基础计算动画实际的值。属性动画系统提供以下evaluators:

IntEvaluator

计算默认属性为int类型的值。

FloatEvaluator

计算默认属性为float类型的值。

ArgbEvaluator

计算表示为十六进制的颜色属性的值。

TypeEvaluator

自定义计算方式的一个接口,如果你想要的动画的对象属性是不是int,float或颜色,你必须实现的TypeEvaluator接口指定如何计算对象属性值的动画。如果你想处理这些类型不同的默认行为,你也可以自定义的TypeEvaluator,指定一个int,float和颜色的值。

时间插补器time interpolator定义了一个作为时间的函数计算的动画中的特定值。例如,您可以指定在整个动画过程中线性发生动画,意味着动画的动作均匀的整个时间,或者你可以指定要使用的非线性时间,例如:结束时加速和开始时减速的动画。下面介绍一下android.view.animation中包含的插值器。如果没有提供你需要的插值器,为了满足需求,可以通过实现timeinterpolator接口并创建你自己的插值器。

1、 AccelerateDecelerateInterpolator

插值器的变化率开始和结束缓慢而中间加速通过。

效果图

011758288

测试代码

//构建animator对象

animator = ValueAnimator.ofFloat(50f,600f);

//设置缩放时间

animator.setDuration(2000);

animator.setInterpolator(new AccelerateDecelerateInterpolator());

animator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setTranslationX(f);

}

});

2、 AccelerateInterpolator

插值器的变化率开始缓慢,然后加快。

效果图

011758289

测试代码

//构建animator对象

animator = ValueAnimator.ofFloat(50f,600f);

//设置缩放时间

animator.setDuration(2000);

animator.setInterpolator(new AccelerateInterpolator());

animator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setTranslationX(f);

}

});

3、 AnticipateInterpolator

先回退一小步,然后再迅速前进

效果图

011758290

测试代码

//构建animator对象

animator = ValueAnimator.ofFloat(50f,600f);

//设置缩放时间

animator.setDuration(2000);

animator.setInterpolator(new AnticipateInterpolator());

animator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setTranslationX(f);

}

});

animator.start();

4、 AnticipateOvershootInterpolator

先回退一小步,然后再迅速前进,在超过右边界一小步

效果图

011758291

测试代码

//构建animator对象

animator = ValueAnimator.ofFloat(50f,600f);

//设置缩放时间

animator.setDuration(2000);

animator.setInterpolator(new AnticipateOvershootInterpolator());

animator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setTranslationX(f);

}

});

animator.start();

5、 BounceInterpolator

实现弹球效果

效果图

011758292

测试代码

//构建animator对象

animator = ValueAnimator.ofFloat(50f,600f);

//设置缩放时间

animator.setDuration(2000);

animator.setInterpolator(new BounceInterpolator());

animator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setTranslationY(f);

}

});

animator.start();

6、 CycleInterpolator

周期运动

效果图

011758293

测试代码

//构建animator对象

animator = ValueAnimator.ofFloat(50f,300f);

//设置缩放时间

animator.setDuration(2000);

animator.setInterpolator(new CycleInterpolator(3)

{

@Override

public float getInterpolation(float input)

{

return super.getInterpolation(input);

}

});

animator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setTranslationY(f);

}

});

animator.start();

7、 DecelerateInterpolator

减速

效果图

011758294

测试代码

//构建animator对象

animator = ValueAnimator.ofFloat(50f,600f);

//设置缩放时间

animator.setDuration(2000);

animator.setInterpolator(new DecelerateInterpolator());

animator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setTranslationY(f);

}

});

animator.start();

8、 LinearInterpolator

匀速

效果图

011758295

测试代码

//构建animator对象

animator = ValueAnimator.ofFloat(50f,600f);

//设置缩放时间

animator.setDuration(2000);

animator.setInterpolator(new LinearInterpolator());

animator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setTranslationY(f);

}

});

animator.start();

9、 OvershootInterpolator

快速前进到右边界上,再往外突出一小步

效果图

011758296

测试代码

//构建animator对象

animator = ValueAnimator.ofFloat(50f,600f);

//设置缩放时间

animator.setDuration(2000);

animator.setInterpolator(new OvershootInterpolator());

animator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setTranslationY(f);

}

});

animator.start();

10、 TimeInterpolator

允许你实现你自己的插值器的接口。

四.ValueAnimator动画使用方式

ValueAnimator,是通过修改属性由一个值变化为另外一个值,然后根据值的变化,按照一定的规则,动态修改View的属性,比如View的位置、透明度、旋转角度、大小等。

ValueAnimator类允许指定一组int,float或色彩值,通过调用它的一个工厂方法:ofint(),offloat()或ofobject()得到一个ValueAnimator对象。例如:

ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);

animation.setDuration(1000);

animation.start();

在这段代码中,ValueAnimator开始计算动画的值,0和1之间,持续时间为1000毫秒,这start()方法运行时。

你也可以指定一个自定义类型的动画做以下:

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);

animation.setDuration(1000);

animation.start();

在这段代码中,ValueAnimator通过一个自定义动画类MyTypeEvaluator来实现在1000ms内完成属性值startPropertyValue到endPropertyValue的转变,调用start()方法开始执行。

然而,前面的代码片段对一个对象并没有实际效果,因为ValueAnimator并不直接操作的对象或属性。你现在最想做的事是通过这些计算值来修改对象的属性。你可以通过定义监听器在ValueAnimator动画寿命期间的重要事件时进行适当处理,如帧更新。在监听过程中,你可以通过调用getanimatedvalue()刷新特定帧的计算值下面通过几个例子来展示实际的应用过程。

例子1:缩放imageview从初始大小1f到0f

1-1.通过代码来实现,activity_main.xml布局如下

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:gravity="center"

tools:context="${relativePackage}.${activityClass}" >

android:layout_width="280dp"

android:layout_height="260dp"

android:src="@drawable/image" />

1-2.java代码操作image控件从1f缩放到0f

public class MainActivity extends Activity

{

private ImageView image;

private ValueAnimator animator;

@Override

protected void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

image = (ImageView)findViewById(R.id.image);

image.setOnClickListener(new View.OnClickListener()

{

@Override

public void onClick(View v)

{

animator.start();

}

});

startValueAnimator();

}

private void startValueAnimator()

{

//构建animator对象,执行缩放值1f-0f

animator = ValueAnimator.ofFloat(1f,0f);

//设置缩放时间

animator.setDuration(1000);

//更新界面

animator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

//获取经过计算得到的单个属性的值,此处是属性scaleX,scaleY的值

float f = (float)animation.getAnimatedValue();

image.setScaleX(f);

image.setScaleY(f);

}

});

}

}

1-3.运行效果

011758297.gif

1-4.xml动画文件实现例1效果

在res目录下新建animator目录,在animator目录下新建animator_scale.xml文件

android:propertyName="scaleX"

android:valueFrom="1.0"

android:valueTo="0.0"

android:valueType="floatType"

/>

android:propertyName="scaleY"

android:valueFrom="1.0"

android:valueTo="0.0"

android:valueType="floatType"

/>

代码中使用,即可实现例1效果

xmlAnimator = AnimatorInflater.loadAnimator(MainActivity.this, R.animator.animator_scale);

xmlAnimator.setTarget(image);

xmlAnimator.start();

例子2:缩放+旋转+平移+渐变消失效果

2-1.具体实现代码

animatorSet = new AnimatorSet();

//平移

pyAnimator = ValueAnimator.ofFloat(50f,600f);

//缩放

sfAnimator = ValueAnimator.ofFloat(1f,0f);

//旋转

xzAnimator = ValueAnimator.ofFloat(0f,180f);

//渐变

jbAnimator = ValueAnimator.ofFloat(1f,0f);

animatorSet.playTogether(pyAnimator,sfAnimator,xzAnimator,jbAnimator);

//设置缩放时间

animatorSet.setDuration(2000);

pyAnimator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setTranslationX(f);

}

});

sfAnimator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setScaleX(f);

image.setScaleY(f);

}

});

xzAnimator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setRotation(f);

}

});

jbAnimator.addUpdateListener(new AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float f = (float)animation.getAnimatedValue();

image.setAlpha(f);

}

});

2-2.开启动画

animatorSet.start();

2-3.执行效果图

011758298.gif

五. objectanimator动画详解

objectanimator是ValueAnimator的子类,结合时序引擎和动画命名一个目标对象的属性的能力,ValueAnimator值计算。这使得动画物体容易得多,因为你不再需要实现valueanimator.animatorupdatelistener,因为动画属性的自动更新。

objectanimator的使用和ValueAnimator非常类似,但也必须随着动画之间的值来指定对象和对象的属性的名称(字符串):

ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);

anim.setDuration(1000);

anim.start();

为了确保objectanimator更新属性正确,你必须做到以下几点:

设置的动画的对象属性必须有一个setter函数。因为objectanimator自动更新的动画,它必须能够通过这个setter方法访问属性。例如,如果属性名为foo,你需要有一个setfoo()方法。如果这个setter方法是不存在的,你有三个选择:

1、如果你有这样做的权利,添加setter方法。

2、使用包装类,你必须改变和有包装获得的值与一个有效的setter方法,提出了它原来的对象权限。

而不是使用ValueAnimator。

如果你指定的属性变化参数(…参数)值只有一个值,它被认为是动画的结束值。因此,在你的动画对象的属性必须有一个getter函数用于获取动画的起始值。例如,如果属性名为foo,你需要有一个getfoo()方法。

你指定的当前动画的属性变化值必须是同一个类型,例如,你指定了float的类型的开始值,就必须确保结束值也是float。

在对某些属性进行动画操作的时候,你可能需要调用invalidate()方法使屏幕重绘自身,在onanimationupdate()回调方法中可以完成这些。例如,如果是设置color属性的动画,Drawable对象当界面重新绘制的时候才会更新。setalpha()和settranslationx()是会自动更新的,因此不需要调用invalidate()方法。

下面通过几个例子展示一下objectAnimator基本用法:

例子1:缩放,旋转,平移,透明度动画

1-1.添加布局文件activity_main.xml

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context="${relativePackage}.${activityClass}" >

android:id="@+id/image"

android:layout_width="80dp"

android:layout_height="80dp"

android:src="@drawable/dph"

/>

android:id="@+id/btn_translate"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:text="平移" />

android:id="@+id/btn_rotate"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:layout_toRightOf="@+id/btn_translate"

android:text="旋转" />

android:id="@+id/btn_scale"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_above="@+id/btn_translate"

android:text="缩放" />

android:id="@+id/btn_alpha"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_above="@+id/btn_rotate"

android:layout_toRightOf="@+id/btn_scale"

android:text="透明度" />

1-2.代码实现

//imageview x方向从50平移到500的位置

ObjectAnimator animator = ObjectAnimator.ofFloat(image, "translationX", 50,500);

animator.setDuration(2000);

animator.start();

//imageview旋转:0度-360度-0度

ObjectAnimator animator = ObjectAnimator.ofFloat(image, "rotation", 0,360,0);

animator.setDuration(2000);

animator.start();

//x,y同时缩放1f-0f-1f(从初始状态到消失再还原)

AnimatorSet animatorSet = new AnimatorSet();

ObjectAnimator animator = ObjectAnimator.ofFloat(image, "scaleX", 1f,0f,1f);

ObjectAnimator animatorY = ObjectAnimator.ofFloat(image, "scaleY", 1f,0f,1f);

animatorSet.playTogether(animator,animatorY);

animatorSet.setDuration(2000);

animatorSet.start();

//透明度从不透明到全透明,再到不透明

ObjectAnimator animator = ObjectAnimator.ofFloat(image, "alpha", 1f,0f,1f);

animator.setDuration(2000);

animator.start();

1-3.效果图

011758299.gif

例子2:往前滚动的小球

2-1.例1的代码稍微改变一下即可实现,代码如下:

AnimatorSet animatorSet = new AnimatorSet();

ObjectAnimator animatorA = ObjectAnimator.ofFloat(image, "translationX", 50,600);

ObjectAnimator animatorB = ObjectAnimator.ofFloat(image, "rotation", 0,720);

animatorSet.playTogether(animatorA,animatorB);

animatorSet.setDuration(1000);

animatorSet.start();

2-2.效果图

011758300.gif

六.使用Animatorset编排多个动画

在许多情况下,你想实现一个动画,取决于一个动画的开始或完成。Android系统允许你捆在一起形成一个animatorset动画,使您可以指定是否同时启动动画,顺序启动动画或在指定的延迟后启动动画。你也可以在animatorset中嵌套animatorset对象。使用起来非常简单,其实上面的例子已经有使用到。

下面通过一个例子来演示一下animatorset的使用:

1.直接上代码:

//旋转动画animatorRotate

ObjectAnimator animatorRotate = ObjectAnimator.ofFloat(image, "rotation", 0,360,0);

animatorRotate.setDuration(1000);

//平移动画animatorTrans

ObjectAnimator animatorTrans = ObjectAnimator.ofFloat(image, "translationX", 50,500);

animatorTrans.setDuration(2000);

//缩放动画animatorSet

AnimatorSet animatorSet = new AnimatorSet();

ObjectAnimator animator = ObjectAnimator.ofFloat(image, "scaleX", 1f,0f,1f);

ObjectAnimator animatorY = ObjectAnimator.ofFloat(image, "scaleY", 1f,0f,1f);

animatorSet.playTogether(animator,animatorY);

animatorSet.setDuration(2000);

//透明度动画animatorAlpha

ObjectAnimator animatorAlpha = ObjectAnimator.ofFloat(image, "alpha", 1f,0f,1f);

animatorAlpha.setDuration(2000);

//动画集合bouncer

AnimatorSet bouncer = new AnimatorSet();

//旋转animatorRotate之前先执行平移animatorTrans

//animatorTrans的同时animatorSet(缩放)

//平移+缩放完成后执行animatorRotate旋转

//最后执行fadeAnim,消失

bouncer.play(animatorTrans).before(animatorRotate);

bouncer.play(animatorTrans).with(animatorSet);

bouncer.play(animatorTrans).after(animatorAlpha);

ValueAnimator fadeAnim = ObjectAnimator.ofFloat(image, "alpha", 1f, 0f);

fadeAnim.setDuration(1000);

AnimatorSet animatorMySet = new AnimatorSet();

//执行fadeAnim之前先执行bouncer动画

animatorMySet.play(bouncer).before(fadeAnim);

animatorMySet.start();

2.运行效果

011758301.gif

关于属性动画的基础知识先说到这,后续会介绍动画的监听,布局动画和TypeEvaluator,ViewPropertyAnimator等等,敬请期待。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值