9_运用手机多媒体

本文探讨了如何在Android中运用手机多媒体,包括创建通知渠道、使用通知、调用摄像头和相册,以及播放音频和视频。介绍了如何在不同场景下创建和管理通知,以及MediaPlayer类在播放音频和视频时的工作流程。
摘要由CSDN通过智能技术生成

运用手机多媒体

使用通知

创建通知渠道

其实通知这个功能设计初衷是好的,但是他已经被开发者都玩坏了.其实就开发者而言,用户用自己的软件越多越好,所以就会频繁的去通知,但是就用户而言,这样就会补没有意义的垃圾影响生活,在过去用户要么选择通知,要么选择不通知,只能一刀切.
所以引入了通知渠道的概念
用户可以自行选择关心哪些信息,自由的选择这些通知渠道的重要程度,然后决定是通知的方式(响铃,震动,不接受…)

创建通知渠道的详细步骤

  1. 需要一个NotificationManager对通知进行管理,可以用过ContextgetSystemService()方法获取,getSystemService()方法接收一个字符传参数用于确定获取系统的那个服务,这里我们传入Context.NOTIFICATION_SERVICE即可,同时,获取NotificationManager的实例可以写成
     val manager=getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    
  2. 接下来使用NotificationChannel类构建一个通知渠道,并调用NotificationManagerNotificationChannel方法完成创建,由于NotificationChannel类和createNotificationChannel()方法都是Android8.0新增的API,所以还需要版本判断,
     if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.0)){
         //渠道Id,渠道名称,重要级别
         val channel=NotifiChannel(channelId,channelName,importance)
         manager.createNotificationChannel(channel)
     }
    
     重要级别有:
         紧急(发出声音并显示为提醒通知)	   IMPORTANCE_HIGH
         高(发出声音)	                IMPORTANCE_DEFAULT
         中等(没有声音)	                    IMPORTANCE_LOW
         低(无声音并且不会出现在状态栏中)	IMPORTANCE_MIN
    

通知(Notification)的基本用法

  • 可以在Activity中创建
  • 可以在BroadcastReceier中创建
  • 可以在Service中创建

流程

  1. 需要一个Builder构造器来创建Notification对象,由于AndroidAPI不太稳定,变动时常会有,所以我们会用过AndroidX中提供的兼容APIAndroidX库中提供了一个NotificationCompat类,使用这个类的构造器创举建了Notification对象
     val notification=NotificationCompat.Builder(context,channelId).build()
     //第一个参数是上下文
     //第二个是渠道的Id,需要和我们在创建通知渠道时时指定的渠道id相匹配
    
  2. 上面只是创建了一个空的Notification对象,我们可以在最后的build()之前连缀任意多的设置方法来创建一个丰富的Notification对象
     val notification=NotificationCompat.Builder(context,channelId)//NotificationCompat可以翻译成通知兼容器
     .setContentTitle("通知的标题")
     .setContentText("通知的内容")
     .setSmallIcon(R.drawable.small_icon)//通知的小图标
     .setLargeIcon(BitmapFactory.decodeResource(getResources),R.drawable,R.drawable.large_icon)//通知的大图标
     .build()
    
  3. 以上工作完成之后,只需要调用NotificationManagernotify()方法就可以让通知显示出来
     manager.nofity(1,notification)
    

实践出真知

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ···>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/sendNotice"
        android:text="发送通知"/>
</LinearLayout>
class MainActivity : AppCompatActivity() {

    lateinit var binding:ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //获取viewBinding来操作布局
        binding=ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)


        //得到通知管理器
        val manager=getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
            //设置一个通知渠道
            //渠道Id,渠道名称,重要级别
            val channel= NotificationChannel("normal","Normal",IMPORTANCE_DEFAULT)

            //通过通知管理器创建一个通知渠道
            manager.createNotificationChannel(channel)
            //系统只会创建一次通知渠道,下次再执行代码系统就会检测到通知渠道,然后不创建
        }

        binding.sendNotice.setOnClickListener {
            val notification= NotificationCompat.Builder(this,"normal")
                .setContentTitle("通知的标题")
                .setContentText("通知的内容")
                .setSmallIcon(R.drawable.small_icon)//通知的小图标
                .setLargeIcon(BitmapFactory.decodeResource(resources,R.drawable.large_icon))//通知的大图标
                .build()
            //显示通知,每个通知指定的id必须都不同
            manager.notify(1,notification)
        }

    }
}

但是我们会发现通知点击会没有效果,可是我们自己的手机上通知点击是有效果的啊?这就涉及到一个新概念了.就是我们下面要看的.

PendingIntent

他和Intent都是意图,可以启动Activity,Service以及发送广播,但不同的是PendingIntent更倾向于在某个合适的时机执行某个动作,而Intent是立即指定动作

  • PendingIntent提供了几个静态方法用于获取PendingIntent的实例
    • getActivity()
      -getBroadcast()
    • getService()
  • 几个方法的参数都是一样的:
    • contxet:上下文
    • 第二个参数一般用不到,传入0就行
    • Intent对象,通过这个来构建出PendingIntent的意图
    • 第四个参数用于确定PendingIntent的行为
      • FLAG_ONE_SHOT 获取的PendingIntent只能使用一次
      • FLAG_ON_CREATE 获取PendingIntent,若描述的Intent不存在则返回NULL值.如果描述的PendingIntent已经存在,则在使用新的Intent之前会先取消掉当前的。你可以通过这个去取回,并且通过取消先前的Intent,更新在Intent中的数据。这能确保对象被给予新的数据。如果无法保证唯一,考虑使用flag_update_current
      • FLAG_CANCEL_CURRENT 会关闭之前PendingIntent
      • FLAG_UPDATE_CURRENT 会更新之前PendingIntent的消息
      • 传0就不会有啥效果
        实践出真知
NotificationActivity中
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ···
    android:orientation="vertical">

    <!--android:layout_centerInParent="true"是在RelativeLayout布局中在父布局的正中心-->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textSize="24sp"
        android:text="通知活动"
        android:id="@+id/notificationTextView"
        android:gravity="center"/>
</RelativeLayout>
class NotificationActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_notification)

        val manager=getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        //进入这个活动之后就会关闭Id是1的通知
        manager.cancel(1)
    }
}
class MainActivity : AppCompatActivity() {

    lateinit var binding:ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ···

        binding.sendNotice.setOnClickListener {
            val intent=Intent(this,NotificationActivity::class.java)
            val pi=PendingIntent.getActivity(this,0, intent,0)
            val notification= NotificationCompat.Builder(this,"normal")
                ···
                .setContentIntent(pi)//设置通知的意图内容
                .setAutoCancel(false)//设置通知是不是点击完了之后就自动关闭
                .build()
            //显示通知,每个通知指定的id必须都不同
            manager.notify(1,notification)
        }

    }
}

通知的进阶技巧(小声逼逼:只有少部分常用API)

  • setStyle()方法:这个方法允许我们构建出富文本的通知内容。也就是说,通知中不光可以有文字和图标,还可以有其他的,setStyle()接收一个NotificationCompat.Style参数,这个参数是用来构建具体的富文本信息,比如长文字、图片
      比如:
      val notification= NotificationCompat.Builder(this,"normal")
          ···
          .setContentText("hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")
          .build()
      直接设置一长串内容的话通知的时候是无法显示完全的,显示不了的就会用省略号代替
    
      如果我们用setStyle()的话
      val notification= NotificationCompat.Builder(this,"normal")
          ···
          .setStyle(NotificationCompat.BigTextStyle().bigText("hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"))
          .build()
      就可以显示完全了
      
      其实setStyle()方法中我们是创建了一个NotificationCompat.BigTextStyle对象,这个对象就是用来封装长文字信息的,只要调用他的bigText()方法并将文字内容传入就可以了
    
      还可以显示图片
      val notification= NotificationCompat.Builder(this,"normal")
          ···
          .setStyle(NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(resources,R.drawable.big_image)))
          .build()
      其实setStyle()方法中我们是创建了一个NotificationCompat.BigPictureStyle对象,这个对象就是用来封装图片信息的,只要调用他的BitmapFactory()decodeResources()方法并将图片解析成Bitmap,再传入Bitmap()就可以了
    
  • 不同重要等级的通知渠道对通知的行为的影响
    需要注意的时通知渠道一旦通过代码创建出来,开发者是不能改变其重要等级的,只能由用户来改
      class MainActivity : AppCompatActivity() {
          ···
    
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              ···
              val manager=getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    
              if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
                  ···
                  val channel2=NotificationChannel("important","Important", IMPORTANCE_HIGH)
                  //这里把通知渠道设定成了高,那么就重要成功很高了,其实还可以试试其他的级别
                  manager.createNotificationChannel(channel2)
              }
    
              binding.sendNotice.setOnClickListener {
                  ···
                  val intent1=Intent(this,NotificationActivity::class.java)
                  val pi1=PendingIntent.getActivity(this,1,intent1,0)
                  val notification1=NotificationCompat.Builder(this,"important")
                      ···
                      .build()
                  manager.notify(2,notification1)
              }
    
          }
      }
      
    

到了万众期待的缓解了-调用摄像头和相册

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.bo.a2_learncameraalbum">
    <!--权限不能忘-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        ···
        <!--在代码里面用到了FileProvider所以需要注册
        android:name的值是固定的
        android:authorities的值必须要跟代码中的FileProvider.getUriForFile()方法的第二个参数一样
        android:exported是否支持其它应用调用当前组件
        android:grantUriPermissions是否授予访问Uri的权限-->
        <provider
            android:authorities="com.bo.a2_learncameraalbum.fileprovider"
            android:name="androidx.core.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
            <!--meta-data指定共享路径-->
        </provider>
    </application>

</manifest>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ···>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/takePhotoBtn"
        android:text="拍照"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/fromAlbumBtn"
        android:text="查看相册里的照片"/>

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView"
        android:layout_gravity="center_horizontal"/>

</LinearLayout>

共享路径的xml配置(./xml/file_paths.xml)

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="/"/>
    <!--external-path 是用来指定Uri共享的
        name里面的值可以随便填
        path里面的值表明共享的具体路径,这里一个/表明是将整个SD卡共享-->
</paths>

注释之王登场

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    val takePhoto=1
    lateinit var imageUri: Uri
    lateinit var outputImage: File

    val formAlbum=2

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        //点击拍照触发事件,拍完照之后会在Activity上显示这张照片
        binding.takePhotoBtn.setOnClickListener {
            /*创建File对象,储存拍照后的照片,拍摄的照片存放在手机SD卡的应用关联缓存目录下
            应用关联缓存目录:SD卡中专门用于存放当前应用缓存数据的位置
            File(第一个参数,第二个参数)
            第一个参数:externalCacheDir-----具体的路径时/sdcard/Android/data/<package name>/cache
            第二个参数:文件名称
            这两个参数会被拼接成一个字符串路径,然后解析成内容Uri,存放在这个路径下
            为什么用关联缓存目录来存放图片?:
            Android6.0开始读写SD卡就成了危险权限,如果放在其他目录就会要申请权限,而再这个目录下就不需要
            Android10.0开始公有的SD卡目录已经不再能被应用程序直接访问了,而是要用作用域存储才行
            作用域存储详情要去gl大大的微信公众号看看*/
            outputImage= File(externalCacheDir,"output_image.jpg")

            //必须检查以免出错,检查一下这个文件存不存在,存在就替换掉
            if(outputImage.exists()){
                outputImage.delete()
            }
            outputImage.createNewFile()

            /*如果当前的Android版本低于7.0就可以直接调用Uri的fromFile()方法,把File对象转化成Uri对象
            此时的Uri对象对应的时这个文件的本地真实路径
            如果不低于的话就需要用FileProvider类的getUriForFile(第一个参数,第二个参数,第三个参数)方法将一个File对象转化成封装过的Uri对象
                    第一个参数:context上下文
                    第二个参数:任意一个字符串,但是还是用项目的authority最好(保持唯一性)
                    第三个参数:需要转化的File对象*/
            /*那为什么要这样if-else呢?
                因为从Android7.0开始直接使用真实本地地址的Uri被认为是不安全的
                会抛出一个FileUriExposedException异常
                而FileProvider是一种特殊的ContentProvider,它使用和ContentProvider相似的机制来保护数据*/
            imageUri=if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
                FileProvider.getUriForFile(this,"com.bo.a2_learncameraalbum.fileprovider",outputImage)
            }else{
                Uri.fromFile(outputImage)
            }
            //启动相机程序(隐式intent)
            //启动相机的动作是"android.media.action.IMAGE_CAPTURE"
            val intent=Intent("android.media.action.IMAGE_CAPTURE")
            //通过intent的附加信息来指定相机拍摄的照片输出(MediaStore.EXTRA_OUTPUT)的位置(imageUri)
            //位置是一个Uri对象来制定的
            intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri)
            //开启一个活动(这个方法开启的活动运行完了之后会回调onActivityResult(),结果返回到onActivityResult()的data中)
            startActivityForResult(intent,takePhoto)
        }

        binding.fromAlbumBtn.setOnClickListener {
            //打开文件管理器
            val intent=Intent(Intent.ACTION_OPEN_DOCUMENT)//指定意图去打开文件
            intent.addCategory(Intent.CATEGORY_OPENABLE)//过滤:只打开能打开的文件

            //指定显示图片
            intent.type="image/*"//只能显示image目录下的图片
            startActivityForResult(intent,formAlbum)
        }


    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when(requestCode){
            takePhoto->{
                if(resultCode== Activity.RESULT_OK){
                    //如果运行的结果是成功的话就显示一下图片
                    /*还记得吗?现在的Android要跨程序获取数据就要借助contentResolver类
                    这里通过contentResolver开辟一个文件输入流,通过路径找到文件读入
                    再然后通过位图工厂把字节流形式的数据转化成一个位图*/
                    val bitmap=BitmapFactory.decodeStream(contentResolver.openInputStream(imageUri))
                    binding.imageView.setImageBitmap(rotateRequired(bitmap))
                }
            }
            formAlbum->{
                if(resultCode==Activity.RESULT_OK&&data!=null){
                    data?.data.let { uri->
                        val bitmap= uri?.let { getBitmapFromUri(it) }
                        binding.imageView.setImageBitmap(bitmap)
                    }
                }
            }
        }
    }

    private fun getBitmapFromUri(uri: Uri): Bitmap? {
        //通过contentResolver用只读的方式加载文件
        return contentResolver.openFileDescriptor(uri,"r")?.use{
            BitmapFactory.decodeFileDescriptor(it.fileDescriptor)
                //然后把文件转成位图
            //use可以在使用完这个文件之后自动关上
        }
    }

    //手机拍摄的时候图片可能拍成歪的,这个函数就是把他扶正
    private fun rotateRequired(bitmap: Bitmap): Bitmap {
        val exif=ExifInterface(outputImage.path)
        val organization=exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL)
        return when(organization){
            ExifInterface.ORIENTATION_ROTATE_90->rotateBitmap(bitmap,90)
            ExifInterface.ORIENTATION_ROTATE_180->rotateBitmap(bitmap,180)
            ExifInterface.ORIENTATION_ROTATE_270->rotateBitmap(bitmap,270)
            else->bitmap
        }
    }
    //具体扶正的逻辑
    private fun rotateBitmap(bitmap: Bitmap,degree:Int):Bitmap{
        val matrix=Matrix()
        matrix.postRotate(degree.toFloat())
        val rotatedBitmap=Bitmap.createBitmap(bitmap,0,0,bitmap.width,bitmap.height,matrix,true)
        bitmap.recycle()//将不需要的Bitmap对象回收
        return rotatedBitmap
    }
}

播放多媒体

播放音频

Android中播放啊音频是要通过MediaPlayer类实现的
bqNrqK.png

Mediaplayer的工作流程

  • 创建一个Mediaplayer对象
  • =>调用setDataSource()方法设置音频文件的路径
  • =>调用prepare()方法使MediaPlayer()进入准备状态
  • =>调用start()方法播放音频
  • =>调用pause()暂停播放
  • =>调用reset()停止播放

操练起来

布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ···>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/play"
        android:text="播放音频"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/pause"
        android:text="暂停播放"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/stop"
        android:text="停止播放"/>

</LinearLayout>
主活动

class MainActivity : AppCompatActivity() {

    private lateinit var binding:ActivityMainBinding

    private val mediaPlayer=MediaPlayer()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding= ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        initMediaPlayer()

        binding.play.setOnClickListener {
            if(!mediaPlayer.isPlaying)
                mediaPlayer.start()
        }

        binding.pause.setOnClickListener {
            if(mediaPlayer.isPlaying)
                mediaPlayer.pause()
        }

        binding.stop.setOnClickListener {
            if(mediaPlayer.isPlaying){
                mediaPlayer.reset()//重置mediaPlayer,重置完了之后需要重新初始化mediaPlayer
                initMediaPlayer()
            }
        }
    }

    private fun initMediaPlayer(){
        /*放在assets中的文件是可以直接加载的到assets管理器中的*/
        val assetsManager=assets
        /*调用openfd()方法打开句柄
                反正就跟把柄一个意思,掌握了你的把柄,你就难逃法网(系统),(系统)要找到你就可以根据句柄来找你*/
        val fd=assetsManager.openFd("music.mp3")
        mediaPlayer.setDataSource(fd.fileDescriptor,fd.startOffset,fd.length)
        mediaPlayer.prepare()
    }

    override fun onDestroy() {
        super.onDestroy()
        mediaPlayer.stop()//停止mediaPlayer的功能
        mediaPlayer.release()//释放mediaPlayer的相关资源
    }
}

播放视频

播放视频主要是使用VideoView类实现
bqDNmn.png
但是VideoView并不是一个万能的视频播放工具,他对视频格式的支持和播放效率方面有很大不足

操练起来

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/play"
            android:text="播放视频"/>

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/pause"
            android:text="暂停播放"/>

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/stop"
            android:text="停止播放"/>
    </LinearLayout>

    <VideoView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/videoView"/>

</LinearLayout>
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding= ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        //通过解析路径的方式获取到文件的Uri对象
        val uri= Uri.parse("android.resource://$packageName/${R.raw.video}")
        binding.videoView.setVideoURI(uri)//通过Uri对象把视频设置到viewView空间中去

        binding.play.setOnClickListener {
            if(!binding.videoView.isPlaying)
                binding.videoView.start()
        }

        binding.pause.setOnClickListener {
            if(binding.videoView.isPlaying)
                binding.videoView.pause()
        }

        binding.stop.setOnClickListener {
            if(binding.videoView.isPlaying){
                binding.videoView.resume()//重新开始播放
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        binding.videoView.suspend()//把videoView占用的资源释放掉
    }
}

可恶,居然只有画面,没有声音,也不知道正不正常

y.setOnClickListener {
if(!binding.videoView.isPlaying)
binding.videoView.start()
}

    binding.pause.setOnClickListener {
        if(binding.videoView.isPlaying)
            binding.videoView.pause()
    }

    binding.stop.setOnClickListener {
        if(binding.videoView.isPlaying){
            binding.videoView.resume()//重新开始播放
        }
    }
}

override fun onDestroy() {
    super.onDestroy()
    binding.videoView.suspend()//把videoView占用的资源释放掉
}

}

**可恶,居然只有画面,没有声音,也不知道正不正常**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值