BlackBerry 多媒体播放编程

news/2024/11/24 7:48:40/

概述

 

移动多媒 体包括 使用移动 终端播放 音乐, 视频,拍 照,录制 视频, 和在线影 音 。 Bla ck Berry 支持 移动多媒 体,你 可以通 过 Bla ckB erry Java 或 Bla ck Berry 浏览器 来创建 自己 的媒体应 用。功 能包括 播放音频 ,视频 , SV G 数 据,录制 音频, 视频,流 媒体,以 及向蓝 牙设备发 送 媒体流。

 

Bla ckBe rr y Ja v a 提供 的 多 媒体支持 包括 MM A P I-J SR 1 3 5 和 P laz m ic M edi a Eng i n e 。本章 主要介 绍

MM A P I 在   Bla c kBerr y 平台 上的应用 。

   

创建第一 个 Bl ac k Be rr y 媒体播 放 程序

 

Bla ckBe rr y 为媒 体开发提 供了 M M A P I ,即 P lay er 和 jav a x.m icr o edi tio n . m edi a 包。它为 媒体资 源播 放,控制 播放, 和管理播 放状态提 供了一 些列方法 。

 

一个 P la y er 有五个 状态: UN REALIZE D , R EALIZE D , P R EFETCHE D , STA R TED , CL OS ED 。 设计这 些状态 目 的是为消 耗时间 和资源的 操作提供 更好的 控制。

 

P lay er 被创 建后的 初始状 态是 UN REA L IZED 。在这 个状态下 , P la y er 实例不 知道媒体 文件的 任何信 息,不能 播放媒 体文件 。

 

P lay er.realiz e() 方法会 把 P layer 的 状体从 UN REALIZE D 变为 R EALIZE D 状 态。 这个方法 会访问 媒体文 件或媒体 资源来 获取必要 的媒体信 息为播 放做准备 。

 

P lay er.p refet ch() 方 法会把 P lay er 的状 态变为 P REFET CH ED 。在 这个状 态下, P l aye r 会 进一步 获取媒 体资源必 要信息 并装载部 分媒体资 源为播 放媒体做 好准备。

 

P lay er.start () 方法 会把 P la y er 的 状态变 为 STA RT ED , P la y er 将 会立即 开始播 放媒体 资 源。

 

P lay er 如果 没有经 过 reali z e () 和 p ref et ch() 是不可能 开始播放 的。 sta rt() 方法 会间接的 呼叫 p refet ch( 0) , 也就是 r ealiz e() ,在 媒体播 放开始 之前 。直接呼 叫 star t() 来 播放 会对媒体 播放造 成延 迟。

 

当 P la y er 播 放媒体 内容完 毕或者 P lay er.st o p () 方法 被叫时, p layer 会从 STA RTE D 状态 变为

P REFE TECH E D 状态 。在   P REFECTE D 状态 的   P l aye r 可以随时 再次播 放或循环 播放。

 

在除 CL OSED 状态以 外的 任何状态 都可以 呼叫 cl o s e () 方法 , p lay er 状态变 为 CL OSE D ,这 时 p layer

释放媒体 资源, 不再可用 。

 

P lay er.d eallo cate () 只能 在 P REFE TCHED , REA L IZED 状态下呼 叫,它 使 p layer 返回前一 个状态 。

 

P lay er 的五 个状态 和状态 之间的转 换 如下 图 所示:

 

 

介绍了 P lay er ,它 的方法 和状态, 下面让 我们来 为 Bla ckB erry 创建第一个 媒体播放 程序。

 

1. 引入需要 的类

 

import   javax.microedition.media.Manager;

import javax.microedition.media.Player;

import   java.io.IOException;

 

 

2.   通过 Manager.createPla yer() 方法获得 Player 实例

 

3. 呼叫 Player.realize() 方法来为 播放做 准备, 应用 程序获取 需要的 媒体信 息。

 

4. 呼叫 Player.prefetch() 使 BlackBerry 应用程 序获 取必要的 媒体信 息,装 载部 分媒体内 容为 播放做好 准备。

 

5. 呼叫 Player.start() 开 始播放。

 

Player   p = Manager.createPlayer( "http://www.test.rim.net/abc.wav" );

 

/**

*   指向媒体 文件的 ab c. w av 的 URL 作为一 个 URI 参数 传入 Manager.createPla yer() 。

*

*   最好规范 是先叫 re al i ze () ,   然后 prefetch () ,   再 start() 。按照 这个持 续可以减 少

*   播放延迟 。

*

*   直接呼叫 start() 其实 是先呼叫 prefetch (0) 也 就是 realize() ,在 开始 播放。

*/

p.start();

 

 

6.   呼叫 Player.stop() 来停 止播放。

 

7. 呼叫 Player.close() 使 Player 进入 CLOSED 状态 , player 实例不 再可用 。

 

 


B lack B er ry   音 频 播放编程

 

播放音频范例

 

本节介绍 如何在 Bl ackB err y 上创建 一个音 频播放器 播放 m p 3 , 并提供 播放, 暂停,停 止的 控 制。 播放器如 下图所 示:

 

 

  


播放器源代码 如下 :

 

 

public   class MusicPlayerApp   extends   UiApplication{  

public static void main(String[] args){  

//   应用程序 启动入 口

getInstance ().enterEventDispatcher();

}  

//   MusicPlayerApp 的单类

private   static MusicPlayerApp   m_instance ;  

private MusicPlayerScreen musicPlayerScreen ;  

public MusicPlayerApp(){  

// 创建播放 器屏幕

musicPlayerScreen   =   new   MusicPlayerScreen();  

//   推出屏幕 堆栈, 显示播 放器屏幕

pushScreen( musicPlayerScreen );

}  

//   实现     MusicPlayerAp p   单类,运 行环境 中只有一 个 MusicPlayerApp 实例

public   static synchronized MusicPlayerApp   getInstance(){

if   ( m_instance   ==   null ){

m_instance = new MusicPlayerApp();

}

return   m_instance ;

}  

 

 

以上代码 表示使 用   Bl ac kB err y   提供的   U I   来 组建 界面。 g e tI ns t an ce( ) 使用 了单例, 即程序 运行环

境 中 只 有 一 个   M usi cPl a y erAp p   的 实 例 , 统 一 通 过   g e tI ns t an ce( ) 来 调 用 , 这 样 可 以 避 免 多 个 M us i cPl a y erAp p   实例而导致对象状态不稳定。 g e tI ns t an ce( ). en t erEv en t D i spa t che r ( ) ,   这行代码 创建了 M usi cPl a y erAp p 的实例并 调用 en t erEv entDi spa t che r ( ) 来启 动应用 。

 

 
// 播放器屏 幕

class   MusicPlayerScreen   extends   MainScreen   implements

FieldChangeListener{  

public static final String SONG_NAME = "All Rise" ;  

// 指向歌曲 的 URI

public   static final String   URI   =   "/sounds/all_rise.mp3" ;  

// 歌曲的格 式, audio/m peg 支持 mp3

public   static final String   AUDIO_FORMAT   =   "audio/mpeg" ;  

// 播放器的 所有状 态

public   static final int PLAYING   =   0;

public static final int READY_FOR_PLAY = 1;

public   static final int PAUSE   =   2;

public   static final int STOP   =   3;  

// 播放器的 当前状 态

private   int m_currentStatus   =   READY_FOR_PLAY ;  

// 播放器实 例

private   Player   m_player ;  

// 播放器标 签 Layout

private   VerticalFieldManager   m_labelManager ;  

// 播放器标 签

private   LabelField   m_songNameField ;

private   LabelField   m_durationField ;

private   LabelField   m_statusField ;  

// 播放按钮 Layout

private   HorizontalFieldManager   m_buttonManager ;  

// 播放器按 钮

private   ButtonField   m_pauseButton ;

private   ButtonField   m_playButton ;

private ButtonField m_stopButton ;  

public MusicPlayerScreen(){  

super (Manager. NO_HORIZONTAL_SCROLL |Manager. NO_VERTICAL_SCROLL );  

// 创建播放 器并做 好播放 准备

createPlayer();  

//   创建界面

initializeUI();

}

 


M us i cPl a y erSce en 构造函数描述 启动 M usi cPl a yerScreen 要执 行的任务 ,创建播 放器和 初始化 界面。

 

private   void initializeUI(){  

setTitle( new LabelField( " 黑莓播放 器 " , Fi el d. FIELD_VCENTER |Field. FIELD_HCENTER ));  

// 歌曲名称 标签

m_songNameField   =   new   LabelField( " 歌曲名 称 :   " + SONG_NAME ,   Fi e ld . USE_ALL_WIDTH );  

// 歌曲时长

m_durationField   =   new   LabelField( " 播放时 长 :   "

+ m_player .getDuration()/1000000/60+ " 分钟 " ,   Fi e ld . USE_ALL_WIDTH );  

// 播放状态

m_statusField   =   ne w   LabelField( " 播放状 态 :   " +getStatus(),   Field. USE_ALL_WIDTH );  

m_labelManager = new

VerticalFieldManager(Manager. NO_HORIZONTAL_SCROLL |Manager. NO_VERTICAL_SCROLL ); m_labelManager .add( m_songNameField ); m_labelManager .add( m_durationField ); m_labelManager .add( m_statusField );  

add( m_labelManager );  

// 暂停按钮

m_pauseButton   =   new   ButtonField( "||" ,

Field. FIELD_VCENTER |Field. FIELD_HCENTER );  

m_pauseButton .setChangeListener( this );  

// 播放按钮

m_playButton   =   new   ButtonField( ">" ,   Field. FIELD_VCENTER |Field. FIELD_HCENTER );

m_playButton .setChangeListener( this );  

// 停止按钮

m_stopButton   =   new   ButtonField( "O" ,   Field. FIELD_VCENTER |Field. FIELD_HCENTER );

m_stopButton .setChangeListener( this );  

m_buttonManager = new

HorizontalFieldManager(Manager. NO_HORIZONTAL_SCROLL |Manager. NO_VERTICAL_SCROLL

);  

m_buttonManager .add( m_pauseButton ); m_buttonManager .add( m_playButton ); m_buttonManager .add( m_stopButton );  

add( m_buttonManager );

}

   

 

函数 i ni t i ali z e U I( ) 创 建所 有 U I 组建, 并按一 定结 构组装标 签和控 制按钮 。

 

 

// 获取当 前播放状 态

private   String   getStatus(){  

switch ( m_currentStatus ){  

case PLAYING :

return   " 正在播放 " ;

case   READY_FOR_PLAY :

return   " 准备完毕 " ;

case   PAUSE :

return   " 暂停 " ;

case   STOP :

return   " 停止 " ;

}  

return   null ;

}  

函数 g etS t atus ( ) 根绝当前 播放器状 态来获 取状态消 息。  

//   根据设 置的播放 状态更 新状态 标签

private   void setPlayerStatus( int   status){

m_currentStatus = status;

m_statusField .setText( " 播放状态 :   " +g et S ta tu s( )) ;

}  

setPl ay erStat us(in t ) 通过 当前播放 器状态 来更新播 放器状态 标签。  

//   创建播放 器

private   void createPlayer(){
try {

// 关闭当前 播放 器

if   ( m_player   !=   null ){

m_player .close();

m_player   =   null ;

}  

// 创建新播 放器

InputStream   is = this .getClass().getResourceAsStream( URI );

m_player = javax.microedition.media.Manager. createPlayer (is,

AUDIO_FORMAT );  

// 预装媒体 资源 准备播放

m_player .prefetch();

} catch (Exception   ex){

ex.printStackTrace();

}

}  

 


函数 cr ea t ePl ay er( ) 创 建 Pl ay er 实例,并预装载媒 体内容为 播放做 好准备。 如果当前 Pl ay er 实例 已经存在 ,它会 先销毁当 前 Pl ay er 然后再创建新 Pl ay er 。

 

public   void fieldChanged(Field   field, int   context)   {

if   (field   == m_playButton ){  

// 当播放按 钮按下 时

//   根据播放 器当前 状态进 行处理

switch   ( m_currentStatus ){

case   PLAYING :

this .setPlayerStatus( PLAYING );

return ;

case READY_FOR_PLAY :

try {

m_player .start();

} catch (Exception   ex){}

setPlayerStatus( PLAYING );

return ;

case   PAUSE :

try {

m_player .start();

} catch (Exception   ex){}

setPlayerStatus( PLAYING );

return ;

case   STOP :

createPlayer();

try {

m_player .start();

} catch (Exception   ex){}

setPlayerStatus( PLAYING );

return ;

}

} else   if (field   == m_pauseButton ){  

// 当暂停按 钮按下 时

//   根据播放 器当前 状态进 行处理

switch   ( m_currentStatus ){

case PLAYING :

try {

m_player .stop();

} catch (Exception   ex){}

setPlayerStatus( PAUSE );

return ;

case   READY_FOR_PLAY :

return ;

case   PAUSE :

return ;

case   STOP :

try {

m_player .close();

} catch (Exception   ex){}

setPlayerStatus( STOP );

return ;

}

}

else if (field == m_stopButton ){  

// 当停止按 按钮按 下时

//   根据播放 器当前 状态进 行处理

switch   ( m_currentStatus ){

case PLAYING :

try {

m_player .stop();

m_player .close();

} catch (Exception   ex){}

setPlayerStatus( STOP );

return ;

case READY_FOR_PLAY :

try {

m_player .close();

} catch (Exception   ex){}

setPlayerStatus( STOP );

return ;

case   PAUSE :

try {

m_player .close();

} catch (Exception   ex){}

setPlayerStatus( STOP );

return ;

case   STOP :

return ;

}

}

}

}

}

 

 

f i el dC ha n g ed ( Fi el d   f i el d,   i nt   con t ex t) 做为按钮监 听接口,根据按钮事件判断执行哪一段按钮事件。

三个按钮 分别是 :播放, 暂停,停 止。在 每个按钮 的事件响 应根据 播放器当 前状态来 执行不 同任 务,详细 描述请 查看注释 。

 

 


Bl ac kB erry   视 频 播放编程

 

Bla ckBe rr y 平 台支 持 M M A P I ,支 持 P la y er 在 Bla ckB erry 界 面框架 上播放 视频 。视频格 式包括 m pg ,

3 gp , av i 等等 。以下代码 片段分别 解释在   M ID P 和   Bla ckBe rr y UI 两 种框架下 的视频播 放。

 

 

 

对于提供 了 L C D UI 支持 的 M I D P 实 现:

 

 

public   void playVideo()   {

try {  

// 创建 Player ,媒 体资 源为网络 上的视 频,并 获取 视频信息 Player p = Manager. createPlayer ( "http://abc.mpg" ); p.realize();  

// 界面控制 器

GUIControl gc;  

// 获取界面 控制器

//   使用 Item 播放 视频

//   把 Item 添加到 Form , 再显示 Form

//   实现了在 Form 播放视 频

if   ((gc   = (GUIControl) p.getControl( "GUIControl" ))   != null )   {

Form   form = new   Form( "My   GUI" );

form.append((Item) gc.initDisplayMode( GUIControl. USE_GUI_PRIMITIVE , null ));

Display.getDisplay ( this ).setCurrent(form);

}  

// 开始播放

p.start();  

}   catch   (MediaException   pe) {

} catch (IOException ioe) {

}

}

 

 

范例 ( 1 )

 

在 M I D P 框 架下界 面方式 有两种, F o r m 和 Can v as 。 For m 界面 是 M I D P 框架 下的高级 UI 组件, 容 器与组件 的结构 。上述代 码是在 M ID P 高级 UI 框架 中播放视 频。 Can v as 是 M ID P 框架 中的低 级界 面,程序 员需要 在 Can v a s 上绘制自 己的 UI 组 件。 范例 ( 2 ) 为 在 Can v as 上播 放视频。  


M ID P 框架 下 Can v a s 组件 上播放视 频 :

   

public   void playVideo( Canvas   canvas ) {

try   {  

// 创建 Player ,媒 体资 源为网络 上的视 频,并 获取 视频信息 Player p = Manager. createPlayer ( "http://abc.mpg" ); p.realize();  

// 视频控制 器

VideoControl vidc;  

// 获取视频 控制器

//   传入 Canvas ,配 置在 C an vas 里播放视 频

//   使用视频 控制配 置播放 参数

if ((vidc   = ( VideoControl ) p.getControl( "VideoControl" ))   !=

null ){

vidc.initDisplayMode( VideoControl. USE_DIRECT_VIDEO , canvas);

vidc.setDisplayFullScreen( true );

vidc.setVisible( true );

}  

// 开始播放

p.start();  

}   catch   (MediaException   pe) {

} catch (IOException ioe) {

}

}

    

 

范例 ( 2 )

 

在使用 Can v as 播 放视频 时,需要 获取 Vid e o C o n tr o l ,它 可以把 C an v as 作为 播放组件 ,同时 提供一 些方法调 整视频 显示位置 和尺寸。

 

以上是 M ID P 框架 下高级 UI 和低级 UI 播 放视频的 方法。在 Bla ckB erry 平台 上如果应 用程序 使用的

是   M I D P 框 架开发 视频播 放,可以 直接使 用这两个 范例。 Bla c kBerr y 平台提 供   Bla ckB erry 特有的   UI

框架,框 架结构 与 J 2 SE 比较类似 , Co n tain er-L a y o u tM an ag er -Co m p o n ent 的结构。 Bla ckB erry 最基 本的 UI 组件 是 Fi eld ,视 频需要在 Fi eld 里进行播 放,如范 例 ( 3 ) 所 示。


Bla ckBe rr y UI 框 架下播放 视频 :

 

public   void playVideo(){
try {

// 创建新播 放器

InputStream   is = this .getClass().getResourceAsStream( URI );

m_player = javax.microedition.media.Manager. createPlayer (is,

VIDEO_FORMAT );  

// 获取视频 文件信 息,这 一步一定 要有, 否则获 取的 VideoControl 将无效

m_player .realize();  

// 获取视频 控制

m_videoControl =

(VideoControl) m_player .getControl( "VideoControl" );  

if ( m_videoControl != null )

{
//   从视频控 制获取 UI 组件,这 个组件 用来显 示视 频,常用 组件比如 Field

m_videoField = (Field) m_videoControl .initDisplayMode

(VideoControl. USE_GUI_PRIMITIVE ,

"net.rim.device.api.ui.Field" );  

// 设置显 示尺 寸

m_videoControl .setDisplayFullScreen( true );  

//   使视频 可见

m_videoControl .setVisible( true );

}  

if   ( m_videoField   !=   null ){

//   添加视频 组件到屏 幕并开始 播放

add( m_videoField );

m_player .start();

}  

} catch (Exception ex){

ex.printStackTrace();

}

}  

 

范例 ( 3 )

 

创建播放 器和播 放流程 与 M I D P 框 架下的 播放器 相同,但 在获取 显示视频 组件时有 所不同 ,对于

Bla ckBe rr y 风格 的界面要 求在   Fi eld 里显 示视频 :

 

Field videoField = (Field)

m_videoControl .initDisplayMode(VideoControl. USE_GUI_PRIMITIVE ,

"net.rim.device.api.ui.Field" );  


要注意的 是获取 Vi d eo C on tro l 之前 一定要 让播放 器获取媒 体信息 , P la y er.r ealiz e() , 否则获 取的

Vid eo C o n tro l 无效。

    

以上是在 三种界 面框架下 的播放方 式。针 对 Bla ckB erry 平 台 Bla ck Berry 风格 界面的视 频播放 ,以 下是一个 完整的 代码例子 :

 

 

import   java.io.InputStream;

import   javax.microedition.media.Player;

import javax.microedition.media.control.VideoControl;

import   net.rim.device.api.ui.Field;

import net.rim.device.api.ui.Manager;

import   net.rim.device.api.ui.UiApplication;

import   net.rim.device.api.ui.container.MainScreen;

 

public class VideoPlayerApp extends UiApplication{ private static VideoPlayerApp m_instance ; private VideoScreen m_videoPlayerScreen ; public static void main(String[] args){

//   应用程序 启动入 口

getInstance ().enterEventDispatcher();

}  

public   VideoPlayerApp(){  

// 创建播放 器屏幕

m_videoPlayerScreen   =   new   VideoScreen();  

//   推出屏幕 堆栈, 显示播 放器屏幕

pushScreen( m_videoPlayerScreen );

}  

//   实现     VideoPlayerAp p 单类,运 行环境 中只有一 个 VideoPlayerApp 实例

public   static synchronized VideoPlayerApp   getInstance(){

if ( m_instance == null ){

m_instance   =   new   VideoPlayerApp();

}

return   m_instance ;

}  

//   播放器屏 幕

class   VideoScreen   extends   MainScreen{  

// 指向本地 视频的 URI

public   static final String   URI   =   "/video/song.3GP" ;;
// 播放器实 例

private   Player   m_player ;  

// 视频播放 控制

private   VideoControl   m_videoControl ;  

private Field m_videoField ;  

public VideoScreen(){  

super (Manager. NO_HORIZONTAL_SCROLL |Manager. NO_VERTICAL_SCROLL );  

// 在 VideoScreen 上播 放视频

playVideo();  

// 创建播放 器

private   void playVideo(){  

try {

//   创建新播 放器

InputStream   is = this .getClass().getResourceAsStream( URI );

m_player = javax.microedition.media.Manager. createPlayer (is,

VIDEO_FORMAT );  

// 获取视频 文件信 息,这 一步一定 要有, 否则获 取的 VideoControl 将无效

m_player .realize();  

// 获取视频 控制

m_videoControl = (VideoControl) m_player .getControl( "VideoControl" );  

if ( m_videoControl != null )

{

//   从视频控 制获取 UI 组件,这 个组件 用来显 示视 频,常用 组件比如 Field

m_videoField = (Field) m_videoControl .initDisplayMode

(VideoControl. USE_GUI_PRIMITIVE ,   "net.rim.device.api.ui.Field" );  

// 设置显示 尺寸

m_videoControl .setDisplayFullScreen( true );  

//   使视频可 见

m_videoControl .setVisible( true );

}  

if   ( m_videoField   !=   null ){  

//   添加视频 组件到屏 幕并开始 播放

add( m_videoField );

m_player .start();

}
} catch (Exception ex){

ex.printStackTrace();

}

}  

//   阻止屏幕 弹出     save 菜单

public   boolean onSavePrompt(){

return   true ;

}

}

}

 


http://www.ppmy.cn/news/292850.html

相关文章

专家看衰Windows Phone 8前景 诺基亚又要悲剧?

尽管诺基亚连续三个季度出现10亿美元以上的亏损,不过近来诺基亚的股价出现了强势反弹,股票价格目前已经在第二季度财报发布之后翻了两番。然而 Research的分析师Pierre Ferragu认为,股价之所以上涨,是受下周即将召开的诺基亚世界大…

诺基亚N97沃达丰多媒体设备无缝连接选件

诺基亚N97沃达丰多媒体设备无缝连接选件   诺基亚世界顶级手机品牌一直带给我们惊喜与它的辉煌和最新技术的产品。它已经抓住了移动通信市场的N系列。现在,这个品牌已经想出了另一个多媒体惊叹诺基亚N97手机是现在挤满了世界领先的沃达丰的网络。该网络连接方便了…

用 Windows Media Encoder 9 架设网上直播

网上的电台发展得越来越快,其中之一的原因是网络的迅速发展,但用来架设电台的软件,无非还是几个:Winamp ShoutCast - 构建MP3流媒体Real Server - 构建Real Media流媒体Windows Media Encoder - 构建Windows Media流媒体QuickTime Broadcaste…

Windows Phone7成为诺基亚核心目标

在上周三(4月27日),诺基亚对外正式宣布:诺基亚(Nokia)将Symbian软件研发工作外包给一家名为“埃森哲(Accenture)”的咨询公司,相关的约3000名研发人员也将随后转移到埃森…

多媒体播放(windows音乐播放)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 在windows平台上面,开发音乐播放音乐不是一件很复杂的事情。当然,你如果需要自己进行音频的编解码工作,那还是有…

诺基亚确认将推出平板电脑 拟配置MeeGo系统

日前诺基亚公司首席执行官Stephen Elop确认了该公司正在紧锣密鼓的为首台平板电脑的发布做准备,但他没有透露该设备的细节信息。有一点可以肯定,该设备没有使用微软的Windows平台,而是一种不同的操作系统。 诺基亚的开发团队正在制定平板电脑…

诺基亚N81手机的最佳外放和内放模式均衡器设置图示

诺基亚N81的扬声器的均衡器默认设置与内设超重低音设置均不能让人有满意的效果,此外放模式属于新建手动设置模式,该设置完成后的外放效果较之前简直判若两“机”,也绝非目前市场上任何一款音乐手机能够比拟的. 官方推荐 从左到右、自下而上第…

关注诺基亚Windows Phone的7个理由

你理想中的智能手机是什么样子?它当然第一眼就得吸引你的眼球,不仅要有卓越的外观设计,还得有卓越的品质。它不需要有繁复的操作,当你想了解朋友的动态,获取最重要的信息时,最重要的信息得一目了然。它还要…