功能强大的Vitamio视频播放器的使用教程
2017-01-05 13:53
447 查看
Vitamio简介:Vitamio是一个支持所有Android设备的多媒体框架。Vitamio与Android默认的MediaPlayer工作方式相似,但包含更加强大的功能!(注意:Vitamio商业化后个人免费、公司收费)
vitamio官网:https://www.vitamio.org
vitamio SDK地址:https://github.com/yixia/VitamioBundle
之前开发一个视频播放类的项目,需要实现在线播放的功能,找了很多视频播放框架,觉得Vitamio视频播放框架还不错,也相对稳定,但是在网上找了很多教程都少之又少,Vitamio官网写的教程也不是很清晰,所以就自己就把Vitamio在github上的demo进行研究,花了点时间写了个demo出来并且将Vitamio的视频控制器界面进行自定义,支持视频亮度、音量的调节,话不多说,先上效果图。
附上 Demo的github地址
自定义视频控制器
8F05796762EDAA5275D59EB31D3248E9.jpg
目前,作者只实现了自定义手势调节亮度、音量的加减以及播放控制的功能,更多的功能等待着大家一起去挖掘。
使用步骤:
1.引入vitamio SDK的方式有两种:
(1)直接以module的方式引入
(2)通过Complie的方式引入
作者是采用的第一种方式,下面是一些需要注意的地方:
清单文件配置:
1)权限设置:
2)application配置:
2.主要代码:
播放视频代码
自定义视频控制器:
主要实现了手势调节视频亮度、音量的加减控制。
自定义控制器布局文件:
视频播放界面布局:
播放界面的布局如下,即可拉伸视频,主要不要嵌套vitamio自定义的CenterLayout布局
github地址
写的不好之处还望大家见谅。、
vitamio官网:https://www.vitamio.org
vitamio SDK地址:https://github.com/yixia/VitamioBundle
之前开发一个视频播放类的项目,需要实现在线播放的功能,找了很多视频播放框架,觉得Vitamio视频播放框架还不错,也相对稳定,但是在网上找了很多教程都少之又少,Vitamio官网写的教程也不是很清晰,所以就自己就把Vitamio在github上的demo进行研究,花了点时间写了个demo出来并且将Vitamio的视频控制器界面进行自定义,支持视频亮度、音量的调节,话不多说,先上效果图。
附上 Demo的github地址
自定义视频控制器
8F05796762EDAA5275D59EB31D3248E9.jpg
目前,作者只实现了自定义手势调节亮度、音量的加减以及播放控制的功能,更多的功能等待着大家一起去挖掘。
使用步骤:
1.引入vitamio SDK的方式有两种:
(1)直接以module的方式引入
(2)通过Complie的方式引入
作者是采用的第一种方式,下面是一些需要注意的地方:
清单文件配置:
1)权限设置:
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
2)application配置:
<!-- 必须初始化 --> <activity android:name="io.vov.vitamio.activity.InitActivity" android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation" android:launchMode="singleTop" android:theme="@android:style/Theme.NoTitleBar" android:windowSoftInputMode="stateAlwaysHidden" />
2.主要代码:
播放视频代码
package com.stx.vitamiodemo; import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.ProgressBar; import android.widget.TextView; import io.vov.vitamio.LibsChecker; import io.vov.vitamio.MediaPlayer; import io.vov.vitamio.Vitamio; import io.vov.vitamio.widget.MediaController; import io.vov.vitamio.widget.VideoView; /** * Vitamio视频播放框架Demo */ public class MainActivity extends AppCompatActivity implements MediaPlayer.OnInfoListener, MediaPlayer.OnBufferingUpdateListener{ //视频地址 private String path = "http://baobab.wdjcdn.com/145076769089714.mp4"; private Uri uri; private ProgressBar pb; private TextView downloadRateView, loadRateView; private CustomMediaController mCustomMediaController; private VideoView mVideoView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //定义全屏参数 int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; //获得当前窗体对象 Window window = MainActivity.this.getWindow(); //设置当前窗体为全屏显示 window.setFlags(flag, flag); //必须写这个,初始化加载库文件 Vitamio.initialize(this); //设置视频解码监听 if (!LibsChecker.checkVitamioLibs(this)) { return; } setContentView(R.layout.activity_main); initView(); initData(); //获取上一次保存的进度 long progress = SPUtils.getLong(this, "progress");//此处使用SharedPreferences保存当前播放进度,SPUtils是我自己封装的工具类 mVideoView.seekTo(progress); mVideoView.start(); } //初始化控件 private void initView() { mVideoView = (VideoView) findViewById(R.id.buffer); mCustomMediaController=new CustomMediaController(this,mVideoView,this); mCustomMediaController.setVideoName("白火锅 x 红火锅"); pb = (ProgressBar) findViewById(R.id.probar); downloadRateView = (TextView) findViewById(R.id.download_rate); loadRateView = (TextView) findViewById(R.id.load_rate); } //初始化数据 private void initData() { uri = Uri.parse(path); mVideoView.setVideoURI(uri);//设置视频播放地址 mCustomMediaController.show(5000); mVideoView.setMediaController(mCustomMediaController); mVideoView.setVideoQuality(MediaPlayer.VIDEOQUALITY_HIGH);//高画质 mVideoView.requestFocus(); mVideoView.setOnInfoListener(this); mVideoView.setOnBufferingUpdateListener(this); mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mediaPlayer) { mediaPlayer.setPlaybackSpeed(1.0f); } }); } @Override public boolean onInfo(MediaPlayer mp, int what, int extra) { switch (what) { case MediaPlayer.MEDIA_INFO_BUFFERING_START: if (mVideoView.isPlaying()) { mVideoView.pause(); pb.setVisibility(View.VISIBLE); downloadRateView.setText(""); loadRateView.setText(""); downloadRateView.setVisibility(View.VISIBLE); loadRateView.setVisibility(View.VISIBLE); } break; case MediaPlayer.MEDIA_INFO_BUFFERING_END: mVideoView.start(); pb.setVisibility(View.GONE); downloadRateView.setVisibility(View.GONE); loadRateView.setVisibility(View.GONE); break; case MediaPlayer.MEDIA_INFO_DOWNLOAD_RATE_CHANGED: downloadRateView.setText("" + extra + "kb/s" + " "); break; } return true; } @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { loadRateView.setText(percent + "%"); } @Override public void onConfigurationChanged(Configuration newConfig) { //屏幕切换时,设置全屏 if (mVideoView != null){ mVideoView.setVideoLayout(VideoView.VIDEO_LAYOUT_SCALE, 0); } super.onConfigurationChanged(newConfig); } @Override protected void onPause() { super.onPause(); //保存进度 SPUtils.putLong(this,"progress",mVideoView.getCurrentPosition()); } }
自定义视频控制器:
主要实现了手势调节视频亮度、音量的加减控制。
package com.stx.vitamiodemo; import android.app.Activity; import android.content.Context; import android.media.AudioManager; import android.os.Handler; import android.os.Message; import android.view.Display; import android.view.GestureDetector; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.TextView; import io.vov.vitamio.widget.MediaController; import io.vov.vitamio.widget.VideoView; /** * Created by xhb on 2016/3/1. * 自定义视频控制器 */ public class CustomMediaController extends MediaController { private static final int HIDEFRAM = 0;//控制提示窗口的显示 private GestureDetector mGestureDetector; private ImageButton img_back;//返回按钮 private TextView mFileName;//文件名 private VideoView videoView; private Activity activity; private Context context; private String videoname;//视频名称 private int controllerWidth = 0;//设置mediaController高度为了使横屏时top显示在屏幕顶端 private View mVolumeBrightnessLayout;//提示窗口 private ImageView mOperationBg;//提示图片 private TextView mOperationTv;//提示文字 private AudioManager mAudioManager; private SeekBar progress; private boolean mDragging; private MediaPlayerControl player; //最大声音 private int mMaxVolume; // 当前声音 private int mVolume = -1; //当前亮度 private float mBrightness = -1f; //返回监听 private View.OnClickListener backListener = new View.OnClickListener() { public void onClick(View v) { if (activity != null) { activity.finish(); } } }; private Handler myHandler = new Handler() { @Override public void handleMessage(Message msg) { long pos; switch (msg.what) { case HIDEFRAM://隐藏提示窗口 mVolumeBrightnessLayout.setVisibility(View.GONE); mOperationTv.setVisibility(View.GONE); break; } } }; //videoview 用于对视频进行控制的等,activity为了退出 public CustomMediaController(Context context, VideoView videoView, Activity activity) { super(context); this.context = context; this.videoView = videoView; this.activity = activity; WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); controllerWidth = wm.getDefaultDisplay().getWidth(); mGestureDetector = new GestureDetector(context, new MyGestureListener()); } @Override protected View makeControllerView() { //此处的 mymediacontroller 为我们自定义控制器的布局文件名称 View v = ((LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate (getResources().getIdentifier("mymediacontroller", "layout", getContext().getPackageName()), this); v.setMinimumHeight(controllerWidth); //获取控件 img_back = (ImageButton) v.findViewById(getResources().getIdentifier("mediacontroller_top_back", "id", context.getPackageName())); mFileName = (TextView) v.findViewById(getResources().getIdentifier("mediacontroller_filename", "id", context.getPackageName())); if (mFileName != null) { mFileName.setText(videoname); } //声音控制 mVolumeBrightnessLayout = (RelativeLayout) v.findViewById(R.id.operation_volume_brightness); mOperationBg = (ImageView) v.findViewById(R.id.operation_bg); mOperationTv = (TextView) v.findViewById(R.id.operation_tv); mOperationTv.setVisibility(View.GONE); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mMaxVolume = mAudioManager .getStreamMaxVolume(AudioManager.STREAM_MUSIC); //注册事件监听 img_back.setOnClickListener(backListener); return v; } @Override public boolean dispatchKeyEvent(KeyEvent event) { System.out.println("MYApp-MyMediaController-dispatchKeyEvent"); return true; } @Override public boolean onTouchEvent(MotionEvent event) { if (mGestureDetector.onTouchEvent(event)) return true; // 处理手势结束 switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_UP: endGesture(); break; } return super.onTouchEvent(event); } /** * 手势结束 */ private void endGesture() { mVolume = -1; mBrightness = -1f; // 隐藏 myHandler.removeMessages(HIDEFRAM); myHandler.sendEmptyMessageDelayed(HIDEFRAM, 1); } private class MyGestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent e) { return false; } /** * 因为使用的是自定义的mediaController 当显示后,mediaController会铺满屏幕, * 所以VideoView的点击事件会被拦截,所以重写控制器的手势事件, * 将全部的操作全部写在控制器中, * 因为点击事件被控制器拦截,无法传递到下层的VideoView, * 所以 原来的单机隐藏会失效,作为代替, * 在手势监听中onSingleTapConfirmed()添加自定义的隐藏/显示, * * @param e * @return */ @Override public boolean onSingleTapConfirmed(MotionEvent e) { //当手势结束,并且是单击结束时,控制器隐藏/显示 toggleMediaControlsVisiblity(); return super.onSingleTapConfirmed(e); } @Override public boolean onDown(MotionEvent e) { return true; } //滑动事件监听 @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { float mOldX = e1.getX(), mOldY = e1.getY(); int y = (int) e2.getRawY(); int x = (int) e2.getRawX(); Display disp = activity.getWindowManager().getDefaultDisplay(); int windowWidth = disp.getWidth(); int windowHeight = disp.getHeight(); if (mOldX > windowWidth * 3.0 / 4.0) {// 右边滑动 屏幕 3/4 onVolumeSlide((mOldY - y) / windowHeight); } else if (mOldX < windowWidth * 1.0 / 4.0) {// 左边滑动 屏幕 1/4 onBrightnessSlide((mOldY - y) / windowHeight); } return super.onScroll(e1, e2, distanceX, distanceY); } @Override public boolean onDoubleTap(MotionEvent e) { playOrPause(); return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return super.onFling(e1, e2, velocityX, velocityY); } } /** * 滑动改变声音大小 * * @param percent */ private void onVolumeSlide(float percent) { if (mVolume == -1) { mVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); if (mVolume < 0) mVolume = 0; // 显示 mVolumeBrightnessLayout.setVisibility(View.VISIBLE); mOperationTv.setVisibility(VISIBLE); } int index = (int) (percent * mMaxVolume) + mVolume; if (index > mMaxVolume) index = mMaxVolume; else if (index < 0) index = 0; if (index >= 10) { mOperationBg.setImageResource(R.drawable.volmn_100); } else if (index >= 5 && index < 10) { mOperationBg.setImageResource(R.drawable.volmn_60); } else if (index > 0 && index < 5) { mOperationBg.setImageResource(R.drawable.volmn_30); } else { mOperationBg.setImageResource(R.drawable.volmn_no); } //DecimalFormat df = new DecimalFormat("######0.00"); mOperationTv.setText((int) (((double) index / mMaxVolume) * 100) + "%"); // 变更声音 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0); } /** * 滑动改变亮度 * * @param percent */ private void onBrightnessSlide(float percent) { if (mBrightness < 0) { mBrightness = activity.getWindow().getAttributes().screenBrightness; if (mBrightness <= 0.00f) mBrightness = 0.50f; if (mBrightness < 0.01f) mBrightness = 0.01f; // 显示 mVolumeBrightnessLayout.setVisibility(View.VISIBLE); mOperationTv.setVisibility(VISIBLE); } WindowManager.LayoutParams lpa = activity.getWindow().getAttributes(); lpa.screenBrightness = mBrightness + percent; if (lpa.screenBrightness > 1.0f) lpa.screenBrightness = 1.0f; else if (lpa.screenBrightness < 0.01f) lpa.screenBrightness = 0.01f; activity.getWindow().setAttributes(lpa); mOperationTv.setText((int) (lpa.screenBrightness * 100) + "%"); if (lpa.screenBrightness * 100 >= 90) { mOperationBg.setImageResource(R.drawable.light_100); } else if (lpa.screenBrightness * 100 >= 80 && lpa.screenBrightness * 100 < 90) { mOperationBg.setImageResource(R.drawable.light_90); } else if (lpa.screenBrightness * 100 >= 70 && lpa.screenBrightness * 100 < 80) { mOperationBg.setImageResource(R.drawable.light_80); } else if (lpa.screenBrightness * 100 >= 60 && lpa.screenBrightness * 100 < 70) { mOperationBg.setImageResource(R.drawable.light_70); } else if (lpa.screenBrightness * 100 >= 50 && lpa.screenBrightness * 100 < 60) { mOperationBg.setImageResource(R.drawable.light_60); } else if (lpa.screenBrightness * 100 >= 40 && lpa.screenBrightness * 100 < 50) { mOperationBg.setImageResource(R.drawable.light_50); } else if (lpa.screenBrightness * 100 >= 30 && lpa.screenBrightness * 100 < 40) { mOperationBg.setImageResource(R.drawable.light_40); } else if (lpa.screenBrightness * 100 >= 20 && lpa.screenBrightness * 100 < 20) { mOperationBg.setImageResource(R.drawable.light_30); } else if (lpa.screenBrightness * 100 >= 10 && lpa.screenBrightness * 100 < 20) { mOperationBg.setImageResource(R.drawable.light_20); } } /** * 设置视频文件名 * * @param name */ public void setVideoName(String name) { videoname = name; if (mFileName != null) { mFileName.setText(name); } } /** * 隐藏或显示 */ private void toggleMediaControlsVisiblity() { if (isShowing()) { hide(); } else { show(); } } /** * 播放/暂停 */ private void playOrPause() { if (videoView != null) if (videoView.isPlaying()) { videoView.pause(); } else { videoView.start(); } } }
自定义控制器布局文件:
<?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:background="@drawable/video_player_bg_color" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:background="#77000000" android:layout_height="34dp"> <ImageButton android:id="@+id/mediacontroller_top_back" android:layout_width="50dp" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:background="@null" android:src="@drawable/ic_player_close_white"/> <TextView android:id="@+id/mediacontroller_filename" style="@style/MediaController_Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="5dp" android:layout_toRightOf="@+id/mediacontroller_top_back" android:ellipsize="marquee" android:singleLine="true" android:text="file name"/> <ImageButton android:id="@+id/mediacontroller_share" android:layout_width="50dp" android:layout_height="match_parent" android:background="@null" android:src="@drawable/ic_action_share_without_padding" android:layout_alignParentRight="true"/> <ImageButton android:id="@+id/mediacontroller_favorite" android:layout_width="50dp" android:layout_height="match_parent" android:background="@null" android:layout_toLeftOf="@id/mediacontroller_share" android:src="@drawable/ic_action_favorites"/> </RelativeLayout> <ImageButton android:id="@+id/mediacontroller_play_pause" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:src="@drawable/paly_selector" android:background="@null"/> <RelativeLayout android:id="@+id/operation_volume_brightness" android:layout_width="150dp" android:layout_height="75dp" android:layout_centerInParent="true" android:background="@drawable/videobg" android:orientation="horizontal" android:padding="0dip" android:visibility="gone"> <ImageView android:id="@+id/operation_bg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:src="@drawable/video_volumn_bg"/> <TextView android:id="@+id/operation_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@+id/operation_bg" android:layout_centerHorizontal="true" android:layout_alignParentBottom ="true" android:text="32:22/45:00" android:textColor="#ffffff" android:textSize="10sp" android:visibility="gone" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_alignParentBottom="true" android:background="#77000000" android:layout_height="50dp"> <TextView android:id="@+id/mediacontroller_time_current" style="@style/MediaController_Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="15dp" android:text="33:33:33" /> <TextView android:id="@+id/mediacontroller_time_total" style="@style/MediaController_Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="15dp" android:text="33:33:33"/> <SeekBar android:id="@+id/mediacontroller_seekbar" style="@style/MediaController_SeekBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toLeftOf="@id/mediacontroller_time_total" android:layout_toRightOf="@id/mediacontroller_time_current" android:focusable="true" android:max="1000"/> </RelativeLayout> </RelativeLayout> </LinearLayout>
视频播放界面布局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <io.vov.vitamio.widget.CenterLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <io.vov.vitamio.widget.VideoView android:id="@+id/buffer" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerHorizontal="true" android:layout_centerVertical="true" /> </io.vov.vitamio.widget.CenterLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:orientation="horizontal"> <ProgressBar android:id="@+id/probar" style="?android:attr/progressBarStyleLarge" android:layout_width="50dp" android:layout_height="50dp" /> <TextView android:id="@+id/download_rate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:textColor="#FFFFFF" android:text="" /> <TextView android:id="@+id/load_rate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:textColor="#FFFFFF" android:text="" /> </LinearLayout> </RelativeLayout>
解决部分视频不能全屏播放
播放界面的布局如下,即可拉伸视频,主要不要嵌套vitamio自定义的CenterLayout布局<!--解决部分视频视频由于分辨率问题,不能全屏播放,在此处不要嵌套vitamio的CenterLayout布局--> <!--<io.vov.vitamio.widget.CenterLayout--> <!--android:layout_width="match_parent"--> <!--android:layout_height="match_parent"--> <!--android:orientation="vertical">--> <io.vov.vitamio.widget.VideoView android:id="@+id/buffer" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_alignParentRight="true" android:layout_alignParentBottom="true"/> <!--</io.vov.vitamio.widget.CenterLayout>--> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:orientation="horizontal"> <ProgressBar android:id="@+id/probar" style="?android:attr/progressBarStyleLarge" android:layout_width="50dp" android:layout_height="50dp"/> <TextView android:id="@+id/download_rate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="" android:textColor="#FFFFFF"/> <TextView android:id="@+id/load_rate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="" android:textColor="#FFFFFF"/> </LinearLayout> </RelativeLayout>
github地址
写的不好之处还望大家见谅。、
相关文章推荐
- 功能强大的 Vitamio 视频播放器的使用教程
- 功能强大的Vitamio视频播放器的使用教程
- 功能强大的Vitamio视频播放器的使用教程
- 功能强大的文件、文件夹比对工具-WinMerge使用教程
- XNA Game Studio是一套有着强大功能和简单界面的游戏制作平台,游戏开发商和游戏玩家都可以使用这套工具开发针对Windows XP以及Xbox360的游戏,XNA Game Studio分为两种版本,一种是面向初学者的EXPRESS版本,还有一种
- XNA Game Studio是一套有着强大功能和简单界面的游戏制作平台,游戏开发商和游戏玩家都可以使用这套工具开发针对Windows XP以及Xbox360的游戏,XNA Game Studio分为两种版本,一种是面向初学者的EXPRESS版本,还有一种是面向专业用户的专业版。用户使用EXPRESS版本开发游戏完全免费,并且可以随意在PC上发行,不过用该工具开发的游戏若是在360上网络发行,就需要交纳99美元的年费。
- 使用AJAX优化客户端——Iconix Pharmaceuticals使用AJAX和J2EE创建功能强大的瘦客户端的实践(译文)
- robocopy 功能强大的 命令行 模式备份软件的简体中文 使用说明
- 使用CMS方法开发功能强大的Web程序
- 16_传智播客Spring2.5视频教程_使用CGLIB实现AOP功能与AOP概念解释
- 功能强大的打包工具 NSIS,全部用脚本搞定 使用经验总结帖(持续更新中。。。)
- 专门针对功能强大的内核级后门设计的手杀工具 --icesword使用方法
- MonoRail学习笔记三:使用方便功能强大的routing功能
- 15_传智播客Spring2.5视频教程_使用JDK中的Proxy技术实现AOP功能
- 15_传智播客Spring2.5视频教程_使用JDK中的Proxy技术实现AOP功能 2
- webwork之--自定义ui theme,使用webwork强大的客户端校验功能
- 使用AJAX优化客户端——Iconix Pharmaceuticals使用AJAX和J2EE创建功能强大的瘦客户端的实践(译文)
- JAR 文件格式的强大功能(java中jar工具的使用)
- 黑客曝光!iPhone全功能破解使用详细教程
- DedeCMS专题功能使用详细教程