通知(Notifications)

通知(Notifications)


如需转载,请注明地址http://blog.csdn.net/carrey1989/article/details/9027585


     我们可以通过notification来在应用UI之外的地方来向用户展示信息。当你告诉系统来发布一个notification的时候,系统会首先在notification area里展示一个图标,如果用户想要查看通知的详细信息,需要打开notification drawer notification area notification drawer 都是由系统控制的区域,用户在任何时候都可以进行查看。

图片1. notification area里显示的通知图标

图片2. notification drawer里的通知
通知的设计:
     通知作为Android UI重要的一部分,拥有其独特的设计准则。要学习如何设计通知以及通知的交互,请阅读Android Design Guide 中的 Notification 标题内容。
注意:
     除了特别说明的地方,本文中所使用的NotificationCompat.Builder类都是来自于第四版Support Library。Notification.Builder类在Android3.0开始被添加进SDK。

在Notification中显示的元素
     在 notification drawer里出现的通知,根据版本和 drawer状态的不同,可以有两种样式。
Normal View样式
     这是标准和常见的 notification drawer中通知的样式。
Big View样式
     在通知展开以后,可以看到的一个更大的视图,比Normal View样式大很多,内容也更多。Big View是从Android4.1以后添加的一个新特性,用来在通知被展开以后显示更大的通知视图。
接下来我们来讲解上述两种样式。
Normal View
     一个Normal View样式的通知区域最高可以有64dp高,即使你创建一个big view样式的通知,在它被展开之前也会显示为normal view的样式,下面是normal view的一个例子:

图3.normal view样式的通知
     上面插图中的编号含义如下:
     1.Content title
     2.Large icon
     3.Content text
     4.Content info
     5.Small icon
     6.通知发布的时间。你可以自己通过setWhen()方法设置一个精确值,如果你不做设置,那么这个时间默认为系统收到通知的时间。
Big View
     big view样式的通知只有在通知被展开的情况下才会显示。“被展开”的含义是发生在这个big view通知位于整个 notification drawer的顶部的时候,或者当用户使用手势展开这个通知的时候。可展开的通知是从Android4.1开始可用的。
     下面的屏幕截图显示了big view style中的inbox-style子样式的通知:

图片4 big view样式通知。
     注意big view样式与normal view样式的大多数内部元素是一样的。唯一的不同是插图编号7,the details area。每一个big view style都可以用几种不同的样式来设置这个区域,这几种样式是:
Big picture style
     the details area 包含了一个最多256dp高的位图。
Big text style
     包含了一个很大的文本块。
Inbox style(上面插图中的)
     包含了一个多行文字的区域。

     所有上面的big view style 还可以有下面这些内部元素,这些元素在normal view中是没有的。
Big content title
     允许你重写normal view的content title,重写的这个title只在通知被展开的时候才出现。
Summary text
     允许你在the details area下方添加一行文字。
下面的章节中将详细讲解实现一个big view style类型的通知的方法。

创建一个通知
     我们使用NotificationCompat.Builder对象来指定通知的UI信息以及action信息。要创建一个Notification对象,我们可以使用NotificationCompat.Builder.build()方法,这个方法将返回一个Notification对象,其中包含了你使用NotificationCompat.Builder对象所做的设定。要发布一个Notification,将前面得到的Notification对象通过NotificationManager.notify()方法来传递给系统。

Notification必须具备的内容
  • a small icon,通过setSmallIcon()来设置
  • a title,通过setContentTitle()来设置
  • Detail text,通过setContentText()来设置
其他可选的notification内容和设置
     所有其他的notification设置和内容都是可选的。要学习其他内容和设置的使用,可以阅读NotificationCompat.Builder文档。

Notification actions(通知点击的活动)
     尽管actions是可选的,你还是应该至少给你的notification添加一个action。一个可以允许用户直接从notification跳转到应用中的一个Activity的action,在这个Activity中,用户可以查看更多的与notification中的信息相关的内容或者执行更多的相关操作。

     一个notification可以提供多种的actions。你始终应该定义一个能够在用户点击notification的时候触发的action;通常来讲,这个action应该打开你应用中的一个Activity。你也可以添加按钮到notification内部,这些按钮可以提供额外的操作,例如让一个闹钟短暂延迟闹铃或者立刻回复一个短信;这个特性在android4.1中被添加。如果你使用了额外添加的按钮,你必须在你应用中的一个activity内实现相同的功能。这主要是出于兼容性的考虑,下面的章节会有相关的介绍。

     在notification内部,启动一个Activity的action是通过包含在一个PendingIntent内部的Intent定义的。要把PendingIntent关联到一个手势,可以调用NotificationCompat.Builder的相关方法。例如,如果你想要在用户点击notification的时候启动一个Activity,你可以将PendingIntent参数添加到setContentIntent()方法。

     在用户点击notification的时候启动一个Activity是最常见的场景。你也可以在用户消除一个notification的时候启动Activity。从Android4.1开始,你可以通过点击一个定义了action的Button启动Activity,要学习这部分内容可以阅读NotificationCompat.Builder文档。

创建一个简单的notification
     下面的内容举例说明了如何创建一个简单的notification,这个notification可以指定用户点击它的时候启动一个Activity。留意其中使用TaskStackBuilder对象来创建PendingIntent部分的代码,这种使用方法在下面的内容中会详细的解释。
NotificationCompat.Builder mBuilder =
        new NotificationCompat.Builder(this)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!");
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(this, ResultActivity.class);

// stack builder 对象包含了一个为用户启动的Activity创建的back stack
// 这确保了用户在点击Back的时候会从这个Activity直接回到Home页面
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// 为resultIntent创建back stack(但是没有在栈中添加resultIntent本身)
stackBuilder.addParentStack(ResultActivity.class);
// 添加resultIntent到栈顶
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
        stackBuilder.getPendingIntent(
            0,
            PendingIntent.FLAG_UPDATE_CURRENT
        );
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 通过下面的mId参数,可以在后续操作中用来更新和移除notification
mNotificationManager.notify(mId, mBuilder.build());


创建一个big view风格的notification
     要创建一个big view风格的notification,首先创建一个NotificationCompat.Builder对象,为其设置必要的normal view style风格的内容和设置,然后调用NotificationCompat.Builder.setStyle()方法,在该方法的参数中传入big view风格的对象(三个之一)。

     记住可展开的通知在android4.1之前的版本是不可用的,要处理不同版本之间的兼容问题,请阅读下面的章节。

     下面的代码片段举例说明了实现一个inbox style风格的big view notification:
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
    .setSmallIcon(R.drawable.notification_icon)
    .setContentTitle("Event tracker")
    .setContentText("Events received")
NotificationCompat.InboxStyle inboxStyle =
        new NotificationCompat.InboxStyle();
String[] events = new String[6];
// Sets a title for the Inbox style big view
inboxStyle.setBigContentTitle("Event tracker details:");
...
// Moves events into the big view
for (int i=0; i < events.length; i++) {

    inboxStyle.addLine(events[i]);
}
// Moves the big view style object into the notification object.
mBuilder.setStyle(inBoxStyle);

// Issue the notification here.
处理不同版本间的兼容问题
     对于某一个版本的android,并不是所有的notification特性都可用,即使这些特性的设置方法在support library v4中的NotificationCompat.Builder类中都有提供。比如,action buttons,依赖于可展开的notifications,只会出现在android4.1或者更高版本,因为可展开的notification本身只在android4.1和更高版本可用。
     要确保最好的兼容性,应该使用NotificationCompat以及其子类来创建notifications,特别是使用NotificationCompat.Builder类。此外,在实现一个notification的时候遵从下面的步骤:

     1.对所有的用户提供notification的完整功能,不管用户使用的是什么版本的android。要做到这一点,确保所有的这些功能都可以在你的应用中的一个activity中得到。你可能需要添加一个新的Activity来实现这一点。
        举个例子,如果你想要使用addAction()方法来为一个控件来提供停止和播放媒体的操作,首先在一个Activity中实现这些控件和功能。
     2.确保所有的用户可以在上一步创建的Activity中接触到其中定义的功能,要实现这一步,需要定义在用户点击notification的时候启动这个Activity。创建一个PendingIntent,然后用setContentIntent()将其添加到Notification。
     3.接下来添加expanded notification新特性给Notification(在android4.1以及以后版本可以用)。记住所有你添加的新特性的功能也必须在之前定义的Activity中实现。

管理Notifications
     当你需要对同一类型的事件信息发布多次通知的时候,你应该避免创建一个完全新的notification。而是应该考虑更新之前的notification,通过改变或者增加之前notification的参数。
     比如,Gmail通过增加未读邮件数量以及添加替换每个邮件的摘要到notification来提醒用户有新邮件收到。这被称作"stacking"the notification(类似一个栈结构的理解),在Notification Design guide中有关于这方面设计的细节。
注意:
     这个Gmail特性需要inbox style风格的big view style notification的支持,这种特性是从android4.1添加的可以展开的的Notification特性的一部分。
     下面来讲解如何更新和删除notification。
更新notifications
     要设置一个Notification被更新,在第一次通过NotificationManager.notify(ID,notification)方法发布了notification之后,更新之前的或者创建新的NotificationCompat.Builder对象,使用其创建一个Notification对象,然后使用与前一次发布notification相同的ID来再一次发布notification。如果上一个notification依然可见,系统就会更新它的内容,如果前一个notification不可见了,系统就会创建一个新的notification。
     下面的代码片段实现了更新notification 的number元素。
mNotificationManager =
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 设置notification的ID,更新的时候可用。
int notifyID = 1;
mNotifyBuilder = new NotificationCompat.Builder(this)
    .setContentTitle("New Message")
    .setContentText("You've received new messages.")
    .setSmallIcon(R.drawable.ic_notify_status)
numMessages = 0;
//开启一个循环处理数据并通知用户
...
    mNotifyBuilder.setContentText(currentText)
        .setNumber(++numMessages);
    // 因为 ID 没有变化, 之前的notification被更新
    mNotificationManager.notify(
            notifyID,
            mNotifyBuilder.build());
...
     生成的Notification效果如下:

图5 更新notification drawer里的notification
移除notifications
     在下述情况发生时,notification会被移除:
  • 用户通过clear all操作或者单独操作某个notification使之消失(如果这个通知可以被清掉)。(通知中心展开后顶部通常有个关闭按钮)
  • 用户点击了通知,并且你在创建通知的时候设置了setAutoCancel();
  • 你调用NotificationManager.cancel()方法,并在参数中传入指定的notification id。这个方法同样可以清掉发布的notifications。
  • 调用NotificationManager.cancelAll()方法,可以清除所有发布的notifications。
启动Activity的同时维护一个导航栈结构
     当你通过点击notification启动一个Activity的时候,你必须保持用户期望的导航习惯。点击Back的时候应该把用户从应用当前的工作流导航到Home页面,点击查看最近任务(Recents)应该把该Activity显示为一个独立的任务。要实现这种导航体验,你应该在一个全新的Task里启动这个Activity。根据你要启动的Activity的性质的不同,你应该以不同的方式来设置用来创建新Task的PendingIntent。这主要涉及到如下两种情况:

常规的Activity
     你要启动的Activity是应用工作流中的一部分。在这种情况下,设置PendingIntent来启动一个新的task,并给这个PendingIntent提供一个返回栈来 重建这个应用的Back行为。 (为什么在这种常规的情况下,是重建应用的back行为,而不是新建呢?自己阅读一下Task and Back Stack模块的开发文档)
     Gmail应用的notification就是这种情况。当你点击该应用的Notification来查看一封email的时候,你可以看到这封邮件的内容。触摸Back可以把你导航回到Home页面。就好像你刚才是通过Home页面进入Gmail页面的感觉一样。
     不管你当前处在哪一个应用中,在你点击notification的时候,都会得到上一段描述的Back行为。举个例子,假如你在Gmail应用中编写一封邮件。然后你点击了一个notification来查看一封邮件,你会马上进入那一封邮件中。然后点击Back会把你带回收件箱,再次点击就直接回到Home页面。而不会把你带回到之前编写邮件的页面了。

特殊的Activity
     用户只有在点击了Notification之后才会看到这个Activity。在这种情况下,这个Activity实际上是扩展了notification,在这个Activity中显示了更多的内容,这些内容在notification中因为空间大小的问题不能显示。在这种情况下,设置PendingIntent来启动一个新的Task,但这次就不需要在创建一个back栈。因为在这种情况下,你的Activity不再属于你应用的工作流程。点击Back将会直接将用户带回到Home页面。

设置一个常规activity的PendingIntent
     创建这种情况下的PendingIntent,步骤如下:
     1.在manifest文件中定义你的应用中activity的层级结构
          a.增加对于android4.0.3之前版本的支持,通过下面的办法来指定Activity的parent:在<activity>节点下添加<meta-data>节点,来指定activity的parent。
             对于这个<meta-data>节点设置两个属性:android:name="android.support.PARENT_ACTIVITY"和android:value="<parent_activity_name>",这里的<parent_activity_name>就是该<activity>的父<activity>的android:name     
             属性的值,代表parent<activity>节点。具体见下面的代码片段:
          b.同样为了支持android4.1以及以后的版本,给<activity>标签添加android:parentActivityName属性,属性值就是parent <activity>的android:name
     最终的xml代码如下所示:
<activity
    android:name=".MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity
    android:name=".ResultActivity"
    android:parentActivityName=".MainActivity">
    <meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value=".MainActivity"/>
</activity>
     2.基于启动Acticity的Intent创建back栈
          a.创建启动Activity的Intent
          b.通过TaskStackBuilder.create()来创建一个TaskStackBuilder
          c.通过addParentStack()将back栈添加到TaskStackBuilder对象,对于manifest中的层级关系内定义的每一个Activity,back栈包含了一个启动这些Activity的Intent。这个方法同样添加了在全新的task启用back stack的标志
             位。
           注意:尽管addParentStack()的参数是一个要启动的Activity的引用。这个方法调用并没有添加启动Activity的Intent到栈中去,添加要启动的Activity的Intent到栈顶在下一步完成
          d.添加在点击notification的时候启动的Activity的Intent到TaskStackBuilder,通过addNextIntent()方法。传递第一步的时候创建的Intent给addNextIntent()作为参数。
          e.如果需要的话,可以通过TaskStackBuilder.editIntentAt()方法来给stack中的Intent添加参数。如果要确保启动后的Activity在通过Back栈向后导航的时候,相应的后续Activity能够显示一些特定的数据,这是很有效的办法。
          f.通过getPendingIntent()来获得包含该back栈的PendingIntent,这个PendingIntent可以在后面被用作setContentIntent()方法的参数。
     下面的代码片段可以具体说明上述过程:
...
Intent resultIntent = new Intent(this, ResultActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// 添加Back栈
stackBuilder.addParentStack(ResultActivity.class);
// 将notification启动Activity的Intent添加到栈顶
stackBuilder.addNextIntent(resultIntent);
// 获得包含整个back stack的PendingIntent
PendingIntent resultPendingIntent =
        stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
...
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(id, builder.build());
设置特殊Activity的PendingIntent
     一个特殊的Activity不需要back栈,所以你并不需要在manifest文件中定义Activity的层级关系,而且你也不需要通过addParentStack()来创建一个back栈结构。你需要做的是,在manifest文件中设置Activity的task选项,并且通过getActivity()方法来得到相应的PendingIntent。

     1.在你的manifest文件中,添加下面的属性到<activity>节点:
     android:name="activityclass"
          activity的完全限定类名
     android:taskAffinity=""
          与代码中的FLAG_ACTIVITY_NEW_TASK结合,可以保证这个Activity不会进入应用默认的task。任何在应用中已经存在的拥有应用默认相关属性的任务都不会被影响。
     android:excludeFromRecents="true"
          把这个新建的task从Recents(最近任务)里排除,所以用户就不会不小心再次导航进这里。

     代码实例如下:
<activity
    android:name=".ResultActivity"
...
    android:launchMode="singleTask"
    android:taskAffinity=""
    android:excludeFromRecents="true">
</activity>
...
     2.创建并发布notification
          a.创建一个启动Activity的Intent
          b.设置Activity在一个新的空的task里启动,通过调用setFlags()方法来实现,参数是FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TASK
          c.设置其他你想要设置的Intent参数
          d.通过调用getActivity()方法来创建PendingIntent对象,这个对象将作为参数传入setContentIntent()方法。
     具体代码实现片段如下:
// 初始化Builder对象.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
// 创建启动Activity的Intent
Intent notifyIntent =
        new Intent(this, ResultActivity.class);
// 设置使用一个新的空的task来启动Activity
notifyIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
// Creates the PendingIntent
PendingIntent notifyIntent =
        PendingIntent.getActivity(
        this,
        0,
        notifyIntent
        PendingIntent.FLAG_UPDATE_CURRENT
);

// Puts the PendingIntent into the notification builder
builder.setContentIntent(notifyIntent);
// Notifications are issued by sending them to the
// NotificationManager system service.
NotificationManager mNotificationManager =
    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Builds an anonymous Notification object from the builder, and
// passes it to the NotificationManager
mNotificationManager.notify(id, builder.build());

在Notification中显示进度信息

     Notifications可以包含动态的进度条来告诉用户正在进行的操作的进度。如果你可以计算出操作所需要的工作量和目前已经完成的工作量,使用"determinate"模式的进度条。如果你无法估算操作的量的信息,那么就使用"indeterminate"模式的进度条。
     notification中的进度条的实现是基于ProgressBar类的。
     对于android4.0以后的版本,要在notification中使用进度条,调用setProgress()方法。对于之前的版本,则必须创建自定义notification layout来包含一个ProgressBar视图。
     下面的章节介绍了使用setProgress()来在notification中显示进度的方法。

可以显示当前进度的进度条
     要显示一个determinate progress bar,需要首先把你要显示的progress bar添加到notification,通过setProgress(max,progress,false)来实现,然后发布你的notification。随着你的操作继续进行,增加你的progress值,然后刷新notification。在操作的结尾,progress的值应该等于max。通常调用setProgress()的惯例是设置max为100,然后按照"完成百分比"来增加progress的值。
     当操作完成的时候你可以让进度条继续显示在那里也可以移除它,不管是哪种方式,都要记住更新notification里的文本来说明操作已经完成。要移除progress bar,调用setProgress(0,0,false)即可,例如:
...
mNotifyManager =
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentTitle("Picture Download")
    .setContentText("Download in progress")
    .setSmallIcon(R.drawable.ic_notification);
// Start a lengthy operation in a background thread
new Thread(
    new Runnable() {
        @Override
        public void run() {
            int incr;
            // Do the "lengthy" operation 20 times
            for (incr = 0; incr <= 100; incr+=5) {
                    // Sets the progress indicator to a max value, the
                    // current completion percentage, and "determinate"
                    // state
                    mBuilder.setProgress(100, incr, false);
                    // Displays the progress bar for the first time.
                    mNotifyManager.notify(0, mBuilder.build());
                        // Sleeps the thread, simulating an operation
                        // that takes time
                        try {
                            // Sleep for 5 seconds
                            Thread.sleep(5*1000);
                        } catch (InterruptedException e) {
                            Log.d(TAG, "sleep failure");
                        }
            }
            // When the loop is finished, updates the notification
            mBuilder.setContentText("Download complete")
            // Removes the progress bar
                    .setProgress(0,0,false);
            mNotifyManager.notify(ID, mBuilder.build());
        }
    }
// Starts the thread by calling the run() method in its Runnable
).start();


     最终的效果如图6所示,左侧截图表示正在执行中的时候,右侧的截图表示操作已经完成时的效果。

图6 执行中和执行完毕的progress bar

显示一个一直活动的进度条(indeterminate)
     要显示一个(indeterminate)一直处于活动状态的进度条,通过setProgress(0,0,true)(前两个参数是忽略的)方法将进度条添加进notification,然后发布notification,最终的效果是得到一个和determinate progress bar一样风格的进度条,唯一的不同的它一直都显示动态的 动画效果
     在操作开始的时候发布notification。动画效果会一直持续一直到你修改你的notification。当操作结束的时候,调用setProgress(0,0,false)然后更新notification来移除这个progress bar。如果不这样做的话,在操作结束的时候动画效果会继续存在。同样也要记住改变notification中的文字来告诉用户操作已经完成了。
     具体的实现代码与上面类似,但是要把上面一段代码中的下面这一部分替换:
// Sets the progress indicator to a max value, the current completion
// percentage, and "determinate" state
mBuilder.setProgress(100, incr, false);
// Issues the notification
mNotifyManager.notify(0, mBuilder.build());
     替换为:
 // Sets an activity indicator for an operation of indeterminate length
mBuilder.setProgress(0, 0, true);
// Issues the notification
mNotifyManager.notify(0, mBuilder.build());
     最终的显示效果如图7所示:

图7 一个不断显示动画效果的进度显示视图

自定义Notification布局
     notification framework允许你自定义notification的布局,通过定义一个RemoteViews对象来定义notification的外观。自定义布局的notification和普通的notification是类似的,但是他们是基于通过xml布局文件定义的RemoteView来实现的。
     自定义notification布局的高度决定于notification视图,普通的视图布局最高64dp,可展开的视图可以到256dp。
     要定义自定义notification布局,首先初始化一个RemoteViews对象,让其填充一个xml布局文件,然后调用setContent()方法而不是setContentTitle()方法。来设置自定义notification布局的内容,使用RemoteViews对象的方法来设置布局内子视图的内容:
     1.为notification创建一个xml布局文件,你可以任意命名,但是必须用.xml后缀。
     2.在你的应用中,使用RemoteViews对象的方法来定义你的notification的icon和text,把这个RemoteViews对象通过setContent()方法传递给你的NotificationCompat.Builder,避免给你的RemoteViews对象设置background因为这样你的文字颜色可能会被掩盖难以看清楚。(版本和rom定制带来的问题)
     RemoteViews类中包含了一些特定的方法可以让你用来比较容易的向你的notification布局中添加Chronometer和ProgressBar。要想了解更多关于为notification提供自定义layout的内容,可以阅读RemoteViews类的文档。
注意:当你使用自定义的notification layout的时候,要确保你的自定义布局在不同的设备朝向和分辨率下都可以正常显示。尽管这条建议是适用于所有的视图布局的,它对于notifications更加重要,因为notification drawer中的空间是严格限制的,不要让你的自定义布局过于复杂,并且确保在各种不同的配置下能正常工作。
为自定义notification中的文本使用style resources
     要始终为自定义notification中的text使用style resources。因为在不同的设备和不同的版本中,notification的背景颜色是变化的,使用style resources可以帮助你适应这种变化。从2.3版本开始,系统为标准的notification layout text定义了一种style。如果你在2.3和以上的版本中用这种相同的style,你就可以确保你的文本可以在notification drawer背景中很好的显示。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值