Android 动画学习小结

2.3以及以前的版本支持3种类型的动画:逐帧动画,布局动画,视图动画。布局动画,视图动画合称为补间动画。

3.0后推出了属性动画

一.逐帧动画

逐帧动画就像动画片一样把一些图片组合并且快速播放,好像物体在运动一样。

创建逐帧动画,使用AnimationDrawable这个类

显示动画的步骤:

1.在布局文件中加入imageview用于显示动画

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >
    
    <Button 
        android:id="@+id/start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="start"
        />
    
    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />
</LinearLayout>

2.准备一些连续播放起来可以形成“动画”的图片,且称为帧图片,然后构造一个xml来声明这些帧图片


loading.xml

<?xml version="1.0" encoding="UTF-8"?>
<animation-list android:oneshot="false"
	xmlns:android="http://schemas.android.com/apk/res/android">
	<item android:duration="100" android:drawable="@drawable/loading_00" />
	<item android:duration="100" android:drawable="@drawable/loading_01" />
	<item android:duration="100" android:drawable="@drawable/loading_02" />
	<item android:duration="100" android:drawable="@drawable/loading_03" />
	<item android:duration="100" android:drawable="@drawable/loading_04" />
	<item android:duration="100" android:drawable="@drawable/loading_05" />
	<item android:duration="100" android:drawable="@drawable/loading_06" />
	<item android:duration="100" android:drawable="@drawable/loading_07" />
</animation-list> 
android:oneshot="false"表明动画连续播放,android:duration表明持续时间100毫秒

3.代码中设置动画

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		 Button start = (Button) findViewById(R.id.start);
	   	 ImageView imgView = (ImageView)findViewById(R.id.img);
	   	 imgView.setBackgroundResource(R.anim.loading);
	
	    final AnimationDrawable frameAnimation = 
	   		 (AnimationDrawable) imgView.getBackground();
	   	 
	    start.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
		    	 if (frameAnimation.isRunning()) {
		    		//动画暂停
		        	 frameAnimation.stop();
		    	 }
		    	 else {
		    		 //动画启动
		        	 frameAnimation.start();
		    	 }
			}
		 });
	}
}

运行程序,点击按钮,可以看到进度条无限转动的画面。

以上转动效果还可以用progressbar实现

    <ProgressBar
        android:id="@+id/progress"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:indeterminateDrawable="@anim/loading" />


二.补间动画(布局动画,视图动画)

补间动画的类型:

缩放动画:沿x轴或者y轴缩小或者放大视图,也可以指定一个支点并围绕该支点来缩放动画

旋转动画:围绕一个支点将视图旋转一定角度

平移动画:沿x或者y轴移动视图

透明度动画:更改视图的透明度


(1).布局动画

布局动画作用于ViewGroup的子节点(LinearLayout下的view,或者ListVIew, GridView等的item),为什么叫布局动画,大概是因为在布局文件中实现吧

这里演示如何在xml文件中创建,下面提到的xml文件都放在anim文件夹下。步骤如下:

1.创建动画的xml文件,节点名称根据你想要的效果(缩放,旋转,平移,透明度)而选择

缩放动画scale.xml:

   <scale 
         android:fromXScale="1"
         android:toXScale="1"
         android:fromYScale="0.1"
         android:toYScale="1.0"
         android:duration="500"
         android:pivotX="50%"
         android:pivotY="50%"
         android:startOffset="100" />

属性说明:

android:fromXScale="1"
android:toXScale="1"

指定在x轴上的放大系数变化值,从1到1表明不变,y轴同理

android:duration="500" 缩放时间500毫秒

android:pivotX="50%"

android:pivotY="50%"

在中间时刻,对象在x,y方向上都为50%

android:startOffset="100" 在动画开始前等待100毫秒


旋转动画rotate.xml:

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
       android:interpolator="@android:anim/accelerate_interpolator"
       android:fromDegrees="0.0" 
      android:toDegrees="360"
      android:pivotX="50%"
      android:pivotY="50%"
      android:duration="500" />

属性说明:角度从0到360度旋转,中间时刻旋转一半角度,一次旋转时间500毫秒

android:interpolator="@android:anim/accelerate_interpolator"

指定动画所用的插值器是系统自带的加速插值器,插值器指定了动画的某个属性,比如角度如何随时间变化,是否是匀速旋转,还是越转越快?这里是越转越快


平移动画translate.xml:

<translate xmlns:android="http://schemas.android.com/apk/res/android"
       android:fromYDelta="-100%" android:toYDelta="0"
      android:duration="500" />

属性说明:从上往下进入屏幕,平移动画具体说明可以参考这个文章:http://www.cnblogs.com/bavariama/archive/2013/01/29/2881225.html


透明度动画alpha.xml:

<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromAlpha="0.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="1.0" />
属性说明:透明度从0到1,渐变持续1秒


2.amin文件夹下创建xml,节点名称为layoutAnimation,属性中声明使用的动画:

layout_controller.xml

<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:delay="30%"
        android:animationOrder="reverse"
        android:animation="@anim/rotate" 
        />
android:animation="@anim/rotate,此布局动画使用了旋转动画

3.在主页面布局中引用此布局动画的xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layoutAnimation="@anim/layout_controller"
    >
    <TextView
      android:id="@+id/list_view_id"
      android:text="sadas"
      android:textSize="40sp"
      android:gravity="center"
      android:layout_gravity="center"
      android:persistentDrawingCache="animation|scrolling"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
       />
</LinearLayout>
android:layoutAnimation="@anim/layout_controller声明了上面的layout_controller.xml。现在运行程序,可以看到textview载入的时候运行了旋转的动画

android:persistentDrawingCache="animation|scrolling 可以提高效率

动画之间还可以组合,相当于把动画元素放入集合类中,像这样:

<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator">
    <translate android:fromYDelta="-100%" android:toYDelta="0" android:duration="500" />
    <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="500" />
</set>

引用它的xml:

<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:delay="30%"
        android:animationOrder="reverse"
        android:animation="@anim/alpha_translate" 
        />


要使得2个Activity切换时播放动画,可以套用以上几种类型的动画xml,最后在代码中加上这句代码就行。

            startActivity(intent);  
            //第一个参数为启动时动画效果,第二个参数为退出时动画效果  
            overridePendingTransition(R.anim.fade, R.anim.hold);  


(2)视图动画

布局动画实现的效果视图动画都可以实现,视图动画使用纯代码制作动画,可以完成比布局动画更复杂的效果(矩阵变换等),但是实现复杂度也更大。

先看一个简单的实现,2个步骤,

1.实现动画类ViewAnimation2,设置动画的形式

2.调用布局总view的startAnimation方法将此动画设置给view。

activity:

public class ViewAnimationActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_layout);
        setupListView();
        this.setupButton();
    }
    private void setupListView()
    {
    	  String[] listItems = new String[] {
    	        "Item 1", "Item 2", "Item 3",
    	        "Item 4", "Item 5", "Item 6",
    	  };
    	  
    	  ArrayAdapter<String> listItemAdapter = 
    		   new ArrayAdapter<String>(this
    		           ,android.R.layout.simple_list_item_1
    		           ,listItems);
    	  ListView lv = (ListView)this.findViewById(R.id.list_view_id);
    	  lv.setAdapter(listItemAdapter);
    }
    private void setupButton()
    {
       Button b = (Button)this.findViewById(R.id.btn_animate);
       b.setOnClickListener(
           new Button.OnClickListener(){
             public void onClick(View v)
             {
                animateListView();
             }
           });
    }
    private void animateListView()
    {
  	  ListView lv = (ListView)this.findViewById(R.id.list_view_id);
  	  lv.startAnimation(new ViewAnimation2());
    }
}

动画实现 ViewAnimation2:

public class ViewAnimation2 extends Animation 
{
    public ViewAnimation2(){}

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        setDuration(2500);
        setFillAfter(true);
        setInterpolator(new LinearInterpolator());
    }
    //interpolatedTime值从0到1
    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final Matrix matrix = t.getMatrix();
        matrix.setScale(interpolatedTime, interpolatedTime);
    }
}
initialize方法是初始化时的回调方法,设置动画的一些属性(持续时间,插值器等)

applyTransformation方法在动画执行的过程中被反复调用,android反复调用这个方法来模拟动画,interpolatedTime参数根据initialize方法中设置的时间,从0到1变化,每次回调时的值都不同。当interpolatedTime值增长到1时,动画播放结束。可以利用applyTransformation这个方法进行对象的矩阵变换,以形成动画。以上的例子是根据时间逐渐增长listview的大小。

setInterpolator(new LinearInterpolator())指定动画匀速运行,matrix.setScale(interpolatedTime, interpolatedTime)让view的大小随时间增长变大。

稍微改进一下上面的动画:

public class ViewAnimationActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_layout);
        setupListView();
        this.setupButton();
    }
    private void setupListView()
    {
    	  String[] listItems = new String[] {
    	        "Item 1", "Item 2", "Item 3",
    	        "Item 4", "Item 5", "Item 6",
    	  };
    	  
    	  ArrayAdapter<String> listItemAdapter = 
    		   new ArrayAdapter<String>(this
    		           ,android.R.layout.simple_list_item_1
    		           ,listItems);
    	  ListView lv = (ListView)this.findViewById(R.id.list_view_id);
    	  lv.setAdapter(listItemAdapter);
    }
    private void setupButton()
    {
       Button b = (Button)this.findViewById(R.id.btn_animate);
       b.setOnClickListener(
           new Button.OnClickListener(){
             public void onClick(View v)
             {
                animateListView();
             }
           });
    }
    private void animateListView()
    {
  	  ListView lv = (ListView)this.findViewById(R.id.list_view_id);
  	  float currX = (float) (lv.getHeight() / 2.0);
  	  float currY = (float) (lv.getWidth() / 2.0);
//  	  lv.startAnimation(new ViewAnimation2());
  	  lv.startAnimation(new ViewAnimation(currX, currY));
    }

public class ViewAnimation extends Animation {
	float centerX, centerY;
    public ViewAnimation(float cx, float cy)
    {
    	centerX = cx;
    	centerY = cy;
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        setDuration(2500);
        setFillAfter(true);
        setInterpolator(new LinearInterpolator());
        
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
    	applyTransformationNew(interpolatedTime,t);
    }
    
    protected void applyTransformationNew(float interpolatedTime, Transformation t) 
    {
        final Matrix matrix = t.getMatrix();
        matrix.setScale(interpolatedTime, interpolatedTime);
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }
}

注意到applyTransformationNew方法中多了preTranslate和postTranslate,相当于在动画缩放前后进行了位置平移。

视图动画其他更丰富的实现效果有待深入研究。


三.属性动画

旧的动画API位于包android.view.animation中,新的动画API位于包android.animation中

属性动画主要有以下一些特性

ValueAnimator 值动画生成器
ObjectAnimator 对象动画生成器
ViewPropertyAnimator 视图属性动画生成器
AnimatorSet 动画生成器集合
Animation Listeners 动画生成器监听
TypeEvaluator 类型求值器
PropertyValuesHolder 属性值持有者
Interpolators 插值器
Keyframes 关键帧
Layout Changes 布局转变
Declaring Animations in XML  xml加载动画


1.ValueAnimator 值动画生成器

ValueAnimator持续跟踪动画的状态,比如动画运行时间和某个属性的值,它包含一个时间插值器TimeInterpolator,定义了动画的插值器。还包含一个类型求值器TypeEvaluator ,定义了如何计算动画的属性值。比如在下图中,view的坐标随着时间的增加而以整数增加,并且是加速增长的,在这个动画中,插值器是AccelerateDecelerateInterpolator,类型求值器是IntEvaluator


看一下API GUIDE上给出的示意图:

对应的代码以及解释:

    	ValueAnimator anim = ValueAnimator.ofInt(10, 200);
    	anim.setDuration(5000);
    	anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
			
			@Override
			public void onAnimationUpdate(ValueAnimator animation) {
				// TODO Auto-generated method stub
	         int value = (Integer) animation.getAnimatedValue();
			}
		});
    	anim.start();

ValueAnimator的实例设置持续时间duration,开始,结束值startPropertyValue和endPropertyValue,ValueAnimator.AnimatorUpdateListener监听属性值的变化,onAnimationUpdate方法在动画执行过程中不断被回调,animation.getAnimatedValue获得某个时间点上的属性值


2.ObjectAnimator对象动画生成器
ObjectAnimator是ValueAnimator的子类,构造和其类似,不过多了个property的参数
以下是一个TextView的淡入动画效果的实现
	        ObjectAnimator fadeOut = 
	        	ObjectAnimator.ofFloat(tv, "alpha", 0f, 100f);
	        fadeOut.setDuration(5000);
	        fadeOut.start();

其中tv是个textview实例,alpha是属性,这个属性不是随便设置的,是因为textview中存在setAlpha这个方法(继承自view),如果你给这个参数指定了一个值,那么实现动画的对象中必须存在对应的set的方法。可以传入的参数有:

translationX
translationY
rotation
rotationX
rotationY
scaleX
scaleY
pivotX
pivotY

y
alpha

3.AnimatorSet 动画生成器集合

AnimatorSet可以把多个动画组合在一起,并且决定他们的播放顺序

textview先淡出再淡入的效果:

    	m_tv.setAlpha(1f);
        ObjectAnimator fadeOut = 
        	ObjectAnimator.ofFloat(tv, "alpha", 0f);
        ObjectAnimator fadeIn = 
        	ObjectAnimator.ofFloat(tv, "alpha", 1f);
        AnimatorSet as = new AnimatorSet();
        as.playSequentially(fadeOut,fadeIn);
        as.setDuration(5000); 
        as.start();
这里new 了一个AnimatorSet对象,并且将2个ObjectAnimator对象当作参数传入playSequentially方法,使它们顺序执行

API GUIDE上的一个例子:

AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();
这段代码的作用是先后播放2个AnimatorSet,播放的动画顺序是bounceAnim->squashAnim1, squashAnim2, stretchAnim1, and stretchAnim2(同时)->bounceBackAnim->fadeAnim

4.ViewPropertyAnimator视图属性动画生成器

ViewPropertyAnimator有些类似于AnimatorSet,它提供了更简便的方法在动画中同时改变view的属性

下面的代码作用是把textview  从屏幕的右下角移动到原来的位置

    	m_tv.setAlpha(1f);
    	float h = m_tv.getHeight();
    	float w = m_tv.getWidth();
    	float x = m_tv.getX();
    	float y = m_tv.getY();
    
    	m_tv.setX(w);
    	m_tv.setY(h);
    	
    	ViewGroup layout = (ViewGroup)m_tv.getParent();
    	layout.setClipChildren(true);
    	
    	ViewPropertyAnimator vpa = m_tv.animate();
    	vpa.x(x);
    	vpa.y(y);
    	
        vpa.setDuration(5000); 
        vpa.setInterpolator(
        		new  AccelerateDecelerateInterpolator());
5.动画监听Animation Listeners

Animator.AnimatorListener 监听开始,结束,重复,退出等事件

ValueAnimator.AnimatorUpdateListener 在上面ValueAnimator的例子中演示过,在动画执行过程中不断回调,可以监听动画属性值的变化
6.TypeEvaluator 类型求值器

Animator提供ofInt,ofFloat来设置动画属性的变化范围,如果想实现自己的变化,可以实现TypeEvaluator接口

public class FloatEvaluator implements TypeEvaluator {

    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}

7.PropertyValuesHolder 属性值持有者

PropertyValuesHolder用来在动画中保存一个或者多个view的属性名/目标值,用来生成动画。以下的代码和上面ViewPropertyAnimator的例子实现的是一样的动画效果

    	m_tv.setAlpha(1f);
    	float h = m_tv.getHeight();
    	float w = m_tv.getWidth();
    	float x = m_tv.getX();
    	float y = m_tv.getY();
    
    	m_tv.setX(w);
    	m_tv.setY(h);
    	PropertyValuesHolder pvhX = 
    		PropertyValuesHolder.ofFloat("x", x);

    	PropertyValuesHolder pvhY = 
    		PropertyValuesHolder.ofFloat("y", y);
    	
    	ObjectAnimator oa
    	= ObjectAnimator.ofPropertyValuesHolder(m_tv, pvhX, pvhY);
        oa.setDuration(5000);
        oa.setInterpolator(
        		new  AccelerateDecelerateInterpolator());
        oa.start();
8.Keyframes 关键帧

Keyframes由时间/值的键值对组成,顾名思义,就是指定动画一些关键时间点上的属性值,它可以和PropertyValuesHolder配合使用

下面的代码作用是,动画运行时,textview从右下移动到左上,当时间到达20%时,透明度变为80%,一半时间时,透明度变为20%,到80%的时间时,透明度变回80%

    	m_tv.setAlpha(1f);
    	
    	float h = m_tv.getHeight();
    	float w = m_tv.getWidth();
    	float x = m_tv.getX();
    	float y = m_tv.getY();
        //3个时间点不同的透明度设置
    	Keyframe kf0 = Keyframe.ofFloat(0.2f, 0.8f);
    	Keyframe kf1 = Keyframe.ofFloat(.5f, 0.2f);
    	Keyframe kf2 = Keyframe.ofFloat(0.8f, 0.8f);    	
    	PropertyValuesHolder pvhAlpha = 
    		PropertyValuesHolder.ofKeyframe("alpha", kf0, kf1, kf2);
    	
    	PropertyValuesHolder pvhX = 
    		PropertyValuesHolder.ofFloat("x", w, x);
    	
    	//end frame
        ObjectAnimator anim = 
        	ObjectAnimator.ofPropertyValuesHolder(m_tv, pvhAlpha,pvhX);
        anim.setDuration(5000);
        anim.start();
9.Layout Changes 布局转变

属性动画的API可以在ViewGroup上设置动画,系统为ViewGroup的子view的显示和消失都设置了默认的动画,要启用它们只要在xml文件中加上

android:animateLayoutChanges="true"

下面代码展示了gridlayout中按钮增加/删除的默认动画

public class MainActivity extends Activity {

    private int numButtons = 1;

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_animations_by_default);

        final GridLayout gridContainer = (GridLayout) findViewById(R.id.gridContainer);

        Button addButton = (Button) findViewById(R.id.addNewButton);
        addButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Button newButton = new Button(MainActivity.this);
                newButton.setText(String.valueOf(numButtons++));
                newButton.setOnClickListener(new View.OnClickListener() {
                    public void onClick(View v) {
                        gridContainer.removeView(v);
                    }
                });
                gridContainer.addView(newButton, Math.min(1, gridContainer.getChildCount()));
            }
        });
    }

}
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add Button"
        android:id="@+id/addNewButton"
        />
    <GridLayout
        android:columnCount="4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/gridContainer"
        android:animateLayoutChanges="true"
        />
</LinearLayout>

也可以自己设置相应的效果:

        LinearLayout yourLayout = (LinearLayout)findViewById(R.layout.xx);
        LayoutTransition lt = new LayoutTransition();
        yourLayout.setLayoutTransition(lt);
        Animator a = lt.getAnimator(LayoutTransition.APPEARING);
        ObjectAnimator oa = ..... //自定义的ObjectAnimator
        lt.setAnimator(LayoutTransition.APPEARING, oa);

10.Declaring Animations in XML 在xml中定义动画

属性动画也可以用xml定义动画,但是xml文件不是放在res/anim下,而是放在res/animator下

标签对应的动画类:

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

<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>

要使xml定义的动画生效,需要加上以下代码:

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

参考资料:http://developer.android.com/guide/topics/graphics/prop-animation.html

                   http://blog.csdn.net/ohehehou/article/details/12144693

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值